Flutter JSON String/Int commands - json

I'm trying to make an app that retrieves data from this JSON list and parse this data to a listview.
With my code below, the first record I expect to see is ID, NAME and PRICE, but it appears that I can't retrieve the PRICE, because it's an int and I'm calling for a string from the JSON list.
Also, 'PRICE' is an int just like 'ID', as sam mentioned below, but 'ID' is fetched just fine whereas 'PRICE' just says 'NULL' in the list view
I don't know how to fix this, and hope I can get the answer I'm looking for on this platform.
FirstPage.dart :
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
void main() {
runApp(MaterialApp(
home: StarWarsData(),
));
}
class StarWarsData extends StatefulWidget {
#override
StarWarsState createState() => StarWarsState();
}
class StarWarsState extends State<StarWarsData> {
final String url = "https://api.coinmarketcap.com/v2/ticker/";
List data;
Future<String> getSWData() async {
var res = await http
.get(Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var resBody = json.decode(res.body);
data = resBody["data"];
});
return "Success!";
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Cryp-Tick Crypto Exchange"),
centerTitle: true,
backgroundColor: Colors.black,
),
body: ListView.builder(
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
return new Container(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Card(
child: Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: <Widget>[
Text("Id: "),
Text('${data[index]["id"]}',
style: TextStyle(
fontSize: 18.0, color: Colors.black87)),
],
)),
),
Card(
child: Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: <Widget>[
Text("Name: "),
Text('${data[index]["name"]}',
style: TextStyle(
fontSize: 18.0, color: Colors.red)),
],
)),
),
Card(
child: Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: <Widget>[
Text("Price: "),
Text('${data[index]["price"]}',
style: TextStyle(
fontSize: 18.0, color: Colors.black87)),
],
)),
),
],
),
),
);
},
),
);
}
#override
void initState() {
super.initState();
this.getSWData();
}
}
The error I receive in the Debug Console:
E/flutter (25480): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception:
E/flutter (25480): type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List' where
E/flutter (25480): _InternalLinkedHashMap is from dart:collection
E/flutter (25480): String is from dart:core
E/flutter (25480): List is from dart:core

