Build cross-platform mobile apps with Google's Flutter

There have been numerous cross-platform mobile frameworks over the years, with steady improvements to developer experience and performance throughout. Google's Flutter is a new addition to the pack, and is particularly interesting because rather than using a JavaScript 'bridge' as many frameworks (such as React Native) do, it compiles to true native code. 

You can find out a bit about how it works in the introductory video below, or read on for a step-by-step guide to getting started with Flutter. For more advice, take a look at our collection of tutorials explaining how to build an app. Or, if you're building a site, choose a website builder and web hosting service from our guides. Not happy with your storage? Here are some cloud storage options.

In this tutorial, we'll look at how to set up Flutter and explore the basics of building apps using the framework, using macOS for our development. We'll assume that you've programmed before and know how to use the Bash shell, but might not have developed for mobile.

01. Get set up

You'll need to download and install a few things before you can make use of Flutter:

  • Xcode for iOS support
  • Android Studio for Android support
  • Android SDK (download via Android Studio once you create a new project)

Once you have all of these, run Xcode and Android Studio, install the additional components they offer and create a new project to make sure they run.

Now you're ready to get started with Flutter. Download the Flutter SDK. Extract it to wherever you'd like it to be installed. We need to update our path so macOS can find Flutter wherever you invoke it. Open (or create) $HOME/.bash_profile:

sudo atom $HOME/.bash_profile

Now add the flutter/bin directory to your $PATH:

export PATH=/Users/simon/dev/flutter/bin:$PATH

Run a shell command to refresh the bash profile:

source $HOME/.bash_profile

Now we can start using the Flutter CLI. The first thing to do is make sure you have the latest version:

flutter upgrade

02. Fix any problems 

Once that's done, Flutter offers a handy diagnostic tool, which will check whether everything that you need is installed and set up correctly. It is really useful:

flutter doctor

Take note of what the doctor tells you and respond to any issues that arise. You might have to run the doctor a couple of times to gain additional feedback and act on it. 

Once the tool is happy with Flutter, Android Toolchain, iOS Toolchain and Android Studio, you are good to go. Don't worry about connected devices for now.

Xcode can require some extra steps at the shell to get it fully set up:

sudo xcode-select --switch /Applications/
brew install --HEAD libimobiledevice
brew install ideviceinstaller
brew install cocoapods
pod setup

And Android Studio often needs the licence agreement accepting:

flutter doctor --android-licenses

03. Find an IDE and start a new project

Now Flutter is set up, you'll want an IDE to work in. There are plugins available for Android Studio and IntelliJ. We're going to go with VS Code, which also has its own Flutter plugin.

Start up VS Code and click View > Command Palette. Type 'install' and select the Extensions: Install Extension action. Search for 'flutter' and install the Flutter plugin. Once done, restart VS Code.

We're now ready to begin developing. Start a new project through the Command Palette by selecting the Flutter: New Project option. Name your project, select where to save it and VS Code will invoke Flutter to generate the new project.

04. Set up virtual devices

To test our project, we'll want a device simulator to allow us on the desktop. In the bottom right-hand corner of VS Code (in the blue bar), you'll see something saying No Devices. If you've developed for mobile before, when you click this, you'll see your simulators available in the Command Palette. 

If you have no simulators, run the following in the terminal to open the iOS simulator for the first time:

open -a simulator

You should then see an iOS simulator available when you restart VS Code.

Android is more complex. Load Android Studio and within an Android project, click Tools > Android > AVD Manager. Select Create Virtual Device.

Select the device to emulate – for example a Google Pixel 2. Click Next and you can also choose a system image (i.e. OS version) to download. On the next page under Emulated Performance, select Hardware – GLES 2.0 to enable hardware graphics acceleration on the emulator. Finish the process.

Once you've created the virtual device in Android Studio, restart VS Code and you'll see your Android emulator appear alongside the iOS simulator in VS Code's device list.

Now, if you click No Devices, you can select an iOS or Android device and a virtual phone will fire up on your desktop. Try it out with an iPhone X simulator.

04. Test the starter app

Now the simulator is running, you can test out the starter app. Hit F5 or click Debug > Start Debugging. The app will load on the simulator and you can try interacting with it. Be patient if it doesn't happen immediately, as it can take some time to build.

Before we can do the same on Android, we need to install gradle dependencies for our project (a small nuance of Flutter right now). Navigate to the project root directory and run:


