Problems with a future in flutter

Viewed 43

I am making a simple weather application, with the OpenWeather API, and it throws me the following error:  "The following _CastError was thrown building FutureBuilder(dirty, state: _FutureBuilderState#9133d): type '_Type' is not a subtype of type 'Weather?' in type cast" The Code is the following:

  Future getForecast(Location location) async {
  Forecast forecast;
  String apiKey = "117bf0ee07134176cf271f0ad49f3807";
  String lat = location.lat;
  String lon = location.lon;
  var url =
      "https://api.openweathermap.org/data/2.5/onecall?lat=$lat&lon=$lon&appid=$apiKey&units=metric";

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

  if (response.statusCode == 200) {
    forecast = Forecast.fromJson(jsonDecode(response.body));
  }

  return _forecast;
}

class _forecast {
} 

This is my complete code:

class CurrentWeatherPage extends StatefulWidget {
  final List<Location> locations;
  final BuildContext context;
  const CurrentWeatherPage(this.locations, this.context);

  @override
  _CurrentWeatherPageState createState() =>
      _CurrentWeatherPageState(this.locations, this.context);
}

class _CurrentWeatherPageState extends State<CurrentWeatherPage> {
  final List<Location> locations;
  Location location;
  final BuildContext context;
  _CurrentWeatherPageState(List<Location> locations, BuildContext context)
      : this.locations = locations,
        this.context = context,
        this.location = locations[0];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Color.fromRGBO(241, 254, 252, 1),
      appBar: AppBar(
        title: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              "Datos ",
              style: TextStyle(fontSize: 22),
            ),
            Text(
              "Meteorológicos",
              style: TextStyle(fontSize: 22, color: Color.fromRGBO(56, 215, 199, 1)),
            )
          ],
        ),
        backgroundColor: Color.fromRGBO(10, 45, 66, 0.9),

      ),
        body: ListView(children: <Widget>[
          currentWeatherViews(this.locations, this.location, this.context),
          forcastViewsHourly(this.location),
          forcastViewsDaily(this.location),
        ]),


    );
  }

  void _changeLocation(Location newLocation) {
    setState(() {
      location = newLocation;
    });
  }

  Widget currentWeatherViews(
      List<Location> locations, Location location, BuildContext context) {
    Weather? _weather;

    return FutureBuilder(
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          _weather = snapshot.data as Weather?;
          if (_weather == null) {
            return Text("Error getting weather");
          } else {
            return Column(children: [
              createAppBar(locations, location, context),
              // CityDropDown(locations),
              weatherBox(_weather!),
              weatherDetailsBox(_weather!),
            ]);
          }
        } else {
          return Center(child: CircularProgressIndicator());
        }
      },
      future: getCurrentWeather(location),
    );
  }

  Widget forcastViewsHourly(Location location) {
    Forecast? _forcast;

    return FutureBuilder(
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          _forcast = snapshot.data as Forecast?;
          if (_forcast == null) {
            return Text("Error getting weather");
          } else {
            return hourlyBoxes(_forcast!);
          }
        } else {
          return Center(child: CircularProgressIndicator());
        }
      },
      future: getForecast(location),
    );
  }

  Widget forcastViewsDaily(Location location) {
    Forecast? _forcast;

    return FutureBuilder(
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          _forcast = snapshot.data as Forecast?;
          if (_forcast == null) {
            return Text("Error getting weather");
          } else {
            return dailyBoxes(_forcast!);
          }
        } else {
          return Center(child: CircularProgressIndicator());
        }
      },
      future: getForecast(location),
    );
  }

  Widget createAppBar(
      List<Location> locations, Location location, BuildContext context) {
    // Location dropdownValue = locations.first;
    return Container(
        padding:
        const EdgeInsets.only(left: 20, top: 15, bottom: 15, right: 20),
        margin: const EdgeInsets.only(
            top: 35, left: 15.0, bottom: 15.0, right: 15.0),
        decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.all(Radius.circular(60)),
            boxShadow: [
              BoxShadow(
                color: Colors.grey.withOpacity(0.1),
                spreadRadius: 5,
                blurRadius: 7,
                offset: Offset(0, 3),
              )
            ]),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            DropdownButton<Location>(
              value: location,
              icon: Icon(
                Icons.keyboard_arrow_down_rounded,
                color: Colors.black,
                size: 24.0,
                semanticLabel: 'Tap to change location',
              ),
              elevation: 16,
              underline: Container(
                height: 0,
                color: Color.fromRGBO(10, 45, 66, 0.9),
              ),
              onChanged: (newLocation) {
                // callback(newValue);
                // setState(() {
                //   location = newValue;
                // });
                var changeLocation = newLocation;
              },
              items:
              locations.map<DropdownMenuItem<Location>>((Location value) {
                return DropdownMenuItem<Location>(
                  value: value,
                  child: Text.rich(
                    TextSpan(
                      children: <TextSpan>[
                        TextSpan(
                            text: '${value.city.capitalizeFirstOfEach}, ',
                            style: TextStyle(
                                fontWeight: FontWeight.bold, fontSize: 16)),
                        TextSpan(
                            text: '${value.country.capitalizeFirstOfEach}',
                            style: TextStyle(
                                fontWeight: FontWeight.normal, fontSize: 16)),
                      ],
                    ),
                  ),
                );
              }).toList(),
            ),
          ],
        ));
  }

  Widget weatherDetailsBox(Weather _weather) {
    return Container(
      padding: const EdgeInsets.only(left: 15, top: 25, bottom: 25, right: 15),
      margin: const EdgeInsets.only(left: 15, top: 5, bottom: 15, right: 15),
      decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.all(Radius.circular(20)),
          boxShadow: [
            BoxShadow(
              color: Colors.grey.withOpacity(0.1),
              spreadRadius: 5,
              blurRadius: 7,
              offset: Offset(0, 3),
            )
          ]),
      child: Row(
        children: [
          Expanded(
              child: Column(
                children: [
                  Container(
                      child: Text(
                        "Viento",
                        textAlign: TextAlign.left,
                        style: TextStyle(
                            fontWeight: FontWeight.w600,
                            fontSize: 12,
                            color: Colors.grey),
                      )),
                  Container(
                      child: Text(
                        "${_weather.wind} km/h",
                        textAlign: TextAlign.left,
                        style: TextStyle(
                            fontWeight: FontWeight.w700,
                            fontSize: 15,
                            color: Colors.black),
                      ))
                ],
              )),
          Expanded(
              child: Column(
                children: [
                  Container(
                      child: Text(
                        "Humedad",
                        textAlign: TextAlign.left,
                        style: TextStyle(
                            fontWeight: FontWeight.w600,
                            fontSize: 12,
                            color: Colors.grey),
                      )),
                  Container(
                      child: Text(
                        "${_weather.humidity.toInt()}%",
                        textAlign: TextAlign.left,
                        style: TextStyle(
                            fontWeight: FontWeight.w700,
                            fontSize: 15,
                            color: Colors.black),
                      ))
                ],
              )),
          Expanded(
              child: Column(
                children: [
                  Container(
                      child: Text(
                        "Presión atmosférica",
                        textAlign: TextAlign.left,
                        style: TextStyle(
                            fontWeight: FontWeight.w600,
                            fontSize: 12,
                            color: Colors.black45),
                      )),
                  Container(
                      child: Text(
                        "${_weather.pressure} hPa",
                        textAlign: TextAlign.left,
                        style: TextStyle(
                            fontWeight: FontWeight.w700,
                            fontSize: 15,
                            color: Colors.black),
                      ))
                ],
              ))
        ],
      ),
    );
  }

  Widget weatherBox(Weather _weather) {
    return Stack(children: [
      Container(
        padding: const EdgeInsets.all(15.0),
        margin: const EdgeInsets.all(15.0),
        height: 160.0,
        decoration: BoxDecoration(
            color: Color.fromRGBO(55, 187, 174, 1),
            borderRadius: BorderRadius.all(Radius.circular(20))),
      ),
      ClipPath(
          clipper: Clipper(),
          child: Container(
              padding: const EdgeInsets.all(15.0),
              margin: const EdgeInsets.all(15.0),
              height: 160.0,
              decoration: BoxDecoration(
                  color: Color.fromRGBO(198, 9, 125, 1),
                  borderRadius: BorderRadius.all(Radius.circular(20))))),
      Container(
          padding: const EdgeInsets.all(15.0),
          margin: const EdgeInsets.all(15.0),
          height: 160.0,
          decoration: BoxDecoration(
              borderRadius: BorderRadius.all(Radius.circular(20))),
          child: Row(
            children: [
              Expanded(
                  child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      mainAxisSize: MainAxisSize.min,
                      children: <Widget>[
                        getWeatherIcon(_weather.icon),
                        Container(
                            margin: const EdgeInsets.all(5.0),
                            child: Text(
                              "${_weather.description.capitalizeFirstOfEach}",
                              style: TextStyle(
                                  fontWeight: FontWeight.normal,
                                  fontSize: 16,
                                  color: Colors.white),
                            )),
                        Container(

                            margin: const EdgeInsets.all(5.0),
                            child: Text(
                              "Máxima:${_weather.high.toInt()}° Mínima:${_weather.low.toInt()}°",
                              textAlign: TextAlign.left,
                              style: TextStyle(
                                  fontWeight: FontWeight.normal,
                                  fontSize: 13,
                                  color: Colors.white),
                            )),
                      ])),
              Column(children: <Widget>[
                Container(
                    child: Text(
                      "${_weather.temp.toInt()}°",
                      textAlign: TextAlign.left,
                      style: TextStyle(
                          fontWeight: FontWeight.bold,
                          fontSize: 60,
                          color: Colors.white),
                    )),
                Container(
                    margin: const EdgeInsets.all(0),
                    child: Text(
                      "Sensación térmica ${_weather.feelsLike.toInt()}°",
                      textAlign: TextAlign.left,
                      style: TextStyle(
                          fontWeight: FontWeight.normal,
                          fontSize: 13,
                          color: Colors.white),
                    )),
              ])
            ],
          ))
    ]);
  }

  Image getWeatherIcon(String _icon) {
    String path = 'assets/icons/';
    String imageExtension = ".png";
    return Image.asset(
      path + _icon + imageExtension,
      width: 70,
      height: 70,
    );
  }

  Image getWeatherIconSmall(String _icon) {
    String path = 'assets/icons/';
    String imageExtension = ".png";
    return Image.asset(
      path + _icon + imageExtension,
      width: 40,
      height: 40,
    );
  }

  Widget hourlyBoxes(Forecast _forecast) {
    return Container(

              margin: EdgeInsets.symmetric(vertical: 0.0),
        height: 150.0,
        child: ListView.builder(
            padding:
            const EdgeInsets.only(left: 8, top: 0, bottom: 0, right: 8),
            scrollDirection: Axis.horizontal,
            itemCount: _forecast.hourly.length,
            itemBuilder: (BuildContext context, int index) {
              return Container(

                  padding: const EdgeInsets.only(
                      left: 10, top: 15, bottom: 15, right: 10),
                  margin: const EdgeInsets.all(5),
                  decoration: BoxDecoration(

                        color: Color.fromRGBO(1, 68, 109, 1),

                      borderRadius: BorderRadius.all(Radius.circular(18)),
                      boxShadow: [
                        BoxShadow(
                          color: Colors.grey.withOpacity(0.1),
                          spreadRadius: 2,
                          blurRadius: 2,
                          offset: Offset(0, 1), // changes position of shadow
                        )
                      ]),
                  child: Column(children: [
                    Text(
                      "${_forecast.hourly[index].temp}°",
                      style: TextStyle(
                          fontWeight: FontWeight.w500,
                          fontSize: 17,
                          color: Color.fromRGBO(56, 215, 199, 1)),
                    ),
                    getWeatherIcon(_forecast.hourly[index].icon),
                    Text(
                      "${getTimeFromTimestamp(_forecast.hourly[index].dt)}",
                      style: TextStyle(
                          fontWeight: FontWeight.w600,
                          fontSize: 12,
                          color: Colors.white),
                    ),
                  ]));
            }));
  }

  String getTimeFromTimestamp(int timestamp) {
    var date = new DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
    var formatter = new DateFormat('h:mm a');
    return formatter.format(date);
  }

  String getDateFromTimestamp(int timestamp) {
    var date = new DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
    var formatter = new DateFormat('E');
    return formatter.format(date);
  }

  Widget dailyBoxes(Forecast _forcast) {
    return Expanded(
        child: ListView.builder(
            shrinkWrap: true,
            physics: ClampingScrollPhysics(),
            padding:
            const EdgeInsets.only(left: 8, top: 0, bottom: 0, right: 8),
            itemCount: _forcast.daily.length,
            itemBuilder: (BuildContext context, int index) {
              return Container(

                  decoration: BoxDecoration(
                    color: Color.fromRGBO(1, 68, 109, 1),
                    borderRadius: BorderRadius.circular(10),

                  ),
                  padding: const EdgeInsets.only(
                      left: 10, top: 5, bottom: 5, right: 10),
                  margin: const EdgeInsets.all(5),
                  child: Row(children: [
                    Expanded(
                        child: Text(
                          "${getDateFromTimestamp(_forcast.daily[index].dt)}",
                          style: TextStyle(fontSize: 14, color: Color.fromRGBO(56, 215, 199, 1)),
                        )),
                    Expanded(
                        child: getWeatherIconSmall(_forcast.daily[index].icon)),
                    Expanded(
                        child: Text(
                          "${_forcast.daily[index].high.toInt()}/${_forcast.daily[index].low.toInt()}",
                          textAlign: TextAlign.right,
                          style: TextStyle(fontSize: 14, color: Colors.white),
                        )),
                  ]));
            }));
  }
}

class Clipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    Path path = Path();
    path.moveTo(0, size.height - 20);

    path.quadraticBezierTo((size.width / 6) * 1, (size.height / 2) + 15,
        (size.width / 3) * 1, size.height - 30);
    path.quadraticBezierTo((size.width / 2) * 1, (size.height + 0),
        (size.width / 3) * 2, (size.height / 4) * 3);
    path.quadraticBezierTo((size.width / 6) * 5, (size.height / 2) - 20,
        size.width, size.height - 60);

    path.lineTo(size.width, size.height - 60);
    path.lineTo(size.width, size.height);
    path.lineTo(0, size.height);

    path.close();

    return path;
  }

  @override
  bool shouldReclip(Clipper oldClipper) => false;
}

Future getCurrentWeather(Location location) async {
  Weather weather;
  String city = location.city;
  String apiKey = "117bf0ee07134176cf271f0ad49f3807";
  var url =
      "https://api.openweathermap.org/data/2.5/weather?q=$city&appid=$apiKey&units=metric";

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

  if (response.statusCode == 200) {
    weather = Weather.fromJson(jsonDecode(response.body));
  }

  return _weather;
}

class _weather {
}

Future getForecast(Location location) async {
  Forecast forecast;
  String apiKey = "117bf0ee07134176cf271f0ad49f3807";
  String lat = location.lat;
  String lon = location.lon;
  var url =
      "https://api.openweathermap.org/data/2.5/onecall?lat=$lat&lon=$lon&appid=$apiKey&units=metric";

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

  if (response.statusCode == 200) {
    forecast = Forecast.fromJson(jsonDecode(response.body));
  }

  return _forecast;
}

class _forecast {
}

I'm new to this, and I've tried many ways, but I can't get it to work. I would be grateful if someone could tell me the error and how to solve it. Thank you!

1 Answers

In your getCurrentWeather method, you assign the return value of the API call to weather, a local variable, but then continue to return _weather, a class not a variable. I don't know why it compiles even, probably because you did not use proper types.

I'll clean up one of the methods, please do the same with all of them where you may have done this. This is how your method should look like:

Future<Weather?> getCurrentWeather(Location location) async {
  String city = location.city;
  String apiKey = "117bf0ee07134176cf271f0ad49f3807";
  var url =
      "https://api.openweathermap.org/data/2.5/weather?q=$city&appid=$apiKey&units=metric";

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

  if (response.statusCode == 200) {
    return Weather.fromJson(jsonDecode(response.body));
  }

  return null;
}

Just a few random other remarks: the future should be a state variable, not called recreated every time build is called. This will happen if you put the call int the FutureBuilder directly. You pass a BuildContext through your class hierarchy. You should not need that. Just remove it altogether.

Related