You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 04:07:33