Flutter helps you to build mobile, web, and desktop applications from a single codebase.
In this article we will focus only on some features that I used to build my application. You can get the full source code of my COVID-19 app from GitHub for learning purposes.
Features you will learn to build in the app are:
1.Rest API: To get the corona cases and display them in the app.
2.Cloud Firestore: To get the corona state-wide helpline numbers from firebase.
3.News API: Get corona news from API.
4.Showing the pie chart: we will show a chart of corona cases with the help of data that we fetched from the rest API in section 2.
If you are new to the cloud_firestore, you can check out these Google code labs tutorials, to get your hands dirty with awesome firebase features.
Once you have created the Flutter project, open pubspec.YAML located in the root of your project and add the below-mentioned dependencies to your Flutter project. Under dependencies:
1.Http Library
2.Cloud Firestore Library
Note: After adding the Cloud Firestore dependency in the pubspec.YAML, you need to configure your app with firebase. Please refer to this tutorial.
After following the above cloud_firestore codelabs tutorial, you know that we need to create a collection structure in a cloud_firestore to load the documents, which will look like these:
Once you’ve created the collection structure and added data into the cloud_firestore, we can proceed to fetch the data into our Flutter app.
Use the code below to load the data in the app
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
class helpline extends StatefulWidget {
@override
_helplineState createState() => _helplineState();
}
class _helplineState extends State < helpline > {
@override
void initState() {
// TODO: implement initState
super.initState();
CheckConnectivity.isConnected()
.then((isConnected) {
if (!isConnected) {
CheckConnectivity.showInternetDialog(context);
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Helpcenters",
style: TextStyle(
fontWeight: FontWeight.w900, fontStyle: FontStyle.italic),
),
backgroundColor: Colors.green[700],
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
StreamBuilder(
stream: Firestore.instance.collection('helpline').orderBy("state").snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData)
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.green)),
);
return Expanded(
child: ListView(
children: snapshot.data.documents.map((document) {
return Column(
children: <Widget>[
ListTile(
onTap: (){
launchCaller(document['number'].toString());
},
title: Row(
children: <Widget>[
Text(document['state'].toString(),style: TextStyle(fontSize: 19,fontWeight: FontWeight.w500),),
],
),
subtitle: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.only(top:10),
child: Row(
children: <Widget>[
Icon(OMIcons.call,color: Colors.blueAccent,),
SizedBox(width: 20,),
Text(document['number'].toString(),style: TextStyle(color: Colors.blueAccent[200]),)
],
),
)
],
),
)
,Divider()
],
);
}).toList(),
));
},
)
],
),
);
}
Once you complete all the above steps you will see output like below:
To show the live corona cases we can use some open source as well as paid APIs to fetch data. Here you can get COVID-19 APIs: link1 and link2.
Note: Be sure to read the API policy before you use it, as some API doesn’t permit commercial use.
In this example I used the math. droid API to get corona case data.
Let’s get started:
Create the model class to parse the JSON response.
lib/models/Covid.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Covid {
int confvalue;
int recvalue;
int deaths;
String lastupdate;
Covid({
this.confvalue,
this.recvalue,
this.deaths,
this.lastupdate
});
factory Covid.fromJson(Map < dynamic, dynamic > json) {
return Covid(
confvalue: json['confirmed']['value'] as int,
recvalue: json['recovered']['value'] as int,
deaths: json['deaths']['value'] as int,
lastupdate: json['lastUpdate'] as String);
}
}
Create a method to fetch data from the API.
1
2
3
4
Future < Covid > getIndiaCount() async {
final response = await http.get(covid_count_api);
return Covid.fromJson(json.decode(response.body));
}
Remember, in the above method “covid_count_api” is the string url of your API which will be parsed after getting a response from the server by our model class.
Declare the global Covid covid_res;
variable to save the fetched response.
Call the above method in another method to fetch data.
1 2 3 4 5 6 7 8 9 10
Future < void > loadcount() async { setState(() { countloading = true; }); covid_res = await getIndiaCount(); print("covid deaths is" + covid_res.deaths.toString()); setState(() { countloading = false; }); }
Note: Declare above all the methods outside the “WidgetBuild()” method of class and don’t forget to call the “load count()
” method in the “init State()” lifecycle.
1 2 3 4 5 6
@override void initState() { // TODO: implement initState super.initState(); loadcount(); }
Now finally display the fetched data in the app.
In the “WidgetBuild()” method of the class under this method in the scaffold body, paste the container widget to display the fetched data from the API.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
Container(
padding: EdgeInsets.all(15),
child: Card(
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(2),
topRight: Radius.circular(2))),
//side: BorderSide( color: Colors.black)),
child: Container(
padding: EdgeInsets.all(10),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.info_outline,
),
SizedBox(
width: 10,
),
Text("India Corona Cases",
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
)),
],
),
Divider(),
Row(
children: <Widget>[
//Icon(Icons.timer, color: Colors.black),
// SizedBox(width: 2),
Flexible(
child: ListTile(
title: Text(
"LastUpdated:",
style: TextStyle(
fontWeight: FontWeight.w500),
),
subtitle: Text(covid_res.lastupdate,
style: TextStyle()),
))
],
),
titleWidget(
'Confirmed',
covid_res.confvalue.toString() ?? '',
Colors.blue),
titleWidget(
'Recovered',
covid_res.recvalue.toString() ?? '',
Colors.green),
titleWidget(
'Deaths',
covid_res.deaths.toString() ?? '',
Colors.red),
],
),
)),
)
Place the below widget out of the Widget build() method of the class.
1
2
3
4
5
6
7
8
Widget titleWidget(title, subtitle, color) {
return ListTile(
title: Text(title,
style: TextStyle(
color: color, fontSize: 17, fontWeight: FontWeight.w500)),
trailing: Text(subtitle, style: TextStyle(color: color, fontSize: 14)),
);
}
You will see the output like this:
Let’s now start fetching the news of corona from API.
To get the news API you can visit the site https://newsapi.org.
After that add the following dependency in your pubspec.yaml
flutter_webview_plugin: ^0.3.11
Step 1: Create the model class for API data. You can use the code below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class News {
String author;
String title;
String description;
String url;
String publishedAt;
String sourcename;
String launchurl;
News({
this.author,
this.title,
this.description,
this.url,
this.publishedAt,
this.sourcename,
this.launchurl
});
factory News.fromJson(Map < String, dynamic > json) {
return News(
author: json['author'] as String,
title: json['title'] as String,
description: json['description'] as String,
url: json['urlToImage'] as String,
publishedAt: json['publishedAt'] as String,
sourcename: json['source']['name'] as String,
launchurl: json['url'] as String
);
}
}
Step 2: Create a new dart class for news API to display.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
String api =
"http://newsapi.org/v2/top-headlines?apiKey=your_api_key_here";
class News_Feed extends StatefulWidget {
@override
_News_FeedState createState() => _News_FeedState();
}
class _News_FeedState extends State < News_Feed > {
@override
void initState() {
super.initState();
CheckConnectivity.isConnected()
.then((isConnected) {
if (!isConnected) {
CheckConnectivity.showInternetDialog(context);
}
});
refreshList();
}
bool loading = false;
var refreshKey = GlobalKey < RefreshIndicatorState > ();
List < News > ls_news;
Future < List < News >> getNews() async {
final response = await http.get(api);
final res = jsonDecode(response.body);
return (res["articles"] as List)
.map < News > ((json) => new News.fromJson(json))
.toList();
}
Future < Null > refreshList() async {
if (this.mounted) {
setState(() {
loading = true;
});
}
refreshKey.currentState?.show(atTop: false);
await Future.delayed(Duration(seconds: 2));
ls_news = await getNews();
if (this.mounted) {
setState(() {
loading = false;
});
}
return null;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"News Feed",
style: TextStyle(
fontWeight: FontWeight.w900, ),
),
backgroundColor: Colors.green[700],
),
body: RefreshIndicator(
child: loading ?
Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation < Color > (Colors.green)),
) :
ListView.builder(
itemCount: ls_news == null ? 0 : ls_news.length,
padding: new EdgeInsets.all(8.0),
itemBuilder: (BuildContext context, int index) {
return new GestureDetector(
child: new Card(
elevation: 1.7,
child: new Padding(
padding: new EdgeInsets.all(10.0),
child: new Column(
children: [
new Row(
children: <Widget>[
new Padding(
padding: new EdgeInsets.only(left: 4.0),
child: new Text(
timeago.format(DateTime.parse(
ls_news[index].publishedAt.toString())),
style: new TextStyle(
fontWeight: FontWeight.w400,
color: Colors.grey[600],
),
),
),
new Padding(
padding: new EdgeInsets.all(5.0),
child: new Text(
ls_news[index].sourcename!=null?ls_news[index].sourcename:"NDTV",
style: new TextStyle(
fontWeight: FontWeight.w500,
color: Colors.grey[700],
),
),
),
],
),
new Row(
children: [
new Expanded(
child: new GestureDetector(
child: new Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
new Padding(
padding: new EdgeInsets.only(
left: 4.0,
right: 8.0,
bottom: 8.0,
top: 8.0),
child: new Text(
ls_news[index].title.toString()!=null? ls_news[index].title.toString():"Corona Virus",
style: new TextStyle(
fontWeight: FontWeight.bold,
),
),
),
new Padding(
padding: new EdgeInsets.only(
left: 4.0,
right: 4.0,
bottom: 4.0),
child: new Text(
ls_news[index].description!=null?ls_news[index].description:"",
style: new TextStyle(
color: Colors.grey[500],
),
),
),
],
),
onTap: () {
Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: DescriptionPage(ls_news[index].launchurl.toString())));
},
),
),
new Column(
children: <Widget>[
new Padding(
padding: new EdgeInsets.only(top: 8.0),
child: new SizedBox(
height: 100.0,
width: 100.0,
child: new Image.network(
ls_news[index].url!=null?ls_news[index].url:"https://image.shutterstock.com/image-vector/illustration-flat-icon-tv-channel-260nw-482689633.jpg",
fit: BoxFit.cover,
),
),
),
],
)
],
),
],
),
),
),
);
},
),
onRefresh: refreshList,
));
}
}
Step 3: Now that you have loaded the corona news from API, let’s show the full news article on click.
For that, create a new dart class file to show a full article page. Here we will be using the Flutter web view plugin that we added earlier.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class DescriptionPage extends StatelessWidget {
static String tag = 'description-page';
num _stackToView = 1;
DescriptionPage(this.urlnews);
final String urlnews;
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: new Text(
"Full Article",
style: TextStyle(
fontWeight: FontWeight.w900, fontStyle: FontStyle.italic),
),
backgroundColor: Colors.green[700],
),
body: Container(
width: MediaQuery.of(context)
.size.width,
height: MediaQuery.of(context)
.size.height,
child: WebviewScaffold(
url: urlnews,
withJavascript: true,
hidden: true,
initialChild: Container(
child: const Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation < Color > (Colors.green)
),
),
),
),
),
);
}
}
Congrats you have completed fetching the news from API.
You will see the output like below:
Do you remember that in the above COVID rest API we fetched three values corona_confirmed_cases, corona_recovered_cases, and corona_death_cases?
We will be using the values to create the pie chart.
First of all, add the chart dependency in the pubspec.yaml.
dependencies:
charts_flutter: ^0.9.0
Step 1: Call the pie chart class with fetched response data.
1
2
3
4
5
6
7
Container(
padding: EdgeInsets.only(
top: 10, left: 20, right: 20, bottom: 20),
child: DrawPieChart(
model: covid_res,
),
),
Here I am calling the “DrawPieChart
” class with a rest API data.
You can see that the “covid_res” is contained with the API response data, which we also used earlier in the “load count()” method in the section of COVID rest API.
Step 2: Creating DrawPieChart class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import 'package:charts_flutter/flutter.dart'
as charts;
class LinearCovid {
final String legendText;
final int data;
LinearCovid(this.legendText, this.data);
}
class DrawPieChart extends StatefulWidget {
final Covid model;
DrawPieChart({
this.model
});
final data = [];
@override
_DrawPieChartState createState() => _DrawPieChartState();
}
class _DrawPieChartState extends State < DrawPieChart > {
final data = [LinearCovid('Confirmed', 0),
LinearCovid('Recovered', 0),
LinearCovid('Deaths', 0)];
bool adding = true;
@override
void initState() {
// TODO: implement initState
super.initState();
data.clear();
data.add(LinearCovid('Confirmed', widget.model.confvalue));
data.add(LinearCovid('Recovered', widget.model.recvalue));
data.add(LinearCovid('Deaths', widget.model.deaths));
setState(() {
adding = false;
});
}
@override
Widget build(BuildContext context) {
final height = MediaQuery.of(context)
.size.height;
return Container(
padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
decoration: BoxDecoration(
color: Color(0xff1b232f),
borderRadius: BorderRadius.circular(10.0)),
height: height * 0.45,
child: charts.PieChart(
adding ? [] : [
new charts.Series < LinearCovid, String > (
id: 'covid-19',
outsideLabelStyleAccessorFn: (LinearCovid l, _) => charts.TextStyleSpec(
color: charts.Color(r: 255, g: 255, b: 255),
),
colorFn: (LinearCovid l, i) {
if (i == 0) return charts.Color(r: 242, g: 185, b: 3);
if (i == 1) return charts.Color(r: 0, g: 128, b: 0);
return charts.Color(r: 255, g: 0, b: 0);
},
labelAccessorFn: (LinearCovid l, _) => '',
// overlaySeries: true,
insideLabelStyleAccessorFn: (LinearCovid l, _) =>
charts.TextStyleSpec(color: charts.Color(r: 0, g: 0, b: 0)),
// fillColorFn: (LinearCovid l, _) =>
// charts.MaterialPalette.green.shadeDefault,
domainFn: (LinearCovid l, _) => l.legendText,
measureFn: (LinearCovid l, _) => l.data,
data: data,
)
],
animate: true,
behaviors: [
charts.DatumLegend(
position: charts.BehaviorPosition.bottom,
entryTextStyle: charts.TextStyleSpec(
color: charts.Color(r: 255, g: 255, b: 255)))
],
defaultRenderer: new charts.ArcRendererConfig(
arcWidth: 40,
arcRendererDecorators: [new charts.ArcLabelDecorator()],
),
));
}
}
So with this you have also completed loading the pie chart. Congrats!
You will see the output like below:
You can check out my:
App screenshots
App Video
App Source code
Thanks for taking the time to read!!!
Happy Fluttering!!!