Building with Flutter

Published Apr 05, 2018

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:

  1. 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.
  2. Hot reload: This is a feature that helps you update your code on the fly without restarting the entire app.
  3. A new approach to Widgets: In Flutter, everything is a widget. From layouts to alignments to padding, etc.
  4. 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:

  1. Clone the GitHub repository.
    git clone -b beta https://github.com/flutter/flutter.git
  2. Add Flutter to your PATH. Follow these instructions
  3. 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:

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 your State object with a widget that extends the StatefulWidget 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

Discover and read more posts from Idorenyin Obong
get started