Flutter天气应用无法显示城市名和温度问题求助
Flutter天气应用问题排查:_weather始终为null导致无法显示城市名和温度
我正在开发一款Flutter天气应用,但遇到了问题:home.dart中的_weather始终为null,导致无法显示城市名和温度。以下是相关代码,请帮忙找出问题并解决显示需求。
api_model.dart代码
import 'package:flutter/material.dart'; class Weather { final String? cityName; final double? temp; final List<WeatherData>? weatherdesc; final double? humidity; Weather({ required this.cityName, required this.temp, required this.weatherdesc, required this.humidity, }); factory Weather.fromJson(Map<String, dynamic> json) { return Weather( cityName: json['name'], temp: (json['main']['temp'] as num).toDouble(), weatherdesc: (json['weather'] as List<dynamic>) .map((e) => WeatherData.fromJson(e as Map<String, dynamic>)) .toList(), humidity: (json['main']['humidity'] as num).toDouble(), ); } } @immutable class WeatherData { final int? id; final String? main; final String? description; final String icon; WeatherData({ required this.id, required this.main, required this.description, required this.icon, }); factory WeatherData.fromJson(Map<String, dynamic> json) { return WeatherData( id: json['id'], main: json['main'], description: json['description'], icon: json['icon'], ); } }
weather_services.dart代码
import 'package:geolocator/geolocator.dart'; import 'package:geocoding/geocoding.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'package:weather_app/model/api_model.dart'; import 'package:logger/logger.dart'; final logger = Logger(); class WeatherServices { final String apiKey; WeatherServices(this.apiKey); Future<String> getCurrentCity() async { logger.d('Checking location permission'); LocationPermission permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { logger.e("Location permission denied"); throw Exception("Location permission denied"); } } if (permission == LocationPermission.deniedForever) { logger.e("Location permission denied forever"); throw Exception("Location permission denied forever"); } logger.d('Getting current position'); Position position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.high); logger.d('Current position: ${position.latitude}, ${position.longitude}'); try { List<Placemark> placemarks = await placemarkFromCoordinates(position.latitude, position.longitude); logger.d('Placemarks length: ${placemarks.length}'); if (placemarks.isNotEmpty) { String? city = placemarks[0].locality; logger.d('City found: $city'); return city ?? "Unknown location"; } else { logger.e('No placemarks found'); throw Exception("No placemarks found"); } } catch (e) { logger.e('Geocoding Error: $e'); throw Exception("Geocoding Error: $e"); } } Future<Weather> getWeather(String cityName) async { final url = 'https://api.openweathermap.org/data/2.5/weather&q=$cityName&appid=$apiKey&units=metric'; logger.d('Fetching weather data from: $url'); final response = await http.get(Uri.parse(url)); if (response.statusCode == 200) { logger.d('Weather data fetched successfully'); logger.d("cityName is {$Weather.cityName}"); return Weather.fromJson(jsonDecode(response.body)); } else { logger.e('Failed to load weather data: ${response.statusCode}'); throw Exception('Failed to load weather data'); } } }
home.dart代码
import 'package:flutter/material.dart'; import 'package:logger/logger.dart'; import 'package:weather_app/model/api_model.dart'; import 'package:weather_app/services/weather_service.dart'; class Home extends StatefulWidget { const Home({super.key}); @override State<Home> createState() => _HomeState(); } class _HomeState extends State<Home> { final _weatherService = WeatherServices("api-key"); Weather? _weather; final logger = Logger(); Future<void> _fetchWeather() async { logger.d("Fetching weather..."); try { final cityName = await _weatherService.getCurrentCity(); logger.d('City: $cityName'); final weather = await _weatherService.getWeather(cityName); setState(() { _weather = weather; }); } catch (e) { logger.e("Failed to fetch weather"); } } @override void initState() { super.initState(); _fetchWeather(); } @override Widget build(BuildContext context) { var height = MediaQuery.of(context).size.height; var width = MediaQuery.of(context).size.width; return Scaffold( body: Stack(children: [ Container( width: double.infinity, height: double.infinity, decoration: const BoxDecoration( image: DecorationImage( image: AssetImage( "assets/images/background.png", ), fit: BoxFit.cover)), ), if (_weather != null) Positioned( top: 120, left: 130, child: Text( _weather!.cityName ?? "null", style: const TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold), ), ), if (_weather != null) Positioned( top: 160, left: 130, child: Text( '${_weather?.temp?.round() ?? "N/A"}°C', style: const TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold), ), ), Positioned( top: 130, bottom: 0, right: 0, left: 0, child: Container( width: width / 1.3, height: height / 1.3, decoration: const BoxDecoration( image: DecorationImage( image: AssetImage( "assets/images/House.png", ), )), ), ), Positioned( bottom: 0, left: 0.0, right: 0.0, child: Container( width: double.infinity, height: height / 2.5, decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color(0xFF2E335A), Color(0xFF48319D), ]), borderRadius: BorderRadius.only( topLeft: Radius.circular(55), topRight: Radius.circular(55), )), ), ), ]), ); } }
问题排查与解决方案
1. API请求URL错误(核心问题)
在weather_services.dart的getWeather方法中,URL拼接存在语法错误:
原URL用&作为第一个参数分隔符,而OpenWeatherMap API要求第一个参数用?开头,正确的URL应为:
final url = 'https://api.openweathermap.org/data/2.5/weather?q=$cityName&appid=$apiKey&units=metric';
错误的URL会导致API返回400请求错误,无法获取有效数据,进而_weather始终为null。
2. 日志输出错误
getWeather方法中的日志语句logger.d("cityName is {$Weather.cityName}");无效,Weather.cityName是类属性,此时未实例化无法获取值,应改为打印返回实例的城市名:
final weather = Weather.fromJson(jsonDecode(response.body)); logger.d("cityName is ${weather.cityName}"); return weather;
3. 异常处理优化
_fetchWeather方法仅打印错误日志,可添加用户可见的错误提示:
catch (e) { logger.e("Failed to fetch weather: $e"); if(mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('获取天气失败: $e')), ); } }
4. 替换占位API密钥
home.dart中WeatherServices("api-key")需替换为你的OpenWeatherMap真实API密钥,否则API会返回401未授权错误。
修复后的关键代码片段
weather_services.dart的getWeather方法:
Future<Weather> getWeather(String cityName) async { // 修复URL的?和&错误 final url = 'https://api.openweathermap.org/data/2.5/weather?q=$cityName&appid=$apiKey&units=metric'; logger.d('Fetching weather data from: $url'); final response = await http.get(Uri.parse(url)); if (response.statusCode == 200) { logger.d('Weather data fetched successfully'); final weather = Weather.fromJson(jsonDecode(response.body)); logger.d("cityName is ${weather.cityName}"); return weather; } else { logger.e('Failed to load weather data: ${response.statusCode}, ${response.body}'); throw Exception('Failed to load weather data: ${response.statusCode}'); } }
home.dart的_fetchWeather方法:
Future<void> _fetchWeather() async { logger.d("Fetching weather..."); try { final cityName = await _weatherService.getCurrentCity(); logger.d('City: $cityName'); final weather = await _weatherService.getWeather(cityName); setState(() { _weather = weather; }); } catch (e) { logger.e("Failed to fetch weather: $e"); if(mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('获取天气失败: $e')), ); } } }
内容的提问来源于stack exchange,提问作者keyvan bvb
相关产品推荐
相关产品推荐

