Getting Started with Flutter

There have been various tools released for cross-platform development over the years, including web-based tools such as PhoneGap from Adobe, powerful frameworks such as Xamarin from Microsoft, and newer tools such as React Native from Facebook. Each toolset has pros and cons and they have met with varying degrees of success in the mobile industry.
Flutter from Google is a recent framework to enter the cross-platform arena, which was announced in February at Mobile World Congress 2018 to be entering a beta phase. Features of Flutter are fast development cycles, fast UI rendering and unique UI design, and native app performance on both platforms.
Introduction to Flutter
Dart programming language is used to write Flutter apps that are originally from Google and now an ECMA standard. Dart language is somewhat similar to languages like Kotlin and Swift and can be trans compiled into JavaScript code.
Getting Started
Flutter application development can be done on Windows, Linux, or macOS. While you can use any editor with the Flutter toolchain, there are IDE plugins for IntelliJ IDEA, Android Studio and Visual Studio Code that can ease the development cycle. We’ll use Android Studio for this tutorial.
Set up an editor
install the Flutter and Dart plugins
To install these:
- Start Android Studio.
- Open plugin preferences (File > Settings > Plugins on Windows & Linux, Preferences > Plugins on macOS,).
- Select Browse repositories, select Flutter plugin and click Install.
- Click Yes to install the Dart plugin.
- Click Restart when prompted.
Create the app
- Select the File > New Flutter Project.
- Select project type – Flutter application, and press Next.
- Make sure Flutter SDK Path text field specifies the location of the SDK. Install the SDK if it is not installed.
- Enter a project name (for example, myapp), and press Next.
- Click Finish.
- Wait until Android Studio installs the SDK, and create the project.
1. main.dart
import 'package:flutter/material.dart'; void main() => runApp(new FlutterApp()); class FlutterApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Flutter', home: new Scaffold( appBar: new AppBar( title: new Text('Flutter'), ), body: new Center( child: new Text('Flutter'), ), ), ); } }
The main() function near the top uses the => operator for a single line function to run the app. You have a class for the app named GHFlutterApp.
You see here that your app itself is a StatelessWidget. almost every entities in a Flutter app are widgets, either stateless or stateful. Override the widget build() method to create your app widget. You’re using the MaterialApp widget that provides a number of components needed for apps following Material Design.
For this getting started tutorial, remove the test file widget_test.dart in the test folder from the project by selecting it and hitting the Delete key.
If you’re on macOS, startup the iOS simulator. You can also use an Android emulator on macOS, Linux, or Windows.
2. Hot Reload
The best aspect of Flutter development is being able to hot reload your app as you make changes. This is like Android Studio’s Instant Run.
3. Widgets
Almost every element of your Flutter app is a widget. Widgets are immutable, since using immutable widgets helps keep the app UI lightweight.
There are two fundamental types of widgets you will use:
Stateless widgets are immutable, meaning that their properties can’t change—all values are final. [1]
Stateful widgets maintain state that might change during the lifetime of the widget. [1]
Stateless and stateful widgets are redrawn in Flutter apps on every frame, the difference being that the stateful widgets delegate their configuration to a State object.
To get started with making your own widgets create a new class at the bottom of main.dart:
class GHFlutter extends StatefulWidget { @override createState() => new GHFlutterState(); }
You have made a StatefulWidget subclass and you’re overriding the createState() method to create its state object. Now add a GHFlutterState class above GHFlutter:
class GHFlutterState extends State<GHFlutter> { }
Add a build() override to GHFlutterState:
@override Widget build(BuildContext context) { }
Fill out build() as follows:
@override Widget build(BuildContext context) { return new Scaffold ( appBar: new AppBar( title: new Text(Strings.appTitle), ), body: new Text(Strings.appTitle), ); }
A Scaffold is a container for material design widgets. It acts as the root of a widget hierarchy. You’ve added an AppBar and a body to the scaffold, and each contains a Text widget.
Update GHFlutterApp class so that it uses your new GHFlutter widget as its home attribute, instead of building a scaffold of its own:
class GHFlutterApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: Strings.appTitle, home: new GHFlutter(), ); } }
4. Making Network Calls
Earlier you imported your own strings.dart file into the project. You can similarly import other packages that are part of the Flutter framework and Dart.
For example, you’ll now use packages available in the framework to make an HTTP network call and also parse the resulting response JSON into Dart objects. Add two new imports at the top of main.dart:
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'strings.dart';
Dart apps are single-threaded, but Dart provides support for running code on other threads as well as running asynchronous code that does not block the UI thread using an async/await pattern.
You’re going to make an asynchronous network call to retrieve a list of GitHub team members. Add an empty list as a property at the top of GHFlutterState, and also add a property to hold a text style:
var _members = []; final _biggerFont = const TextStyle(fontSize: 18.0);
The underscores at the beginning of the names make the members of the class private.
To make the asynchronous HTTP call, add a method _loadData() to GHFlutterState:
_loadData() async { String dataURL = "https://api.github.com/orgs/raywenderlich/members"; http.Response response = await http.get(dataURL); setState(() { _members = JSON.decode(response.body); }); }
The underscores at the beginning of the names make the members of the class private.
To make the asynchronous HTTP call, add a method _loadData() to GHFlutterState:
_loadData() async { String dataURL = "https://api.github.com/orgs/raywenderlich/members"; http.Response response = await http.get(dataURL); setState(() { _members = json.decode(response.body); }); }
You’ve added the async keyword onto _loadData() to tell Dart that it’s asynchronous, and also the await keyword on the http.get() call that is blocking. You’re using a dataUrl value that is set to the GitHub API endpoint that retrieves members for a GitHub organization.
When the HTTP call completes, you pass a callback to setState() that runs synchronously on the UI thread. In this case, you are decoding the JSON response and assigning it to the _members list.
Add an initState() override to GHFlutterState that calls _loadData() when the state is initialized:
@override void initState() { super.initState(); _loadData(); }
5. Using a ListView
Now that you have a Dart list of members, you need a way to display them in a list in the UI. Dart provides a ListView widget that will let you show the data in a list. ListView acts like a RecyclerView on Android and a UITableView on iOS, recycling views as the user scrolls through the list to achieve smooth scrolling performance.
Add a _buildRow() method to GHFlutterState:
Widget _buildRow(int i) { return new ListTile( title: new Text("${_members[i]["login"]}", style: _biggerFont) ); }
You are returning a ListTile widget that shows the “login” value parsed from the JSON for the ith member, and also using the text style you created before.
Update the build method to have it’s body be a ListView.builder:
body: new ListView.builder( padding: const EdgeInsets.all(16.0), itemCount: _members.length, itemBuilder: (BuildContext context, int position) { return _buildRow(position); }),
That’s just how easy it is to make a network call, parse the data, and show the results in a list!
6. Adding dividers
To add dividers into the list, you’re going to double the item count, and then return a Divider widget when the position in the list is odd:
body: new ListView.builder( itemCount: _members.length * 2, itemBuilder: (BuildContext context, int position) { if (position.isOdd) return new Divider(); final index = position ~/ 2; return _buildRow(index); }),
Be sure not to miss the * 2 on itemCount. You’ve removed padding from the builder now that you have dividers. In itemBuilder, you’re either returning a Divider(), or instead calculating a new index by integer division and using _buildRow() to build a row item.