Skip to content

Getting Started with Flutter

Another thing that came out with a bang in I/O this year is Flutter with its 1.5 release. With all the noise surrounding it, I decided to go check it out.

So Flutter seems to be a “Portable UI toolkit for building beautiful native-compiled apps for mobile, web, and desktop from a single codebase”. Doesn’t this sound familiar…

Mobile development is getting even more insane by the year with how many different ways we can do about it. I might look into comparing Flutter and React Native pretty soon…. let’s just not talk about Xamarin for meantime.

For this article, this isn’t a tutorial of my own making. I’m gonna be following the whole Getting Started bit on Flutter.io. This is more of me going through it, letting you guys know the steps along the way, and just me adding some of my own observations and comments.

Installation

Installing Flutter is pretty straightforward. I don’t think I can put it better than their own installation and setup page.

Since this is mostly an Android site, I’m going to be using Android Studio for this one. If you have a different IDE of choice, don’t worry, Flutter is supported in quite a few, just head to the above page for how to do it.

Once that’s done, you should have also installed the Flutter and Dart plugins for Android Studio. This exposes a new option for creating new projects saying “Create New Flutter Project”. This is where it all begins.

Test Drive

Before getting my hands dirty with the code, I’m just going to open my emulator and see what we got in our default template that Android Studio generated.

It’s a pretty nice starter, a bit more to play around with compared to the classic Hello World.

Hot Reload

So a shining feature of Flutter is to see updates pretty much instantly as you code instead of having to build your app every time. This is Hot Reload and it can trigger within 2 seconds of smashing that Ctrl+S command.

I tried it out by changing the primary theme color of the app

primarySwatch: Colors.blue -> green

And I have to say, it works beautifully. I don’t even lose my state, my counter is still on 2!

Writing a Flutter App

For this, I’ve been following the Codelab on Flutter.io. The first step is to replace the whole lib/main.dart file with this code.

Looks like this is the central piece of the app’s code. Another thing to notice is everything here is a Widget as far as UI goes. The views are widgets, the layouts are widgets, even the ‘app’ itself is a widget. There is a Scaffold widget which holds the full widget tree.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(
          child: Text('Hello World'),
        ),
      ),
    );
  }
}

The next step was to install an external package, english_words into the pubsbec.yaml file.

While the pubspec file is open, Android Studio presents a few commands on top. Click ‘Packages get’ to load in the package we just added.

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^0.1.2
+ english_words: ^3.1.5

Back to main.dart, add the import statement for the english_words package on top of the file, then right under Widget build, create a WordPair object. It’s a slight disappointment for me to see we can’t just yoink in the WordPair and let Studio import the package automatically. I hope they add that in later.

import 'package:english_words/english_words.dart';
...

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();
    ...

        body: Center(
          child: Text(wordPair.asPascalCase),
        ),
    ...
  }
}

Next, change the child: Text into the wordPair, transforming it into Pascal Case for aesthetics then we got a random pair of words showing on screen (remember to use Hot Reload).

Stateless vs Stateful Widgets

The Text with the WordPair we created just now belongs to the MyApp widget which is Stateless. It’s immutable once drawn and won’t change during it’s lifetime. It’s static. We’re going to create a list that infinitely keeps generating random WordPairs as the user scrolls. Since this isn’t static at all, we’re going to make it through a Stateful widget.

Creating the Stateful Widget

To create a Stateful Widget, we need 2 things: 1) a State class, and 2) a Stateful Widget class that creates the said State class.

Start by adding this code for the State class at the bottom of main.dart. Notice how it returns with an error telling us we need a build method. On to that later.

class RandomWordsState extends State<RandomWords> {
  // TODO Add build() method
}

Now below that, we add this Stateful Widget class. Notice how it creates the previous State class.

class RandomWords extends StatefulWidget {
  @override
  RandomWordsState createState() => RandomWordsState();
}

Back to the State class, let’s add the build method. As you’d expect, build() returns a Widget and that’s our pair of Random Words!

class RandomWordsState extends State<RandomWords> {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();
    return Text(wordPair.asPascalCase);
  }
}

Now we’re going back to our MyApp widget and removing the WordPair code we put there earlier and replacing the child: Text() with child: RandomWords(), the Stateful Widget we created earlier.

body: Center(
  child: RandomWords(),
),

Hot Reload the app and we got ourselves what looks like pretty much the same result… but it’s Stateful!

Creating the Infinite Scroll List View

We’ll be working in the RandomWordsState from now on. Add the _suggestions and _biggerFonts as shown above (the _ before the variable is Dart’s way of saying private).

class RandomWordsState extends State<RandomWords> {

  final _suggestions = <WordPair>[];
  final _biggerFont = const TextStyle(fontSize: 18.0);

  ...
}

Then add the Widget _buildSuggestions where we build our ListView. To break things down, the itemBuilder callback is called once per item pairing. For odd items, we return a divider. For even items, we grab a WordPair from the _suggestions list above.

class RandomWordsState extends State<RandomWords> {
  ...

  Widget _buildSuggestions() {
    return ListView.builder(
        padding: const EdgeInsets.all(16.0),
        itemBuilder: (context, i) {
          if (i.isOdd) return Divider();
  
          final index = i ~/ 2;
          if (index >= _suggestions.length) {
            _suggestions.addAll(generateWordPairs().take(10));
          }
          return _buildRow(_suggestions[index]);
        });
  }
}

Note that these suggestions are generated in packs of 10 for good performance. The i~/ 2 is a division that returns an integer result. We need these because every other item is a divider and we want this index to reflect the number of WordPairs in the list. So for every 10 WordPairs displayed, we generate 10 more.

Now _buildRow isn’t defined yet. Let’s do that. Add it below _buildSuggestions.

Widget _buildRow(WordPair pair) {
  return ListTile(
    title: Text(
      pair.asPascalCase,
      style: _biggerFont,
    ),
  );
}

Then back to the build function of RandomWordsState, we’ll replace the WordPair generation stuff with a Scaffold which defines our app’s visual layout.

We will in fact use this to replace the Scaffold in the MyApp class.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Startup Name Generator',
      home: RandomWords(),
    );
  }
}

Hot Reload once more and see your finished app in all its beauty!

Do I hear my next company being called SadTea?

One thing I noticed while using Flutter is that the app doesn’t stick around in my phone after I’m done with it which is nice. I tend to make lots of small apps I test on my physical device and then forget about them while they clutter my app drawer.

While we did load the WordPairs quite lazily, you might notice the performance is still a bit crap. Fret not, you’re in debug mode. It lets you do all that fancy Hot Reload stuff at the cost of a bit of performance. Terminate that and run your app via this command in the terminal instead.

flutter run --profile

What you’ll find is that the app runs much more smoothly and the terminal even gives you a link where you can observe your performance graphically! How cool is that?

Conclusion

Flutter seems pretty attractive to me. While it’s nowhere near the first to do it, I like how we can easily write layouts in the main codebase itself. ListViews are easy to make (a huge plus coming from an Android Dev), it’s got many development perks, multi-platform coding is always amazing, and it’s quite streamlined as well.

I do have a couple gripes with it, the need for semicolons is a minor one coming from someone who got so used to Kotlin but my bigger gripe like I mentioned earlier is how import statements aren’t automatically generated as we write, but I believe that’s something that could be fixed in the future.

Tags: