0

I am tryin to use model in my flutter app to get this concept up and running in my app. I am quite new to OOP so i want to learn as much as i can. My problem is that i have API response from OpenWeather API my method is calling api getting data. That works fine and the i make manual decode and accessing propetires by hand weather["main"] aa usually with JSON. But what i want is to factor/pre-procesds my JSON by model. Below code is working good but i want to apply concept of using JSON serialization and i have no idea how to start. All my attempts failed...:/

I have generated models with https://app.quicktype.io/ with help of guy form my previous answer.

Model:

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;
  }
}

Network to make API call:

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);
    }
  }
}

Weather where i display data:

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),
            ),
          )
        ],
      ),
    );
  }
}
1

2 Answers 2

2

This is a really basic example. Here you see the Car model class and his basic attributes. Data would be fetching by CarRepository class, this class do network stuff and map data from json to model. The CarScreen class is an Stateful widget and call CarRepository after initState. If data fetched from network, cars would be displayed in an list. While fetching an loading indicator would displayed.

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),
            );
          },
        ),
      );
    }
  }
}
Sign up to request clarification or add additional context in comments.

3 Comments

hey great explanation i will try to apply it. now i understand how to do it
I have a question if in my response i have nested array or object how to represent it in Car class
I tried your code but when i want to print result i get [Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast', Instance of 'Forcast',]
1

This example include nested array with car objects. I hope it will help.

There is an relationship between producer and car.

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


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

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



class Producer {
  //
  // Attributes
  //
  int id;
  String name;
  List<Car> cars;

  //
  // Constructor
  //
  Producer({
    @required this.id,
    @required this.name,
    @required this.cars,
  });

  // convert Json to an producer object object
  factory Producer.fromJson(Map<String, dynamic> json) {
    return Producer(
      id: json['id'] as int,
      name: json['name'] as String,
      cars: (json['cars'] as List ?? []).map((c) {
        return Car.fromJson(c);
      }).toList(),
    );
  }
}

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,
    );
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.