方法 '[]' 在 null 上调用。接收器:在 json 中访问键:值对时为空 T。



我正在开发一个与车辆相关的移动应用程序。我必须创建一个表单,应该有几个字段填写关于车辆的信息(如regNum,品牌,型号,类型…)。为了获取下拉按钮字段的数据,我必须进行http请求(类型,品牌,模型)。

我希望每当我在相应的下拉列表中更改车辆品牌时,车辆模型下拉列表只更新与所选品牌对应的模型。

下面是我的代码:# 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();
}
}
);
}
}

这是我想要获取的数据:我比较['vehicleBrand']['name']->brand属性并添加['name']->model

输入图片描述

下面是实际的错误:

======== 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)

这是对象

的反序列化
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');
}
}

如果JSON中的任何条目缺少vehicleBrand,您将获得null错误。

因为你直接访问嵌套JSON数据(即Map类),你必须检查每个嵌套级别实际上有数据,否则当对象为空时,你可能会得到一个null错误。

:

if (snapshot.data.models[i]['vehicleBrand']['name']==brandName) {
// do something
}

应该是这样的:

if (snapshot.data.models[i] != null && snapshot.data.models[i]['vehicleBrand'] != null && snapshot.data.models[i]['vehicleBrand']['name'] == brandName) {
// do something
}
一般来说,像这样直接访问JSON数据是不安全的、重复的和冗长的。将JSON数据转换为对象(即反序列化)可能会更好,这样您可以获得类型安全的好处(属性是您期望的类型)&可以创建产生安全/合理值的方法/getter,这样当数据不完美时就不会得到null错误。

查看Flutter关于序列化的文章以获取更多信息。

最新更新