首页 > 解决方案 > Flutter 如何 JSON 序列化数据表单 api

问题描述

我正在尝试在我的颤振应用程序中使用模型来让这个概念在我的应用程序中启动并运行。我对 OOP 很陌生,所以我想尽可能多地学习。我的问题是我有来自 OpenWeather API 的 API 响应,我的方法是调用 api 获取数据。这很好用,我通常使用 JSON 手动解码和手动访问属性 weather["main"] aa。但我想要的是按模型分解/预处理我的 JSON。下面的代码运行良好,但我想应用使用 JSON 序列化的概念,但我不知道如何开始。我所有的尝试都失败了......:/

我已经用https://app.quicktype.io/在 Guy的帮助下生成了我之前的答案。

模型:

import 'dart:convert';

Forcast forcastFromJson(String str) => Forcast.fromJson(json.decode(str));

String forcastToJson(Forcast data) => json.encode(data.toJson());

class Forcast {
  String cod;
  double message;
  int cnt;
  List<ListElement> list;
  City city;

  Forcast({
    this.cod,
    this.message,
    this.cnt,
    this.list,
    this.city,
  });

  factory Forcast.fromJson(Map<String, dynamic> json) => new Forcast(
        cod: json["cod"],
        message: json["message"].toDouble(),
        cnt: json["cnt"],
        list: new List<ListElement>.from(
            json["list"].map((x) => ListElement.fromJson(x))),
        city: City.fromJson(json["city"]),
      );

  Map<String, dynamic> toJson() => {
        "cod": cod,
        "message": message,
        "cnt": cnt,
        "list": new List<dynamic>.from(list.map((x) => x.toJson())),
        "city": city.toJson(),
      };
}

class City {
  int id;
  String name;
  Coord coord;
  String country;

  City({
    this.id,
    this.name,
    this.coord,
    this.country,
  });

  factory City.fromJson(Map<String, dynamic> json) => new City(
        id: json["id"],
        name: json["name"],
        coord: Coord.fromJson(json["coord"]),
        country: json["country"],
      );

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

class Coord {
  double lat;
  double lon;

  Coord({
    this.lat,
    this.lon,
  });

  factory Coord.fromJson(Map<String, dynamic> json) => new Coord(
        lat: json["lat"].toDouble(),
        lon: json["lon"].toDouble(),
      );

  Map<String, dynamic> toJson() => {
        "lat": lat,
        "lon": lon,
      };
}

class ListElement {
  int dt;
  MainClass main;
  List<Weather> weather;
  Clouds clouds;
  Wind wind;
  Sys sys;
  DateTime dtTxt;
  Rain rain;
  Rain snow;

  ListElement({
    this.dt,
    this.main,
    this.weather,
    this.clouds,
    this.wind,
    this.sys,
    this.dtTxt,
    this.rain,
    this.snow,
  });

  factory ListElement.fromJson(Map<String, dynamic> json) => new ListElement(
        dt: json["dt"],
        main: MainClass.fromJson(json["main"]),
        weather: new List<Weather>.from(
            json["weather"].map((x) => Weather.fromJson(x))),
        clouds: Clouds.fromJson(json["clouds"]),
        wind: Wind.fromJson(json["wind"]),
        sys: Sys.fromJson(json["sys"]),
        dtTxt: DateTime.parse(json["dt_txt"]),
        rain: json["rain"] == null ? null : Rain.fromJson(json["rain"]),
        snow: json["snow"] == null ? null : Rain.fromJson(json["snow"]),
      );

  Map<String, dynamic> toJson() => {
        "dt": dt,
        "main": main.toJson(),
        "weather": new List<dynamic>.from(weather.map((x) => x.toJson())),
        "clouds": clouds.toJson(),
        "wind": wind.toJson(),
        "sys": sys.toJson(),
        "dt_txt": dtTxt.toIso8601String(),
        "rain": rain == null ? null : rain.toJson(),
        "snow": snow == null ? null : snow.toJson(),
      };
}

class Clouds {
  int all;

  Clouds({
    this.all,
  });

  factory Clouds.fromJson(Map<String, dynamic> json) => new Clouds(
        all: json["all"],
      );

  Map<String, dynamic> toJson() => {
        "all": all,
      };
}

class MainClass {
  double temp;
  double tempMin;
  double tempMax;
  double pressure;
  double seaLevel;
  double grndLevel;
  int humidity;
  double tempKf;

  MainClass({
    this.temp,
    this.tempMin,
    this.tempMax,
    this.pressure,
    this.seaLevel,
    this.grndLevel,
    this.humidity,
    this.tempKf,
  });

