Develop Flutter Mobile App from Scratch in 5 hours
Vít Uličný
·01/03/2024
·23 min.
1. Motivation to build Flutter mobile app
Quite often I run into a problem of keeping track the game score while playing hockey with my friends so I’ve decided to build a mobile app that might help me and my team solve that problem.
I don’t want to use either native Android mobile app or native iOS mobile app.
I want to try ReactNative and Flutter because the knowledge of these tools will allow me to write the apps that will share the same codebase for iOS and Android.
This part of the article will contain information about the developing app with Flutter. It will not cover Flutter and Dart basics as such as the setup process. It will include the step by step guide of creating GameScore mobile application alongside with some basic description of widgets and links to documentation and useful articles. You will have to install Dart, get some core concepts of Dart and install Flutter on your own.
2. What the Flutter is
According to wiki Flutter is:
an open-source mobile application development framework created by Google. It is used to develop applications for Android and iOS, as well as being the primary method of creating applications for Google Fuchsia.
Flutter is actually a set of libraries which contains Flutter Engine for rendering.
Foundation library that provides some low-level classes and functions.
Widgets which are indeed a core library for building an application with Flutter.
Material and Cupertino which conform to specific design languages and other libraries.
In my experience, you will most likely spend most of the time on working with Widgets, Cupertino and(or) Material. For more information about Flutter please visit the official website.
3. Let's build a Flutter mobile app from scratch
Here is how the completed application will look like.
To get started please set up a new Flutter project in Android Studio.
After you will do it remove everything from `main.dart` file and write the code which implements our app main screen:
This code does a couple of things:
-
It runs an app
-
It defines basic layout structure with the help of Scaffold Material application Widget, which contains the AppBar(the thing at the top of the screen that has the title and may have the leading button, and some actions after the title)
-
It renders the list of teams and a start game control
Now try to run it, it. That is how our home screen should look like:
Let go through it line by and discover what that code does:
import 'package:flutter/material.dart';
Imports a material library alongside with foundation, widgets, and others.
void main() => runApp(MaterialApp(home: GameScoreApp()));
main function is an entry point of a Dart application.
runApp function inflate the given widget and attach it to the screen.
MaterialApp widget represents an app that uses material design. You can’t use material widgets such as Scaffold, IconButton and other without it.
GameSocreApp our root StatelessWidget for holding all the widgets that we need to build UI.
class GameScoreApp extends StatelessWidget {
StatelessWidget is a core building block of Flutter application alongside with StatefulWidget(we will cover that one later). StatelessWidget is useful when your widgets tree doesn’t need to mutate any inner state and requires only some configuration information. Its main purpose is to hold the subset of other widgets to describe the Flutter mobile application UI more correctly. For UI that will need to change dynamically consider using StatefulWidger. Each class that inherits from StatelessWidget should define its own build method that is returning the instance of Widget.
return Scaffold(
The scaffold is a widget that implements the basic material design UI layout for the Flutter app. It will expand to use all available space
It may contain an application bar, application body, drawers, snack bars, bottom sheets, etc.
This is so far my favorite widget within few 3!! lines of code you can have a basic Material application layout.
appBar: AppBar(title: Text("Game Score App")),
Scaffold's appBar property unlike Scaffold’s body property which accepts any type of Widget instance accepts only in AppBar Widget instance which implements a material design app bar. which accepts title property which typically a Text widget that contains text describing the purpose of this application, actions property accepts the list of widgets to display after title usually they are common action buttons or a popup menu, leading property is used for rendering a Widget that will be displayed before the title. What is neat about leading property is that if you will use AppBar with Scaffold and leave it blank it will automatically place their appropriate Widget for example if we will navigate from one screen to another it will place there a back button, which will actually work or if we will use a drawer it will put there a button which will toggle the visibility drawer.
body: ListView.builder(itemCount: 3, itemBuilder: (builderContext, i) {
The body property represents the primary content of the Scaffold widget.
ListView represents a scrollable Material UI list view. May take the list of items and make them scrollable. ListView.builder constructs an items lazily meaning only a specific number of list items are constructed and when a user scrolls ahead, the earlier ones are destroyed.
return Column(
Column a Widget that places it, children, vertically, doesn’t have a scroll if you need a scrollable view you have to check Slivers or ListView.
ListTile(
ListTile widget is commonly used to populate a list view. Contains title, leading - widget before the title, trailing - widget after title and others.
title: Text("Team #${i + 1}"),
Text widget represents a string of text, can be styled with TextStyle. TextStyle is a Widget that defines the size, position, and rendering styles of text.
Divider()
Divider widget draws a horizontal line :)
onTap: () { print("Starts a new game!"); },
onTap property of a ListTile accepts a function and is called when the user taps this list tile.
title: Center(
Center is a Widget that centers a child in it.
We have our main GameScoreApp screen but it doesn’t make a lot of sense at the moment because we can’t start a new game or change team names for example. Let’s start with a team data to do it we need to change our GameScoreApp to inherit from StatefulWidget instead of StatelessWidget.
The core difference between StatefulWidget and StatelessWidget is that StatefulWidget should contain inner State<TWidget> that is responsible for holding data and mutating data. It also has some lifecycle methods like StatefulWidget.createState(which you should define in your StatefulWidget), State.initState, State.didChangeDependencies and others.
Add a Team to your main.dart:
Add a _GameScoreAppState:
In the code above we added _teams property to our state which will hold our teams names and scores.
List _teams = [Team(name: "Devils"), Team(name: "Penguins")];
We also changed the team ListTile widget title property so it will use team name from the list.
ListTile( title: Text(_teams[i].name), ),
Next step will be adding ability to edit team names. To do so we will create a _TeamTile StatefulWidget, state for it and move there our team rendering widgets. Our _TeamTile should also have the team property to hold the information about team that it’s rendering. This is how the code for it should look like at the moment:
and in the _GameScoreAppState class change this
to this:
return _TeamTile(team: _teams[i]);
In order to be able to edit the name of the team we will use TextField widget. The TextField is a widget that represents material ui design text field, which allows user to manipulate the text. We will also need a TextEditingController widget to listen for the TextField values changes.
Add TextEditingController and _editing as a properties in _TeamTile widget:
TextEditingController _textFieldController; bool _editing = true;
Override the default initState function to initialize our _textFieldController:
void initState() { super.initState(); _textFieldController = TextEditingController.fromValue(TextEditingValue(text: widget.team.name)); }
Extract our team name rendering widget to a separate function:
Widget _buildShowTile() { return Text(widget.team.name); }
Write a function that will render a TextField for our team name:
Widget _buildEditTile() { return TextField(controller: _textFieldController); }
Refactor our _TeamTile’s build method to render text field when editing teams name and text when just showing:
title: _editing ? _buildEditTile() : _buildShowTile()
After all modifications your _TeamTile code should look like that:
Now run the app and you will see that teams names are replaced with text fields.
Ok, so far so good, we have rendered teams list with the start new game and ability to edit team names in text fields. we will change _editing prop to false next and we will implement textfield focusing when user will tap a list tile text. we need to write a function that will toggle our _editing state property:
void _toggleEditing() { setState(() { _editing = !_editing; }); }
Ok now we need to call that function when users tap a Text widget to do so we will use GestureDetector which just detects gestures like tap, double tap, long press, etc. Then we will wrap a team tile text widget with the GestureDetector.
Widget _buildShowTile() { return GestureDetector( onTap: () => _toggleEditing(), child: Text(widget.team.name) ); }
At the current state application changes the view to edit but it does not save text field data, not focuses on textfield and not switches back to show view on confirm, to fix it we will use FocusNode that is a node in a focus tree, focus tree keeps track which focuses node is in the focus. Let add a focus node to our _TeamTileState so we should be able to focus on a TextWidget.
Then we will need to write function that will actually focus on our text node, but we might have a problem our text field widget is not always rendered in the Flutter application tree, so we need to wait until it renders to do so we can use Timer look at the timer as at the setTimeout in javascript it just calls a callback function after some delay,
Our app work as expected now we will write the functionality that will change the view of not focused _TeamTile, to do so we will add the listener to the focusNode and check if the focusNode is not focused and then just call a toggleEditing function we will put this code into our initState lifecycle method
// in initState _focusNode.addListener(() { if (!_focusNode.hasFocus) { _toggleEditing(); } })
The complete _TeamTileState code after these changes should like that:
Wasn’t that hard right? And we are almost done with our teams view show/edit functionality, there is only one thing left – saving the data from a text field on blur/submit to our _teams list it will render correct name. In order to do it, we will need to create a function in _GameScoreAppState that will update a team name and pass it down the tree, I know what you are thinking about why not to use some better solution? The answer is dead simple for the simplicity of an article.
But for curious one, I will say that in flutter there is an InheritedWidget which efficiently propagate information down the widget tree and it’s extremely powerful things like FocusScope.of and Theme.of are just InheritedWidgets at the end, you can also have a look at BloC and Redux state management libraries.
So let’s code it. We should add a team name setter in _GameScoreState and update _TeamTile to be able to pass our setter down the tree.
We should add a new argument setTeamName to our _TeamTile constructor and initialize it, also we will add a setTeamName function to simplify a function call in our _TeamTileState:
Finally, in _TeamTileState we need to call our function from the widget on blur and pass there an updated name
if (!_focusNode.hasFocus) { _toggleEditing(); widget.setTeamName(_textFieldController.text); }
Let’s refactor our app before moving forward.
Here is how the Flutter app will look after our changes:
Create a new file lib/screens/game_setup.dart define a GameSetup StatelessWidget there that accepts List<Team> teams and Function setTeamName as an argument and define such a properties cut the build function from GameScoreAppState and copy it to GameSetup. Move the Team class to lib/models/team.dart and import it into game_setup.dart as well as dart:async. Copy _TeamTile and _TeamTileState into screens/game_setup.dart. After all these changes your game_setup.dart should look like this:
Change the main function of main.dart to and _GameScoreAppState#build into:
void main() => runApp(GameScoreApp()); @override Widget build(BuildContext context) { return MaterialApp(home: GameSetup(teams: _teams, setTeamName: _setTeamName,)); }
We have completed the functionality of the team's list let’s move to the “Start new game” which is the last item at this screen. Start a new game should navigate us to the game screen which will contain the game score, team name and controls to change the game score.
First, create a skeleton for our game screen in screens/game.dart and put the following content into it:
There are a couple of new widgets at this screen one of them is FractionallySizedBox its main purpose is to size its child to a fraction of the total available space and another is EdgeInsets that represents offsets, you can use it to specify margin and paddings.
Till that moment it should be clear what the code above is doing so I will not go into details about it. But if you are unconfident these official Flutter guides may help you. https://flutter.dev/docs/development/ui/widgets-intro
Our next step is to modify MaterialApp to use named routes:
Don’t forget to import your Game screen into main.dart.
Next step is to change Start New Game button onTap function so it will actually navigate us to Game Screen to do so we will use Navigator widget you can look at it as at browser history it’s a stack with a push and pop function which allows you to go back and forth in the navigation stack. Replace your onTap method at game_setup.dart with this one.
When you will click at it app should navigate to the game screen
onTap: () { Navigator.of(context).pushNamed("/game"); },
The first screen is now ready, you are able to edit teams names and start a new game.
4. Game Screen of the Game Score mobile app
That is how our game screen will look like:
We should place icon buttons at the top and at the bottom for each team to be able to manipulate the game score, also we need to display a team name and the score for each team.
There are a couple of new Flutter widgets here. Expanded – forces its child to fill all available space in the main axis. IconButton – which is material UI icon button, Icon – the name stands for itself, Icons supported material app icons.
Ok, no we need to define the changeTeamScore function, keep in mind that we are managing state outside the Game screen so we need to define this function at our GameScoreAppState and in order to use it in the Game screen we need to pass it there as a property:
we will add the increment and decrement functions to the Game class next:
… rest of the Game class.
And that is it the GameScore Flutter application is ready.
5. Conclusions from developing Game Score Flutter mobile app
During this tutorial we learned:
• The core building blocks of the Flutter app, like MaterialApp, StatelessWidget, Text.
• How to build layouts in Flutter.
• What is the difference between Stateless and Stateful widgets and how to use them.
• How to handle user interactions in Flutter.
• Basics of working with forms, how to manipulate text in a TextField with TextEditingController and how to change focus with FocusNode.
• How to use Navigator with named routes in Flutter to navigate to different screens.
What I personally like in Flutter:
• Dart compiles to ARM C/C++ so it’s close to native code and you can gain some extra performance
• Flutter is developed by Google which means that Google will do as many efforts as possible to support all native features for it.
• Rich Material UI widgets library
• Learn once write for both platforms Android and iOS especially if you are not hesitating to use material UI at ios.
• Flutter supports hot reload which is really handy.
• Widget tree inspector
Useful links:
Flutter Documentation:
https://flutter.dev/docs
Google codelabs Flutter tutorials:
https://codelabs.developers.google.com/?cat=Flutter
Awesome Flutter GitHub repo:
https://github.com/Solido/awesome-flutter
Some of the example apps that you can learn from:
https://github.com/nisrulz/flutter-examples
https://github.com/flutter/samples
https://github.com/iampawan/FlutterExampleApps
https://github.com/brianegan/flutter_architecture_samples
In the next article, we will go through developing the exact same application using ReactNative.
Do you have an idea for a new project?
Describe it to us! We will be happy to answer all your questions, or we will immediately arrange a meeting.
Get in touch with Vít! He will discuss everything with you.
Vít Uličný
Founder & CEO