Skip to content
Elvis Chidera

How to build a Cryptocurrency price list app using Flutter SDK

flutter, dart9 min read

Flutter is Google’s new open-source toolkit for helping developers build iOS and Android apps with just one codebase. Flutter apps are written in the Dart programming language and compile to native code, so the performance is really, really great.

In this tutorial, I will show you how to use Flutter to build an app that shows the current price of different cryptocurrencies. I will walk you through the fundamentals of Flutter and Dart.

banner

Before we start, install Flutter and the Flutter editor plugin if you haven’t done so already. The installation should be straight-forward, but if you bump into issues you can leave a comment on this post and I will be glad to help out.

For this tutorial, I will be using Android Studio, but you can also use IntelliJ or Visual Studio Code.

Also, some object-oriented programming (OOP) experience is required. Relax! You don’t need years of experience — if you know what classes and objects are, you should be fine.

Let’s get started

On Android Studio or IntelliJ, click on the File menu -> New -> New Flutter Project. If you don’t see the New Flutter Project option, ensure you have installed the Flutter plugin. If you are using Visual Studio Code, follow the steps here to create a new project.

image1

When the page opens, select Flutter Application and click on the Next button.

image2

The next page lets you configure the project. You can use a similar configuration like the image below. Just ensure that the Flutter SDK path points to the directory where you downloaded Flutter.

image3

The last page lets you configure your company domain name, and you can set it to any domain name. After that, click on the Finish button.

The project creation should begin after clicking the finish button, which usually takes a few minutes.

When it’s done, your project should look like this.

image4

A file called main.dart was created in the lib folder. It contains code for a demo app. Since we will be building our app from scratch, open up the main.dart file and delete/clear all the code in it.

If your project includes a test directory that contains the file widget_test.dart, delete this file before continuing. It contains tests for the code we just deleted.

Flutter apps are written in the Dart programming language. The main.dart file is a Dart source file (.dart extension). The Dart convention is to name source files using lowercase_with_underscores.

Let’s start writing some Dart code. We will begin with the programming tradition: printing “Hello World!”

To do that we will have to create something called the main function. The main function is a top-level function that every Flutter app has that serves as the entry point into your app. Think of it like the entrance to a house.

image5

When you run your app on a device, execution will start from the main function. Let’s create a simple main function, so enter the following code into your main.dart file.

main.dart
1// This is where the app starts executing.
2void main() {
3 print('Hello World'); // Prints Hello World! to the console
4}

As you can see, creating the main function is easy. The second line contains the main function declaration: its return type(void) and name (main). The main function returns void meaning it returns nothing.

The third line does the printing to the console. We call the print function and pass a string argument to it. Note that in Dart, you can use single quotes (‘string’) or double quotes (“string”) when declaring a string literal.

To run the code, click on the green run (play) button at the top of Android Studio or IntelliJ. Ensure you have an actual device connected or you have an emulator running.

image6

After the app successfully starts, you should see “Hello World!” printed on the console.

image7

But if you check your device or emulator, you will see something disappointing.

A blank screen

Well, this was expected, as we are currently only printing to the console. There was nothing added to the app UI, and that is why it is blank.

So let’s fix this by adding some elements to the app UI. Our app will be using material design, so let’s add a package to the main.dart file to help with that.

main.dart
1import 'package:flutter/material.dart';

Just like any modern programming language, you can import a library/package to use in your code. Here we are importing the material.dart package. This package contains code that helps us create a material styled app.

The material.dart package has a function called runApp. The runApp takes a widget and attaches it to the screen. Well, what is a widget?

Widgets

You can think of widgets as views or UI elements. They are the things you see (and some you don’t see) when you run your app on a device. In Flutter, you will be using widgets a lot, because the main idea is that your app UI is made entirely out of widgets.

Flutter already comes with a suite of powerful widgets like text and images. The material.dart package we just imported has several material design widgets that we will be using shortly.

Let’s use the runApp method now to show “Hello World!” at the center of the device screen. Replace the content of the main function with the code below.

main.dart
1void main() {
2 print('Hello World!');
3
4 // Runs the MaterialApp widget
5 runApp(new MaterialApp(
6 // This is the widget that is displayed first when the application is started normally
7 home: new Center(
8 // The Text widget is wrapped in a center widget to center it on the screen
9 child: new Text('Hello World!'),
10 ),
11 ));
12}

Let me explain some of the new stuff in the code above

  1. new: To create an object, you usually use the new keyword with a constructor for a class. (OOP)

  2. new MaterialApp(): Here we are creating a new widget object called MaterialApp. The MaterialApp widget creates a number of useful things needed by a material design app.

  3. home:: In Dart, we can clearly state the name of each parameter within the function/constructor call. The widget passed in as the home parameter is displayed first when the app is started normally.

  4. new Center(child: new Text('Hello World!')) : We wrap the Text widget inside a Center widget so that the text gets centered on the screen. The Text widget is a child of the Center widget. Yes, widgets can be nested.

image10

If you run the code again and open up your device, you should get a slightly better screen now.

image11

Cool! We were able to show an ugly looking text centered on the device screen.

The next steps

Let’s take a few steps forward now. We will be getting the cryptocurrency prices from the CoinMarketCap API. The API returns a JSON array. Here is a sample response from the API:

coin_market_cap_api_response.json
1[
2 {
3 "name": "Bitcoin",
4 "price_usd": "11525.7",
5 "percent_change_1h": "-0.18",
6 ...
7 },
8 ...
9]

We will be making a request to the CoinMarketCap API and decoding the JSON from the app. We will have to include a couple of new packages to the main.dart file.

main.dart
1import 'dart:async';
2import 'dart:convert';
3import 'package:http/http.dart' as http;

Here’s an overview of the new packages:

  1. dart:async: Provides the Future class, which I will talk more about below.

  2. dart:convert: Provides the json variable we will use to decode the JSON string response.

  3. package:http/http.dart: Provides the function we will use to make HTTP GET requests.

Let’s add a new function to the main.dart file that makes a request to the CoinMarketCap API.

main.dart
1Future<List> getCurrencies() async {
2 String apiUrl = 'https://api.coinmarketcap.com/v1/ticker/?limit=50';
3 // Make a HTTP GET request to the CoinMarketCap API.
4 // Await basically pauses execution until the get() function returns a Response
5 http.Response response = await http.get(apiUrl);
6 // Using the JSON class to decode the JSON String
7 return JSON.decode(response.body);
8}

Let’s walk through the new code

-> Future<List> : We are basically saying that the getCurrencies() function will return a List sometime in the future. It will make an HTTP request to the CoinMarketCap API and return a List of currencies when done.

The getCurrencies() function is asynchronous. If you have some JavaScript experience, you can think of Futures as Promises. I created the images below to help you understand Futures in Dart.

Bob calls the asynchronous function askKateForBalloons() which returns a Future<Balloons>

Bob can keep the Future<Balloons>

Bob can know when the Future completes

-> async and await :

Await expressions let you write asynchronous code almost as if it were synchronous. The http.get(url) function is asynchronous, returning a Future<Response> immediately when it’s called. We want to wait for the Response so we can decode the JSON string, but we also don’t want to use ugly callbacks.

The await expression evaluates http.get(url), and then suspends the currently running function (getCurrencies()) until the result is ready — that is, until the Future has completed.

To use await, the code must be in a function marked as asynchronous. An async function is a function whose body is marked with an async modifier. When you call an async function, it immediately returns a Future. The body of the function is scheduled for execution later.

You can read more about async and await in Dart here.

-> http.get(url) : Makes a HTTP GET request to the CoinMarketCap API. This function is asynchronous and returns a Future immediately.

  1. JSON.decode(response.body) : Decodes the JSON string response.

Let’s test the getCurrencies() function we just created. We do that by making a call to it in our main function and printing the returned value to the console.

main.dart
1// Since we are using await within the main function, we have to make it asynchronous too
2void main() async {
3 // Testing the getCurrencies function
4 // We wait for the currency data to arrive
5 List currencies = await getCurrencies();
6 // Before printing it to the Console
7 print(currencies);
8
9 runApp(new MaterialApp(
10 home: new Center(
11 child: new Text('Hello World!'),
12 ),
13 ));
14}

