Reputation: 25
I am using an API from disease.sh in my COVID-19 tracker project and but when I called worldData ['cases']
in the UI, an error occurred:
The method '[]' was called on null.
Receiver: null
Tried calling: []("cases")
Here is my code:
import 'package:flutter/material.dart';
import 'api.dart';
import 'package:http/http.dart';
import 'dart:convert';
Map worldData;
fetchWorldData() async {
Response response =
await get(Uri.parse('https://disease.sh/v3/covid-19/all'));
worldData = json.decode(response.body);
}
Widget coloredCard() => Card(
shadowColor: Colors.red,
elevation: 8,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.red[500], Colors.red[500]],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
)),
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Comfirmed',
style: TextStyle(
fontSize: 23,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
worldData['cases'].toString(),
style: TextStyle(
fontSize: 50,
color: Colors.white,
),
),
],
),
),
);
I tried to replace worldData['cases']
with "123"
and the error disappeared.
If you can help me, I will be very grateful.
Upvotes: 0
Views: 92
Reputation: 471
Your fetchWorldData
function is async. You need to handle the UI according to the result of the async function. In this case you can use FutureBuilder
.
I've updated your code with FutureBuilder. It will work, but the FutureBuilder
should be obtained before e.g. in initState
. Please have a look at the code below also.
Future<Map<String, dynamic>> fetchWorldData() async {
Response response =
await get(Uri.parse('https://disease.sh/v3/covid-19/all'));
return json.decode(response.body);
}
Widget coloredCard() => Card(
shadowColor: Colors.red,
elevation: 8,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.red[500], Colors.red[500]],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
)),
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Comfirmed',
style: TextStyle(
fontSize: 23,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
FutureBuilder<Map<String, dynamic>>(
future: fetchWorldData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(
snapshot.data['cases'].toString(),
style: TextStyle(
fontSize: 50,
color: Colors.white,
),
);
} else {
return Text('there is no data yet');
}
},
),
],
),
),
);
The full example with the good point that was mentioned in comment by Problematic Dude.
The future must have been obtained earlier, e.g. during State.initState, State.didUpdateWidget, or State.didChangeDependencies. It must not be created during the State.build or StatelessWidget.build method call when constructing the FutureBuilder. If the future is created at the same time as the FutureBuilder, then every time the FutureBuilder's parent is rebuilt, the asynchronous task will be restarted.
class FullFutureExample extends StatefulWidget {
@override
_FullFutureExampleState createState() => _FullFutureExampleState();
}
class _FullFutureExampleState extends State<FullFutureExample> {
Future _covidFuture;
@override
void initState() {
super.initState();
_covidFuture = fetchWorldData();
}
@override
Widget build(BuildContext context) {
return coloredCard();
}
Future<Map<String, dynamic>> fetchWorldData() async {
Response response =
await get(Uri.parse('https://disease.sh/v3/covid-19/all'));
return json.decode(response.body);
}
Widget coloredCard() => Card(
shadowColor: Colors.red,
elevation: 8,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.red[500], Colors.red[500]],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
)),
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Comfirmed',
style: TextStyle(
fontSize: 23,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
FutureBuilder(
future: _covidFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(
snapshot.data['cases'].toString(),
style: TextStyle(
fontSize: 50,
color: Colors.white,
),
);
} else {
return Text('there is no data yet');
}
},
),
],
),
),
);
}
Upvotes: 1