Dart中如何等待异步函数完成后再执行?Flutter场景咨询
解决Flutter异步获取城市列表后再构建UI的问题
嘿,我来帮你搞定这个异步加载的难题!在Flutter里,直接在build方法里调用异步函数肯定会踩坑——因为build会立刻执行,而你的fetchCitiesList还没来得及拿到城市数据,这时候cities还是空的,要么显示空白,要么直接报错。下面给你两种最常用的靠谱方案:
方案1:用FutureBuilder(官方推荐,更简洁)
FutureBuilder是Flutter专门用来处理异步数据构建UI的组件,它会自动监听异步操作的状态(加载中、成功、失败),帮你自动更新UI,不用手动管理状态。
步骤1:修改异步方法,让它返回Future
先把fetchCitiesList改成返回Future类型,这样FutureBuilder才能监听它:
import 'dart:convert'; import 'dart:io'; import 'dart:async'; Future<List<String>> fetchCitiesList() async { var url = 'https://gnsyms.000webhostapp.com/cities.php'; var httpClient = HttpClient(); try { var req = await httpClient.getUrl(Uri.parse(url)); var res = await req.close(); if (res.statusCode == HttpStatus.OK) { // 把响应转换成字符串再解析成JSON var jsonString = await res.transform(utf8.decoder).join(); List<dynamic> jsonList = jsonDecode(jsonString); // 假设返回的是字符串类型的城市列表,根据你的实际JSON结构调整 return jsonList.cast<String>(); } else { throw Exception('加载城市失败,状态码:${res.statusCode}'); } } catch (e) { throw Exception('请求错误:$e'); } finally { // 记得关闭HttpClient,避免资源泄漏 httpClient.close(); } }
步骤2:用FutureBuilder包裹列表UI
在build方法里用FutureBuilder替代直接生成列表,根据异步状态显示不同内容:
class CitiesScreen extends StatefulWidget { const CitiesScreen({super.key}); @override State<CitiesScreen> createState() => _CitiesScreenState(); } class _CitiesScreenState extends State<CitiesScreen> { late final Future<List<String>> _citiesFuture; @override void initState() { super.initState(); // 把Future存在State变量里,避免每次build重复发起请求 _citiesFuture = fetchCitiesList(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('城市列表')), body: FutureBuilder<List<String>>( future: _citiesFuture, builder: (context, snapshot) { // 加载中:显示进度指示器 if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } // 请求失败:显示错误信息 else if (snapshot.hasError) { return Center(child: Text('出错了:${snapshot.error}')); } // 请求成功:生成列表 else if (snapshot.hasData) { var cities = snapshot.data!; return ListView.builder( itemCount: cities.length, itemBuilder: (context, index) { return ListTile(title: Text(cities[index])); }, ); } // 没有数据的情况 else { return const Center(child: Text('暂无城市数据')); } }, ), ); } }
方案2:用StatefulWidget + setState(手动管理状态)
如果你更习惯手动控制状态更新,可以用这种方式:在initState里启动异步请求,拿到数据后调用setState触发UI刷新。
完整代码示例
import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; class CitiesScreen extends StatefulWidget { const CitiesScreen({super.key}); @override State<CitiesScreen> createState() => _CitiesScreenState(); } class _CitiesScreenState extends State<CitiesScreen> { List<String>? _cities; String? _errorMessage; @override void initState() { super.initState(); // 在页面初始化时发起请求 _fetchCitiesList(); } void _fetchCitiesList() async { var url = 'https://gnsyms.000webhostapp.com/cities.php'; var httpClient = HttpClient(); try { var req = await httpClient.getUrl(Uri.parse(url)); var res = await req.close(); if (res.statusCode == HttpStatus.OK) { var jsonString = await res.transform(utf8.decoder).join(); List<dynamic> jsonList = jsonDecode(jsonString); // 拿到数据后调用setState,通知Flutter刷新UI setState(() { _cities = jsonList.cast<String>(); }); } else { setState(() { _errorMessage = '加载失败,状态码:${res.statusCode}'; }); } } catch (e) { setState(() { _errorMessage = '请求错误:$e'; }); } finally { httpClient.close(); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('城市列表')), body: _buildBody(), ); } Widget _buildBody() { // 先处理错误情况 if (_errorMessage != null) { return Center(child: Text(_errorMessage!)); } // 加载中显示进度条 if (_cities == null) { return const Center(child: CircularProgressIndicator()); } // 没有数据的情况 if (_cities!.isEmpty) { return const Center(child: Text('暂无城市数据')); } // 数据正常,生成列表 return ListView.builder( itemCount: _cities!.length, itemBuilder: (context, index) { return ListTile(title: Text(_cities![index])); }, ); } }
重要提醒
- 绝对不要在
build方法里直接调用异步函数,否则每次UI刷新都会重复发起请求,浪费网络资源。 - 一定要处理网络请求的异常,给用户清晰的错误反馈,避免应用崩溃。
- 如果接口返回的是复杂JSON对象(不是简单字符串列表),建议创建对应的模型类来解析数据,比如用
json_serializable插件简化解析工作。
内容的提问来源于stack exchange,提问作者Ghanshyam Savaliya
相关产品推荐
相关产品推荐