If you run the code above, you should see the API response printed to the console.

image15

Well, in the real world bad things can happen. For example, you might not be connected to the internet, so the request to the CoinMarketCap API will fail. For this tutorial, we will assume we are in Wakanda.

In a production app, you will have to handle network failure. You do that by putting the HTTP call in a try…catch block.

Building out the UI

Now that we have a list of currencies, let’s go ahead to build the UI to show that list.

When writing Flutter apps, you’ll usually create new widget classes. A widget’s main job is to implement a build function, which describes the widget in terms of other, lower-level widgets.

Let’s create a new Widget called CryptoListWidget. Add the code below to the bottom of your main.dart file.

main.dart
1class CryptoListWidget extends StatelessWidget {
2
3 // This is a list of material colors. Feel free to add more colors, it won't break the code
4 final ListMaterialColor _colors = [Colors.blue, Colors.indigo, Colors.red];
5 // The underscore before a variable name marks it as a private variable
6 final List _currencies;
7
8 // This is a constructor in Dart. We are assigning the value passed to the constructor
9 // to the _currencies variable
10 CryptoListWidget(this._currencies);
11
12 @override
13 Widget build(BuildContext context) {
14 // Build describes the widget in terms of other, lower-level widgets.
15 return new Text('Hello World!');
16 }
17
18}

Let’s walk through the new code above:

  1. StatelessWidget : You will usually create Widgets that are subclasses of either StatelessWidget or StatefulWidget, depending on whether your widget manages any state. We are using StatelessWidget because we have our data already and we won’t be updating it in this tutorial.

  2. final List<MaterialColor> colors : Variables in a StatelessWidget should be final (meaning they are constant or do not change). Here we are declaring a final variable that holds a list of MaterialColors. The underscore () before the variable name makes it private (inaccessible from other classes).

  3. CryptoListWidget(this._currencies) : This is the constructor for our widget. It assigns the list of currencies we pass into the constructor to the _currencies variable.

  4. build function: The build method returns a lower-level Widget (Text) that describes how it will look.

Let’s replace the Text widget in the build function above with a new widget called Scaffold. The Scaffold widget implements the basic material design visual layout structure. Replace the build function above with the one below.

main.dart
1@override
2 Widget build(BuildContext context) {
3 return new Scaffold(
4 body: _buildBody(),
5 backgroundColor: Colors.blue,
6 );
7 }

The Scaffold class provides APIs for showing drawers, floating an action button, bottom bar, snack bar, and so on. We will be adding a floating action button later.

image16

You should get a warning that _buildBody() isn’t defined for the class CryptoListWidget. Let’s go ahead to create _buildBody() function inside of the CryptoListWidget class.

main.dart
1Widget _buildBody() {
2 return new Container(
3 // A top margin of 56.0. A left and right margin of 8.0. And a bottom margin of 0.0.
4 margin: const EdgeInsets.fromLTRB(8.0, 56.0, 8.0, 0.0),
5 child: new Column(
6 // A column widget can have several widgets that are placed in a top down fashion
7 children: <Widget>[
8 _getAppTitleWidget(),
9 _getListViewWidget()
10 ],
11 ),
12 );
13}

The syntax here should be familiar. We are using two new Widgets:

  1. Container widget: A Container widget is just a container :) (for other widgets).

  2. Column widget: A Column widget layouts a list of child widgets in the vertical direction. It has a parameter called children that takes a list of widgets.

Let’s create the two functions we called in the _buildBody() function. The first one is _getAppTitleWidget() .

main.dart
1Widget _getAppTitleWidget() {
2 return new Text(
3 'Cryptocurrencies',
4 style: new TextStyle(
5 color: Colors.white,
6 fontWeight: FontWeight.bold,
7 fontSize: 24.0),
8 );
9}

This function returns a regular Text widget with a style that makes it bold and white with a font size of 24.0.

The text is going to look like this when we run the app.

image17

We can’t run the app yet because we haven’t created the other function called _getListViewWidget(). Let’s quickly create it using the code below.

main.dart
1Widget _getListViewWidget() {
2 // We want the ListView to have the flexibility to expand to fill the
3 // available space in the vertical axis
4 return new Flexible(
5 child: new ListView.builder(
6 // The number of items to show
7 itemCount: _currencies.length,
8 // Callback that should return ListView children
9 // The index parameter = 0...(itemCount-1)
10 itemBuilder: (context, index) {
11 // Get the currency at this position
12 final Map currency = _currencies[index];
13
14 // Get the icon color. Since x mod y, will always be less than y,
15 // this will be within bounds
16 final MaterialColor color = _colors[index % _colors.length];
17
18 return _getListItemWidget(currency, color);
19 }));
20}

The _getListViewWidget() returns a ListView widget that is wrapped in a Flexible widget. We use the ListView.builder to easily create the list. We pass in an itemCount which tells the builder how many currencies to show.

The itemBuilder callback will be called for each item and you have to return a new widget. In the code above we are calling a function called _getListItemWidget() that returns a Widget.

Before we create the _getListItemWidget() function, let’s quickly create the individual elements for the ListView item widget. We want each item in the ListView to look like this:

image18

So, we have three main widgets:

  1. A round icon widget with the currency name’s first character

  2. A text widget with the currency name

  3. A text widget with the price and percent change in 1 hour.

Let’s create the widgets. For simplicity sake, I created a function for each of them. The first function called _getLeadingWidget() returns the round icon with the text.

main.dart
1CircleAvatar _getLeadingWidget(String currencyName, MaterialColor color) {
2 return new CircleAvatar(
3 backgroundColor: color,
4 child: new Text(currencyName[0]),
5 );
6}

The widget will look like this:

image19

The second function called _getTitleWidget returns the Text widget for the currency name.

The third function called _getSubtitleWidget() returns the Text widget for the currency current price and percent change in 1 hour.

main.dart
1Text _getTitleWidget(String currencyName) {
2 return new Text(
3 currencyName,
4 style: new TextStyle(fontWeight: FontWeight.bold),
5 );
6}
7
8Text _getSubtitleWidget(String priceUsd, String percentChange1h) {
9 return new Text('\$$priceUsd\n1 hour: $percentChange1h%');
10}

Both of these widgets should look like this:

image20

Let’s wrap all three widgets in something called a ListTile widget. The ListTile widget is based on the Material Design List documentation. It shows all three widgets in this style.

image21

We will create a new function called _getListTile that returns a ListTile widget.

main.dart
1ListTile _getListTile(Map currency, MaterialColor color) {
2 return new ListTile(
3 leading: _getLeadingWidget(currency['name'], color),
4 title: _getTitleWidget(currency['name']),
5 subtitle: _getSubtitleWidget(
6 currency['price_usd'], currency['percent_change_1h']),
7 isThreeLine: true,
8 );
9}

Finally, let’s create the _getListItemWidget() . It is going to return a Container widget that has a top padding of 5.0 and has a Card widget child. The card widget has the ListTile returned by _getListTile as it’s child.

main.dart
1Container _getListItemWidget(Map currency, MaterialColor color) {
2 // Returns a container widget that has a card child and a top margin of 5.0
3 return new Container(
4 margin: const EdgeInsets.only(top: 5.0),
5 child: new Card(
6 child: _getListTile(currency, color),
7 ),
8 );
9}

The widget will look like this.

image22

We have successfully completed our CryptoListWidget. But we have to update the main function to show the newly created widget instead of the Text widget.

main.dart
1void main() async {
2 // Bad practice alert :). You should ideally show the UI, and probably a progress view,
3 // then when the requests completes, update the UI to show the data.
4 List currencies = await getCurrencies();
5 print(currencies);
6
7 runApp(new MaterialApp(
8 home: new CryptoListWidget(currencies),
9 ));
10}

That’s it. You can hit the run button again. If everything works well and you are connected to the internet, you should a screen that looks like this.

image23

Really cool right?

The app you see will be slightly different from the screenshot above:

  1. It does not have a floating action button at the bottom right.

  2. The text color of the percent change in 1 hour is black.

I decided not to include them in the tutorial. But you can check the project Github repository to see how I was able to achieve it.

The completed app can be downloaded here.

Thanks for reading and hope you enjoy Flutter as much as I do.

© 2024 by Elvis Chidera. All rights reserved.