How to Create App in Flutter For Web

Photo of Szymon Nitecki

Szymon Nitecki

Oct 4, 2019 • 15 min read
randall-ruiz-MWnsCV-QUC0-unsplash

When I first heard about Flutter I thought - great… another mobile app framework.

After several months I saw that the popularity of Flutter increased, and I was really curious why. I started a playground project and really fell in love with the simplicity of creating mobile apps in Flutter. But it wasn’t enough for me.

I heard that Flutter apps can run in the browser. I thought: now I can be a frontend developer without knowing JS, yay! I started playing around and wanted to create something nice. My goal was to create a part of Netguru’s landing page. I want to share with you my first experience with Flutter for the web and how it differs from working with Flutter on mobile.

A bit of background

Flutter in a stable version has been with us since December 2018. Since then a lot of improvements have been made to Flutter, but also many new ideas came up and are under development. As I said earlier, Flutter mainly targets mobile devices, so most effort is put into support for Android and iOS devices. Despite the fact that Flutter is a mobile-first technology, technically it’s possible to run Flutter apps almost everywhere.

From the beginning, it was known that Google was also planning target Flutter for the web. During Flutter live 2018, the company announced the Hummingbird project, which is a place to run Flutter apps in the browser. One year later, during Google I/O 2019, they announced a technical preview release for developers. Recently, in Flutter 1.9, web support has been merged into the main Flutter repository, which means your app written in Flutter for mobile can be launched on the web without any changes. So what has changed during the transformation? This is something we’ll focus on today.

Technical overview

Before we move on to the implementation details, let’s see how it is possible that Flutter, which was designed for mobile apps, can be launched in the browser. Flutter consists of two high-level components: the engine (written in C++ and/or JS) and the framework (written in Dart). The implementation of the framework is shared by both mobile and web. The difference lays deeper – it rests in the engine. On mobile, the Flutter engine uses Skia (a multi-platform rendering library), C++, Dart and parts of platform-specific code for Android (Java) and iOS (Objective-C). As the web version is based on different technologies, the engine itself is implemented using other tools.

Experimenting with Flutter for Web

As you can see from the picture above, Flutter for web uses different technologies to render its content in the browser. Adding web support involved implementing Flutter’s core drawing layer on top of standard browser APIs. Using a combination of DOM, Canvas, and CSS, Google was provided a portable, high-quality, and performant user experience across modern browsers. They implemented this core drawing layer in Dart and used Dart’s optimized JavaScript compiler to compile the Flutter core and framework along with applications into a single, minified source file that can be deployed to any web server.

As Flutter for web is not yet a production-ready tool, you need to take into account several things:

  • The Flutter API is exactly the same for mobile and web, but some features are still not implemented in the web version.
  • The default interactions with the browser are still under development, so a web page created with Flutter may feel more like a mobile app than a real web app.
  • Currently, only Chrome is supported.

Getting started

To add web support for an existing app, some steps needs to be done. I won’t go through the installation of the Flutter plugin and IDE and will head directly to the things necessary to build a web app. If you want to read more about installing Flutter itself, I recommend the official documentation. Below, I listed steps needed for running Flutter on the web:

  1. Call $ flutter channel master to switch to master branch of flutter
  2. Upgrade your Flutter version to 1.9 by calling $ flutter upgrade
  3. Call $ flutter config --enable-web to enable web support globally
  4. Go to your project directory
  5. Call $ flutter create . to create web part of Flutter project
  6. Call $ flutter run -d chrome and your app will show in the browser
  7. If you want to hot reload your project just hit R

If you are a beginner, I encourage you to check out the official examples in this repository.

My first web page

I started playing around and wanted to implement a UI. Netguru’s landing page was my first bet. I wanted to have a page that looks equally good on desktop and mobile, so I implemented different UIs for different screen sizes. What I also wanted to achieve is similarity, so I used a custom font along with some web page assets. Below you can see the final effect.

The implementation of UIs on the web is identical as on mobile except for a couple of additional plugins that allow interactions with the browser. I won’t discuss each line of code, but I would like to share with you some of the most interesting parts (and differences from mobile). If you would like to see the whole source here is a link to the repository. I also published the site, so you can check it out. So let's see how the project is structured, what changed in the latest release of Flutter for web, and how to achieve a proper UI for different screen sizes.

Project structure and entry point

The project structure for web and for mobile is mostly the same apart from a new web directory, which contains all the configuration and entry points for the web (equivalent to the ios and android directories). The web directory contains a single file by default: index.html. There you can set up a name and basic data of your page. It also contains a reference to the main.dart.js file generated by the compiler, which is responsible for initialising the app.

Different screen sizes

As you can see above, the pager is a shared element on small and big devices. The only difference is the menu and the header (which changes into a hamburger button on small screens). So how to achieve such an effect?

First of all, I declared a helper function that allows me to check if the screen width is big enough to fit large content. In this case, “enough” is 1100 pixels.

isLarge(BuildContext context) => MediaQuery.of(context).size.width >= 1100;

Now, thanks to collection-if introduced in Dart 2.3, I could add widgets based on screen size. Check out the snippet below.

