Affiliate links on Android Authority may earn us a commission. Learn more.
Google Flutter - what is it, and how to use it for cross-platform app creation
Read Next: Cross-platform mobile development — challenges, options, and why you should consider it
Porting your Android app to iOS requires time and effort. Along the way you may start to wonder whether this is really the best use of your time — wouldn’t it make more sense to concentrate on polishing the work you’ve done so far, rather than simply re-creating it on another platform? Ideally, you’d be able to cut your development time by creating a single app that can run across multiple platforms.
Google Flutter is a user interface (UI) toolkit that promises to do exactly that, giving you a way to develop UIs for Android and iOS from a single codebase.
By the end of this article you’ll have added Flutter support to your Android Studio installation, explored some of its core features, and created a “Hello World” application, written entirely in Google Flutter.
Why should I use Google Flutter?
Google Flutter isn’t based on Kotlin or Java. If you want to develop for it, you’ll need to learn Dart.
While the thought of learning yet another programming language may be off putting, there are some pretty compelling reasons to give Flutter a try:
- Cross-platform: By using the same renderer, framework, and libraries, Flutter lets you create a single UI to run on both Android and iOS. To ensure your application feels equally at home on either platform, Flutter provides widgets styled according to Cupertino (iOS) and Material Design (Android) guidelines, plus a number of Flutter packages that give you access to some platform-specific services and APIs.
- Compatible with other programming languages: Flutter integrates with Java code on Android, and ObjectiveC and Swift on iOS, so you don’t have to completely rewrite your existing applications to start using Flutter.
- Hot reload. It’s common practice to work on your application’s code while the application itself is running on an Android device to test changes as you make them. The time it takes Android Studio to push each set of changes to the running application can really eat into your development time. To help you be more productive, Flutter has a “hot reload” feature that can inject updated source code into a running Dart Virtual Machine (VM). Using hot reload, you’ll typically see the results of your changes in under a second. Application state is also preserved, so you don’t need to spend time recreating the desired state manually. If your app has a login screen you won’t have to re-enter your login credentials following each hot reload.
Installing Google Flutter and Dart
The Google Flutter SDK includes pretty much everything you’ll need to get started with Flutter, including the Dart SDK. However, it’s hosted on GitHub, you should probably clone this SDK using Git, rather than downloading it.
One of the major benefits of cloning a project, is that it’s much easier to keep up to date with new releases.
Once you’ve installed the latest version of Git, you’re ready to clone Flutter:
- Head over to Flutter’s GitHub page.
- Click “Clone or download,” and then copy the URL.
- Open a Command Prompt (Windows) or Terminal (Mac) window, and type “git clone,” followed by “-b beta” (since we’re cloning Flutter’s beta branch), and the project’s URL:
git clone -b beta https://github.com/flutter/flutter.git
- Next, add Flutter to your path using the following command:
export PATH=$PWD/flutter/bin:$PATH
- Check whether there’s any missing dependencies or outstanding tasks you need to perform by running the following Command Prompt/Terminal command:
flutter doctor
- If the subsequent report warns you that your Flutter installation is “x” days old, then run the following:
Flutter upgrade
Google Flutter may also warn that your Xcode installation is incomplete. You only need Xcode if you’re developing for iOS. If you’re only interested in Android, save yourself some time and disk space by ignoring this warning.
Adding Google Flutter and Dart to Android Studio
You can use Google Flutter with any text editor, but it’s far easier to use Android Studio’s dedicated Flutter and Dart plugins.
Assuming you’re running Android Studio 3.0 or later:
- Select “Android Studio > Preferences…” from the toolbar.
- Select “Plugins” from the left-hand menu.
- Give the “Browse repositories…” button a click.
- Search for “Flutter.” When the plugin appears, click its accompanying “Install” button.
- At this point, a popup should prompt you to install Dart. Click “Yes.”
- Restart Android Studio.
Creating your first Google Flutter app
The best way to learn a new technology, is to use it, so let’s create a “Hello World” app using Flutter and Dart:
- Select “New > New Flutter Project…” from the Android Studio toolbar.
- Select “Flutter application > Next.”
- Give your project a name and specify where it should be stored.
- Point Android Studio in the direction of your Flutter SDK by clicking the little “…” icon and navigating to the “Flutter” folder (on Mac, this is typically stored in your Home directory). Click “Open.”
- Enter your company domain, and then click “Finish.”
Open your project’s flutter_app/java/main.dart file. The “Flutter application” template contains lots of code (which we’ll be exploring later) but for now let’s keep things simple and create an app that displays a “Hello World” message. Delete everything in this file and replace it with the following:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Welcome to Flutter',
home: new Scaffold(
appBar: new AppBar(
title: new Text('This is the title text'),
),
body: new Center(
child: new Text('Hello World'),
),
),
);
}
}
To run this app, follow these steps:
- Launch an Android Virtual Device (AVD) or attach a physical Android device to your development machine.
- Open the “Flutter Device Selection” dropdown (where the cursor is positioned in the following screenshot) and select your device.
- Select “Run > main.dart” from the Android Studio toolbar.
This app may be simple, but it demonstrates some of the core concepts of developing with Flutter, so let’s break it down:
void main() => runApp(new MyApp());
In Dart, the => expression is shorthand for { return expression; }.
class MyApp extends StatelessWidget {
In Flutter, almost everything is a widget. Some Flutter widgets are the rough equivalent of Android’s Views. For example the Text widget is equivalent to a TextView. However, in Flutter rows, columns, padding, and even the application itself are also classed as widgets, which is why our application class extends from something called StatelessWidget.
StatelessWidgets are widgets that contain no State. All of a StatelessWidget’s values are final and cannot change at runtime, so the StatelessWidget simply renders what’s passed to its constructors.
The opposite of StatelessWidget is a StatefulWidget, which can change at runtime.
@override
Widget build(BuildContext context) {
The build() method describes how to display a widget. Flutter calls build() when it inserts a widget into the widget hierarchy, and then re-runs build() every time that widget needs to be updated.
return new MaterialApp(
Flutter comes with an implementation of Material Design that you can apply by declaring “MaterialApp” as your app’s top level widget.
Here, we’re using the MaterialApp theme out-of-the-box, but you can customize this theme by creating a new ThemeData() instance that contains your desired changes, for example:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Welcome to Flutter',
theme: new ThemeData(
primaryColor: Colors.white,
),
home: new Scaffold(
appBar: new AppBar(
title: new Text('This is the title text'),
),
body: new Center(
child: new Text('Hello World'),
),
),
);
}
}
home: new Scaffold(
The “home” argument references the widget that defines the main UI, which in this instance is Scaffold.
A Scaffold widget gives your app a structure that follows Material Design guidelines, by providing an app bar, title, and background color. It also provides APIs for displaying drawers, snackbars, and bottom sheets.
appBar: new AppBar(
title: new Text('This is the title text'),
),
Here, we’re setting the text for our app bar. While apps like Scaffold and AppBar are specific to Material Design apps, Text is a generic widget that you can use in any app.
body: new Center(
child: new Text('Hello World'),
Center is a layout widget that takes a single child and positions it in the middle of the screen, similar to a LinearLayout with a central gravity.
Creating our second Flutter app: User interaction and StatefulWidgets
We’ve created our first Google Flutter application, but it’s not exactly an exciting app. Learning a new toolkit and an entire programming language is slightly beyond the scope of this article, so for our second application let’s cheat and use a template:
- Select “New > New Flutter Project…”
- Select the “Flutter application” template, and then click “Next.”
- Complete the standard project setup.
- Run this app, by opening the “Flutter Device Selection” dropdown and selecting your device from the list.
- Select “Run > main.dart” from the Android Studio toolbar.
This app counts how many times you’ve tapped the Floating Action Button.
If you open the flutter_app/java/main.dart file, you’ll see the following:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
//Prefixing an identifier with an underscore makes it private to its class//
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
//Increment the counter following the click//
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
//Set the appbar’s title, by passing the .title defined in MyHomePage//
title: new Text(widget.title),
),
body: new Center(
//Column is a layout widget, similar to Center//
child: new Column(
//mainAxisAlignment position the widgets inside the Column//
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'You have pushed the button this many times:',
),
new Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _incrementCounter,
//Similar to Android’s contentDescription, tooltips provide text labels for//
//assistive technologies such as screen readers//
tooltip: 'Increment',
//Display the ‘Add’ icon. See the Icons class docs for a list of supported icons//
child: new Icon(Icons.add),
),
);
}
}
This automatically-generated code demonstrates a number of Flutter features we haven’t touched on yet:
StatefulWidgets
In Google Flutter, widgets are either stateless or stateful. A StatefulWidget has State information that can change at runtime, in response to user interaction.
A StatefulWidget consists of these things:
- A widget that extends the StatefulWidget class, which is immutable. The actual StatefulWidget is a temporary object used to construct the app in its current state.
- A State class, which contains some mutable data that might change over the widget’s lifetime. State objects persist between calls to build(), so they can retain information.
You insert the State object into the widget tree, by calling createState.
You also need to define a setState method, which informs Flutter when the widget’s State changes, causing the framework to rebuild this particular widget.
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
//Override the createState() method to attach the _MyHomePageState class//
_MyHomePageState createState() => new _MyHomePageState();
}
//Add the _MyHomePageState class//
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Rows and columns
In Flutter, you arrange widgets by placing them inside other widgets, most commonly in two orientations:
- A Row takes a list of child widgets and displays them horizontally.
- A Column takes a list of child widgets and displays them vertically.
Both Rows and Columns have various properties that let you control exactly where each child appears onscreen, and how much space they occupy within the Row or Column. Here, we’re using a mainAxisAlignment property to specify that the child widgets should be centered along the parent’s main axis:
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
If the parent is a Column, then the main axis is the vertical axis. If the parent is a Row, then the main axis is the horizontal axis.
Detecting user interaction
There’s another big difference between this application and our “Hello World” project — it can detect user input!
If a widget already supports event detection, you just need to pass a function that can handle the desired interaction, for example:
floatingActionButton: new FloatingActionButton(
//Pass the onPressed parameter, which is essentially the equivalent of onClick//
onPressed: _incrementCounter,
tooltip: 'Increment',
child: new Icon(Icons.add),
),
);
}
}
The easiest way to check whether a widget supports event detection, is to review its documentation. For example, the FloatingActionButton page makes it clear this widget supports onPressed.
If a widget doesn’t support event detection, you can make it interactive by wrapping it in a GestureDetector widget, which can detect gestures like taps, presses, and drags.
In this snippet, GestureDetector responds to user input by calling onTap and then printing a message to the Console:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new GestureDetector(
child: new FlutterLogo(
size: 300.0,
),
//When the child widget is tapped....//
onTap: () {
//...print the following//
print("onTap!");
},
),
));
}
}
Push changes to your application in under a second
If you’re familiar with Android Studio’s “Instant Run,” then you’ll know just how much time you can save by selectively pushing changes, rather than building a new APK every time you tweak your application code.
Google Flutter has a similar “Hot Reload” feature which lets you push updated code and resources to a running Dart VM while maintaining the application’s state. This application is perfect for demonstrating hot reload:
- Click the FAB button a few times so the counter is displaying anything other than 0.
- Make an arbitrary change to the project’s code, for example changing:
home: new MyHomePage(title: 'Flutter Demo Home Page'),
To:
home: new MyHomePage(title: 'Flutter'),
- Click the little “Flutter Hot Reload” button in the Android Studio toolbar (it uses exactly the same icon as Instant Run).
- Take a look at your application. The title should have changed without resetting the counter to zero, demonstrating that the application state has been preserved.
Wrapping up
In this article, we got an introduction to Google Flutter and Dart — what it is, how to set it up, and how to create some simple apps.
Will you be adding Dart to the list of languages you use for Android development? Or are you sticking with Java (or Kotlin?) Let us know in the comments below!