1

I'm trying to parse some complex JSON from a weather API, and display it in a list view. I've used quickType to generate a model for me, but cannot seem to parse the data to a list. I'm fairly certain that the main part that is wrong is my logic in the parsing of the data in the service file, as the data from the API comes in a complex structure that I don't know how to parse. Because of the API data, its split up into 2 maps or lists? Hours and meta. And therefore there being an extra class. Please help me.

Here's the service/ parse file:

  import 'weather_model.dart';
import 'package:http/http.dart' as http;
import 'dart:io';

class Service {
  static const lat = '-33.7506';
  static const lng = '18.4401';
  static const params =
      'swellDirection,windSpeed,windDirection,wavePeriod,waveHeight,airTemperature';

  static Future<List<Hour>> getConditions() async {
    try {
      final response = await http.get(
          Uri.encodeFull(
              'https://api.stormglass.io/v2/weather/point?lat=$lat&lng=$lng&params=$params&start=2020-12-11&end=2020-12-12'),
          headers: {
            HttpHeaders.authorizationHeader:
                'exampleapi'
          });

      if (200 == response.statusCode) {
        final conditions = conditionsFromJson(response.body);
        print(response.body);
        return conditions.hours;
      }
    } catch (e) {
      print('Not working');
      return List<Hour>();
    }
  }
}

Heres my Main file:

    import 'package:flutter/material.dart';
import 'package:moreapi_practise/weather_model.dart';
import 'Service.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'My API Practice'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<Hour> _conditions;
  bool _loading;

  @override
  void initState() {
    super.initState();
    _loading = true;
    Service.getConditions().then((conditions) {
      _conditions = conditions;
      _loading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_loading ? 'Loading...' : 'Conditions'),
      ),
      body: Container(child: ListView.builder(itemBuilder: (context, index) {
        Hour condition = _conditions[index];
        return ListTile(
          title: Text('${condition.airTemperature}'),
        );
      })),
    );
  }
}

And then my model:

   // To parse this JSON data, do
//
//     final conditions = conditionsFromJson(jsonString);

import 'dart:convert';

Conditions conditionsFromJson(String str) => Conditions.fromJson(json.decode(str));

String conditionsToJson(Conditions data) => json.encode(data.toJson());

class Conditions {
    Conditions({
        this.hours,
        this.meta,
    });

    List<Hour> hours;
    Meta meta;

    factory Conditions.fromJson(Map<String, dynamic> json) => Conditions(
        hours: List<Hour>.from(json["hours"].map((x) => Hour.fromJson(x))),
        meta: Meta.fromJson(json["meta"]),
    );

    Map<String, dynamic> toJson() => {
        "hours": List<dynamic>.from(hours.map((x) => x.toJson())),
        "meta": meta.toJson(),
    };
}

class Hour {
    Hour({
        this.airTemperature,
        this.swellDirection,
        this.time,
        this.waveHeight,
        this.wavePeriod,
        this.windDirection,
        this.windSpeed,
    });

    AirTemperature airTemperature;
    SwellDirection swellDirection;
    DateTime time;
    SwellDirection waveHeight;
    SwellDirection wavePeriod;
    SwellDirection windDirection;
    SwellDirection windSpeed;

    factory Hour.fromJson(Map<String, dynamic> json) => Hour(
        airTemperature: AirTemperature.fromJson(json["airTemperature"]),
        swellDirection: SwellDirection.fromJson(json["swellDirection"]),
        time: DateTime.parse(json["time"]),
        waveHeight: SwellDirection.fromJson(json["waveHeight"]),
        wavePeriod: SwellDirection.fromJson(json["wavePeriod"]),
        windDirection: SwellDirection.fromJson(json["windDirection"]),
        windSpeed: SwellDirection.fromJson(json["windSpeed"]),
    );

    Map<String, dynamic> toJson() => {
        "airTemperature": airTemperature.toJson(),
        "swellDirection": swellDirection.toJson(),
        "time": time.toIso8601String(),
        "waveHeight": waveHeight.toJson(),
        "wavePeriod": wavePeriod.toJson(),
        "windDirection": windDirection.toJson(),
        "windSpeed": windSpeed.toJson(),
    };
}

