Summary
The default Flutter app created with Android Studio is a simple counter app.
In this chapter, we will learn Flutter step by step through the source code of this counter demo.
Then in the following sections, based on this example, some new features will be added step by step to introduce other concepts and technologies of Flutter applications.
Create and run your first Flutter App
Install Flutter and Dart plugin
Before creating a Flutter project, you need to install the Flutter and Dart plugins in Android Studio.
- Start Android Studio.
- Open the Android Studio plug-in preferences (macOS:
Preferences>Plugins
, Windows:File>Settings>Plugins
). - Select Marketplace, select the
Flutter
andDart
plugin and click Install. - Click Restart when prompted.
Create Flutter App
- Select
File > New > New Flutter Project
- Select
Flutter
project and clickNext
- Enter the project name (eg:
counter
) and clickNext
- Click
Finish
- Wait for Android Studio to install the SDK and create the project.
Now we create a Flutter project called counter
, which contains a simple demo application using Material components.
In the project directory, the code for your application is located in lib/main.dart
.
Run our app
-
Locate Android Studio toolbar:
-
In the
Target selector
, select an Android device running the application. If it is not listed, please selectTools>Android>AVD Manager
and create one there. -
Click the
Run
icon in the toolbar. -
If everything is ok, you should see the launched application on your device or emulator.
How does this work
In this counter example, every time you click the floating button with a "+" sign in the lower right corner, the number in the center of the screen will increase by 1.
In this example, the main Dart code is in the lib/main.dart
file. Below is its source code (I removed some comments):
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Import material package
The first line of code imports the Material UI component library.
import 'package:flutter/material.dart';
Material is a standard mobile and web visual design language. Flutter provides a rich set of Material style UI components by default.
The application entry
Similar to C/C++
and Java
, the main
function in the Flutter application is the entry point of the application. The runApp
method is called in the main
function, and its function is to start the Flutter application. runApp accepts a Widget parameter, in this example it is a MyApp
object, and MyApp()
is the root component of the Flutter application.
void main() {
runApp(const MyApp());
}
MyApp
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
The MyApp
class represents the Flutter application, which inherits the StatelessWidget
class, which means that the application itself is also a widget.
In Flutter, most things are widgets (or "components"), including alignment (Align), padding (Padding), gesture processing (GestureDetector), etc. They are all widgets.
When Flutter builds a page, it calls the widget's build
method. The main job of the widget is to provide a build()
method to describe how to build the UI interface (usually by combining and assembling other basic widgets).
If you have React
development experience, then you should understand it well, the build
method is equivalent to the render
method in React.
MaterialApp
is a Flutter APP framework provided in the Material library, through which you can set the name, theme, language, homepage, and routing list of the application. MaterialApp
is also a widget.
The home
parameter of MaterialApp
sets the home page for the Flutter application, which is also a widget.
MyHomePage
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
MyHomePage
is the home page of the application, which inherits from the StatefulWidget
class, which means that it is a Stateful widget
. We will introduce Stateful widgets
in detail in a later article. For now, we only need to think that there are two differences between Stateful
widgets and Stateless
widgets:
Stateful
widgets can have states, which can be changed during the widget's life cycle, whileStateless
widgets are immutable.- The Stateful widget consists of at least two classes:
- A
StatefulWidget
class. - A
State
class, theStatefulWidget
class itself is unchanged, but the state held in theState
class may change during the widget's life cycle.
- A
_MyHomePageState
_MyHomePageState
is a State
class. Because we only need to maintain a click counter, we define a _counter
state:
int _counter = 0;
We create a function to increase _counter
:
void _incrementCounter() {
setState(() {
_counter++;
});
}
When the button is clicked, this function will be called. The function of _incrementCounter
is to increment _counter
first, and then call the setState
method. The function of the setState
method is to notify the Flutter framework that a state has changed.
After the Flutter framework receives the notification, it will execute the build
method to rebuild the interface according to the new state. Flutter has optimized this method to make the re-execution fast , So you can rebuild anything that needs to be updated without modifying each widget separately.
Build the UI interface:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
The logic of building the UI interface is in the build method. When MyHomePage
is created for the first time, the _MyHomePageState
class will be created. When the initialization is completed, the Flutter framework will call the widget’s build method to build the widget tree, and finally render the widget tree to the device screen superior. So, let's see what is done in the build method of _MyHomePageState
:
Scaffold
is a page scaffold provided in the Material library. It provides a default navigation bar, title, and body attributes that contain the home screen widget tree (same as "component tree"). The widget tree can be very complicated. In the examples later in this book, routes are created by Scaffold by default.- The component tree of the body contains a
Center
component, andCenter
can align its sub-component tree to the center of the screen. - the
Center
subcomponent is aColumn
component, and the function ofColumn
is to arrange all its subcomponents in order along the vertical direction of the screen. - The
Column
subcomponent is twoTexts
, and the firstText
displays the fixed text "You have pushed the button this many times:", the secondText
displays the value of the_counter
state. floatingActionButton
is the floating button with"+"
in the lower right corner of the page. ItsonPressed
attribute accepts a callback function to handle the click event. In this example, the_incrementCounter
method is directly used as its processing function.
Very good, we have read the source code of the Counter example. Now, let us review the execution flow of the entire application:
- When the
floatingActionButton
button in the lower right corner is clicked, the_incrementCounter
method will be called. - In the
_incrementCounter
method, the_counter
state is incremented first. - Then
setState
will notify the Flutter framework that the state has changed. - The Flutter framework will call the
build
method to rebuild the UI in the new state, and finally display it on the device screen.