Getting Started With Flutter
Dive into the Flutter framework, which lets you build iOS, Android, web and desktop apps with a single codebase, by writing a cross-platform app using VS Code. By Jonathan Sande.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Getting Started With Flutter
30 mins
- Getting Started
- Introduction to Flutter
- Setting up Your Development Environment
- Creating a New Project
- Editing the Code
- Running the App
- Using Hot Reload
- Importing a File
- Understanding Widgets
- Creating Widgets
- Making Network Calls
- Importing Packages
- Using Asynchronous Code
- Using a ListView
- Adding Dividers
- Parsing to Custom Types
- Downloading Images With NetworkImage
- Cleaning up the Code
- Adding a Theme
- Where to Go From Here?
Making Network Calls
Earlier, you imported strings.dart into the project. Similarly, you can import other packages from the Flutter framework or even from other developers. For example, you’ll now use a couple of additional packages to make an HTTP network call and parse the resulting JSON response into Dart objects.
Importing Packages
Add two new imports at the top of main.dart:
import 'dart:convert';
import 'package:http/http.dart' as http;
You’ll notice the http
package isn’t available. That’s because you haven’t added it to the project yet.
Navigate to pubspec.yaml in the root folder of your project. In the dependencies section, right under cupertino_icons: ^1.0.2, add the following line:
http: ^0.13.3
cupertino_icons
package has.Now, when you save your pubspec.yaml, the Flutter extension in VS Code will run the flutter pub get
command. Flutter will get the declared http package and make it available in main.dart.
You’ll now see blue lines under your two most recent imports in main.dart, which indicates they’re currently unused.
Don’t worry. You’ll use them in just a bit.
Using Asynchronous Code
Dart apps are single-threaded, but Dart provides support for running code on other threads. It also supports running asynchronous code that doesn’t block the UI thread. It uses the async/await pattern to do this.
async
methods all runs on the UI thread. It’s just scheduled to run later, when the UI isn’t busy. If you actually did want to run some code on another thread, then you’d need to create what’s called a new Dart isolate.Next, you’ll make an asynchronous network call to retrieve a list of GitHub team members. To do this, add an empty list as a property in _GHFlutterState
as well as a property to hold a text style:
var _members = <dynamic>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
As you learned earlier, the underscores at the beginning of the names make the members of the class file-private. The dynamic
keyword tells Dart that the list could hold anything. Generally, it’s not ideal to use dynamic
because it opts out of the type safety system that Dart has. However, when making network calls, dealing with dynamic
is unavoidable.
To make the asynchronous HTTP call, add _loadData
to _GHFlutterState
:
Future<void> _loadData() async {
const dataUrl = 'https://api.github.com/orgs/raywenderlich/members';
final response = await http.get(Uri.parse(dataUrl));
setState(() {
_members = json.decode(response.body) as List;
});
}
Here, you’ve added the async
keyword onto _loadData
to tell Dart that it’s asynchronous. Another clue that it’s asynchronous is the Future
return type. You put await
in front of http.get()
because that’s another asynchronous call that might take a while.
When the HTTP call completes, you pass a callback to setState
that runs synchronously on the UI thread. In this case, you’re decoding the JSON response and assigning it to the _members
list. If you set _members
without calling setState
, then Flutter wouldn’t rebuild the UI and your users wouldn’t realize that the state had changed.
Add an initState
override to _GHFlutterState
:
@override
void initState() {
super.initState();
_loadData();
}
This method calls _loadData
when the state class is first created.
Now that you’ve created a list of members in Dart, you need a way to display them in a list in the UI.
Using a ListView
Dart provides a ListView that lets you show the data in a list. ListView
acts like a RecyclerView
on Android or a UICollectionView
on iOS, recycling views as the user scrolls through the list to achieve smooth scrolling performance.
Add _buildRow
to _GHFlutterState
:
Widget _buildRow(int i) {
return ListTile(
title: Text('${_members[i]['login']}', style: _biggerFont),
);
}
Here, you’re returning a ListTile that shows the login name parsed from the JSON for the member at index i
. It also uses the text style you created before.
Replace the body
line in the build
method of _GHFlutterState
with the following:
body: ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: _members.length,
itemBuilder: (BuildContext context, int position) {
return _buildRow(position);
}),
The padding adds some empty space around each list item — 16 logical pixels, in this case. Setting itemCount
tells ListView
how many rows it will have in total. Finally, call itemBuilder
for every new row that’s visible on the screen, which you use as an opportunity to build your custom ListTile
in _buildRow
.
At this point, you’ll probably need to do a full restart rather than a hot reload. The Hot Restart button is useful for that. It’s still faster than completely stopping the app and rebuilding it.
After the restart, you’ll see the following:
That’s how easy it is to make a network call, parse the data and show the results in a list!
Now, it’s time to make the list a bit prettier.
Adding Dividers
To add dividers into the list, you’ll use ListView.separated
instead of ListView.builder
. Replace the Scaffold’s body
with the code below:
body: ListView.separated(
itemCount: _members.length,
itemBuilder: (BuildContext context, int position) {
return _buildRow(position);
},
separatorBuilder: (context, index) {
return const Divider();
}),
Using ListView.separated
gives you a separatorBuilder
option, which allows you to add a Divider between the list tiles. Now that you have dividers, you also removed the padding from the builder.
Hot reload. Now, you’ll see dividers between the rows:
To add padding to each row, wrap ListTile
with Padding
inside _buildRow
. The easiest way to do that in VS code is to put your cursor on ListTile
and press Command-. on macOS or Control-. on Windows. Then increase the padding to 16.0
, as shown in the animated GIF below.
Alternatively, you can replace _buildRow
with the following code to achieve the same result:
Widget _buildRow(int i) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: ListTile(
title: Text('${_members[i]['login']}', style: _biggerFont),
),
);
}
ListTile
is now a child widget of Padding
. Hot reload to see the padding appear on the rows, but not on the dividers.