  factory MainClass.fromJson(Map<String, dynamic> json) => new MainClass(
        temp: json["temp"].toDouble(),
        tempMin: json["temp_min"].toDouble(),
        tempMax: json["temp_max"].toDouble(),
        pressure: json["pressure"].toDouble(),
        seaLevel: json["sea_level"].toDouble(),
        grndLevel: json["grnd_level"].toDouble(),
        humidity: json["humidity"],
        tempKf: json["temp_kf"].toDouble(),
      );

  Map<String, dynamic> toJson() => {
        "temp": temp,
        "temp_min": tempMin,
        "temp_max": tempMax,
        "pressure": pressure,
        "sea_level": seaLevel,
        "grnd_level": grndLevel,
        "humidity": humidity,
        "temp_kf": tempKf,
      };
}

class Rain {
  double the3H;

  Rain({
    this.the3H,
  });

  factory Rain.fromJson(Map<String, dynamic> json) => new Rain(
        the3H: json["3h"] == null ? null : json["3h"].toDouble(),
      );

  Map<String, dynamic> toJson() => {
        "3h": the3H == null ? null : the3H,
      };
}

class Sys {
  Pod pod;

  Sys({
    this.pod,
  });

  factory Sys.fromJson(Map<String, dynamic> json) => new Sys(
        pod: podValues.map[json["pod"]],
      );

  Map<String, dynamic> toJson() => {
        "pod": podValues.reverse[pod],
      };
}

enum Pod { D, N }

final podValues = new EnumValues({"d": Pod.D, "n": Pod.N});

class Weather {
  int id;
  MainEnum main;
  Description description;
  String icon;

  Weather({
    this.id,
    this.main,
    this.description,
    this.icon,
  });

  factory Weather.fromJson(Map<String, dynamic> json) => new Weather(
        id: json["id"],
        main: mainEnumValues.map[json["main"]],
        description: descriptionValues.map[json["description"]],
        icon: json["icon"],
      );

  Map<String, dynamic> toJson() => {
        "id": id,
        "main": mainEnumValues.reverse[main],
        "description": descriptionValues.reverse[description],
        "icon": icon,
      };
}

enum Description {
  CLEAR_SKY,
  BROKEN_CLOUDS,
  LIGHT_RAIN,
  MODERATE_RAIN,
  FEW_CLOUDS
}

final descriptionValues = new EnumValues({
  "broken clouds": Description.BROKEN_CLOUDS,
  "clear sky": Description.CLEAR_SKY,
  "few clouds": Description.FEW_CLOUDS,
  "light rain": Description.LIGHT_RAIN,
  "moderate rain": Description.MODERATE_RAIN
});

enum MainEnum { CLEAR, CLOUDS, RAIN }

final mainEnumValues = new EnumValues({
  "Clear": MainEnum.CLEAR,
  "Clouds": MainEnum.CLOUDS,
  "Rain": MainEnum.RAIN
});

class Wind {
  double speed;
  double deg;

  Wind({
    this.speed,
    this.deg,
  });

  factory Wind.fromJson(Map<String, dynamic> json) => new Wind(
        speed: json["speed"].toDouble(),
        deg: json["deg"].toDouble(),
      );

  Map<String, dynamic> toJson() => {
        "speed": speed,
        "deg": deg,
      };
}

class EnumValues<T> {
  Map<String, T> map;
  Map<T, String> reverseMap;

  EnumValues(this.map);

  Map<T, String> get reverse {
    if (reverseMap == null) {
      reverseMap = map.map((k, v) => new MapEntry(v, k));
    }
    return reverseMap;
  }
}

网络进行 API 调用:

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

class NetworkHelper {
  NetworkHelper({this.text});

  String text;
  String apiKey = '';

  Future<dynamic> getData(text) async {
    http.Response response = await http.get(
        'https://api.openweathermap.org/data/2.5/weather?q=$text&appid=$apiKey&units=metric');

    if (response.statusCode == 200) {
      var decodedData = jsonDecode(response.body);

      return decodedData;
    } else {
      print(response.statusCode);
    }
  }

  Future<dynamic> getForcast(text) async {
    http.Response response = await http.get(
        'http://api.openweathermap.org/data/2.5/forecast?q=${text}&units=metric&appid=$apiKey');
    if (response.statusCode == 200) {
      var decodedData = jsonDecode(response.body);

      return decodedData;
    } else {
      print(response.statusCode);
    }
  }

  Future<dynamic> getDataLocation(lat, lon) async {
    http.Response response = await http.get(
        'https://api.openweathermap.org/data/2.5/weather?lat=$lat&lon=$lon&appid=$apiKey&units=metric');

    if (response.statusCode == 200) {
      var decodedData = jsonDecode(response.body);

      return decodedData;
    } else {
      print(response.statusCode);
    }
  }

