Flutter Bloc对接OpenWeatherAPI报WeatherErrorstate问题排查
Flutter Bloc状态管理问题排查
你一直触发WeatherErrorstate是以下几个问题叠加导致的,逐个修复即可:
- 初始请求参数为空:应用启动时输入框无内容,你在初始状态分支直接取
textEditingController.text作为请求参数,空城市名会让OpenWeather接口直接返回错误状态码,repository层判断非200就返回null,最终抛出错误状态。 - 请求URL拼接错误:你写的接口地址末尾多了无效的
#字符,会干扰接口参数解析,即使传了正确城市名也可能请求失败。 - 生命周期逻辑错误:在
BlocBuilder的builder方法内直接add事件,会触发「状态更新→builder重建→再次add事件」的死循环,重复请求会打乱正常状态流转。 - 实例创建冲突:你在homepage的build方法里手动new了Repository实例,和main.dart中BlocProvider注入的实例不匹配,容易出现依赖不一致问题。
- 全局Controller风险:你把
TextEditingController定义成了全局变量,没有和页面生命周期绑定,会造成内存泄漏。
修复方案
1. 修正网络层代码
打开networkfile.dart,删掉URL末尾多余的#,增加空参数拦截,加调试日志方便定位接口错误:
import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:weatherapi/Model/weathermodel.dart'; class Repository { Future<Weather?> getdata({required String location}) async { // 空参数直接拦截,不发无效请求 if (location.trim().isEmpty) return null; // 移除末尾无效的#符号 final url = Uri.parse( 'https://api.openweathermap.org/data/2.5/weather?q=${location.trim()}&appid=c1b66d13f980552b4cdd1123f5c20c72' ); final response = await http.get(url); if (response.statusCode != 200) { // 调试时打开这行日志,能直接看到接口返回的具体错误原因 // print('接口报错:${response.statusCode} ${response.body}'); return null; } final decodedData = jsonDecode(response.body); return Weather.fromJson(decodedData); } }
2. 修正页面逻辑
打开homepage.dart,把Controller放到State内部管理,初始请求放到initState生命周期,不要在builder里触发事件,增加查询触发按钮:
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:weatherapi/Bloc/weather_bloc.dart'; import 'package:weatherapi/Model/weathermodel.dart'; class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { // 把Controller放到State内部,绑定页面生命周期 final TextEditingController textEditingController = TextEditingController(); @override void initState() { super.initState(); // 初始请求默认城市,不要传空字符串 context.read<WeatherBloc>().add( loadWeatherDataEvent(location: 'beijing') ); } @override void dispose() { textEditingController.dispose(); super.dispose(); } // 统一的查询触发方法 void _searchWeather() { final keyword = textEditingController.text.trim(); if (keyword.isEmpty) return; context.read<WeatherBloc>().add(loadWeatherDataEvent(location: keyword)); } @override Widget build(BuildContext context) { // 删除build方法内手动创建Repository的代码,统一用BlocProvider注入的实例 return MaterialApp( home: Scaffold( body: BlocBuilder<WeatherBloc, WeatherState>( builder: (context, state) { if (state is WeatherLoadingstate) { return const Center( child: CircularProgressIndicator(color: Colors.yellow), ); } if (state is Weatherloadedstate) { return _buildWeatherPage(state.weathermodel); } if (state is WeatherErrorstate) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('天气查询失败,请检查城市名或网络连接'), const SizedBox(height: 16), ElevatedButton( onPressed: _searchWeather, child: const Text('重试') ) ], ), ); } // 初始状态显示加载指示器即可,不要在这里触发事件 return const Center( child: CircularProgressIndicator(color: Colors.red), ); }, ) ), ); } Widget _buildWeatherPage(Weather weather) { return Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( controller: textEditingController, onSubmitted: (_) => _searchWeather(), decoration: InputDecoration( hintText: '输入城市名', suffixIcon: IconButton( icon: const Icon(Icons.search), onPressed: _searchWeather, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(30), ), ), ), const SizedBox(height: 24), Text( weather.cityname.toString(), style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold), ) ], ), ); } }
3. 检查依赖注入配置
确认main.dart中Bloc和Repository的注入逻辑正确,不要在页面内手动创建相关实例:
void main() { runApp( MultiRepositoryProvider( providers: [ RepositoryProvider(create: (context) => Repository()), ], child: MultiBlocProvider( providers: [ BlocProvider( create: (context) => WeatherBloc( repository: context.read<Repository>() ), ), ], child: const MyApp(), ), ) ); }
额外注意
如果改完代码仍报错,先打开networkfile.dart里被注释的日志打印,运行后看控制台输出的接口状态码和返回信息,常见的其他报错原因:
- 安卓没有在
AndroidManifest.xml中配置网络权限 - iOS没有在
Info.plist中配置允许非HTTPS请求 - 输入的城市名拼写错误,接口无法匹配
- OpenWeather的appid失效或超出调用配额
内容的提问来源于stack exchange,提问作者Diljith
相关产品推荐
相关产品推荐

