All Ruby on Rails Node JS Android iOS React Native Frontend Flutter QA

How to Create App in Flutter For Web

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 plans to also target Flutter for the web. During Flutter live 2018 they 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 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 common both for mobile and web. The difference lays deeper - 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 technology, 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 able to provide 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.
  • 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 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 globally enable web support
  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 some UI. Netguru’s landing page was my first bet. I wanted to have a page that looks good both on desktops 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. 

Experimenting with Flutter for Web

The implementation of UIs on the web is identical as on mobile except a couple 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 things that  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 is the project structured, what changed in last release of Flutter for web and how to achieve the proper UI for different screen sizes.

Project structure and entry point

The project structure for web and for mobile is mostly the same except a new web directory which contains all the configuration and entry point for the web (equivalent to ios and android directories). The web directory by default contain a single file - 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 app initialisation.

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 user that parts of the page are not implemented. I wanted to show the native Android / iOS dialog on mobile and simple browser alert on the web. To create such functionality I needed to call JS function from Dart code. 

On Android or iOS to call some native methods you need to create 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 does. First of all, Dart communicates with JS through 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 indicate if 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 the web everything worked as intended (after I clicked some button, the alert has shown). But when I tried to compile it to the mobile app it gave me 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 regular import i added this import.

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

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

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 separate repository. This was good as 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 main mobile repository. Google made a big step forward merging the main repository with the forked one. During last year they made a lot of improvements. Below you can find couple of examples.

 

Custom images

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

Now: You can use images from your app 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 the web

Now: You can use custom fonts exactly like on mobile

 

Selectable texts

In the past: Texts were not selectable (like on mobile native app)

Now: There is a new widget that allows to select and copy text

 

Text editing

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

Now: You can select, copy, paste without issues

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

Future of Flutter for web

You need to keep in mind that the current state of Flutter for web is just a technical preview. Before stable release, many things will change, especially in terms of stability and UI. Flutter for web is still under development and Google aims to make it better. Here are some things that will be improved in the following releases:

  • Fast, jank-free 60 frames-per-second performance for interactive graphics.
  • Consistent behavior and visuals with Flutter on other platforms.
  • High-productivity developer tooling that integrates 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 experiences.
  • Support for plugins. For features like location, camera, and file access, they hope 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 especially for the web. That’s why I feel like Flutter won’t stand out against these technologies. Anyway, I’m a big Flutter fan, so I’m keeping fingers crossed for it.


Photo by Randall Ruiz on Unsplash

New call-to-action
Looking for new opportunities? Check our offers!
READ ALSO FROM Web Development
Read also
Need a successful project?
Estimate project or contact us