class AirTemperature {
    AirTemperature({
        this.noaa,
        this.sg,
    });

    double noaa;
    double sg;

    factory AirTemperature.fromJson(Map<String, dynamic> json) => AirTemperature(
        noaa: json["noaa"].toDouble(),
        sg: json["sg"].toDouble(),
    );

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

class SwellDirection {
    SwellDirection({
        this.icon,
        this.meteo,
        this.noaa,
        this.sg,
    });

    double icon;
    double meteo;
    double noaa;
    double sg;

    factory SwellDirection.fromJson(Map<String, dynamic> json) => SwellDirection(
        icon: json["icon"].toDouble(),
        meteo: json["meteo"] == null ? null : json["meteo"].toDouble(),
        noaa: json["noaa"].toDouble(),
        sg: json["sg"].toDouble(),
    );

    Map<String, dynamic> toJson() => {
        "icon": icon,
        "meteo": meteo == null ? null : meteo,
        "noaa": noaa,
        "sg": sg,
    };
}

class Meta {
    Meta({
        this.cost,
        this.dailyQuota,
        this.end,
        this.lat,
        this.lng,
        this.params,
        this.requestCount,
        this.start,
    });

    int cost;
    int dailyQuota;
    String end;
    double lat;
    double lng;
    List<String> params;
    int requestCount;
    String start;

    factory Meta.fromJson(Map<String, dynamic> json) => Meta(
        cost: json["cost"],
        dailyQuota: json["dailyQuota"],
        end: json["end"],
        lat: json["lat"].toDouble(),
        lng: json["lng"].toDouble(),
        params: List<String>.from(json["params"].map((x) => x)),
        requestCount: json["requestCount"],
        start: json["start"],
    );

    Map<String, dynamic> toJson() => {
        "cost": cost,
        "dailyQuota": dailyQuota,
        "end": end,
        "lat": lat,
        "lng": lng,
        "params": List<dynamic>.from(params.map((x) => x)),
        "requestCount": requestCount,
        "start": start,
    };
}

2 Answers 2

1

Please check the working code for your main.dart. The issue was that you were calling Service.getConditions in initState. Service.getConditions is a future and you were not waiting for the Future to complete before loading the data in ListView. The code below will show you one of the ways of how you can wait for the Future to complete and then load the data. In ListView you were showing condition.airTemperature so I made a bit of change there and now showing condition.airTemperature.noaa& condition.airTemperature.sg.

import 'package:flutter/material.dart';
import 'package:moreapi_practise/weather_model.dart';
import 'Service.dart';


void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'My API Practice'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<Hour> _conditions;
  bool _loading;
  Future myFuture;

  Future _getService() async {
    await Service.getConditions().then((conditions) {
      setState(() {
        _conditions = conditions;
        _loading = false;
      });
    });
  }

  @override
  void initState() {
    super.initState();
    _loading = true;
    myFuture = _getService();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_loading ? 'Loading...' : 'Conditions'),
      ),
      body: _loading
          ? const Center(
              child: CircularProgressIndicator(),
            )
          : Container(
              child: ListView.builder(
                itemCount: _conditions.length,
                itemBuilder: (context, index) {
                  final Hour condition = _conditions[index];
                  return ListTile(
                    title: Text(
                        '${condition.airTemperature.noaa} ${condition.airTemperature.sg}'),
                  );
                },
              ),
            ),
    );
  }
}
Sign up to request clarification or add additional context in comments.

3 Comments

Wow this worked perfectly thank you, I was looking completely in the wrong direction, all very new to dart and JSON, thank you again!
As soon as I see a beginners question that involves a future the first thing I check is if they are waiting for the future to return. Please also upvote the answer if you think it solved your problem. Thanks.
I Will know next time, also my reputation score is too low for it to show publicly:(
0

Try json['hours’] as List<dynamic>. Then you can pass that to a parsing method and iterate over the elements.

List<Hour> hours = parseHourList(json['hours'] as List<dynamic>);

// ...

List<Hour> parseHourList(List<dynamic> jsonList) {
  final List<Hour> list = [];
  for(dynamic element in jsonList) {
    final Hour hour = Hour.fromJson(element as Map<String, dynamic>);
    list.add(hour);
  }
  return list;
}

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.