This is a guide to creating your first Flutter app. If you
are familiar with object-oriented code and basic programming
concepts such as variables, loops, and conditionals,
you can complete this tutorial. You don’t need
previous experience with Dart, mobile, or web programming.

What you’ll build in part 1

You’ll implement a simple mobile app that generates proposed
names for a startup company. The user can select and unselect names,
saving the best ones. The code lazily generates names.
As the user scrolls, more names are generated.
There is no limit to how far a user can scroll.

The animated GIF shows how the app works at the completion of part 1.

What you’ll learn in part 1

How to write a Flutter app that looks natural on iOS, Android,
and the web.

Basic structure of a Flutter app.

Finding and using packages to extend functionality.

Using hot reload for a quicker development cycle.

How to implement a stateful widget.

How to create an infinite, lazily loaded list.

In part 2
of this codelab, you’ll add interactivity, modify the app’s theme,
and add the ability to navigate to a new screen
(called a route in Flutter).

What you'll use

You need two pieces of software to complete this lab: the
Flutter SDK and an editor.
This codelab assumes Android Studio, but you can use your preferred
editor.

You can run this codelab using any of the following devices:

A physical device (Android
or iOS) connected to your
computer and set to developer mode.

As you type, Android Studio gives you suggestions for libraries to import.
It then renders the import string in gray, letting you know that the
imported library is unused (so far).

Use the English words package to generate the text instead of
using the string “Hello World”:

{step1_base → step2_use_package}/lib/main.dart

@@ -9,6 +10,7 @@

9

10

class MyApp extends StatelessWidget {

10

11

@override

11

12

Widget build(BuildContext context) {

13

+ final wordPair = WordPair.random();

12

14

return MaterialApp(

13

15

title: 'Welcome to Flutter',

14

16

home: Scaffold(

@@ -16,7 +18,7 @@

16

18

title: Text('Welcome to Flutter'),

17

19

),

18

20

body: Center(

19

- child: Text('Hello World'),

21

+ child: Text(wordPair.asPascalCase),

20

22

),

21

23

),

22

24

);

Note:
“Pascal case” (also known as “upper camel case”),
means that each word in the string, including the first one,
begins with an uppercase letter. So, “uppercamelcase” becomes
“UpperCamelCase”.

If the app is running, hot reload
to update the running app. Each time you click hot reload,
or save the project, you should see a different word pair,
chosen at random, in the running app.
This is because the word pairing is generated inside the build
method, which is run each time the MaterialApp requires rendering,
or when toggling the Platform in Flutter Inspector.

Android

iOS

Problems?

If your app is not running correctly, look for typos.
If you want to try some of Flutter’s debugging tools,
check out the DevTools suite of debugging and profiling tools.
If needed, use the code at the following links to get back on track.

Step 3: Add a Stateful widget

Stateless widgets are immutable, meaning that their
properties can’t change—all values are final.

Stateful widgets maintain state that might change
during the lifetime of the widget. Implementing a stateful
widget requires at least two classes: 1) a StatefulWidget class
that creates an instance of 2) a State class. The StatefulWidget
class is, itself, immutable, but the State class persists over the
lifetime of the widget.

In this step, you’ll add a stateful widget, RandomWords, which creates
its State class, RandomWordsState. You’ll then use RandomWords as
a child inside the existing MyApp stateless widget.

Create a minimal state class. Add the following to the bottom
of main.dart:

Notice the declaration State<RandomWords>. This indicates that we’re
using the generic
State
class specialized for use with RandomWords. Most of the app’s logic
and state resides here—it maintains the state for the RandomWords
widget. This class saves the generated word pairs, which grows infinitely
as the user scrolls, and favorite word pairs (in
part 2),
as the user adds or removes them from the list by toggling the heart icon.

RandomWordsState depends on the RandomWords class. You’ll add that next.

Add the stateful RandomWords widget to main.dart.
The RandomWords widget does little else beside creating its State class:

After adding the state class, the IDE complains that
the class is missing a build method. Next, you’ll add a basic
build method that generates the word pairs by moving the
word generation code from MyApp to RandomWordsState.

Remove the word generation code from MyApp by making the changes shown in
the following diff:

{step2_use_package → step3_stateful_widget}/lib/main.dart

@@ -10,7 +10,6 @@

10

10