Column(
	children: <Widget>[
  		if (isLarge(context))
			HeaderLarge()
        	else
            	HeaderSmall(),
        	if (isLarge(context))
            	MenuLarge()
   	 ],
  )

When the screen is large, I use HeaderLarge; otherwise I use HeaderSmall. The same goes for the menu – I add the menu only if it is a large screen.

Calling JavaScript methods from Dart

I only created the first page of Netguru's landing page, so I wanted to somehow inform the user that other parts of the page are not implemented. I wanted to show the native Android / iOS dialogue on mobile and a simple browser alert on web. To create such functionality, I needed to call a JS function from the Dart code.

On Android or iOS, to call some native methods you need to create a special channel that allows calling native functions from Dart code.

As you could read earlier, Dart is compiled to JS and HTML, so interoperability between Dart and JS should be easy... and it is. First of all, Dart communicates with JS through the dart:js package which provides a low-level API for JS. For most use cases, it's more convenient to use the js package, which a wrapper of dart:js, as it provides annotations and is easier to use.

Inside a web directory in index.html I added a simple JS function that show the alert with given text.

<script>
function myCustomAlert(text) {
alert(text);
}
</script>

Next, I created the js.dart file which is a bridge between JS and Dart. As an annotation argument I passed the JS function name. Thanks to annotations Dart2Js compiler knows which methods should be called.

@JS()
library main;

import 'package:js/js.dart';

@JS('myCustomAlert')
external void alert(dynamic text);

I imported the file regular way by calling

import 'js.dart';

And created method for showing the alert. Take a look at the kIsWeb constant that indicates if an app is running on the web.

void _showNotImplementedAlert(BuildContext buildContext) {
if (kIsWeb){
alert("This feature is not implemented yet!");
} else {
// mobile implementation
}
}

And here is a funny thing. When I ran the app on web, everything worked as intended (after I clicked some button, the alert has shown). But when I tried to compile it to a mobile app, it gave me an error that dart:js is not found. Well, that makes sense as JS is not supported on mobile devices.

The solution was not so obvious to me. Dart supports conditional imports, so instead of a regular import, I added this import.

import 'js_no.dart'
if (dart.library.js) 'js.dart';

The bad thing about this solution is that I also needed to create a separate js_no.dart, which contains the same function that js.dart did.

In your script, you can add any JS script that you need. My example was just to show how to interoperate with it from Dart.

What changed in Flutter 1.9

At the beginning, Flutter for web was developed in a separate repository. This was good, as the main repository was stable, but it also introduced some limitations – it was not possible to use all Flutter plugins because they were only accessible for the main mobile repository. Google made a big step forward by merging the main repository with the forked one. Over the past year, they made a lot of improvements. Below you can find a couple of examples.

Custom images

In the past: To have some custom images you had to keep them in the web directory and use them as a network image

Now: You can use images from your app’s assets without any restrictions

Fonts

In the past: To use custom fonts you needed to create some additional configuration file to make it work on web

Now: You can use custom fonts exactly like on mobile

Selectable text

In the past: Text was not selectable (like on a mobile native app)

Now: There is a new widget that enables selecting and copying text

Text editing

In the past: There were problems with text selection, typing, copying, etc.

Now: You can select, copy, and paste without issues.

Of course, the list is much longer, but the above are the most annoying ones in my opinion.

The future of Flutter for web

You need to keep in mind that the current state of Flutter for web is just a technical preview. By the time we get a stable release, many things will change, especially in terms of stability and UI. Flutter for web is still under development, and Google is working towards making it better. Below are some things that will be improved in the future releases.

  • Fast, jank-free 60 frames-per-second performance for interactive graphics.
  • Behaviour and visuals consistent with Flutter on other platforms.
  • High-productivity developer tooling that integrates well with existing development patterns.
  • Support for core web features across all modern browsers.
  • Support for mouse scrolling, hover, and focus – features that are not required in mobile.
  • Support for plugins. For features like location, camera, and file access – they are hoping to bridge mobile and the web with a single API.
  • Out-of-the-box support for technologies like Progressive Web Apps.
  • Unifying web development tooling under the existing Flutter CLI and IDE integration.
  • Debugging web applications using DevTools.
  • Improved performance, browser support, and accessibility.

In my opinion, Flutter for web in its current state is good for experimenting. In the future, I see it as a good alternative to web development that has a complex UI. On the other hand, there are a lot of technologies that are already there and are crafted specifically for the web. That’s why I am afraid that Flutter won’t stand out from these technologies. Anyway, I’m a big Flutter fan, so I’m keeping fingers crossed for it.


Photo by Randall Ruiz on Unsplash

Photo of Szymon Nitecki

More posts by this author

Szymon Nitecki

Szymon is studying at Cracow University of Economics. He started to improve his programming skills...
How to build products fast?  We've just answered the question in our Digital Acceleration Editorial  Sign up to get access

We're Netguru!

At Netguru we specialize in designing, building, shipping and scaling beautiful, usable products with blazing-fast efficiency
Let's talk business!

Trusted by:

  • Vector-5
  • Babbel logo
  • Merc logo
  • Ikea logo
  • Volkswagen logo
  • UBS_Home