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碰巧读到请求完成后的值才会更新。
修复方案
- 改造
CanteenInfo类,移除构造函数里的自动请求逻辑,把数据请求方法暴露出来,让外部可以await等待请求完成 - 把数据加载逻辑移到
initState生命周期中,整个页面生命周期只触发一次,不要放在build里 - 等网络请求彻底返回后,再更新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
相关产品推荐
相关产品推荐

