Building your First Flutter App
Welcome to my first tutorial on Flutter. I have never written any post on cross-platform or hybrid app framework but Flutter has changed this mindset of mine.
Previously, I have developed on React Native, Cordova, Phone Gap, Ionic and now of these really work out for me until I found Flutter along with its huge community of developers and its showcase apps.
What is Flutter?
In a nutshell, Flutter is a multi-layered system, such that higher layers are easier to use and allow you to express a lot with little code and lower layers give you more control at the expense of having to deal with some complexity.
Flutter Framework is written entirely in Dart. Most of the engine is written in C++, with Android-specific parts written in Java, and iOS-specific parts written in Objective-C. Like React Native, Flutter also provides reactive-style views, but Flutter takes a different approach to avoiding performance problems caused by the need for a JavaScript bridge by using a compiled programming language, namely Dart.
Dart is compiled “ahead of time” (AOT) into native code for multiple platforms. This allows Flutter to communicate with the platform without going through a JavaScript bridge that does a context switch. It also compiles to native code which in turn improves app startup times.
In Flutter, it is all about Widgets. Widgets are the elements that affect and control the view and interface to an app.
Flutter renders the widget tree and paints it to a platform canvas. This is nice and simple (and fast). It’s Hot-Reload capability allows real-time development experience.
You can read more about Flutter and learn about its goodness here.
Before getting into the real stuff, let’s look at Alibaba’s members feedback about Flutter and why they used it to develop an app call Xiangyu. Enjoy!
Getting Hands-on
Today we will be building a very simple Flutter app that can be deployed on both iOS & Android called Contactly as we go through this tutorial. This is a very simple ContactsList app which will demonstrate the capabilities of Flutter. Capabilities include:
- TextField & Validations
- Button Clicks
- Navigations
- Image Rendering (Local & Online)
- Error Alert Dialog
- Scrollable List View
- List View Search
- JSON File Parsing
- JSON to Objects Mapping
- Opening External Web Browser
The final product of this app should look something like this:
- Login via a Pin Code.
- Load a list of contacts from JSON.
- Search for a particular contact.
- Tap to view contact details.
- Tap to view contact info in an external browser.
Project Structure
Open the project with Xcode and you should see a project structure as such:
- android -> User
- assets
- ios
- lib (Project Work all done here)
- test (ignore for this tutorial)
Install Flutter
What am I using at time of writing?
- Macbook Pro running macOS High Sierra
- Android Studio 3.2.1
- Xcode 10.1
- Flutter 1.0
I cannot guarantee that my tutorial will work for every configuration and platform, hence, I will not include configuration troubleshooting here to keep this tutorial short and objective-oriented.
First up, head over to Flutter Installation page to install Flutter. I will skip the steps here as the steps in the document is detailed enough.
Once you run
flutter doctor
and you got (1~4 checked), you are good to go! Its not necessary to haveConnected Devices
checked.
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.0.0, on Mac OS X 10.13.6 17G4015, locale en-SG)
[✓] Android toolchain — develop for Android devices (Android SDK 28.0.3)
[✓] iOS toolchain — develop for iOS devices (Xcode 10.1)
[✓] Android Studio (version 3.2)
[✓] Connected device (2 available)
Start a new Flutter Project
1. Click Start a new Flutter Project.
2. Select Flutter Application and click Next.
3. Fill in Project name as Contactly, but feel free to rename it to your liking. Navigate and specify the Flutter SDK path. Specify your project location and give a simple description. Then click Next.
4. Fill in a Company domain this will be replicated in your Bundle Identifier (iOS) & Package Name (Android). Then, for my case, I checked both Kotlin & Swift support. Then, click Finish.
Trial on iOS Simulator
It will be a good checkpoint here once the project has initialised to try running it on your iOS Simulator.
Once you started your Flutter Application, some boilerplate codes are automatically generated with a Running App that allows you to hit a button, and perform some text update. So go ahead and try running it.
1. Find a dropdown somewhere at the top right that says <no devices>, click on it and select Open iOS Simulator.
2. Your last selected Simualtor Hardware will be chosen, which is iPhone XR for my case.
Click on Run, which is the green triangle and the app should open in your simulator. You should be able to interact with the Demo app and push a few buttons!
Build Contactly’s Main Page
With the Demo App running successfully, we are now ready to start building our first Flutter App!
Let’s start by cleaning all the codes in main.dart
. First important line of code is to import our material package so we can build the UI of the app.
import ‘package:flutter/material.dart’;
To ensure that the app knows what to run after it finishes launching, add the main()
method like this:
void main() => runApp(ContactlyApp());
Not liking to hardcode stuffs, lets create a Constants.dart
file to place some of our constant values we will be using in this app.
- Right-click on
lib
. - Select
New
. - Select
Package
. - Name it
helpers
.
Now we have a separate folder to store our helper classes.
- Right-click on
helpers
. - Select
New
. - Select
File
. - Name it
Constants.dart
.
Then you can put in these codes:
Here we import the same material package
so we can use Color
declaration for appDarkGreyColor
and also declare an appTitle
to be used app-wide.
Now head back to main.dart
add this import statement after the first import line.
import ‘helpers/Constants.dart’;
Let’s start building our Main Page by adding these lines of codes:
MaterialApp is one of the convenience widgets which allows customisations like adding navigation routes, appBar etc.
Setting debugShowCheckedModeBanner
to false gets rid of the Red Debug label at the top right. We use our just declared appTitle
in our constant file here to give it a title. Then, we set the primaryColor
.
All the codes looks good here and you might be eager to try running it. If you really did, you will get a huge red-colored error screen!
This is because we are just not yet ready to paint the canvas. Be Patient!
In most tutorial, they will guide you on building everything into main.dart
. But I find that we could make it cleaner by separating each page into separate files, which you will be eventually doing so when building production-ready apps.
Build Contactly’s Login Page
So let’s go ahead can create a new page call LoginPage.dart
and place it under lib
. Perform the same ritual of importing material package
.
Here we will be creating a Stateless Widget since we need not store any form of data. You can find my details about Stateless VS Stateful here.
Before we go on to code, lets look at how our screen should look like:
It will have:
1. An Image Logo.
2. A TextField with Placeholder.
3. A Login Button.
Now, let’s paste in this code and we will go through them in awhile!
OMG! That’s a huge chunk of code!
Yes, but do not be alarmed. This is the first time we are really going deep into huge piles of codes, trust me, after going through these, you will get more familiar with how Flutter works :)
I have broken down this large piece of code into 4 major parts we are to digest them easier:
1. As mentioned earlier, we will be creating a stateless widget for our LoginPage
here.
2. We will introduce our first TextEditingController
here, which has a main responsibility of handling all text editing logic.
3. If we don’t implement the build
method, an error will be thrown as it is required when we extend StatelessWidget
to paint the UI canvas of the page. Furthermore, there are 4 other methods implemented here:
- a. Firstly, we have our
logo
. It is embedded in a Circular Frame; CircularAvatar with abigRadius
of66.0
. It also has anappLogo
image. We will need to add these 2 values in our constants file. Head over toConstants.dart
and add these lines of codes:// Images
Image appLogo = Image.asset(‘assets/images/flutter-logo-round.png’);
// Sizes
const bigRadius = 66.0;
and don’t forget to import ourConstants.dart
intoLoginPage.dart
.
import ‘helpers/Constants.dart’;
- If we run the app now we will probably get an error saying that the image asset cannot be loaded. We know the path is given to load the Image but there are 2 missing pieces, the image itself and the path that we need to include in
pubspec.yaml
. Firstly, you can get the image I use from here. Then, create a new directory callassets
in the root directory, and create a sub-directory callimages
. Your image should be placed inroot/assets/images
.
Then, head over to pubspec.yaml
and add
assets:
— assets/images/flutter-logo-round.png
to inform the app during runtime what assets to bundle together so it can be loaded.
- b. Now that we have our logo loaded probably, let’s move to the next UI item, `pinCode`. It is a TextFormField. We set our
_pinCodeController
under this TextFormField with a prefix_
which tells the compiler that this variable isprivate
. You can read thesettings
as most are self-explanatory likekeyboardType
isphone
,maxLength
is4
characters,maxLines
is1
,autoFocus
set to true to straightaway trigger keyboard when page is displayed. We also give the textfield a simple styling usingdecoration
andstyle
. - c. Next, is our
loginButton
. We create a padding on all sides (left, right, top, bottom) using symmetric. We also use RaisedButton which automatically comes with the elevation UI effect when user interacts with it. - d. Lastly, we return the main UI structural class call Scaffold which gels all our newly created UI components together in a ListView.
UI codes are tough 😭
Phew. That was like an Effiel Tower of Codes… Before we run our app, we need to fill in some constant values that we have just introduced into Constants.dart
.
1. Add Color appGreyColor = Color.fromRGBO(64, 75, 96, .9);
in // Colors
.
2. Add these in // Strings
:const pinCodeHintText = “Pin Code”;
const loginButtonText = “Login”;
Lastly, add const buttonHeight = 24.0;
in // Sizes
.
We also need to tell our main()
to run LoginPage
as the home page. So head back to main.dart
and add home: LoginPage()
after theme
. Your build code should look like this:
Now if we run the app, we should see the completed Login Screen!
Building Contacts List Page
Now that we are warmed up a little, we can go a little faster. We will now build the main ingredient of this app, which is the Contact List page. We will create a new file call HomePage.dart
. Go ahead and import material
package.
Contacts List Page will be a Stateful widget since we need to maintain the state of our contacts data. So go ahead and add these first few lines of boilerplate codes:
The first class HomePage
will be called and used when navigating/presenting the page, while the private class _HomePageState
will be called everytime the HomePage
is called. This is also the mutable state object which we will maintain as the page get called.
Before we go into the coding again, let’s look at how our contact list screen looks like:
There are many things that we will need to do here:
- Allow navigation from
LoginPage
toHomePage
(Routing). - Populate JSON data and map to ListView.
- Add Search Capability (Search Button at Top Left)
- UI Components of each List Item.
1. Allow navigation from LoginPage
to HomePage
(Routing)
Lets hook up our navigation route between LoginPage
& HomePage
. Headover to Constants.dart
and add these tags:
Then headover to main.dart
and add these just before our build
function:
These allow us to use tags to associate each individual page. :) Finally, lets add the routes to our build
function just after home
.
We can’t really test this out yet as we have not implemented the UI for our ListView, so let’s do that first.
2. Populate JSON data and map to ListView.
You can download the sample JSON file from here and create a data
package folder under assets
and put this file in it. Then update pubspec.yaml
with - assets/data/records.json
under assets
.
Now that we have our sample data in JSON format, we need to create:
1. Record
Class to hold the data of each item.
2. RecordList
Class to hold the list of data.
3. RecordService
Class to perform the loading task.
1. Record
Class to hold the data of each item.
Let’s first create a new models
package under lib
and create a new file call Record.dart
. You can put in these lines of code into the file:
Here we associate factory
as a static method of Record
.
2. RecordList
Class to hold the list of data.
In the same models
package, create another file call RecordList.dart
. Then put in these lines of code:
3. RecordService Class to perform the loading task.
Lastly, create another file call RecordService.dart
in the same package and paste these codes in:
In summary:
loadRecords()
will parse the records.json
file and map it into a RecordList
object, holding a list of Record
objects.
Now let’s use this in our HomePage
by adding these import statements at the top:
import ‘models/Record.dart’;
import ‘models/RecordList.dart’;
import ‘models/RecordService.dart’;
import ‘models/RecordList.dart’;
import ‘models/RecordService.dart’;
3. Add Search Capability (Search Button at Top Left)
4. UI Components of each List Item.
We are going to dive right in the development of the entire page:
Declare Variables
- We declared
_filter
so we can implement a listener for our searches. - We declared
records
to keep the state of our raw data, as well asfilteredRecords
to keep the state of searched data. - We use
_searchText
to validate our searches. - The
_searchIcon
is an Icon representation. - The
_appBarTitle
is really just a text widget which we will use widely.
Init State
Since its a Stateful
widget we can add some small settings when the state is initialised:
We empty our records data and get fresh data from our json file. Here we need not really use an Async Call but its really to introduce its concept and how you could call it if you were to perform a data fetch from a server.
The Scaffold
Remember that in our previous section, we return a Scaffold
in our build
function as the main UI structure:
_buildBar
Like most ListView
pages we seen in mobile apps, there is a navigation bar at the top. So we can add these lines of code to generate the AppBar we want:
_buildList
Going Top-Down, we have our ListView
next! So go ahead and add these lines of code:
Here, we handle the mapping of our RecordList
data into our ListVew
, and also handle any searches performed.
_buildListItem
The final piece of our ListView
is the UI for each ListViewItem
:
This is a long chunky piece of code. We can again break down and digest this in a more simple way:
- We used a given material design class call Card to create our Card-Like UI.
- In each Card, we have a ListTile. And in each ListTile we have: 3. leading:
CircleAvatar
Image wrapped in Hero which will allow us to do some cool animation as we navigate to the detail page later. (Record's Photo) 4. title: Which holds the name of the contact. 5. subtitle: which is wrapped in Flexible to allow for growing texts. 6. trailing: which is a right arrow icon to signify interactivity.
With these, we could now run the app, try navigating from LoginPage to HomePage!
_filter TextEditController Listener
To allow search capability, we need to enable the text editor’s listener (add this after _buildListItem
method):
_searchPressed
When the searchButton
is triggered, we will perform some UI changes:
1. Search
icon will change to Close
icon.
2. appTitle
will display a search field.
3. As we input search texts, the list will reload and re-render with filtered results.
So let’s add these codes in:
and add this call this method in onPressed
in _buildBar
:
Try running the app now and perform some searches! like “Mark”…
Building Contact Details Page
To finish up our Contactly App
let’s build our final Details
Page to allow the app to show some more info about a contact. Let’s look at how it should look like first:
It has a bigger image, a name, address and contact details. One hidden feature not shown here is to allow user to navigate to an external web browser to view the technology’s website. So let’s get started!
In lib
, create a new file call DetailsPage.dart
and paste in these codes:
1. We create a StatelessWidget
for our DetailsPage
.
2. We create a constructor to take in a Record
object from HomePage
.
3. Based on the Record
object, we build the UI and populate its:
4. Photo
5. Name
6. Address
7. Phone Number
8. URL
We see a new UI component here call GestureDetector which allows us to wrap UI component to allow interactivity.
URL Launcher
Let’s create a new file call URLLauncher.dart
in helpers
directory. To perform a url launch, we need to install a new package call url-launcher. To do this, we need to update our pubspec.yaml
like this and run flutter packages get
:
And this is how we install extra packages to increase the capabilities of our app :).
Great! You have just gained another skill!
Going back to our URLLauncher.dart
, paste these codes to implement our launcher method:
Great! the last step is to allow navigation from HomePage to our DetailsPage. Head over back to HomePage.dart
and add this line of code:
Don’t forget to import this in HomePage.dart
:
and this in DetailsPage.dart
:
Viola! You are done with the app (not just iOS but Android too)! Run it and enjoy your great work :)
Parting Words
You have just gone through a very basic tutorial to get you started in developing on Flutter. In my own opinion, Flutter is developed based on the knowledge of popular mobile apps around where we can easily build UI components in just a few lines of codes. While it’s scalability is still questionable, we can see that Google and it’s community is investing a lot in this framework, and we could possibly forsee a bright future ahead for Flutter, striving past React Native.
You can download the finished project here.
You can also view an exciting collection of flutter plugins here.