Flutter请求API获取数据期间展示加载动画/进度指示器的实现方案
解决方案
核心思路
问题根源是异步请求未完成时,UI直接读取了未初始化的可空字段,导致空值渲染和报错。我们通过新增加载状态控制UI渲染逻辑即可解决:
- 新增加载状态标记,页面初始化时默认处于加载中
- 数据请求完成后更新加载状态
- 根据加载状态渲染加载动画/真实数据,从根源避免空值被读取
修改后的代码
1. UI层代码修改(WeatherHome.dart)
import 'package:api_practice/worker/data.dart'; import 'package:flutter/material.dart'; class WeatherHome extends StatefulWidget { @override _WeatherHomeState createState() => _WeatherHomeState(); } class _WeatherHomeState extends State<WeatherHome> { TextEditingController searchcont = TextEditingController(); DataClass datainstace = DataClass(); // 新增加载状态变量,初始为加载中 bool _isLoading = true; // 可选:新增错误状态变量,处理请求失败场景 bool _isError = false; void data() async { try { await datainstace.getcurrentlocation(); await datainstace.getdata(); // 数据请求成功,更新状态 if(mounted) { setState(() { _isLoading = false; _isError = false; }); } } catch (e) { // 处理请求/定位失败场景 if(mounted) { setState(() { _isLoading = false; _isError = true; }); } print("请求出错:$e"); } } @override void initState() { data(); super.initState(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.blueGrey[900], body: SafeArea( child: _isLoading // 加载中状态:展示加载动画 ? const Center( child: CircularProgressIndicator( color: Colors.white, ), ) : _isError // 错误状态:展示错误提示+重试按钮 ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text("加载失败,请检查网络或定位权限", style: TextStyle(color: Colors.white, fontSize: 16),), const SizedBox(height: 20,), ElevatedButton( onPressed: () { setState(() { _isLoading = true; }); data(); }, child: const Text("重试") ) ], ), ) // 加载完成:展示真实天气数据 : Column( children: [ Stack( alignment: Alignment.center, children: [ Padding( padding: const EdgeInsets.fromLTRB(15, 20, 15, 15), child: TextField( controller: searchcont, decoration: InputDecoration( hintText: "Search City", hintStyle: TextStyle( color: Colors.blueGrey[700], fontSize: 17), focusColor: Colors.blueGrey[700], filled: true, fillColor: Colors.white, border: OutlineInputBorder( borderRadius: BorderRadius.circular(50), borderSide: BorderSide.none, ), ), ), ), Positioned( bottom: 30, right: 26, child: InkWell( onTap: () { // 搜索城市逻辑可以在这里实现 }, child: Icon( Icons.search_rounded, size: 30, color: Colors.blueGrey[700], ), ), ), ], ), const SizedBox(height: 30,), Text( datainstace.area ?? "未知地区", style: const TextStyle(fontSize: 25, color: Colors.white), ), const SizedBox(height: 20,), Image.network( datainstace.iconurl ?? "", width: 100, height: 100, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return const Icon(Icons.cloud, size: 100, color: Colors.white,); }, ), // 剩余温度、湿度等字段渲染逻辑可以在这里继续添加 ], ), ), ); } }
2. 可选优化:Data类增加状态码判断
避免接口请求失败时解析报错:
Future<void> getdata() async { if(lat == null || long == null) { throw Exception("未获取到定位信息"); } var url = Uri.parse( 'http://api.openweathermap.org/data/2.5/weather?lat=$lat&lon=$long&appid=5174d985695c2502f2a5307e7cbc6036&units=metric'); var response = await http.get(url); // 新增状态码判断 if(response.statusCode != 200) { throw Exception("接口请求失败,状态码:${response.statusCode}"); } Map data = jsonDecode(response.body); // 原有解析逻辑不变 discription = data["weather"][0]['description']; main = data["weather"][0]['main']; iconurl = "http://openweathermap.org/img/w/" + data["weather"][0]['icon'] + ".png"; Map main_data = data["main"]; temperature = main_data["temp"].toString(); humidity = main_data["humidity"].toString(); pressure = main_data["pressure"].toString(); Map wind_data = data["wind"]; wind = wind_data["speed"].toString(); Map sys_data = data["sys"]; countryname = sys_data["country"]; area = data["name"]; }
额外优化点
- 接口链接添加
&units=metric参数,可以直接获取摄氏度为单位的温度,不需要额外转换 - 所有可空字段渲染时都添加
??默认值兜底,避免极端场景下的空值问题 Image.network添加errorBuilder参数,处理图片加载失败的情况- 所有
setState前加mounted判断,避免页面销毁后触发更新导致的内存泄漏
内容的提问来源于stack exchange,提问作者Anas Shafi
相关产品推荐
相关产品推荐

