-1

I'm trying to render weather forecast data, but having issues rendering.

in WeatherItem.dart

import 'package:flutter/material.dart';
import 'package:forecast/models/weather_data.dart';

class WeatherItem extends StatelessWidget {
  final WeatherData weather;

  const WeatherItem({Key? key, required this.weather}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          children: <Widget>[
            Text(weather.placeName ?? 'NA',
                style: const TextStyle(color: Colors.white)),
            Text('${weather.temperature}',
                style: const TextStyle(color: Colors.white, fontSize: 32.0)),
            Text('${weather.feels_like.toString()}°F',
                style: const TextStyle(color: Colors.white)),
            Text('${weather.humidity}',
                style: const TextStyle(color: Colors.white)),
            Text('${weather.wind_speed}',
                style: const TextStyle(color: Colors.white)),
          ],
        ),
      ),
    );
  }
}

in forcastData.dart

import 'package:forecast/models/weather_data.dart';

class ForecastData {
  final List list;

  ForecastData({required this.list});

  factory ForecastData.fromJson(Map<String, dynamic> json) {
    var list = json['list']?.map((e) => e)?.toList(growable: true) ?? [];

    list.forEach((e) {
      WeatherData w = WeatherData(
          placeName: json['city']['name'],
          temperature: e['main']['temp'],
          feels_like: e['main']['temp'],
          pressure: e['main']['temp'],
          humidity: e['main']['temp'],
          wind_speed: e['main']['temp']);

      list.add(w);
    });

    return ForecastData(
      list: list,
    );
  }
}

weather.dart

import 'package:flutter/material.dart';
import 'package:forecast/models/weather_data.dart';

class Weather extends StatelessWidget {
  final WeatherData weather;

  const Weather({Key? key, required this.weather}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Text('${weather.placeName}',
            style: const TextStyle(color: Colors.white)),
        Text('${weather.temperature}',
            style: const TextStyle(color: Colors.white, fontSize: 32.0)),
        Text('${weather.feels_like.toString()}°F',
            style: const TextStyle(color: Colors.white)),
        Text('${weather.pressure}',
            style: const TextStyle(color: Colors.white)),
        Text('${weather.humidity}',
            style: const TextStyle(color: Colors.white)),
        Text('${weather.wind_speed}',
            style: const TextStyle(color: Colors.white)),
      ],
    );
  }
}

in main.dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:forecast/models/forecast_data.dart';
import 'package:forecast/models/weather_data.dart';
import 'package:forecast/widgets/weather.dart';
import 'package:forecast/widgets/weather_item.dart';
import 'package:http/http.dart' as http;

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  WeatherData weatherData = WeatherData();
  ForecastData? forecastData;
  bool isLoading = false;

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

    loadWeather();
    loadForecast();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Weather App',
      theme: ThemeData(
        primarySwatch: Colors.red,
      ),
      home: Scaffold(
        backgroundColor: Colors.blueGrey,
        appBar: AppBar(
          title: const Text('Flutter Weather App'),
        ),
        body: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              Expanded(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: Weather(weather: weatherData)),
                  ],
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: IconButton(
                  icon: const Icon(Icons.refresh),
                  tooltip: 'Refresh',
                  onPressed: () {},
                  color: Colors.white,
                ),
              ),
              SafeArea(
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Container(
                    height: 200.0,
                    child: Builder(builder: (context) {
                      return ListView.builder(
                          itemBuilder: (context, index) => WeatherItem(
                              weather: forecastData?.list.elementAt(index)));
                    }),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  loadWeather() async {
    setState(() {
      isLoading = true;
    });

    String url =
        'https://api.openweathermap.org/data/2.5/forecast?q=London&units=metric&appid=API';

    http.Response response = await http.get(Uri.parse(url));

    if (response.statusCode == 200 && response.statusCode == 200) {
      return setState(() {
        weatherData = WeatherData.fromJson(jsonDecode(response.body));
      });
    }
  }

  loadForecast() async {
    setState(() {
      isLoading = true;
    });

    String url =
        'https://api.openweathermap.org/data/2.5/forecast?q=London&units=metric&appid=API';

    http.Response response = await http.get(Uri.parse(url));

    if (response.statusCode == 200 && response.statusCode == 200) {
      return setState(() {
        forecastData = ForecastData.fromJson(jsonDecode(response.body));

        isLoading = false;
      });
    }
  }
}

I get an error message that says Null is not a subtype of type 'WeatherData' on the screen. I have set default value if it's null for Text widget in weahtherItem.dart. Any idea where the error is coming from? Even though there's default value in case the value is null?

Any help is appreciated. enter image description here enter image description here

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Kim
  • 85
  • 5

1 Answers1

0

Your problem is here:

return ListView.builder(
  itemBuilder: (context, index) => WeatherItem(
    weather: forecastData?.list.elementAt(index)
  )
);

If forecastData hasn't finished loading yet then it is calling: weather: Null

You can fix it with a null check:

if (forecastData != null) {
  return ListView.builder(
    itemBuilder: (context, index) => WeatherItem(
      weather: forecastData.list.elementAt(index)
    )
  );
} else {
  return Container();
}
Tea Curran
  • 2,923
  • 2
  • 18
  • 22
  • Thank you so much, after adding the code I figured out the value was null as a container was loaded.. I'm still not sure if I'm iterating over each item correctly. I now see an error in terminal that says Unhandled Exception: Concurrent modification during iteration: Instance(length:41) of '_GrowableList'... – Kim Jan 01 '22 at 23:52