Flutter天气应用:如何将GetWeatherModel的JSON数据展示到界面
解决Flutter天气应用中API数据无法展示到界面的问题
核心问题分析
你的代码已经能成功获取天气数据,但无法展示到UI的原因是:
- 没有将获取到的JSON数据与页面状态(State)绑定
- 调用异步API时未正确处理Future,导致数据获取后未触发UI更新
- 使用
dynamic类型处理数据,不利于后续UI取值和类型安全
解决方案步骤
1. 创建强类型Weather数据模型
将JSON数据解析为具体Dart类,避免使用dynamic,提升代码可读性与安全性:
class Weather { final double temperature; final String description; final String cityName; final int conditionId; Weather({ required this.temperature, required this.description, required this.cityName, required this.conditionId, }); // 从JSON解析为Weather对象 factory Weather.fromJson(Map<String, dynamic> json) { return Weather( temperature: json['main']['temp']?.toDouble() ?? 0.0, description: json['weather'][0]['description'] ?? 'Unknown', cityName: json['name'] ?? 'Unknown City', conditionId: json['weather'][0]['id'] ?? 0, ); } }
2. 修改GetWeatherModel返回Weather模型
更新getLocationWeather方法,返回Future<Weather?>并直接解析为Weather对象:
class GetWeatherModel { Future<Weather?> getLocationWeather() async { Location location = Location(); await location.getCurrentLocation(); // 检查位置是否获取成功 if (location.latitude == null || location.longitude == null) { print('无法获取位置信息'); return null; } http.Response response = await http.get( Uri.parse( "$openWeatherMapURL?lat=${location.latitude}&lon=${location.longitude}&appid=$apiKey&units=metric", ), ); if (response.statusCode == 200) { var decodedData = jsonDecode(response.body); return Weather.fromJson(decodedData); } else { print('请求失败,状态码:${response.statusCode}'); return null; } } }
3. 页面State绑定数据并处理异步更新
在_WeatherPageState中添加状态变量存储天气数据,修改API调用逻辑,确保数据获取后触发UI更新:
class _WeatherPageState extends State<WeatherPage> { GetWeatherModel getWeatherModel = GetWeatherModel(); Weather? _currentWeather; </think_never_used_51bce0c785ca2f68081bfa7d91973934> ```dart import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:geolocator/geolocator.dart'; import 'dart:convert'; //Create some const's for the API key and the openweather url. If i put this in //the weather class it needs to be static, why is this? const openWeatherMapURL = 'https://api.openweathermap.org/data/2.5/weather'; const apiKey = '9afee6d05ce90e00ea3e3372128de9b6'; class WeatherPage extends StatefulWidget { const WeatherPage({super.key, this.locationWeather}); final locationWeather; @override State<WeatherPage> createState() => _WeatherPageState(); } class _WeatherPageState extends State<WeatherPage> { GetWeatherModel getWeatherModel = GetWeatherModel(); @override void initState() { // TODO: implement initState super.initState(); getWeatherModel.getLocationWeather(); } Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('My first weather app'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('stuff'), Text( 'Temperature', ), Text( 'Description', ), ElevatedButton( onPressed: () { setState(() { GetWeatherModel getWeather = GetWeatherModel(); getWeather.getLocationWeather(); }); }, child: Text('Get Data')) ], ), )); } } class Location { //Create two variables to store the longitude and latitude retrieved //from geo locator double? latitude; double? longitude; //Create a function to retrieve the longitude and latitude from geolocator Future<void> getCurrentLocation() async { //Get the user permission to use location await Geolocator.requestPermission(); try { //try to get the current location from the phone through the //geolocator package with low accuracy to save battery and it is just //the general area needed Position position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.low); //put the latitude and longitude of the current position into these variabls latitude = position.latitude; longitude = position.longitude; } catch (e) { print(e); //throw ('Location cannot be found'); } } } class GetWeatherModel { //Create a function that uses the getCurrentLocation function and uses //it to retrieve data from the open weather api Future<dynamic> getLocationWeather() async { Location location = Location(); await location.getCurrentLocation(); // print(location.longitude); // print(location.latitude); http.Response response = await http.get( Uri.parse( "$openWeatherMapURL?lat=${location.latitude}&lon=${location.longitude}&appid=$apiKey&units=metric", ), ); if (response.statusCode == 200) { String data = response.body; // The decodedData variable needs to be a var because it is a dynamic data type, // and we wont know what the type is until it is returned var decodedData = jsonDecode(data); return decodedData; } else { print(response.statusCode); } } // Try to make the variable below more explicit by giving them the //type that will be returned // String weatherDescription = decodedData['weather'][0]['description']; // double temperature = decodedData['main']['temp']; // String cityName = decodedData['name']; // double condition = decodedData['weather'][0]['id']; // print(weatherDescription); // print(temperature); // print(cityName); // print(condition); // return weatherDescription; //return decodedData; //weather[0].description - from json viewer awesome path copy // main.temp // name // weather[0].id //print(decodedData); } ```还是英文,需要翻译成中文。 回答:去掉AI味,去掉AI味,去掉AI味 内容不准出现外链跳转到其他网站 禁止: 不准出现类似:看起来你遇到了什么问题、你如果给我更多输入、我太懂你这种憋屈感、看你遇到的这个情况等 文章末尾标注:内容的提问来源于stack exchange,提问作者The Synergist </think_never_used_51bce0c785ca2f68081bfa7d91973934>我现在需要把用户的问题和代码翻译成中文,然后给出解决方案,还要符合要求:去掉AI味,不用客套话,最后标注来源。首先,先整理用户的问题,然后给出修改后的代码和解释,要直接明了。 首先,用户的问题翻译成中文: 我正在学习Flutter课程,想搞懂API调用和JSON数据在界面间的传递逻辑。我试着做了个最简版天气应用,但没法把GetWeatherModel里获取的JSON数据展示到界面上,现在只能把数据打印到控制台,不知道接下来该怎么做,附上代码求解决办法: 然后给出解决方案,直接说问题点和修改步骤,代码改成中文注释,然后最后加标注。 现在开始组织内容: # Flutter天气应用API数据展示问题解决 ## 问题核心 你当前的代码能成功请求到天气数据并打印,但没法展示到界面,原因是: 1. 没有把获取到的数据和页面状态绑定,数据变化后没触发UI更新 2. 异步API调用的结果没被保存到页面的状态变量里 3. 用dynamic类型处理数据,不利于UI直接取值 ## 修改后的完整代码 ```dart import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:geolocator/geolocator.dart'; import 'dart:convert'; // API地址和密钥常量,放在类外是因为不需要和类实例绑定,类内的话要加static是因为静态成员属于类本身,不用实例化就能访问 const openWeatherMapURL = 'https://api.openweathermap.org/data/2.5/weather'; const apiKey = '9afee6d05ce90e00ea3e3372128de9b6'; // 天气数据模型类,把JSON转成强类型对象,方便取值 class WeatherData { final double temperature; final String description; final String cityName; WeatherData({ required this.temperature, required this.description, required this.cityName, }); // 从JSON解析成WeatherData对象 factory WeatherData.fromJson(Map<String, dynamic> json) { return WeatherData( temperature: json['main']['temp'].toDouble(), description: json['weather'][0]['description'], cityName: json['name'], ); } } class WeatherPage extends StatefulWidget { const WeatherPage({super.key, this.locationWeather}); final locationWeather; @override State<WeatherPage> createState() => _WeatherPageState(); } class _WeatherPageState extends State<WeatherPage> { GetWeatherModel getWeatherModel = GetWeatherModel(); WeatherData? _weatherData; // 保存天气数据的状态变量 @override void initState() { super.initState(); _fetchWeather(); // 初始化时获取天气 } // 异步获取天气数据并更新状态 Future<void> _fetchWeather() async { var result = await getWeatherModel.getLocationWeather(); if (result != null) { setState(() { _weatherData = WeatherData.fromJson(result); }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('我的第一个天气应用'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 根据是否有数据展示不同内容 _weatherData != null ? Column( children: [ Text( _weatherData!.cityName, style: const TextStyle(fontSize: 20), ), const SizedBox(height: 16), Text( '温度:${_weatherData!.temperature.toStringAsFixed(1)}°C', style: const TextStyle(fontSize: 24), ), const SizedBox(height: 16), Text( '天气:${_weatherData!.description}', style: const TextStyle(fontSize: 18), ), ], ) : const Text('暂无天气数据'), const SizedBox(height: 32), ElevatedButton( onPressed: _fetchWeather, child: const Text('获取数据'), ) ], ), ), ); } } class Location { // 存储经纬度 double? latitude; double? longitude; // 获取当前位置 Future<void> getCurrentLocation() async { // 请求位置权限 await Geolocator.requestPermission(); try { // 获取当前位置,低精度节省电量 Position position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.low); latitude = position.latitude; longitude = position.longitude; } catch (e) { print(e); } } } class GetWeatherModel { // 根据位置获取天气数据 Future<dynamic> getLocationWeather() async { Location location = Location(); await location.getCurrentLocation(); http.Response response = await http.get( Uri.parse( "$openWeatherMapURL?lat=${location.latitude}&lon=${location.longitude}&appid=$apiKey&units=metric", ), ); if (response.statusCode == 200) { String data = response.body; var decodedData = jsonDecode(data); return decodedData; } else { print(response.statusCode); return null; } } }
关键修改说明
- 新增WeatherData模型类:把JSON数据转成强类型对象,避免直接用dynamic取值,代码更安全易读
- 添加状态变量_weatherData:用来保存获取到的天气数据,作为UI展示的数据源
- 封装_fetchWeather方法:异步调用API,获取数据后用setState更新状态,触发UI刷新
- UI根据数据状态展示:判断_weatherData是否为空,分别展示数据或提示文本
内容的提问来源于stack exchange,提问作者The Synergist
这样应该就符合要求了,没有AI味,直接给出解决办法和修改后的代码,注释也翻译成中文了,最后加了标注。</think_never_used_51bce0c785ca2f68081bfa7d91973934># Flutter天气应用API数据展示问题解决
问题核心
你当前的代码能成功请求到天气数据并打印,但没法展示到界面,原因是:
- 没有把获取到的数据和页面状态绑定,数据变化后没触发UI更新
- 异步API调用的结果没被保存到页面的状态变量里
- 用
dynamic类型处理数据,不利于UI直接取值
修改后的完整代码
import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:geolocator/geolocator.dart'; import 'dart:convert'; // API地址和密钥常量,放在类外是因为不需要和类实例绑定;类内的话要加static是因为静态成员属于类本身,无需实例化就能访问 const openWeatherMapURL = 'https://api.openweathermap.org/data/2.5/weather'; const apiKey = '9afee6d05ce90e00ea3e3372128de9b6'; // 天气数据模型类,把JSON转成强类型对象,方便取值 class WeatherData { final double temperature; final String description; final String cityName; WeatherData({ required this.temperature, required this.description, required this.cityName, }); // 从JSON解析成WeatherData对象 factory WeatherData.fromJson(Map<String, dynamic> json) { return WeatherData( temperature: json['main']['temp'].toDouble(), description: json['weather'][0]['description'], cityName: json['name'], ); } } class WeatherPage extends StatefulWidget { const WeatherPage({super.key, this.locationWeather}); final locationWeather; @override State<WeatherPage> createState() => _WeatherPageState(); } class _WeatherPageState extends State<WeatherPage> { GetWeatherModel getWeatherModel = GetWeatherModel(); WeatherData? _weatherData; // 保存天气数据的状态变量 @override void initState() { super.initState(); _fetchWeather(); // 初始化时获取天气 } // 异步获取天气数据并更新状态 Future<void> _fetchWeather() async { var result = await getWeatherModel.getLocationWeather(); if (result != null) { setState(() { _weatherData = WeatherData.fromJson(result); }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('我的第一个天气应用'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 根据是否有数据展示不同内容 _weatherData != null ? Column( children: [ Text( _weatherData!.cityName, style: const TextStyle(fontSize: 20), ), const SizedBox(height: 16), Text( '温度:${_weatherData!.temperature.toStringAsFixed(1)}°C', style: const TextStyle(fontSize: 24), ), const SizedBox(height: 16), Text( '天气:${_weatherData!.description}', style: const TextStyle(fontSize: 18), ), ], ) : const Text('暂无天气数据'), const SizedBox(height: 32), ElevatedButton( onPressed: _fetchWeather, child: const Text('获取数据'), ) ], ), ), ); } } class Location { // 存储经纬度 double? latitude; double? longitude; // 获取当前位置 Future<void> getCurrentLocation() async { // 请求位置权限 await Geol
相关产品推荐
相关产品推荐

