#api #flutter #flutter-layout #flutter-dependencies
#API #flutter #flutter-layout #flutter-зависимости
Вопрос:
Я вызываю api ниже:
{
"1": {
"data": {
"sensors": {
"fuel": [
{
"sensor_value": 2.9081154,
"sensor_unit": "m",
"percentage": 83.09,
"value": 48705.07,
"unit": "L",
"status": "normal",
"sensor": "fuel",
"name": "main_fuel"
}
],
"battery": [
{
"voltage": 4005,
"value": 100,
"status": "normal",
"unit": "%",
"sensor": "battery"
}
]
},
"latitude": null,
"longitude": null,
"speed": null,
"ignition": null,
"voltage": null,
"gsm": null,
"satellites": null,
"tracked_at": "2020-11-23 03:35:47",
"tracker": "jejaka",
"temperature": null
}
},
"2": {
"data": {
"sensors": {
"fuel": [
{
"sensor_value": 2.90697352,
"sensor_unit": "m",
"percentage": 83.06,
"value": 48687.99,
"unit": "L",
"status": "normal",
"sensor": "fuel",
"name": "main_fuel"
}
],
"battery": [
{
"voltage": 3901,
"value": 100,
"status": "normal",
"unit": "%",
"sensor": "battery"
}
]
},
"latitude": null,
"longitude": null,
"speed": null,
"ignition": null,
"voltage": null,
"gsm": null,
"satellites": null,
"tracked_at": "2020-11-23 03:44:02",
"tracker": "jejaka",
"temperature": null
}
}
}
Вот мой вызов api:
class Services{
// ignore: missing_return
static Future<Map<String, Tank>> fetchData() async {
String url = 'http://192.168.10.17/api/device';
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
final SharedPreferences prefs = await _prefs;
final token = prefs.getString('access_token');
final response = await http.get(url, headers: {
'Authorization': 'Bearer $token'
});
print('Token: $token');
if(response.statusCode == 200) {
final tank = tankFromJson(response.body);
return tank;
}else if(response.statusCode == 400) {
print('Connection to server is bad');
}else if(response.statusCode == 500){
print('No authorization');
}
}
}
Этому api требуется пользовательский токен для возврата данных api, как указано выше. Я получил данные, но теперь, когда я реализую данные, я получаю сообщение об ошибке ниже:
The getter 'data' was called on null.
Receiver: null
Tried calling: data
Вот как я реализую код. API находится в listview. Я попытался поместить его в будущий конструктор, но я получил ошибку:
class TankCard extends StatefulWidget {
@override
_TankCardState createState() => _TankCardState();
}
class _TankCardState extends State<TankCard> {
final _tankRepository = Services();
Tank tank;
initState() {
Services.fetchData().then((tank){
setState((){
tank = tank;
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
return ListView.builder(
padding: EdgeInsets.all(0),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: 2,
itemBuilder: (BuildContext context, int index){
return Column(
children: [
Container(
margin: EdgeInsets.only(top: 10),
width: MediaQuery.of(context).size.width * 1.1,
child: Card(
child: Column(
children: [
Container(
padding: EdgeInsets.only(top: 12),
child: Center(
child: Text('Tank 1 - Bukit Tinggi',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600
)),
)
),
Container(
padding: EdgeInsets.all(10),
child: SvgPicture.asset(
'assets/tank-icon-fill.svg',
// width: 50,
)
),
Container(
margin: EdgeInsets.fromLTRB(0, 5, 5, 0),
child: Column(
children: [
Wrap(
children: [
Icon(Icons.bolt),
Container(
margin: EdgeInsets.only(top: 4),
child: Text('${tank.data.sensors.battery[0].value.toString()}%')
)
],
),
Container(
margin: EdgeInsets.only(left: 15),
child: Text('Battery', style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w300,
),),
)
],
),
),
Модель резервуара, которую я получил по этому URL-адресу -> https://app.quicktype.io /
Буду признателен за любую помощь.
Ответ №1:
Вы можете скопировать вставить выполнить полный код ниже
Шаг 1: Использовать bool
isLoading
для проверки готовности данных или нет
Шаг 2: Использовать Map<String, Tank> payload
Шаг 3: ListView
Используется int no = index 1;
, поскольку Map
данные "1"
и "2"
Шаг 4: использование ${payload[no.toString().trim()].data.sensors.battery[0].value.toString()}
фрагмент кода
Map<String, Tank> payload;
bool isLoading = true;
initState() {
Services.fetchData().then((tank) {
setState(() {
payload = tank;
print(payload.toString());
print(payload["1"].data.sensors.battery[0].value.toString());
isLoading = false;
});
});
...
return isLoading
? CircularProgressIndicator()
: ListView.builder(
padding: EdgeInsets.all(0),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: 2,
itemBuilder: (BuildContext context, int index) {
int no = index 1;
...
child: Text(
'${payload[no.toString().trim()].data.sensors.battery[0].value.toString()}%'))
],
рабочая демонстрация
полный код
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
Map<String, Tank> tankFromJson(String str) => Map.from(json.decode(str))
.map((k, v) => MapEntry<String, Tank>(k, Tank.fromJson(v)));
String tankToJson(Map<String, Tank> data) => json.encode(
Map.from(data).map((k, v) => MapEntry<String, dynamic>(k, v.toJson())));
class Tank {
Tank({
this.data,
});
Data data;
factory Tank.fromJson(Map<String, dynamic> json) => Tank(
data: Data.fromJson(json["data"]),
);
Map<String, dynamic> toJson() => {
"data": data.toJson(),
};
}
class Data {
Data({
this.sensors,
this.latitude,
this.longitude,
this.speed,
this.ignition,
this.voltage,
this.gsm,
this.satellites,
this.trackedAt,
this.tracker,
this.temperature,
});
Sensors sensors;
dynamic latitude;
dynamic longitude;
dynamic speed;
dynamic ignition;
dynamic voltage;
dynamic gsm;
dynamic satellites;
DateTime trackedAt;
String tracker;
dynamic temperature;
factory Data.fromJson(Map<String, dynamic> json) => Data(
sensors: Sensors.fromJson(json["sensors"]),
latitude: json["latitude"],
longitude: json["longitude"],
speed: json["speed"],
ignition: json["ignition"],
voltage: json["voltage"],
gsm: json["gsm"],
satellites: json["satellites"],
trackedAt: DateTime.parse(json["tracked_at"]),
tracker: json["tracker"],
temperature: json["temperature"],
);
Map<String, dynamic> toJson() => {
"sensors": sensors.toJson(),
"latitude": latitude,
"longitude": longitude,
"speed": speed,
"ignition": ignition,
"voltage": voltage,
"gsm": gsm,
"satellites": satellites,
"tracked_at": trackedAt.toIso8601String(),
"tracker": tracker,
"temperature": temperature,
};
}
class Sensors {
Sensors({
this.fuel,
this.battery,
});
List<Fuel> fuel;
List<Battery> battery;
factory Sensors.fromJson(Map<String, dynamic> json) => Sensors(
fuel: List<Fuel>.from(json["fuel"].map((x) => Fuel.fromJson(x))),
battery:
List<Battery>.from(json["battery"].map((x) => Battery.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"fuel": List<dynamic>.from(fuel.map((x) => x.toJson())),
"battery": List<dynamic>.from(battery.map((x) => x.toJson())),
};
}
class Battery {
Battery({
this.voltage,
this.value,
this.status,
this.unit,
this.sensor,
});
int voltage;
int value;
String status;
String unit;
String sensor;
factory Battery.fromJson(Map<String, dynamic> json) => Battery(
voltage: json["voltage"],
value: json["value"],
status: json["status"],
unit: json["unit"],
sensor: json["sensor"],
);
Map<String, dynamic> toJson() => {
"voltage": voltage,
"value": value,
"status": status,
"unit": unit,
"sensor": sensor,
};
}
class Fuel {
Fuel({
this.sensorValue,
this.sensorUnit,
this.percentage,
this.value,
this.unit,
this.status,
this.sensor,
this.name,
});
double sensorValue;
String sensorUnit;
double percentage;
double value;
String unit;
String status;
String sensor;
String name;
factory Fuel.fromJson(Map<String, dynamic> json) => Fuel(
sensorValue: json["sensor_value"].toDouble(),
sensorUnit: json["sensor_unit"],
percentage: json["percentage"].toDouble(),
value: json["value"].toDouble(),
unit: json["unit"],
status: json["status"],
sensor: json["sensor"],
name: json["name"],
);
Map<String, dynamic> toJson() => {
"sensor_value": sensorValue,
"sensor_unit": sensorUnit,
"percentage": percentage,
"value": value,
"unit": unit,
"status": status,
"sensor": sensor,
"name": name,
};
}
class Services {
// ignore: missing_return
static Future<Map<String, Tank>> fetchData() async {
/*String url = 'http://192.168.10.17/api/device';
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
final SharedPreferences prefs = await _prefs;
final token = prefs.getString('access_token');
final response = await http.get(url, headers: {
'Authorization': 'Bearer $token'
});
print('Token: $token');
*/
String jsonString = '''
{
"1": {
"data": {
"sensors": {
"fuel": [
{
"sensor_value": 2.9081154,
"sensor_unit": "m",
"percentage": 83.09,
"value": 48705.07,
"unit": "L",
"status": "normal",
"sensor": "fuel",
"name": "main_fuel"
}
],
"battery": [
{
"voltage": 4005,
"value": 100,
"status": "normal",
"unit": "%",
"sensor": "battery"
}
]
},
"latitude": null,
"longitude": null,
"speed": null,
"ignition": null,
"voltage": null,
"gsm": null,
"satellites": null,
"tracked_at": "2020-11-23 03:35:47",
"tracker": "jejaka",
"temperature": null
}
},
"2": {
"data": {
"sensors": {
"fuel": [
{
"sensor_value": 2.90697352,
"sensor_unit": "m",
"percentage": 83.06,
"value": 48687.99,
"unit": "L",
"status": "normal",
"sensor": "fuel",
"name": "main_fuel"
}
],
"battery": [
{
"voltage": 3901,
"value": 100,
"status": "normal",
"unit": "%",
"sensor": "battery"
}
]
},
"latitude": null,
"longitude": null,
"speed": null,
"ignition": null,
"voltage": null,
"gsm": null,
"satellites": null,
"tracked_at": "2020-11-23 03:44:02",
"tracker": "jejaka",
"temperature": null
}
}
}
''';
http.Response response = http.Response(jsonString, 200);
if (response.statusCode == 200) {
final tank = tankFromJson(response.body);
return tank;
} else if (response.statusCode == 400) {
print('Connection to server is bad');
} else if (response.statusCode == 500) {
print('No authorization');
}
}
}
class TankCard extends StatefulWidget {
@override
_TankCardState createState() => _TankCardState();
}
class _TankCardState extends State<TankCard> {
final _tankRepository = Services();
Map<String, Tank> payload;
bool isLoading = true;
initState() {
Services.fetchData().then((tank) {
setState(() {
payload = tank;
print(payload.toString());
print(payload["1"].data.sensors.battery[0].value.toString());
isLoading = false;
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
return isLoading
? CircularProgressIndicator()
: ListView.builder(
padding: EdgeInsets.all(0),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: 2,
itemBuilder: (BuildContext context, int index) {
int no = index 1;
return Column(children: [
Container(
margin: EdgeInsets.only(top: 10),
width: MediaQuery.of(context).size.width * 1.1,
child: Card(
child: Column(children: [
Container(
padding: EdgeInsets.only(top: 12),
child: Center(
child: Text('Tank ${no} - Bukit Tinggi',
style: TextStyle(
fontSize: 14, fontWeight: FontWeight.w600)),
)),
/*Container(
padding: EdgeInsets.all(10),
child: SvgPicture.asset(
'assets/tank-icon-fill.svg',
// width: 50,
)),*/
Container(
margin: EdgeInsets.fromLTRB(0, 5, 5, 0),
child: Column(
children: [
Wrap(
children: [
Icon(Icons.bolt),
Container(
margin: EdgeInsets.only(top: 4),
child: Text(
'${payload[no.toString().trim()].data.sensors.battery[0].value.toString()}%'))
],
),
Container(
margin: EdgeInsets.only(left: 15),
child: Text(
'Battery',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w300,
),
),
)
],
),
),
]))),
]);
});
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: TankCard(),
);
}
}
Комментарии:
1. Спасибо, чувак, это работает. Могу ли я узнать, зачем использовать полезную нагрузку?
2. tank путают с типом данных Tank. поэтому я использую полезную нагрузку для представления Map<String, Tank>
Ответ №2:
Как отлаживать проблемы такого типа с помощью / JSON в flutter:
- Перед анализом объекта проверьте правильность вашего подключения к API посредством регистрации ответа.
- Убедитесь, что проанализированный вами объект указан правильно (иначе говоря, не null). Через регистрацию object.toString(). print(tank.toString()) в вашем случае перед возвратом значения
- Проверьте в своем основном, что возвращаемое значение не равно null
- Проверьте / поймите свой JSON, идите шаг за шагом, пытаясь получить 1 уровень глубже за раз (сначала распечатайте полный объект, затем распечатайте tank.1)
Если вы выполните следующие действия, вы обнаружите, где он не работает
Комментарии:
1. куда я должен поместить «1»?
2. я пробовал ставить ‘1’, но это выдает мне ошибку
3. ${tank.1.data.sensors.battery[0].value}%’ Проверьте, что резервуар не является нулевым резервуаром. toString()
4. я пробовал это ‘${tank.1.data.sensors.battery[0].value. toString()}%’ но это выдает ошибку. Он не может обнаружить значение 1
5. удалите toString в конце, он не используется таким образом, он ищет значение tostring в вашем json check tank не равно null (сам объект)