class MyApp extends StatelessWidget {

11

11

@override

12

12

Widget build(BuildContext context) {

13

- final wordPair = WordPair.random();

14

13

return MaterialApp(

15

14

title: 'Welcome to Flutter',

16

15

home: Scaffold(

@@ -18,8 +17,8 @@

18

17

title: Text('Welcome to Flutter'),

19

18

),

20

19

body: Center(

21

- child: Text(wordPair.asPascalCase),

20

+ child: RandomWords(),

22

21

),

23

22

),

24

23

);

25

24

}

Restart the app.
The app should behave as before, displaying a word
pairing each time you hot reload or save the app.

Tip:
If you see the following warning on a hot reload, consider restarting
the app:

Reloading…
Some program elements were changed during reload but did not run when
the view was reassembled; you might need to restart the app (by pressing “R”)
for the changes to have an effect.

It might be a false positive, but restarting ensures that your changes
are reflected in the app’s UI.

Problems?

If your app is not running correctly, look for typos.
If you want to try some of Flutter’s debugging tools,
check out the DevTools suite of debugging and profiling tools.
If needed, use the code at the following link to get back on track.

Step 4: Create an infinite scrolling ListView

In this step, you’ll expand RandomWordsState to generate
and display a list of word pairings. As the user scrolls, the list
displayed in a ListView widget, grows infinitely. ListView’s
builder factory constructor allows you to build a list view
lazily, on demand.

Add a _suggestions list to the RandomWordsState
class for saving suggested word pairings.
Also, add a _biggerFont variable for making the font size larger.

Note:
Prefixing an identifier with an underscore enforces
privacy
in the Dart language.

Next, you’ll add a _buildSuggestions() function to the RandomWordsState
class. This method builds the ListView that displays the suggested
word pairing.

The ListView class provides a builder property, itemBuilder, that’s a
factory builder and callback function specified as an anonymous function.
Two parameters are passed to the function—the BuildContext,
and the row iterator, i. The iterator begins at 0 and increments each
time the function is called. It increments twice for every suggested word pairing:
once for the ListTile, and once for the Divider. This model allows the suggested
list to grow infinitely as the user scrolls.

The itemBuilder callback is called once per suggested word pairing,
and places each suggestion into a ListTile row. For even rows, the
function adds a ListTile row for the word pairing. For odd rows, the
function adds a Divider widget to visually separate the entries. Note
that the divider might be difficult to see on smaller devices.

Add a one-pixel-high divider widget before each row in the ListView.

The expression i ~/ 2 divides i by 2 and returns an integer result.
For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2. This calculates the
actual number of word pairings in the ListView, minus the divider
widgets.

If you’ve reached the end of the available word pairings, then generate
10 more and add them to the suggestions list.

The _buildSuggestions() function calls _buildRow() once per
word pair. This function displays each new pair in a ListTile,
which allows you to make the rows more attractive in the next step.

In the RandomWordsState class, update the build() method to use
_buildSuggestions(), rather than directly calling the word
generation library.
(Scaffold
implements the basic Material Design visual layout.)
Replace the method body with the highlighted code:

In the MyApp class, update the build() method by changing the title, and
changing the home to be a RandomWords widget:

{step3_stateful_widget → step4_infinite_list}/lib/main.dart

@@ -10,15 +10,8 @@

10

10

class MyApp extends StatelessWidget {

11

11

@override

12

12

Widget build(BuildContext context) {

13

13

return MaterialApp(

14

- title: 'WelcometoFlutter',

15

- home: Scaffold(

14

+ title: 'StartupNameGenerator',

15

+ home: RandomWords(),

16

- appBar: AppBar(

17

- title: Text('Welcome to Flutter'),

18

- ),

19

- body: Center(

20

- child: RandomWords(),

21

- ),

22

- ),

23

16

);

24

17

}

Restart the app. You should see a list of word pairings no matter how far
you scroll.

Android

iOS

Problems?

If your app is not running correctly, look for typos.
If you want to try some of Flutter’s debugging tools,
check out the DevTools suite of debugging and profiling tools.
If needed, use the code at the following link to get back on track.

Profile or release runs

Important:
Do not test the performance of your app with debug and
hot reload enabled.

So far you’ve been running your app in debug mode. Debug
mode trades performance for useful developer features such
as hot reload and step debugging. It’s not unexpected to
see slow performance and janky animations in debug mode.
Once you are ready to analyze performance or release your
app, you’ll want to use Flutter’s “profile” or “release”
build modes. For more details,
see Flutter’s build modes.

Next steps

The app from part 2

Congratulations!

You’ve written an interactive Flutter app that runs on both iOS and Android.
In this codelab, you’ve: