Here is the GitHub – COVID-19 Tracker link for this project.
Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
For help getting started with Flutter, view the online documentation, which offers tutorials, samples, guidance on mobile development, and a full API reference.
Photo Preview
What Motivated me to do this project
Most of us learners are at home trying to stay safe and quarantined. The media is giving a lot of emphasis to the number of COVID-19 cases, and the “flatten the curve” movement is trending everywhere, so I thought of using my flutter skills to make a COVID-19 tracking app with graphs, because graph provides us with Visual representations of what is happening in a concise manner. In this blog, we will build together this app and learn a few concepts regarding the flutter framework.
Glossary
Stateless Widget:
These widgets do not depend on any mutable configurations, they build once using fixed parameters, and that’s it. The parameters can not change after that. They are ideal for buttons, icons, etc. They do not hold any state information; we can not use them to store any piece of information.
Stateful Widget:
They are used to store state-based information; their information(stored data) do not die on refresh. It survives hot reload and refresh. They are ideal when we need to store any data for some time.
Scaffold:
Scaffold widget makes Flutter developer’s life easy by providing APIs for drawers, BottomNavigationBar, FloatingActionButton, AppBar, etc. They are greedy, which means they occupy the whole screen available to them.
AppBar:
This widget is useful in building the top of the screen. They contain space for leading widgets, title, and action buttons. We are also allowed to add things (widgets) in the flexible area, and bottom of the App Bar.
ListView:
ListView widget is a scrollable list of widgets arranged linearly. It is useful in scrolling over any combination of linearly arranged widgets. We are using ListTile Widgets in ListView displays in this project.
ListTile:
ListTile widget is a row which contains space of text, with additional blocks for leading and trailing icons.
Navigator:
This widget manages a set of child widgets in a stack-based manner; we use this widget to jump from one page to another.
Plan
In this article, we are going to learn how to make a COVID-19 tracker App for android and IOS using Google’s Flutter Framework. Flutter framework is a Google’s UI toolkit that can is useful in creating a beautiful natively compiled application for android, ios, and the web.
Steps
Step 1:
Add all the required dependencies in the “pubspec.yaml” file. We are going to use HTTP(for making API calls), and charts_flutter for drawing charts. ^ icon in the front of versions is saying that give me this version or something compatible with this version.
dependencies: | |
flutter: | |
sdk: flutter | |
cupertino_icons: ^0.1.2 | |
http: ^0.12.0+4 | |
charts_flutter: ^0.9.0 |
Step 2:
Edit main.dart file, as shown below. This represents the basic initial setup of the app with routes. We are using the MaterialApp widget to use the inbuilt material built-ins. initialRoutes tells us the starting/landing page of our app, and routes describe the navigation flow of our app.
Since we have only one more page to navigate to, so we are using ‘/graphs’ as the only other route. We are going to discuss routes and Navigation in the next steps.
-
- MaterialApp is used for all material design apps.
- The build function is called every time the Wiget is built and attached to a Widget Tree.
- It is mandatory to write initialRoute when the home page is not defined. As in this case, the initialRoute points to MyHomePage Widget.
import 'package:flutter/material.dart'; | |
import 'dart:convert' as convert; | |
import 'package:http/http.dart' as http; | |
import 'graph.dart'; | |
void main() => runApp(MyApp()); | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
initialRoute: '/', | |
routes: { | |
'/': (context) => MyHomePage(), | |
'/graph': (context) => GraphPage(), | |
}, | |
title: 'Corona Tracker', | |
); | |
} | |
} |
Step 3:
The next step is to make a class CallAPI. The only responsibility of this class is to fetch the data from thevirustracker API and then call the function passed to the getData function.
This is the speciality of the dart language because here we can give a function as an argument, as functions are treated as first-class citizens in Dart. The call to the API is asynchronous, which means that our program will keep running and not wait for data and block code execution.
This asynchronous code execution makes dart a powerful language for app development, We are using async and await keyword to do exactly that. It is advisable to check the response.statusCode and show the proper error message if necessary before proceeding further.
-
- We are using the “convert” library to convert the response body to the JSON structure.
- We can check the response status using response.statusCode; it is 200 for OK. 400/500 code means there is something wrong; in that case, print the error message or log it somewhere as per your requirement.
typedef void func(); | |
var jsonResponse; | |
class CallAPI{ | |
static String url = 'https://api.thevirustracker.com/free-api?countryTimeline=US'; | |
static void getData(func f) async{ | |
var response = await http.get(url); | |
if (response.statusCode == 200) { | |
jsonResponse = convert.jsonDecode(response.body); | |
print("API SUCCESS!"); | |
f(); | |
} else { | |
print('Request failed with status: ${response.statusCode}.'); | |
} | |
} | |
} |
Step 4:
Define the class that will hold individual data objects. num represents the total number of positive COVID-19 cases, and the date contains the date in string format.
-
- We are defining classes instead of Lists of dates and nums because it is easier to pass in a bundle for any Widget.
class DataPoint{ | |
String date; | |
int num; | |
DataPoint(this.date, this.num); | |
} |
Step 5:
This is the most essential part of this app. Here we are building a Stateful Widget named MyHomePage. Stateful widgets are unique widgets, they can hold information that stays(unlike stateless Widget where data is lost after each build). Firstly we override the initstate function(which is called once in the beginning) to load the data, here we pass loadData function which gets called after data is fetched from the remote server.
loadData stores the JSON response in a DataPoint list by filtering only required fields. LoadData function calls setState to refresh the appearance based on the data fetched(it takes only one frame to refresh). The build function returns the basic Scaffold object.
The icon at the appBar is used to navigate to the graph page. We are passing the list using the arguments parameter of the Navigator.pushNamed function. Navigator in flutter is also a widget that stores the navigation path in a stack we can push and pop paths using Navigator.push() and Navigator.pop() based on our requirement.
-
- Use initState to initialize the Database connection, data loading, etc. Use dispose for breaking the connection and other such tasks.
- setState function calls the build method in the next frame.
- Setstate takes a function as its argument; it calls this function before refreshing.
- We have two types of screens here; we are selecting them based on whether there is some data in jsonResponse.
- InkWell is a material Widget that gives us the splash effect.
- When InkWell is tapped, onTap function is executed, we can use this function to Navigate to other pages as per our requirement.
- Navigation in flutter is performed using Navigator Widget, which works like a stack, which means we have push and pop functionality.
class MyHomePage extends StatefulWidget { | |
@override | |
_MyHomePageState createState() => _MyHomePageState(); | |
} | |
class _MyHomePageState extends State<MyHomePage> { | |
List<DataPoint> timeline = List<DataPoint>(); | |
void loadData(){ | |
setState(() { | |
jsonResponse = jsonResponse['timelineitems'][0]; | |
// print(jsonResponse); | |
jsonResponse.forEach((key, val){ | |
if(key != 'stat'){ | |
//print(key); | |
//print(val['total_cases']); | |
timeline.add(DataPoint(key, val['total_cases'])); | |
} | |
}); | |
print(timeline.length); | |
print("\n\n"); | |
//print(timeline); | |
}); | |
} | |
@override | |
void initState() { | |
super.initState(); | |
CallAPI.getData(loadData); | |
} | |
@override | |
Widget build(BuildContext context) { | |
if(jsonResponse == null){ | |
return Scaffold( | |
appBar: AppBar( | |
title: Center( | |
child: Text( | |
'CoronaVirus Tracker', | |
), | |
), | |
backgroundColor: Colors.purple, | |
actions: <Widget>[ | |
Padding( | |
padding: const EdgeInsets.all(16.0), | |
child: InkWell( | |
child: Icon(Icons.show_chart, color: Colors.white,), | |
onTap: (){}, | |
splashColor: Colors.red, | |
), | |
), | |
], | |
), | |
body: Center(child: Text('Loading Data…'),), | |
); | |
} | |
else{ | |
return Scaffold( | |
appBar: AppBar( | |
title: Center( | |
child: Text( | |
'CoronaVirus Tracker', | |
), | |
), | |
backgroundColor: Colors.purple, | |
actions: <Widget>[ | |
Padding( | |
padding: const EdgeInsets.all(16.0), | |
child: InkWell( | |
child: Icon(Icons.show_chart, color: Colors.white,), | |
onTap: (){ | |
Navigator.pushNamed(context, '/graph', arguments: timeline); | |
}, | |
splashColor: Colors.red, | |
), | |
), | |
], | |
), | |
body: ShowListView(timeline), | |
); | |
} | |
} | |
} |
Step 6:
This is the final Widget of the main page of our App(ShowListView). ShowListView uses ListView.builder function to build listview, It returns a Column Widget, which contains the data and a divider.
class ShowListView extends StatelessWidget { | |
final List<DataPoint> timeline; | |
ShowListView(this.timeline); | |
@override | |
Widget build(BuildContext context) { | |
print(timeline.length); | |
print("\n\n"); | |
return ListView.builder( | |
itemCount: timeline.length, | |
itemBuilder: (context, index){ | |
return Column( | |
children: <Widget>[ | |
ListTile( | |
leading: Text("${timeline[index].date}"), | |
title: Center(child: Text("Total Cases: ${timeline[index].num}")), | |
), | |
Divider(), | |
] | |
); | |
} | |
); | |
} | |
} |
Step 7:
Now we are going to cover the graph.dart file. It contains GraphPage as its main Widget. In this Widget, we are accessing the passed arguments using “ModalRoute.of(context).settings.arguments“, this Widget returns a similar Scaffold with MyChartBody, which we will cover next.
import 'package:flutter/material.dart'; | |
import 'package:charts_flutter/flutter.dart' as charts; | |
import 'main.dart'; | |
List<DataPoint> args; | |
class GraphPage extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
args = ModalRoute.of(context).settings.arguments; | |
return Scaffold( | |
appBar: AppBar( | |
title: Center( | |
child: Text( | |
'CoronaVirus Tracker', | |
), | |
), | |
backgroundColor: Colors.purple, | |
), | |
body: MyChartBody(), | |
); | |
} | |
} |
Step 8:
MyChartBody makes an array of charts.Series with DataPoints as inputs, and it is passed to ExponentialCurve function as an argument.
class MyChartBody extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
final List<DataPoint> args = ModalRoute.of(context).settings.arguments; | |
var seriesList = [charts.Series<DataPoint, int>( | |
id: 'Sales', | |
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault, | |
domainFn: (_,ind) => ind, | |
measureFn: (DataPoint dp, _) => dp.num, | |
data: args)]; | |
return ExponentialCurve(seriesList); | |
} | |
} |
- Step 9: ExponentialCurve is a stateful widget that returns a RepaintBoundary Widget with charts.LineChart widget with all the data as an argument plus we have an animate parameter; for beautiful visual display, we are setting it to true.
class ExponentialCurve extends StatefulWidget { | |
final List<charts.Series<DataPoint, int>> lst; | |
ExponentialCurve(this.lst); | |
@override | |
_ExponentialCurveState createState() => _ExponentialCurveState(); | |
} | |
class _ExponentialCurveState extends State<ExponentialCurve> { | |
@override | |
Widget build(BuildContext context) { | |
return RepaintBoundary(child: charts.LineChart(this.widget.lst, animate: true,)); | |
} | |
} |
Click the run button, and you have your COVID-19 tracker App.
Reflective Analysis
After doing this project, I learned about the ways to refresh the page based on the arrival of data. I learned to use an API in an optimized way and store relevant information for use. As a result, I am now able to make apps based on various APIs without any hesitation.
When you learn to use APIs in apps, your view about app-building changes because there are thousands of APIs to support your app and make it more interesting. Flutter is an instrumental technology for building an app that scales. It is for professionals with tight deadlines because once a person is comfortable about the framework, he/she can confidently approach any design one can think of because flutter makes customizing widgets very easy. Flutter is becoming popular among developers because of its widget catalogue, as it helps in designing of all the apps one can think of.
Future Directions
- Add region-based information and allow users to filter based on regions because most people are mostly concerned about their locality.
- We can add some statistical measures to measure the spreading pattern of the virus as it can help people to take smart decisions.
- Add future simulation based on parameters entered by the users, because people will like to know and take decisions based on that.
- We can add the severity of a region based on recent regional COVID cases, and let the users know they lie in danger/safe zones.
Learning Tools and Strategies
- Use hot reload functionality for fast development time because it is one of the perks of using Dart language.
- Try experimenting with buttons, icons, and lists and other widgets as it contains most of the things one can think of. Use Widget Catalog for doing some research about new widgets.
- Always search for packages before implementing your own because most of the time, people have faced the challenges you are facing, and most probably they made some libraries for help. Use pub.dev to search for existing packages.
Here is the GitHub – COVID-19 Tracker link for this project.
I can’t wait to see your app on Google Play Store.
Happy Fluttering!
This is a very nice blog and educative. Following the current pandemic of covid-19, this application can be used to give a clear visual representation of the new cases of covid-19. Since the smart phone technology has taken over and almost everyone owns one its highly applicable in the current technology. It’s also show how flutter API can be used in building very nice applications.
The future directions of this project include.
– Showing a distinction between gender, splitting the new cases in terms of gender that is female and male.
– Add region-based information and allow users to filter based on regions because most people are mostly concerned about their locality.
– Provide graphs showing new cases as per locality to show the most affected areas.
– Add future simulation based on parameters entered by the users, because people will like to know and take decisions based on that.
– Include a safety tab to show how to prevent yourself from the covid-19 infection
Thanks for your valuable comment. I hope this article was helpful for you in grasping flutter concepts.
the api from thevirustracker.com doesn’t work