简体   繁体   中英

Flutter : A value of type 'List<Type>?' can't be assigned to a variable of type 'List<Type>'

I want to fetch and print datas from this json file:

[{"id": "76d751584e134ebf854bebda8998334b", "data": {"timestamp": "15/05/2021-13:08:38", "temperature": 21.9, "pressure": 1007.4342535, "humidity": 43.994442131}}, {"id": "6188e169eeda4547b3e87414d50664df", "data": {"timestamp": "15/05/2021-15:08:38", "temperature": 22.28, "pressure": 1006.47539066, "humidity": 46.1434366089}}, {"id": "4eb75a12fd6c4eb8a94b20106aab4e2a", "data": {"timestamp": "15/05/2021-17:08:38", "temperature": 22.71, "pressure": 1006.52941164, "humidity": 47.7676641555}}]

This is my flutter code witch is supposed to do the job:

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

class SensorData with ChangeNotifier {
  //final id;
  final timestamp;
  final temperature;
  final pressure;
  final humidity;

  SensorData({
    //@required this.id,
    @required this.timestamp,
    @required this.temperature,
    @required this.pressure,
    @required this.humidity,
  });

  factory SensorData.fromJson(Map<String, dynamic> json) {
    return new SensorData(
      //id: json['id'] as String,
      timestamp: json['timestamp'] as String,
      temperature: json['temperature'] as double,
      pressure: json['pressure'] as double,
      humidity: json['humidity'] as double,
    );
  }
}

class App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  Future<List<SensorData>> fetchSensorDatas() async {
    final http.Response response = await http.get(Uri.http(<my_API_url>));
      var responseJson = json.decode(response.body);
      return (responseJson['data'] as List)
          .map((p) => SensorData.fromJson(p))
          .toList();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: new Container(
        child: new Column(
          children: [
            new Container(
              height: 200,
            ),
            new ListView(
              children: <Widget>[
                new FutureBuilder<List<SensorData>>(
                  future: fetchSensorDatas(),
                  builder: (context, snapshot) {
                    if (snapshot.hasData){
                      List<SensorData> posts = snapshot.data;
                      return new Column(
                          children: posts.map((post) => new Column(
                            children: <Widget>[
                              new Text(post.temperature),
                            ],
                          )).toList()
                      );
                    }
                    else if(snapshot.hasError)
                    {
                      return snapshot.error;
                    }
                    return new Center(
                      child: new Column(
                        children: <Widget>[
                          new Padding(padding: new EdgeInsets.all(50.0)),
                          new CircularProgressIndicator(),
                        ],
                      ),
                    );
                  },
                ),
                
              ],
            ),
          ],
        ),
      ),
    );
  }
}

I have 2 errors:

  • snapshot.data returns me "A value of type 'List?' can't be assigned to a variable of type 'List'."
  • snapshot.error returns me "The return type 'Object?' isn't a 'Widget', as required by the closure's context.".

Where am I getting wrong?

Many thanks in advance!!

snapshot.data returns me "A value of type 'List?' can't be assigned to a variable of type 'List'."

The problem is inside the fetchSensorDatas method. Since responseJson is a Map , every time you access a key from it, there is a possibility that this key does not exist and it'll return null (therefore returning the nullable type List? ). If you are sure that the key exists, you can do the following:

final List data = responseJson['data']! as List;
return data.map((p) => SensorData.fromJson(p)).toList();

snapshot.error returns me "The return type 'Object?' isn't a 'Widget', as required by the closure's context.".

That's because you're returning multiple types inside the builder parameter of FutureBuilder , and therefore the return type of it will be the common ascestor of all types being returned, which in this case is Object . Note that you're returning a Column in the first condition, an Error in the second condition and a Center in the third condition. You can replace the second return with some Wigdet :

else if (snapshot.hasError){
  // Alternative 1: return a Text widget with the error message
  return Text(snapshot.error?.toString() ?? "");
  
  // Alternative 2: print the error message and return an empty widget
  print(snapshot.error);
  return SizedBox.shrink();

  // Alternative 3: display the user that an error has occurred with a descriptive widget
  return Text("An error has occured.");

  // Alternative 4: rethrow the error
  throw snapshot.error;
}

I figured it out!

First of all my model was very poor, so I rewrote it like this:

import 'dart:convert';

List<SensorData> sensorDataFromJson(String str) => List<SensorData>.from(json.decode(str).map((x) => SensorData.fromJson(x)));

String sensorDataToJson(List<SensorData> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class SensorData {
    SensorData({
        required this.id,
        required this.data,
    });

    String id;
    Data data;

    factory SensorData.fromJson(Map<String, dynamic> json) => SensorData(
        id: json["id"],
        data: Data.fromJson(json["data"]),
    );

    Map<String, dynamic> toJson() => {
        "id": id,
        "data": data.toJson(),
    };
}

class Data {
    Data({
        required this.timestamp,
        required this.temperature,
        required this.pressure,
        required this.humidity,
    });

    String timestamp;
    double temperature;
    double pressure;
    double humidity;

    factory Data.fromJson(Map<String, dynamic> json) => Data(
        timestamp: json["timestamp"],
        temperature: json["temperature"].toDouble(),
        pressure: json["pressure"].toDouble(),
        humidity: json["humidity"].toDouble(),
    );

    Map<String, dynamic> toJson() => {
        "timestamp": timestamp,
        "temperature": temperature,
        "pressure": pressure,
        "humidity": humidity,
    };
}

Then I have a method returning the get request:

Future<List<SensorData>> getSensorDatas() async {
    final response = await http.get(Uri.http('192.168.1.3:5000', '/pigarden/'));
    return sensorDataFromJson(response.body);
}

And finally my app file looks like this:

FutureBuilder<List<SensorData>>(
        future: SensorDatasRepository().getSensorDatas(),
        builder: (context, snapshot) {
          if (snapshot.hasError) {
            showDialog(
              context: context,
              builder : (BuildContext context) => const AlertDialog(
                title: Text("Error"),
                content: Text('An error has occured...'),
              ),
            );
          } else if (snapshot.hasData) {
            return ListView.builder(
              itemCount: snapshot.data!.length,
              itemBuilder: (context, index) => ListTile(
                title: Text(snapshot.data![index].id),
                subtitle: Text(
                  snapshot.data![index].data.temperature.toString(),
                  softWrap: false,
                  overflow: TextOverflow.ellipsis,
                ),
              ),
            );
          }
          return Center(
            child: CircularProgressIndicator(),
          );
        },
      ),

And now it works just fine!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM