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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 13:42:01