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

Flutter应用Web API解析正常但页面UI加载缓慢问题求助

问题根因

代码存在3个显性逻辑错误,直接导致渲染卡顿、数据更新延迟:

  • 构造函数内的异步方法无等待机制:CanteenInfo构造函数里直接调用getWebsiteData(),但这是异步Future,没有任何手段监听它的完成状态。实例化对象时网络请求会在后台静默执行,后续调用取值逻辑时大概率请求还没返回,拿到的永远是初始值"yes";控制台能打印正确结果,只是因为你加的print语句刚好在请求完成后读到了内存中更新后的值,但页面根本没收到数据更新的通知。
  • 数据加载逻辑写在build方法中:Flutter的build方法会在帧刷新、状态变化时被频繁触发(每秒可能执行多次),当前代码每次build都会执行getHours(),满足条件时还会重复触发loadData(),等于不断新建CanteenInfo实例、重复发送网络请求,大量无效回调直接阻塞UI线程,渲染自然会卡顿。
  • 加载状态逻辑矛盾:loadData方法里刚新建完CanteenInfo实例就立刻await getHours(),但此时网络请求还在后台运行,getHours()直接把初始值"yes"塞进state,紧接着就把flag设为false退出加载态,等于加载指示器根本没等数据返回就消失,页面一直卡在初始值,直到某次build碰巧读到请求完成后的值才会更新。
修复方案
  1. 改造CanteenInfo类,移除构造函数里的自动请求逻辑,把数据请求方法暴露出来,让外部可以await等待请求完成
  2. 把数据加载逻辑移到initState生命周期中,整个页面生命周期只触发一次,不要放在build里
  3. 等网络请求彻底返回后,再更新state、退出加载状态

修复后代码

改造后的CanteenInfo类

class CanteenInfo {
  final String canteenUrl = 'https://sigarra.up.pt/feup/pt/mob_eme_geral.cantinas';
  List plates = ["sopa", "carne", "peixe", "dieta", "vegetariano"];
  List canteenMenu = [];
  String workingHours = "";
  String date = "";

  // 移除构造函数内的自动请求,改为显式调用的异步方法
  Future<void> getWebsiteData() async {
    var response =  await http.get(Uri.parse(canteenUrl));
    if (response.statusCode == 200) {
      var data = json.decode(response.body);
      workingHours = data[3]["horario"];
      date = data[3]["ementas"][0]["data"];
      canteenMenu.clear(); // 避免重复调用时重复添加数据
      for (int i = 0; i < 5; i++) {
        canteenMenu.add(data[3]["ementas"][0]["pratos"][i]["descricao"]);
      }
    }
    else {
      throw Exception('Failed to read $canteenUrl');
    }
  }
}

改造后的页面State代码

class _WorkingHours extends State<WorkingHours> {
  String hours = "";
  late CanteenInfo canteen;
  bool loading = true; // 统一管理加载状态

  @override
  void initState() {
    super.initState();
    canteen = CanteenInfo();
    // 初始化时仅触发一次数据加载
    loadData();
  }

  Future<void> loadData() async {
    setState(() {
      loading = true;
    });
    try {
      // 等待网络请求完全完成
      await canteen.getWebsiteData();
      if (mounted) { // 避免页面销毁后触发setState报错
        setState(() {
          hours = canteen.workingHours;
          loading = false;
        });
      }
    } catch (e) {
      if (mounted) {
        setState(() {
          hours = "获取营业时间失败";
          loading = false;
        });
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    // build方法仅负责根据当前state渲染UI,不要写任何数据加载、状态更新逻辑
    if (loading) {
      return const Scaffold(
          body: Center(
              child: CircularProgressIndicator()
          )
      );
    }

    return Scaffold(
      appBar: AppBar(
        title: const Text('EasyFood'),
      ),
      body: Center(
        child: Container (
          margin: const EdgeInsets.only(top: 100),
          child: Column(
            children: <Widget>[
              const Text(
                'Horário de Funcionamento',
                style: TextStyle(
                  fontSize: 35,
                  fontWeight: FontWeight.bold,
                  decoration: TextDecoration.underline,
                ),
              ),
              const Text(
                  '(Segunda a sexta)',
                  style: TextStyle(
                    fontSize: 15,
                  )
              ),
              const SizedBox(height: 100),
              Text(
                  'Lunch: $hours',
                  style: const TextStyle(
                      fontSize: 20
                  )
              ),
            ],
          ),
        ),
      ),
    );
  }
}

额外提示:build方法的唯一职责是根据当前状态渲染UI,绝对不要在里面写网络请求、setState这类会触发状态更新的逻辑,否则会导致无限重绘、重复请求、UI阻塞,这是Flutter开发中非常容易踩的基础坑。

内容的提问来源于stack exchange,提问作者Guilherme Moreira

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 10:24:13