I don't think the problem is what you think. You have your data typed as a List:
List data;
And you're populating it like this:
data = resBody["data"];
The error says:
type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List'
This suggests that resBody["data"]` is a map and not a list, and if you look at the JSON in the URL you're pulling, it starts like this:
{
"data": {
"1": {
That data object is not a list/array, it's a an object/map. You should change the type of your data variable to Map<String, dynamic> of convert the data to a List before storing it in the variable.
Edit after some comments
I was able to make this work by adding .values.toList() and stripping the JSON down to just the first two records:
However with all of the JSON from that url it seems to error. So, maybe something in the JSON is triggering a bug. Even more strangely, it seems to work fine in DartPad!

Related

How to generate a List to the second screen in flutter from a JSON?

I am trying to create a list from the value inside a JSON. I can get the length but not for the data. My main goal is to create a list of items based on their popularity.
child: Row(
children: [
...List.generate(
"items"[0].length,
(index) {
if (["items"][index].isPopular) { // here I want to generate a list for the value isPopular
return BreakfastCard(breakfast: ('items'[0] as Map<String, dynamic>[index]); // Here I want to call the function from other screen
}
And when I tried to change the code to this
"items"[0].length,
(index) {
if (["items"][index][isPopular]) { // the error here is *Conditions must have a static type of 'bool'.*
return BreakfastCard(breakfast: ['items'][0][index]); // the error here is *The argument type 'String' can't be assigned to the parameter type 'Breakfast'.*
The JSON data is like this
{
"items":[{
"id": 1,
"rating": "0.0",
"images": [
"assets/images/cilantro.png"
],
"title": "Cilantro and Kale Pesto Toast with a Fried Egg",
"time": 15,
"description": "Sliced bread is the perfect blank canvas, ready to be loaded up with virtuous ingredients.",
" rating": "4.8",
"isFavorite": false,
"isPopular": true,
}]
}
Here is my code for the card. In this part, there were no error and it show what I want.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:fema/models/Breakfast.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../constants.dart';
import '../size_config.dart';
class BreakfastCard extends StatefulWidget {
BreakfastCard({
Key? key,
this.width = 140,
this.aspectRetio = 1.02,
required this.breakfast,
}) : super(key: key);
final double width, aspectRetio;
Breakfast breakfast;
#override
_BreakfastCardState createState() => _BreakfastCardState();
}
class _BreakfastCardState extends State<BreakfastCard> {
#override
Widget build(BuildContext context) {
Future<String> _loadloadBreakfastAsset() async {
return await rootBundle.loadString('assets/breakfast.json');
}
Future<BreakfastCard> loadBreakfast() async {
String jsonAddress = await _loadloadBreakfastAsset();
final jsonResponse = json.decode(jsonAddress);
// This now updates the breakfast property in the main class.
widget.breakfast = Breakfast.fromJson(jsonResponse);
// This return value is thrown away, but this line is necessary to
// resolve the Future call that FutureBuilder is waiting on.
return Future<BreakfastCard>.value();
}
SizeConfig().init(context);
return FutureBuilder(
future: loadBreakfast(),
builder: (BuildContext, AsyncSnapshot<dynamic>snapshot){
return Padding(
padding: EdgeInsets.only(left: getProportionateScreenWidth(20)),
child: SizedBox(
width: getProportionateScreenWidth(140),
child: GestureDetector(
onTap: (){},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: 1.02,
child: Container(
padding: EdgeInsets.all(getProportionateScreenWidth(20)),
decoration: BoxDecoration(
color: kSecondaryColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(15),
),
child: Hero(
tag: widget.breakfast.id.toString(),
child: Image.asset(widget.breakfast.images[0]),
),
),
),
const SizedBox(height: 10),
Text(
widget.breakfast.title,
style: const TextStyle(color: Colors.black),
maxLines: 2,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"${widget.breakfast.calories} cal |",
style: TextStyle(
fontSize: getProportionateScreenWidth(18),
fontWeight: FontWeight.bold,
color: kPrimaryColor,
),
),
Text(
"${widget.breakfast.time} min",
style: TextStyle(
fontSize: getProportionateScreenWidth(18),
fontWeight: FontWeight.w600,
color: kPrimaryColor,
),
),
InkWell(
borderRadius: BorderRadius.circular(50),
onTap: () { widget.breakfast.isFavorite = !widget.breakfast.isFavorite;},
child: Container(
padding: EdgeInsets.all(getProportionateScreenWidth(8)),
height: getProportionateScreenWidth(28),
width: getProportionateScreenWidth(28),
child: SvgPicture.asset(
"assets/icons/Heart Icon_2.svg",
color: widget.breakfast.isFavorite
? const Color(0xFFFF4848)
: const Color(0xFFDBDEE4),
),
),
),
],
)
],
),
),
),
);
}
);
}
}
And my problem is in here. Where the list will be generated. I am new to flutter and I have difficulties to solve the problem. In here I can correctly create a function to fetch the data from the BreakfastCard.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:fema/components/breakfast_card.dart';
import 'package:fema/models/Breakfast.dart';
import 'package:flutter/services.dart';
import '../../../size_config.dart';
import 'section_title.dart';
class Breakfast extends StatelessWidget {
const Breakfast({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
Future<String> _loadloadBreakfastAsset() async {
return await rootBundle.loadString('assets/breakfast.json');
}
Future<Breakfast> loadBreakfast() async {
String jsonAddress = await _loadloadBreakfastAsset();
Map<String,dynamic> map = json.decode(jsonAddress);
List<dynamic> items = map["items"];
// This return value is thrown away, but this line is necessary to
// resolve the Future call that FutureBuilder is waiting on.
return Future<Breakfast>.value();
}
return FutureBuilder(
future: loadBreakfast(),
builder: (BuildContext, AsyncSnapshot<dynamic>snapshot){
return Column(
children: [
Padding(
padding:
EdgeInsets.symmetric(horizontal: getProportionateScreenWidth(20)),
child: SectionTitle(title: "BREAKFAST", press: () {}),
),
SizedBox(height: getProportionateScreenWidth(20)),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
...List.generate(
items[0].length,
(index) {
if (items[index].isPopular) {
return BreakfastCard(breakfast: );
}
return const SizedBox
.shrink(); // here by default width and height is 0
},
),
SizedBox(width: getProportionateScreenWidth(20)),
],
),
)
],
);
});
}
}
You need to put "items" into a Map first. You are trying to use the String object "items" to populate your list. You want the data from the json to populate it.
Get the data into a usable object first.
Map<String, dynamic> map = jsonDecode(data);
List<dynamic> items = map["items"];
Then you can populate your list from that data.
child: Row(
children: [
...List.generate(
items.length,
(index) {
if (items[index].isPopular) {
return BreakfastCard(breakfast: (items[0]));
}
Your errors are from trying to use json data improperly as the conditional for an if statement.
The other error is because you are trying to send a String as an argument for a Breakfast object when it needs something else (I don't know what that is. You didn't post what a BreakfastCard class looks like at the time I wrote this.)
Dart and Flutter documentation is very good. Try this out https://flutter.dev/docs/development/data-and-backend/json

Expected a value of type 'int', but got one of type 'String' in Flutter

I've been working on a quotes app that fetches data from a rest api, and displays each quote at a time randomly in the center of the screen with a press of a button. But can't quite get it right
I have made a method which fetches the json data, which is fetchQuotesData(), and it stores the unprocessed json in QuotesData. This is later converted into a list as QuotesList.
class _MyAppState extends State<MyApp> {
List QuotesList = [];
var _data;
var c;
final url = "https://type.fit/api/quotes";
fetchQuoteData() async {
Response response = await get(Uri.parse(url));
final QuotesData = jsonDecode(response.body);
setState(() {
QuotesList = QuotesData;
});
}
#override
void initState() {
fetchQuoteData();
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/pic/image4.jpg'),
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(
Colors.black.withOpacity(0.6), BlendMode.darken)
),
),
child: Scaffold(
backgroundColor: Colors.transparent,
body: Center(
// Use future builder and DefaultAssetBundle to load the local JSON file
child: FutureBuilder(
future: fetchQuoteData(),
builder: (context, snapshot) {
_data = snapshot.data.toString();
var range = new Random();
c = range.nextInt(_data.length);
return Ui_Card(c);
},
),
),
bottomNavigationBar: BottomAppBar(
color: Colors.indigo.shade900,
child: Container(
margin: const EdgeInsets.only(left: 40.0,right: 40.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
tooltip:'Random Quotes',
icon: Icon(Icons.format_quote_outlined) ,
iconSize: 40,
color: Colors.white,
onPressed: (){
HapticFeedback.heavyImpact();
setState(() {
});
},
),
],
),
),
),
),
);
}
Widget Ui_Card(index){
return new Container(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(25.0),
child: Text(_data[c]['text'],
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 22.0,
color: Colors.white,
fontFamily: 'Raleway-Italic',
fontStyle: FontStyle.italic,),
textScaleFactor: 2.0,)
),
Text(_data[c]['author'],
style:TextStyle(
fontWeight: FontWeight.w500,
color: Colors.white,
fontFamily: 'Raleway-Bold',
fontSize: 18.0
),
textAlign: TextAlign.left,
),
],
),
),
);
}
}
I am suspecting some errors in the builder, or snapshot data, but not sure where I'm stuck
As the comment on your question mentions, Dart is a strongly typed language. When you use 'var' you're relying on type inference to figure out the type you want for the variable. I've only been using Dart for about a year, but in my experience so far I try to never use 'var' because it can result in harder-to-debug error messages. Also if you set your variable types the linter seems to be better at picking up type mismatches.
var _data;
...
_data = snapshot.data.toString();
Above you set _data equal to a String.
child: Text(_data[c]['text'],
Here you are trying to access it as something else - maybe a List<Map<String,String>> or a Map<int, Map<String,String>>
My hunch is your error message is coming from ['text']. Maybe _data's inferred type can take a two-dimensional int index. The characters of a Dart string can be accessed with an int index - i.e. string[0] is the first character, but it returns an int, and int isn't an indexed type AFAIK, so I don't know what Dart is doing with your second index dimension that wants an int. I suspect if you change it to an int - i.e. _data[0][0] you'll get a different error message.
Try defining _data as the type you want it to be, then see if the linter shows the error in your source or you get a more descriptive error message.

API Key not working in Flutter Weather API App (Android)

I'm learning Flutter and trying to build an Android Application. Basically a weather app where it fetches the API Key from a site. I keep getting the error " The argument type 'String' can't be assigned to the parameter type 'Uri'. ". What does this even mean and how do I get this to work?
main.dart
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() => runApp(
MaterialApp(
title: "Weather App",
home: Home(),
)
);
class Home extends StatefulWidget {
#override
State<StatefulWidget> createState(){
return _HomeState();
}
}
class _HomeState extends State<Home> {
var temp;
var description;
var currently;
var humidity;
var windSpeed;
Future getWeather () async {
http.Response response = await http.get("http://api.weatherapi.com/v1/current.json?key=e5bd00e528e346ff8a840254213009&q=Chatham Ontario&aqi=no");
var results = jsonDecode(response.body);
setState((){
this.temp = results['current']['temp_c'];
this.description = results['current'][0]['last_updated'];
this.currently = results['current'][0]['condition']['text'];
this.humidity = results['current']['humidity'];
this.windSpeed = results['current']['wind_kph'];
});
}
#override
void initState() {
super.initState();
this.getWeather();
}
#override
Widget build (BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height / 3,
width: MediaQuery.of(context).size.width,
color: Colors.red,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding:EdgeInsets.only(bottom: 10.0),
child: Text(
"Currently in Chatham-Kent",
style: TextStyle(
color: Colors.white,
fontSize: 14.0,
fontWeight: FontWeight.w600
),
),
),
Text(
temp != null ? temp.toString() + "\u00B0" : "Loading",
style: TextStyle(
color: Colors.white,
fontSize: 40.0,
fontWeight: FontWeight.w600
),
),
Padding(
padding:EdgeInsets.only(top: 10.0),
child: Text(
currently != null ? currently.toString() : "Loading",
style: TextStyle(
color: Colors.white,
fontSize: 14.0,
fontWeight: FontWeight.w600
),
),
),
],
),
),
Expanded(
child: Padding(
padding: EdgeInsets.all(20.0),
child: ListView(
children: <Widget>[
ListTile(
leading: FaIcon(FontAwesomeIcons.thermometerHalf),
title: Text("Temperature"),
trailing: Text(temp != null ? temp.toString() + "\u00B0" : "Loading"),
),
ListTile(
leading: FaIcon(FontAwesomeIcons.cloud),
title: Text("Weather"),
trailing: Text(description != null ? description.toString() : "Loading"),
),
ListTile(
leading: FaIcon(FontAwesomeIcons.sun),
title: Text("Humidity"),
trailing: Text(humidity != null ? humidity.toString() : "Loading"),
),
ListTile(
leading: FaIcon(FontAwesomeIcons.wind),
title: Text("Wind Speed"),
trailing: Text(windSpeed != null ? windSpeed.toString() : "Loading"),
)
],
)
)
)
],
),
);
}
}
pubspec.yaml
version: 1.0.0+1
environment:
sdk: '>=2.10.0 <3.0.0'
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
font_awesome_flutter: ^8.0.0
http: ^0.13.3
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^1.0.0
flutter:
uses-material-design: true
Well, Your error clearly saying The argument type 'String' can't be assigned to the parameter type 'Uri' so, you have to convert url string to Uri.
LIKE THIS:
var uri = Uri.parse("http://api.weatherapi.com/v1/current.json?key=e5bd00e528e346ff8a840254213009&q=Chatham Ontario&aqi=no");
http.Response response = await http.get(uri);
Change this line of code
http.Response response = await http.get("http://api.weatherapi.com/v1/current.json?key=e5bd00e528e346ff8a840254213009&q=Chatham Ontario&aqi=no");
to
http.Response response = await http.get(Uri.parse("http://api.weatherapi.com/v1/current.json?key=e5bd00e528e346ff8a840254213009&q=Chatham Ontario&aqi=no"));
Based on HTTP package docs here.
As of 0.13.0-nullsafety.0 version All APIs which previously allowed a String or Uri to be passed now require a Uri.
so what happening here is that you need to parse(convert) your string to uri first before the call.
So your weather function will be like this:
Future getWeather () async {
http.Response response = await http.get(Uri.parse("http://api.weatherapi.com/v1/current.json?key=e5bd00e528e346ff8a840254213009&q=Chatham Ontario&aqi=no"));
var results = jsonDecode(response.body);
setState((){
this.temp = results['current']['temp_c'];
this.description = results['current'][0]['last_updated'];
this.currently = results['current'][0]['condition']['text'];
this.humidity = results['current']['humidity'];
this.windSpeed = results['current']['wind_kph'];
});
}

The method '[]' was called on null. Receiver: null T while accessing key:value pair in json

I am working on a mobile app related to vehicles. I have to create a form that should have several fields to be filled about a vehicle's info (like regNum, brand, model,type...).
In order to fetch the data for the dropdown button field I have to make http request(for type,brand,model).
I want whenever I change the vehicle brand in its corresponding dropdown, the vehicle model dropdown field to be updated only with models corresponding to the selected brand.
Here is my code:
#VehicleForm
class VehicleForm extends StatefulWidget {
final Future<VehicleTypes> types;
final Future<VehicleBrands> brands;
final Future<VehicleModels> models;
VehicleForm(this.types, this.brands, this.models);
#override
VehicleFormState createState() => VehicleFormState(types,brands,models);
}
class VehicleFormState extends State<VehicleForm>{
final Future<VehicleTypes> types;
final Future<VehicleBrands> brands;
final Future<VehicleModels> models;
String brandName;
VehicleFormState(this.types, this.brands, this.models);
void handleBrandChanged(String brand){
setState(() {
print(brand);
brandName=brand;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Vehicle')
),
body: Container(
child: Column(
children: [
Container(
margin: EdgeInsets.only(
top:20,
),
alignment: Alignment.center,
child:Text('General Info',
style: TextStyle(
fontSize: 22,
color:Colors.blue,
),
),
),
Container(
child: Column(
children: [
Container(
child: TextFormField(
decoration: const InputDecoration(
hintText: 'Registration number'
),
),
margin: EdgeInsets.all(10),
),
Container(
child: TextFormField(
decoration: const InputDecoration(
hintText: 'Vehicle km'
),
),
margin: EdgeInsets.all(10),
),
Container(
width:200,
child: VehicleTypeMenu(types),
),
Container(
width:200,
child: VehicleBrandMenu(brands,brandName,handleBrandChanged),
),
Container(
width:250,
child: brandName==null ? TextFormField(
decoration: const InputDecoration(
hintText: 'Vehicle Model'
),
): VehicleModelMenu(models,brandName),
),
VehicleYearDropdown(),
VehicleMonthDropdown(),
],
),
)
],
)
)
);
}
//VehicleBrand
class VehicleBrandMenu extends StatelessWidget{
final Future<VehicleBrands> brands;
final String brandName;
final ValueChanged<String> onChanged;
VehicleBrandMenu(this.brands,this.brandName,this.onChanged);
void handleBrandChanged(String brandName){
onChanged(brandName);
}
#override
Widget build(BuildContext context) {
return FutureBuilder<VehicleBrands>(
future: brands,
builder: (context,snapshot){
if(snapshot.hasData){
List<String> vehicleBrands = List<String>();
for(int i=snapshot.data.brands.length-1;i>=0;i--){
vehicleBrands.add(snapshot.data.brands[i]['name'].toString());
}
return DropdownButton<String>(
hint: Text("Select Vehicle Brand"),
value:brandName,
onChanged: handleBrandChanged,
items: vehicleBrands.map((String vehicleBrand){
return DropdownMenuItem(
value:vehicleBrand,
child: Row(
children: [
Text('$vehicleBrand')
],
),
);
}).toList(),
);
} else if(snapshot.hasError){
return Text('${snapshot.error}');
} else{
return TextFormField(
decoration: const InputDecoration(
hintText: 'Vehicle Model'
),
);
}
}
);
}
}
//VehicleModel(the problem occurs here)!
class VehicleModelMenu extends StatefulWidget{
final Future<VehicleModels> models;
final String brandName;
VehicleModelMenu(this.models,this.brandName);
#override
VehicleModelMenuState createState() => VehicleModelMenuState(models,brandName);
}
class VehicleModelMenuState extends State<VehicleModelMenu>{
final Future<VehicleModels> models;
final String brandName;
var firstItem;
VehicleModelMenuState(this.models,this.brandName);
#override
Widget build(BuildContext context) {
return FutureBuilder<VehicleModels>(
future: models,
builder: (context,snapshot){
if(snapshot.hasData){
print(brandName);
List<String> vehicleModels = List<String>();
for(int i=snapshot.data.models.length-1;i>=0;i--){ //The problem occurs in this loop
if(snapshot.data.models[i]['vehicleBrand']['name']==brandName){ //I check for equal brand
vehicleModels.add(snapshot.data.models[i]['name']); //I add only the needed models
}
}
return DropdownButton<String>(
hint: Text("Select Vehicle Model"),
value: firstItem,
onChanged: (String model) {
setState(() {
firstItem = model;
});
},
items: vehicleModels.map((String vehicleModel) {
return DropdownMenuItem(
value: vehicleModel,
child: Row(
children: [
Text('$vehicleModel')
],
),
);
}).toList(),
);
} else if(snapshot.hasError){
return Text('${snapshot.error}');
} else {
return CircularProgressIndicator();
}
}
);
}
}
Here is the data I want to fetch: I compare the ['vehicleBrand']['name']->brand property and add ['name']->model
enter image description here
Here is the actual error:
======== Exception caught by widgets library =======================================================
The following NoSuchMethodError was thrown building FutureBuilder<VehicleModels>(dirty, state: _FutureBuilderState<VehicleModels>#5813d):
The method '[]' was called on null.
Receiver: null
Tried calling: []("name")
The relevant error-causing widget was:
FutureBuilder<VehicleModels> file:///D:/Android%20Apps/login_form/lib/vehicleFormElements/vehicleModel.dart:23:12
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1 VehicleModelMenuState.build.<anonymous closure> (package:test_flutter_app/vehicleFormElements/vehicleModel.dart:30:59)
#2 _FutureBuilderState.build (package:flutter/src/widgets/async.dart:751:55)
#3 StatefulElement.build (package:flutter/src/widgets/framework.dart:4744:28)
#4 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4627:15)
Here is the deserialisation to object
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
class VehicleModels {
final List<dynamic> models;
VehicleModels({this.models});
factory VehicleModels.fromJson(Map<String,dynamic> json){
return VehicleModels(
models: json['data']['results'],
);
}
}
Future<VehicleModels> getVehicleModels(String cookie)async{
final http.Response response = await http.get(
'https://gara6.bg/auto-api/vehicleModels?pageSize=2147483647',
headers: <String, String>{
'Content-Type': 'application/json',
'cookie':cookie,
},
);
if(response.statusCode==200){
return VehicleModels.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
}
else{
throw Exception('Failed to retrieve vehicle models');
}
}
If any entry in your JSON is missing vehicleBrand you will get that null error.
Since you're accessing nested JSON data (i.e. Map class) directly, you have to check each nested level actually has data or else you can get a null error trying to access a value when the object is null.
So this:
if (snapshot.data.models[i]['vehicleBrand']['name']==brandName) {
// do something
}
should be something like this:
if (snapshot.data.models[i] != null && snapshot.data.models[i]['vehicleBrand'] != null && snapshot.data.models[i]['vehicleBrand']['name'] == brandName) {
// do something
}
In general, directly accessing JSON data like this is unsafe, repetitive and verbose. It would probably be better to convert your JSON data into objects (i.e. deserialize) where you can get the benefits of Type-safety (properties are the type you're expecting) & can create methods/getters that produce safe/sane values so you don't get null errors when data isn't perfect.
Check out the Flutter article on serialization for more info.

Error: type 'List<dynamic>' is not a subtype of type 'String'

I am new to flutter. I am trying to fetch the data from Firestore by Stream. But it is giving me error. I am trying to fetch a String and a List but I don't know what to change in code to fetch the List. In code, 'name' field is String and 'overview' field is a List.
import 'package:firebaseAuth/firebaseAuthDemo.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class FindDiseases extends StatefulWidget {
final User user;
const FindDiseases({Key key, this.user}) : super(key: key);
#override
_FindDiseasesState createState() => _FindDiseasesState();
}
class _FindDiseasesState extends State<FindDiseases> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
FirebaseAuth _auth = FirebaseAuth.instance;
List diseasesList = [];
dynamic data;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.teal,
//automaticallyImplyLeading: false,
title: Text(
"Diseases List",
),
),
key: _scaffoldKey,
body: Container(
color: Colors.white,
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection("diseases")
.orderBy('id')
.snapshots(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot user = snapshot.data.docs[index];
return Card(
color: Colors.white,
child: FlatButton(
onPressed: () {},
child: ListTile(
title: Text(user['name']),
subtitle: Text(user['overview']),
),
),
);
},
);
},
),
),
);
}
Data is something like as shown below:
"name": "Agoraphobia",
"overview": "[
'Losing weight.', 'Stopping medications or supplements.', 'Having surgery.'
]"
It is because you are using
subtitle: Text(user['overview']),
as a string while in data this data is in list format try to get index wise data from it then try to show as string
In your data "overview" is a type of list and you are trying to get it as string.
List<String> list = user['overview'];
String data = String.join(", ", list);
return Card(
color: Colors.white,
child: FlatButton(
onPressed: () {},
child: ListTile(
title: Text(user['name']),
subtitle: Text(data),
),
),
);