Building with Flutter
Flutter is a mobile cross-development platform still in its beta. Flutter most importantly gives us the native feel with a better performance as compared to its competitors. In this article, we will learn why Flutter is a good option for development and we will also create a simple app with it. You can find the GitHub repository for this sample here.
Introduction
Many companies prefer to use “one stone to kill two birds.” Therefore opting for cross-platform development is a natural decision. Over the years, cross-development platforms have been introduced with the aim of achieving this feat. The reasons for this choice include:
- A quick prototype of a product.
- A wider range of users with lesser investments etc.
This option and decision obviously saves time and cost - two very important entities.
The earlier released cross-development platforms such as PhoneGap, Cordova, Ionic among others mostly used JavaScript and as such, they came with some bottlenecks. This was due to the extra effort required for the JavaScript code to communicate with the native code. According to a senior software engineer at Google here, these platforms needed a bridge to communicate with the native code thereby causing performance lapses in the process.
Why Flutter?
Flutter is an open source mobile app SDK from Google used in developing Android and IOS apps. The aim of Flutter is to produce the native feel which is rare in its competitors while utilizing a single codebase. Flutter considers differences in alignments, scrolling behaviors, typography, icons, and more when rendering your app to its platform. Flutter has been in development as far back as 2015 - the first commit which contained the AUTHORS
, LICENSE
and README.md
files was made on 30th October, 2015.
However, it was officially announced and presented to the world during the Google IO in 2017. And so, the question arises - is Flutter any different from other cross-development platforms ?
Yes! Definitely! And here are some of its features you should take note of:
- Better performance: One of the major reasons for the success and better performance of Flutter is the choice of language- Dart. Dart is compiled “ahead of time” (AOT) into native code and so it is easier for Flutter to communicate to the platform rather than use a bridge like the case of the JavaScript frameworks. Similarly, the way Flutter works, it needs to create and destroy a large number of short-lived objects very fast. Dart’s garbage collection is very well suited for such a use case.
- Hot reload: This is a feature that helps you update your code on the fly without restarting the entire app.
- A new approach to Widgets: In Flutter, everything is a widget. From layouts to alignments to padding, etc.
- Flutter is reactive: Flutter is inspired by React Native and just like React Native, it provides reactive-style views.
Setting up!
Before we get started with coding. We need to setup Flutter on our system. You can do that following the steps here:
- Clone the GitHub repository.
git clone -b beta https://github.com/flutter/flutter.git - Add Flutter to your PATH. Follow these instructions
- Run this command in your terminal:
flutter doctor
This is so you will be alerted of any other necessary dependency needed to complete the setup. You can always fall back to the Flutter docs if you encounter any hitch while setting this up.
Before going ahead, you should also have the following:
- An IDE(Integrated Development Environment). You can use Intellij IDEA, Android Studio or Visual Studio Code. You can learn how to configure them here.
- An introductory knowledge of OOP(Object Oriented Programming).
What we will Build
In this demo, we will make use of the Rick and Morty API to build a simple Flutter app that will do the following:
- Fetch all the characters from the API and parse the
JSON
response. - Setup a list view to display the response which includes the image and the name of a character.
Building our app
Open your IDE and create a new Flutter application. We will name it rick_and_morty
. This conforms to the naming conventions of Flutter projects (i.e small letters with underscores only). The project will have this file structure.
The only functionality we will implement here that will require an external package is loading of images. So, go ahead, open your pubspec.yaml
file and add the cached_network_image
package:
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.0
cached_network_image: "^0.3.0"
# Other dependencies if you so which!
Click the ‘Packages get’ button at the top right corner so that the package will be fetched. The rest of our modifications for this app will be done in the main.dart
file. The default app comes with a whole lot of boilerplate codes that we do not need for this demo. You should clear them out and have just this in the file:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
var httpClient = new HttpClient();
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Rick and Morty',
home: new RickAndMorty(),
);
}
}
Here in this snippet, we have a class which extends a stateless widget. And in the overridden build
method, we return a MaterialApp with some modified properties. The title property is changed to ‘Rick and Morty’ - this will reflect as the text on our app bar. We also modified the home property to return yet another widget which we will implement later on. The class is called in the main
method and this is the widget shown when we run our app. We also created an instance of HttpClient
which we will use for our network calls.
The RickAndMorty
widget which is returned through the home property is going to be a stateful widget. Flutter has two types of widgets, stateless and stateful widgets. Stateless widgets are widgets whose properties cannot change - that is the equivalent of constants while stateful widgets are widgets whose properties can change. Let us now set up our RickAndMorty
widget. Paste this snippet still in your main.dart
file:
class RickAndMorty extends StatefulWidget {
@override
createState() => new RickAndMortyState();
}
class RickAndMortyState extends State<RickAndMorty> {
var modelList = <Model>[];
final _customFont = const TextStyle(fontSize: 18.0);
@override
void initState() {
super.initState();
_getRickAndMortyCharacters();
}
@override
Widget build(BuildContext context) {
return new Scaffold (
appBar: new AppBar(
title: new Text('Rick and Morty'),
),
body: _buildListView()
);
}
}
Variables and methods prefixed with _ are private to the class.
To create a stateful widget, we create a class which extends StatefulWidget
, we then override the createState
method to return a new class which extends State<RickAndMorty>
. We did exactly that in the snippet above. According to the docs:
If you want to visually present stateful data in a widget, you should encapsulate this data in a
State
object. You can then associate yourState
object with a widget that extends theStatefulWidget
class.
In the RickAndMortyState
class, we created a new list to hold custom data. This data is contained in the Model
class stored in the model.dart
file. Create a new file model.dart
and set it up like this:
class Model{
String name;
String imageUrl;
}
The class contains two strings to hold the name of the character and the image URL.
Still in the RickAndMortyState
class, we overridded the initState
method and made our network call here by calling the _getRickAndMortyCharacters
function. Setup the function within the class like this:
_getRickAndMortyCharacters() async {
var url = 'https://rickandmortyapi.com/api/character';
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((LogRecord rec) {
print('${rec.level.name}: ${rec.time}: ${rec.message}');
});
var _LOG = new Logger("R&M");
var httpClient = new HttpClient();
List<Model> result = <Model>[];
try {
var request = await httpClient.getUrl(Uri.parse(url));
var response = await request.close();
if (response.statusCode == HttpStatus.OK) {
var json = await response.transform(UTF8.decoder).join();
var data = JSON.decode(json);
for (var k in data['results']) {
var model = new Model();
model.name = k['name'].toString();
model.imageUrl = k['image'].toString();
result.add(model);
}
httpClient.close();
} else {
_LOG.severe(response.statusCode);
_LOG.severe(response);
}
} catch (exception) {
_LOG.severe(exception);
}
if (!mounted) return;
setState(() {
modelList = result;
});
}
This function performs our network call and assigns the result to our modelList
. The setState
function is an internal function used to inform the framework of a change in the object.
Finally, in the build
method of the RickAndMortyState
class, we return a widget where we still specify the title and body. Here, the body is another method - _buildListView
which returns a widget and setups our list-view. Create a function within the class and set up the method like this:
Widget _buildListView(){
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: modelList.length,
itemBuilder: (context, i) {
// Add a one-pixel-high divider widget before each row in theListView.
if (i.isOdd) return new Divider();
return _buildRow(modelList[i]);
}
);
}
In the ListView.builder
, we added padding to the view, we set our length to be the size of the list, we returned a divider line to add some swagger to our list, and we returned another widget by calling the _buildRow
function. The _buildRow
function describes how a single item in the list will look like. Create the function within the class and set it up like this:
Widget _buildRow(Model model) {
return new ListTile(
title: new Text(
model.name,
style: _customFont,
),
leading: new CachedNetworkImage(
imageUrl: model.imageUrl,
placeholder: new CircularProgressIndicator(),
errorWidget: new Icon(Icons.error),
)
);
}
The list item will have an image and a text. The image is added using the leading property. This property is configured to be the first item horizontally on the list and it precedes the title property which we used for the character’s title. We used the CachedNetworkImage
package to load the image. When we run our app, we should see something like this:
Some more awesome resources!
- https://github.com/Solido/awesome-flutter#introduction
- https://codelabs.developers.google.com/codelabs/flutter
- https://medium.com/@matthew.smith_66715/why-we-chose-flutter-and-how-its-changed-our-company-for-the-better-271ddd25da60
- https://hackernoon.com/why-native-app-developers-should-take-a-serious-look-at-flutter-e97361a1c073
Conclusion
In this article, we have been introduced to yet another cross-development platform - Flutter. We were able to evaluate reasons why Flutter is a good choice for developing mobile apps as against earlier released platforms. Some of the reasons we were able to unravel include better performance, its reactive nature, its material design implementation, etc.
We were equally able to build a simple demo app where we consumed an API to show off some awesome qualities of Flutter. Flutter has already produced some success stories. Feel free to play around with the GitHub repository. I can’t wait to see the next great thing you will build with this platform. Holla if you get stuck!
This post was first published here
I just wondering if we could use future for syncing _getRickAndMontyCharacters method. That is what crossing my mind logically.