  Future<dynamic> getForcastLocation(lat, lon) async {
    http.Response response = await http.get(
        'http://api.openweathermap.org/data/2.5/forecast?lat=$lat&lon=$lon&units=metric&appid=$apiKey');
    if (response.statusCode == 200) {
      var decodedData = jsonDecode(response.body);

      return decodedData;
    } else {
      print(response.statusCode);
    }
  }
}

我显示数据的天气:

import 'package:flutter/material.dart';
import 'package:weather/common/format.dart';

import 'package:weather/service/Network.dart';
import 'package:weather/service/location.dart';

class Weather extends StatefulWidget {
  Weather({this.text});
  final String text;

  _WeatherState createState() => _WeatherState();
}

class _WeatherState extends State<Weather> {
  NetworkHelper networkHelper = NetworkHelper();
  Location location = Location();
  Formats formats = Formats();
  int temperature;
  String cityName;
  String description;
  bool isLoading = true;
  dynamic newData;
  String city;
  @override
  void initState() {
    super.initState();
    city = widget.text;
    buildUI(city);
  }

  buildUI(String text) async {
    var weatherData = await networkHelper.getData(text);
    var forecastData = await networkHelper.getForcast(text);
    double temp = weatherData['main']['temp'];
    temperature = temp.toInt();
    cityName = weatherData['name'];
    description = weatherData['weather'][0]['description'];
    newData = forecastData['list'].toList();
    setState(() {
      isLoading = false;
    });
  }

  buildUIByLocation() async {
    await location.getCurrentLocation();
    var weatherLocation = await networkHelper.getDataLocation(
        location.latitude, location.longitude);
    var forcastLocation = await networkHelper.getForcastLocation(
        location.latitude, location.longitude);
    double temp = weatherLocation['main']['temp'];
    temperature = temp.toInt();
    cityName = weatherLocation['name'];
    description = weatherLocation['weather'][0]['description'];
    newData = forcastLocation['list'].toList();
    setState(() {
      isLoading = false;
    });
  }

  Widget get _pageToDisplay {
    if (isLoading == true) {
      return _loadingView;
    } else {
      return _weatherView;
    }
  }

  Widget get _loadingView {
    return Center(child: CircularProgressIndicator());
  }

  Widget get _weatherView {
    return SafeArea(
      child: Column(
        children: <Widget>[
          Flexible(
            flex: 1,
            child: Container(
              margin: EdgeInsets.fromLTRB(12, 1, 30, 0),
              decoration: new BoxDecoration(
                color: Color(0xff4556FE),
                borderRadius: BorderRadius.all(Radius.circular(10.0)),
                boxShadow: [
                  BoxShadow(
                    color: Color(0xFFD4DAF6),
                    offset: Offset(20, 20),
                  ),
                  BoxShadow(
                    color: Color(0xFFadb6ff),
                    offset: Offset(10, 10),
                  ),
                ],
              ),
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Expanded(
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: <Widget>[
                        Text(
                          '$cityName',
                          style: TextStyle(fontSize: 25, color: Colors.white),
                        ),
                        SizedBox(
                          height: 5,
                        ),
                        Text(
                          '$temperature°C',
                          style: TextStyle(fontSize: 50, color: Colors.white),
                        ),
                      ],
                    ),
                  ),
                  Expanded(
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: <Widget>[
                        Text(
                          '$description',
                          style: TextStyle(fontSize: 25, color: Colors.white),
                        ),
                      ],
                    ),
                  )
                ],
              ),
            ),
          ),
          SizedBox(
            height: 30,
          ),
          Flexible(
            flex: 2,
            child: Container(
              margin: EdgeInsets.fromLTRB(12, 10, 12, 0),
              decoration: new BoxDecoration(
                color: Color(0xff4556FE),
                borderRadius: BorderRadius.vertical(top: Radius.circular(10.0)),
              ),
              child: ListView.builder(
                  padding: const EdgeInsets.all(8.0),
                  itemCount: newData.length,
                  itemBuilder: (BuildContext context, int index) {
                    return Container(
                        margin: const EdgeInsets.all(4.0),
                        height: 50,
                        child: Center(
                          child: Row(
                            crossAxisAlignment: CrossAxisAlignment.center,
                            mainAxisAlignment: MainAxisAlignment.spaceAround,
                            children: <Widget>[
                              Text(
                                formats.readTimeStamp(newData[index]['dt']),
                                style: TextStyle(
                                    color: Colors.white, fontSize: 14),
                              ),
                              Text(
                                newData[index]['weather'][0]['main'].toString(),
                                style: TextStyle(
                                    color: Colors.white,
                                    fontSize: 18,
                                    fontWeight: FontWeight.w600),
                              ),
                              Text(
                                formats.floatin(newData[index]['main']['temp']),
                                style: TextStyle(
                                    color: Colors.white,
                                    fontSize: 16,
                                    fontWeight: FontWeight.w600),
                              ),
                            ],
                          ),
                        ));
                  }),
            ),
          ),
        ],
      ),
    );
  }

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: <Widget>[
          IconButton(
            padding: EdgeInsets.fromLTRB(0, 0, 20, 0),
            icon: Icon(Icons.autorenew, color: Colors.black, size: 30),
            onPressed: () {
              if (city == "") {
                setState(() {
                  isLoading = true;
                  buildUIByLocation();
                });
              } else {
                setState(() {
                  isLoading = true;
                  buildUI(city);
                });
              }
            },
          ),
          IconButton(
            padding: EdgeInsets.fromLTRB(0, 0, 15, 0),
            icon: Icon(
              Icons.location_on,
              color: Colors.black,
              size: 30,
            ),
            onPressed: () async {
              setState(() {
                city = '';
                isLoading = true;
              });
              await buildUIByLocation();
            },
          )
        ],
        leading: IconButton(
          icon: Icon(Icons.arrow_back_ios, color: Colors.black),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
        elevation: 0,
        backgroundColor: Colors.transparent,
        title: const Text(
          'Change location',
          style: TextStyle(color: Colors.black),
        ),
      ),
      body: Stack(
        children: <Widget>[
          Container(
            decoration: BoxDecoration(color: Color(0xFFfafafa)),
            child: Padding(
              padding: const EdgeInsets.fromLTRB(5, 20, 5, 0),
              child: Center(child: _pageToDisplay),
            ),
          )
        ],
      ),
    );
  }
}

