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?
Parsing to Custom Types
In the previous section, the JSON parser took the members in the JSON response and assigned them to _members
. Although you defined the list as dynamic
, the actual type that Dart put in the list was Map
, a data structure that holds key-value pairs. This is the equivalent of a Map in Kotlin or a Dictionary in Swift.
However, you also want to be able to use your own custom types.
Add a new Member
type at the bottom of main.dart:
class Member {
Member(this.login);
final String login;
}
Member
has a constructor that sets the login property when you create a member object.
Update the _members
declaration in _GHFlutterState
so that it’s a list of Member
objects:
final _members = <Member>[];
You used final
in place of var
because, instead of reassigning a new list to _members
, you’re going to add items to the existing list now.
Replace setState
in _loadData
with the following code:
setState(() {
final dataList = json.decode(response.body) as List;
for (final item in dataList) {
final login = item['login'] as String? ?? '';
final member = Member(login);
_members.add(member);
}
});
This turns each decoded map into a Member
and adds it to the list of members.
?
and double ??
question marks? Check out the Dart Basics tutorial for more details about these operators.Flutter is still complaining inside ListTile
because it was expecting Map
rather than Member
. Replace the title
line of ListTile
with the following:
title: Text('${_members[i].login}', style: _biggerFont),
You’ll see an error if you try a hot reload, so hot restart. You’ll see the same screen as before, except that it now uses your new Member
class.
Downloading Images With NetworkImage
In GitHub, each member has a URL for their avatar. Your next improvement is to add that avatar to the Member
class and show the avatars in the app.
Update Member
to add an avatarUrl
property. It should look like this now:
class Member {
Member(this.login, this.avatarUrl);
final String login;
final String avatarUrl;
}
Since avatarUrl
is now a required parameter, Flutter complains at you in _loadData
. Replace the setState
callback in _loadData
with the following updated version:
setState(() {
final dataList = json.decode(response.body) as List;
for (final item in dataList) {
final login = item['login'] as String? ?? '';
final url = item['avatar_url'] as String? ?? '';
final member = Member(login, url);
_members.add(member);
}
});
The code above uses the avatar_url
key to look up the URL value in the map parsed from JSON, then set it to the url
string, which you pass on to Member
.
Now that you have access to the URL for the avatar, add it to your ListTile
. Replace _buildRow
with the following:
Widget _buildRow(int i) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: ListTile(
title: Text('${_members[i].login}', style: _biggerFont),
leading: CircleAvatar(
backgroundColor: Colors.green,
backgroundImage: NetworkImage(_members[i].avatarUrl),
),
),
);
}
This adds a CircleAvatar
to the leading edge of your ListTile
. While you’re waiting for the images to download, the background of the CircleAvatar
will be green.
Do a hot restart rather than a hot reload. You’ll see your member avatars in each row:
Cleaning up the Code
Most of your code is now in main.dart. To make the code a little cleaner, you’ll refactor classes into their own files.
Create files named member.dart and ghflutter.dart in the lib folder. Move Member
into member.dart and both _GHFlutterState
and GHFlutter
into ghflutter.dart.
You won’t need any import statements in member.dart, but the imports in ghflutter.dart should be:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'member.dart';
import 'strings.dart' as strings;
You also need to update the imports in main.dart. Replace the entire file with the following:
import 'package:flutter/material.dart';
import 'ghflutter.dart';
import 'strings.dart' as strings;
void main() => runApp(const GHFlutterApp());
class GHFlutterApp extends StatelessWidget {
const GHFlutterApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: strings.appTitle,
// TODO: add theme here
home: const GHFlutter(),
);
}
}
Save everything and rerun the app. You won’t see a change, but the code is now a little cleaner. :]
You might have noticed a new comment: // TODO: add theme here
. There, you’ll make one last change before the tutorial is over.
Adding a Theme
Your final improvement is to easily add a theme to the app by adding a theme attribute to the MaterialApp
you created in main.dart.
Find // TODO: add theme here
and replace it with the following:
theme: ThemeData(primaryColor: Colors.green.shade800),
Here, you’re using a shade of green as a Material Design color value for the theme.
Save and hot reload to see the new theme in action:
The app screenshots so far have been from the Android emulator. You can also run the final themed app in iOS Simulator:
And here’s how it looks on the Chrome web browser:
Feel free to run it as a Windows, Mac or Linux app as well. It only requires a little extra setup and adding desktop support to your app. On macOS, you should also give the app permission to access the internet.
Here’s the app running on macOS:
Now that’s what you call cross-platform! :]
Where to Go From Here?
Download the completed project files by clicking the Download Materials button at the top or bottom of the tutorial.
You can open the project in either VS Code or Android Studio.
Open it in VS Code by opening the root folder. You’ll need to fetch packages before running the project. Do so by pressing Command-Shift-P on MacOS or Control-Shift-P on Windows or Linux to open the command palette and running the Flutter: Get Packages command.
To open the project in Android Studio, choose Open an existing project from the Welcome to Android Studio screen and navigate to choose the root folder of the final project. Then choose Get dependencies on the 'Pub get' has not been run
line in Android Studio.
There’s a lot more to learn about Flutter and Dart. Here are some places to start:
- The main Flutter page at flutter.dev. You’ll find lots of great documentation and other information.
- Learn more about widgets in Introduction to Widgets.
- If you’re coming from another platform, check out the Flutter guides for Android, iOS and React Native developers.
- For a deep dive into the most important aspects of Flutter, read our Flutter Apprentice book.
And you’ll definitely want to browse the raywenderlich.com Flutter library! There’s an ever-increasing selection of books, articles and videos to help you on your Flutter journey.
We hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!