Exploring Flutter for Cross-platform Mobile Development

Published on

At my work, we have a weekly meeting for mobile developers where a presentation is given by a different developer each week. When my turn came around recently, I decided to spend some time researching Flutter, which is a cross-platform framework from Google. Being an Android developer, I’ve always been interested in cross-platform frameworks and the potential benefits they could provide, but I don’t often hear good things about them from coworkers and others in the Android development community. When I explored Flutter, I found a framework that is surprisingly good, albeit early in development.

To be clear, Flutter has been in a tech preview phase for a while and only hit alpha on May 15, 2017 1, or 11 days ago at the time of this writing. Because of this, it’s important to understand that Flutter still has a long way to go in the way of libraries and general improvements to the framework. With that being said, now that Flutter is in alpha I’m sure the Flutter developers want people to try the framework and report any bugs they come across or suggestions they have.

Easy Setup

Setting up a development environment for Flutter was incredibly straight-forward for me. The installation instructions are easy to follow, and the Flutter CLI includes a doctor command, which checks your system for Flutter dependencies and tells you exactly what you need to do to install missing dependencies.

IntelliJ IDEA seems to be the primary editor for Flutter apps, as the Flutter team maintains a plugin for it. My experience with Flutter development in IntelliJ has been very good. The plugin worked flawlessly, providing autocompletion, code formatting, and giving you an easy way to run Flutter apps on both iOS and Android emulators and physical devices.

Dart

Before actually using Flutter, I had to take a little detour and learn about the Dart programming language. Flutter apps are written in Dart, which gets ahead-of-time (AOT) compiled to machine code, meaning no interpreter is involved when the app runs on a device.

Initially, I wasn’t really sure what to think about the Dart language. It seems like a combination of Java and JavaScript (ECMAScript), which makes sense because Google was originally pushing it as a web language that would eventually have support in Chrome. Dart has a lot of features that you would see in ES2015 to ES2017, as well as optional typing, abstract classes and interfaces, generics, isolates (similar to green threads?), and more.

After spending a little bit of time writing some Dart code, I found it to be a pretty easy language to use. It’s expressive, faster to write and understand than Java, and it just stays out of your way.

Rendering and Performance

One of the more interesting facets of Flutter is that it doesn’t use the built-in UI widgets from either mobile platform. Instead, Flutter uses a 2D graphics library, called skia, which serves as the rendering engine and draws Flutter UI widgets directly to the screen, bypassing the native UI.

By rendering the UI itself and using some of its own special sauce during the layout, painting, and compositing steps, Flutter apps are able to achieve a consistent 60 frames per second. I found the below video to be pretty informative about how Flutter is able to achieve its performance goals.

Though Flutter doesn’t use the native UI widgets, it’s a cross-platform framework and has widgets for both Android and iOS. From what I could tell, being that Flutter is a Google invention, there seemed to be more Material (Android) widgets than “Cupertino” (iOS) widgets, but it does look like they’re actively developing new Cupertino widgets.

When I tried a couple of Flutter apps on both platforms, they were mostly great. On Android, I found that the performance was better than apps built with the native Android framework. If there were dropped frames anywhere, I sure didn’t see them. On iOS, the biggest thing that stuck out to me was the scrolling. It didn’t quite feel right because of the physics, but I’m sure that would just require a little tweaking by the Flutter team.

There were two talks about Flutter at Google I/O 2017 a few days ago, and the talk below shows how easy it is to make an app for both platforms.

Widgets and State

In Flutter, everything is a “widget” and widgets are composable. The way I understand this concept is that a widget can primarily do a couple of things:

  • Create a piece of UI, like a screen, button, or a paragraph of text.
  • Wrap another widget to modify how it looks or works, such as
    • applying styles, like colors, fonts, etc.
    • creating padding, margin, or layouts.
    • defining input or gesture recognition.

There are two main types of widgets: StatelessWidget and StatefulWidget. A StatelessWidget, just as it sounds, doesn’t have any state and defines a build method that describes how the widget appears.

class RedSquare extends StatelessWidget {
  final Widget child;

  RedSquare({ Key key, this.child }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return new Container(
      decoration: new BoxDecoration(
        color: new Color(0xFFFF0000),
        shape: BoxShape.rectangle,
      ),
      child: child,
    );
  }
}

A StatefulWidget, on the other hand, keeps track of its state through a separate class that extends the State class. The State class is actually what contains the build method for a StatefulWidget, basically describing how the state should appear.

class FizzBuzzScreen extends StatefulWidget {
  FizzBuzzScreen({Key key}) : super(key: key);

  @override
  _FizzBuzzScreenState createState() => new _FizzBuzzScreenState();
}

class _FizzBuzzScreenState extends State<FizzBuzzScreen> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  String _getFizzBuzz() {
    String fizzBuzz = '';

    if (_counter % 3 == 0) {
      fizzBuzz += 'Fizz';
    }

    if (_counter % 5 == 0) {
      fizzBuzz += 'Buzz';
    }

    return fizzBuzz.length == 0 ? _counter.toString() : fizzBuzz;
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Center(
        child: new Text('($_counter): ${_getFizzBuzz()}'),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _incrementCounter,
        child: new Icon(Icons.add),
      ),
    );
  }
}

In the example above, we have a screen that displays text for a FizzBuzz counter, using an interpolated string that calls the _getFizzBuzz method to determine what text to show, and a Floating Action Button (FAB) that calls the _incrementCounter method when pressed. The key to getting the widget, a screen in this case, to update for the user is found in the _incrementCounter method. Inside that method, we call setState, which is defined on the State class. We pass an anonymous function to setState and mutate the state as needed within that function. setState will then take care of rebuilding your widget, which will then display the updates to the user.

If you’re familiar with the concept of Flux, you might recognize that this is somewhat similar. You have a one-way data flow, and the UI reacts to changes in the state. I found it to be very refreshing that Flutter chose and enforces this sort of pattern.

Hot Reload

Long build times are something that mobile developers experience on a daily basis. One of the great things about Flutter is the inclusion of hot reload. Hot reload allows you to update the source of the app on the fly without having to restart it.

In my experience, hot reload for a simple app with a few screens was incredibly fast. The time between clicking the hot reload button in IntelliJ and seeing the UI update in my emulators was about half a second (according to the Flutter logs and my eyes). The experience is very similar to what you get with a common tool front-end developers use called LiveReload.

Even better, hot reload doesn’t clear the state of your widgets. If you change a piece of UI and use hot reload, all of the information that the widget previously had is still there. You don’t have to start from the home screen of your app every time you update the UI!

Flutter's Hot Reload

(Image from the Getting Started Guide)

My Verdict

I was very surprised to find such a solid framework in Flutter. Overall, Flutter looks like a very promising cross-platform framework and I’m excited to see where it goes. I will absolutely be keeping up with future developments of the framework and I might even start contributing libraries! I definitely encourage you to try out Flutter for yourself.

Have you used Flutter? What did you think? Send me a tweet!


Verify the signed markdown version of this article:

curl https://keybase.io/sethlopez/key.asc | gpg --import && \
curl https://sethlopez.me/article/exploring-flutter-for-cross-platform-mobile-development/ | gpg