Flutter: First Impressions

Photo by  Joe Green  on  Unsplash

Photo by Joe Green on Unsplash

At Adapptor we like to stay across emerging cross-platform technologies. Flutter, Google’s answer to React Native, has been making waves recently, so we thought we’d take a look.

So what is it? Flutter, like React Native, is comprised of a runtime system, rendering engine, platform SDK and tooling. Unlike React Native, which runs on a Javascript engine, Flutter utilises  Dart, which is a statically compiled language with a Java-esque feel to it.

We will quickly run through an explanation of these parts, what they are and how they compare to React Native, before wrapping up with some first impressions of the technology and its viablitity as a cross-platform option.

In future posts we’ll dive a bit deeper into specific topics like native-interop and asynchronous programming in Flutter.

🖌 Runtime and Renderer

The Flutter runtime, like React Native, is written in C++ and hosts the Dart virtual machine. It interfaces with the underlying native platform and provides rendering, as well as common functionality like networking, I/O, file access etc.

Flutter uses Skia, an open-source rendering library, to draw all of its widgets onto a single app canvas. Skia is the rendering engine that powers popular web-browsers like Google Chrome and Mozilla Firefox. React Native, on the other hand, uses the native drawing capabilities of the platform to render its components. There are trade-offs to both approaches.

The advantage to Flutter’s approach is that you can be confident that widgets will render almost identically on both platforms. This means less tweaking of view styles and less Platform.OS === "android" ? /* hack */ : /* normal */ like code.

The downside is that you lose some of the look and feel of the native platform components. This is fine if your app has a highly branded look, but overall your apps will feel more Android than iOS.

Another downside is that it is difficult to integrate components that render natively (e.g. a map view). Luckily, the Flutter team have recently implemented the PlatformView to alleviate this.

Flutter gives you direct access to the drawing API, so you can even use it to make more general 2D programs, like games (see Flame).

🎯 Dart

From a distance Dart looks like Java. It has similar keywords, hanging curly braces and semi-colons. It will feel familiar, at least, to anyone with Java experience, which is almost everyone with an undergraduate degree in Computer Science. 

One might be disappointed by Dart at first. Maybe you expected modern features found in languages like Swift and Kotlin, like optional/null type safety. Don’t despair, Dart may surprise you with its depth. For example, it has an Actor-based concurrency system similar to Erlang (see Isolates), it supports mixins, has generic type reification and code generators.

One nice thing about Flutter is that the whole ecosystem is comprised of code written in a statically type-checked language (Dart, Objective-C, Swift, Java, Kotlin). In comparison to React Native, which is Javascript-based, you get the feeling of greater compile-time safety and in practice, you will see less undefined is not a function errors. 

Typescript or Flow do help mitigate this problem by providing better compile-time type-safety. Unfortunately, a lot of packages in the React Native are written in pure ES6 and a lot of problems do emerge at runtime.

📖 Platform SDK

In React Native you use JSX, a superset of HTML and Javascript, to write declarative view code. By comparison, Flutter widgets are constructed via parameterised constructor invocation:

// React Native (JSX)
return (
  <View>
    <Text></Text>
    <TouchableOpacity onPress=>
      <Text>Increment</Text>
    </TouchableOpacity>
  </View>
);

// Flutter
return Column(
  children: <Widget>[
    Text('$_count'),
    IconButton(icon: Icon(Icons.add), onPressed: _incrementCount)
  ],
);

It can be difficult to track the closing parentheses, so most IDEs (IntelliJ and VS Code) will insert a // Widgetcomment after each closing bracket to simulate the closing tags you would find in JSX.

You might have picked that Flutter has distinct widgets for layout like ColumnRowGridCenterFill etc. By comparison React Native allows you to set flex layout properties on most components via the style property. In practice I have found that this slightly increases the depth of the render tree but also makes it clearer, at a glance, how the layout works.

In Flutter there is a distinction between stateful and stateless widgets. You can think of this as comparable to a convention in the React community for separating components into Presentational and Container. There have been in-roads made introducing the Redux pattern into Flutter as well, but at this point the debug tools and third-party middleware is not as mature.

🔧 Tooling

The Flutter development experience feels leaner than React Native. You install the flutter command line program and then run flutter doctor to figure out anything you’re missing. This is a nice touch, because one of the nightmarish parts of cross-platform development is getting all of your dependencies installed and playing nicely.

In general, I found the flutter command-line experience to be more intuitive and streamlined than the react-nativeequivalent. Compile times and dependency download also seem faster. This is probably because React, and a lot of the other JS libraries, are large and require time to bundle together into a single JS blob.

Hot-reloading seems to actually work in Flutter and I haven’t once experienced my computer grinding to a halt when running the app for long periods. In supported IDEs (IntelliJ) you can use the stepping debugger to execute code line-by-line, drop and replay stack frames etc.

Visual Studio Code and IntelliJ are well supported by Flutter. If you use Emacs or Vim then your mileage will vary, although this is also true for React Native.

💥 Wrapping up

The outlook for Flutter looks good: it has strong tooling, more compile-time safety, a powerful runtime and rendering engine. 

One area I haven’t looked at yet is how easy it is to interop with the underlying native platform. A cursory look at the documentation reveals a promising API and good language support (Swift and Kotlin). I haven’t looked into its limitations. For example, threading support, latency passing data back and forth across the native bridge etc.

Stay posted for more articles about Flutter and other cross-platform technologies. Sayonara! 👋

Elliot Bulmer