Then you can open an Android emulator and run the project in Debug mode as you did for iOS.

A great feature of Flutter is that it supports 'hot reloading' – that is, you can modify your source and see the changes reflected in the simulator right away. In main.dart, let's make a couple of changes to the MyApp class while the iPhone X simulator runs:

home: new MyHomePage(title: 'Recipe Book'),=

You should see the changes take effect as soon as you save.

06. Explore Flutter's widget system

What we haven't yet talked about is the Dart programming language that Flutter uses. Dart is an object-oriented language with C-style syntax, developed by Google and all Flutter development uses it. If you've already got some experience of web or mobile development, then it shouldn't be totally alien to you.

Everything in Flutter is based on widgets, which are the building blocks of an app. If you've used React before, Flutter's approach is very similar and widgets are analogous to components. Essentially your entire app can be decomposed into a hierarchy of widgets.

This is easily seen within the MyApp class. MyApp is a StatelessWidget (this means it's immutable). The build() method we are overriding tells Flutter how the widget should be rendered. This is similar to React's render() function. 

The method returns a MaterialApp widget, which represents an app that uses Google's Material Design. This in turn has several properties, which are themselves widgets: ThemeData defines the visual style to be used and MyHomePage is a custom widget defined further down the main.dart file that contains the body of the app.

07. Edit your content

Unlike the stateless MyApp, MyHomePage is a stateful widget. This means its behaviour is defined by the _MyHomePageState class, enabling it to store information and change accordingly, such as when you press the button on the app. 

You can see it's currently using many built-in widgets Flutter provides to handle common things like layout, buttons and text display. Let's make some changes to that class to modify what our app presents.

class _MyHomePageState extends State<MyHomePage> {
 Widget build(BuildContext context) {
   return new Scaffold(
     appBar: new AppBar(
       title: new Text(widget.title),
     body: new Container(
       decoration: new BoxDecoration(color: Colors.brown[100]),
       child: new RecipeWidget()

We've disposed of the existing content and replaced it with a brown Container widget but we also need to create a custom widget, RecipeWidget, that will be put in the container.

class RecipeWidget extends StatelessWidget {
 Widget build(BuildContext context) {
   List<Widget> widgets = new List<Widget>();
   return new ListView(children: widgets);

08. Load some assets

Next, let's load some assets with the app so we can add them to the ListView widget within RecipeWidget. Static assets for an app are specified in pubspec.yaml under the 'flutter' section:

   - img/breakfast.jpg
   - img/curry.jpg
   - img/pasta.jpg

We'll also create a simple data structure in main.dart, pairing images with strings to act as a thumbnail and summary for recipes.

var recipes = {
   'breakfast.jpg': 'Start the day right with this nutritious breakfast.',
   'pasta.jpg': 'Wow your friends by making your own fresh pasta.',
   'curry.jpg': 'Show off your culinary skills with a rich curry.',

09. Finish up

Finally, let's update RecipeWidget to build a list of widgets displaying the recipes. We use Image.asset to load the static assets we included in pubspec.yaml.

recipes.forEach((thumb,caption) => widgets.add(
       new Container(child: 
         new Padding(padding: 
           new EdgeInsets.all(16.0), child:
             new ListTile(
               leading: Image.asset('img/' + thumb, width:80.0),
               title: Text(caption)
             new BoxDecoration(
                 border: new Border(
                     bottom: new BorderSide(color: Colors.brown)
                 color: Colors.brown[200]

Hopefully you're starting to get a feel for how Flutter uses widgets to construct apps. Try using the simulator to rotate the device. The Flutter layout automatically accommodates the changes. For comparison, try the app on the Android emulator. 

This article was originally published in net, the world's best-selling magazine for web designers and developers. Buy issue 310 or subscribe.

Read more:

Thank you for reading 5 articles this month* Join now for unlimited access

Enjoy your first month for just £1 / $1 / €1

*Read 5 free articles per month without a subscription

Join now for unlimited access

Try first month for just £1 / $1 / €1

Simon Jones

Simon Jones is a technology leader with experience managing product and engineering teams of up to 200 staff, both at startups and large corporates. He is a proficient full-stack developer, in particular with Go & React + TypeScript. He now works at Facebook in London. Formerly CTO at Norwegian VC-backed startup Just, and Engineering Director at Barclays and American Express.