标签: jsonflutterdart

解决方案


这是一个非常基本的例子。在这里,您可以看到 Car 模型类及其基本属性。数据将由 CarRepository 类获取,该类执行网络工作并将数据从 json 映射到模型。CarScreen 类是一个有状态的小部件,在 initState 之后调用 CarRepository。如果从网络获取数据,汽车将显示在列表中。在获取加载指示器时会显示。

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

class Car {
  //
  // Attributes
  //
  int id;
  String name;
  String color;
  int speed;

  //
  // Constructor
  //
  Car({
    @required this.id,
    @required this.name,
    @required this.color,
    @required this.speed,
  });

  // convert Json to an car object object
  factory Car.fromJson(Map<String, dynamic> json) {
    return Car(
      id: json['id'] as int,
      name: json['name'] as String,
      color: json['color'] as String,
      speed: json['speed'] as int,
    );
  }
}

class CarRepository {
  /// Load all cars form api and will convert it
  /// to an list of cars.
  ///
  ///  {
  ///     "results": [
  ///         {
  ///           "id": 1,
  ///           "name": "Tesla Model 3",
  ///           "color": "red",
  ///           "speed": 225
  ///         },
  ///         {
  ///           "id": 3,
  ///           "name": "Tesla Model S",
  ///           "color": "black",
  ///           "speed": 255
  ///         }
  ///     ]
  ///  }
  ///
  ///
  static Future<List<Car>> fetchAll() async {
    String query = '';
    String url = Uri.encodeFull("https://xxx.de/api/cars" + query);
    final response = await http.get(url);

    if (response.statusCode == 200) {
      Map data = json.decode(response.body);
      final cars = (data['results'] as List).map((i) => new Car.fromJson(i));
      return cars.toList();
    } else {
      return [];
    }
  }
}

class CarsScreen extends StatefulWidget {
  _CarsScreenState createState() => _CarsScreenState();
}

class _CarsScreenState extends State<CarsScreen> {
  bool _isLoading = true;
  List<Car> _cars = [];

  @override
  void initState() {
    super.initState();
    fetchCars();
  }

  Future fetchCars() async {
    _cars = await CarRepository.fetchAll();
    setState(() {
      _isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) {
      return Scaffold(
        appBar: AppBar(
          title: Text('Cars'),
        ),
        body: Container(
          child: Center(
            child: CircularProgressIndicator(),
          ),
        ),
      );
    } else {
      return Scaffold(
        appBar: AppBar(
          title: Text('Cars'),
        ),
        body: ListView.builder(
          itemCount: _cars.length,
          itemBuilder: (context, index) {
            Car car = _cars[index];
            return ListTile(
              title: Text(car.name),
              subtitle: Text(car.color),
            );
          },
        ),
      );
    }
  }
}

推荐阅读