Flutter中如何提取Future<userInfo>变量并展示用户信息?
个人资料系统异步加载用户信息显示问题
我正在开发一个个人资料系统,用户可编辑部分属性。目前已实现获取用户之前输入的所有信息,以及更新数据库中各项记录的功能。我尝试将用户已保存的信息显示在界面上,方便用户在修改前查看。
以下是我的GET请求代码,它能正常工作并返回userInfo对象:
class _MyStatefulWidgetState extends State<MyStatefulWidget> { final _formKey = GlobalKey<FormState>(); StorageService storage = StorageService(); userInfo info = userInfo(0, 0,"","", "", "", ""); Future<userInfo> fetchUserInfo() async { String jwt = await storage.readSecureData("jwt") as String; Map<String, dynamic> map = Jwt.parseJwt(jwt); String url = 'http://localhost:8087/getinfo/' + map["id"]; final response = await http.get(Uri.parse(url)); if (response.statusCode == 200) { print(response.body); return userInfo.fromJson(jsonDecode(response.body)); } else { throw Exception('Failed to load userInfo'); } }
这是我的界面构建代码,目前大部分字段都是硬编码的。我尝试用info.address展示家庭地址,但没有效果——因为userInfo对象是在异步函数里初始化的,没法在同步的build方法里直接访问:
@override Widget build(BuildContext context) { userInfo info = userInfo(0, 0,"","", "", "", ""); final ButtonStyle style = ElevatedButton.styleFrom( primary: Colors.green, fixedSize: Size(200, 50), textStyle: const TextStyle(fontSize: 20), ); return Padding( padding: EdgeInsets.all(0), child: ListView( children: <Widget>[ Container( height: 410, margin: const EdgeInsets.fromLTRB(20, 20, 20, 20), padding: const EdgeInsets.fromLTRB(20, 20, 20, 20), decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.all(Radius.circular(18)) ), child: Column( children: <Widget> [ const Text("Patient Details" , style: TextStyle(fontSize: 40, color: Colors.white)), Row( children: [ const Text("Medicare:" , style: TextStyle(fontSize: 30, height: 2, color: Colors.white)), const Text("1234567891" , style: TextStyle(fontSize: 30, height: 2, color: Colors.black)), ]), Row( children: [ const Text("Home Address:" , style: TextStyle(fontSize: 30, height: 2, color: Colors.white)), ]), Row( children: [ Text(info.address , style: TextStyle(fontSize: 30, height: 2, color: Colors.black)), ]), Row( children: [ const Text("Mobile:" , style: TextStyle(fontSize: 30, height: 2, color: Colors.white)), const Text("0447171002" , style: TextStyle(fontSize: 30, height: 2, color: Colors.black)), ]), Container( margin: const EdgeInsets.fromLTRB(0, 25, 0, 0), child: ElevatedButton.icon( style: style, icon: const Icon(Icons.edit), label: const Text('Edit Personal'), onPressed: () async { Navigator.push( context, MaterialPageRoute(builder: (context) => const EditInfoApp())); }, ) )]), ), Container( height: 470, margin: const EdgeInsets.fromLTRB(20, 20, 20, 20), padding: const EdgeInsets.fromLTRB(20, 20, 20, 20), decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.all(Radius.circular(18)) ), child: Column( children: <Widget> [ const Text("Health Information" , style: TextStyle(fontSize: 40, color: Colors.white)), Row( children: [ const Text("Health Status: " , style: TextStyle(fontSize: 30, height: 2, color: Colors.white)), const Text("average" , style: TextStyle(fontSize: 30, height: 2, color: Colors.black)), ]), Row( children: [ const Text("Allergies:" , style: TextStyle(fontSize: 30, height: 2, color: Colors.white)), ]), Row( children: [ const Text("nuts, Peanuts, fish" , style: TextStyle(fontSize: 30, height: 2, color: Colors.black)), ]), Row( children: [ const Text("Diagnosis:" , style: TextStyle(fontSize: 30, height: 2, color: Colors.white)), ]), Row( children: [ const Text("Asthma, constipation" , style: TextStyle(fontSize: 30, height: 2, color: Colors.black)), ]), Container( margin: const EdgeInsets.fromLTRB(0, 25, 0, 0), child: ElevatedButton.icon( style: style, icon: const Icon(Icons.edit), label: const Text('Edit Health'), onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => const EditHealthApp())); }, ) )]), ), ])); } }
解决方案
方法1:使用FutureBuilder
直接在build方法里用FutureBuilder处理异步请求,它会根据异步操作的状态(加载中、成功、失败)自动更新UI:
修改后的build方法核心部分:
@override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( primary: Colors.green, fixedSize: const Size(200, 50), textStyle: const TextStyle(fontSize: 20), ); return Padding( padding: EdgeInsets.all(0), child: FutureBuilder<userInfo>( future: fetchUserInfo(), // 传入异步请求方法 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) { // 请求成功,获取userInfo数据 final userData = snapshot.data!; return ListView( children: <Widget>[ Container( height: 410, margin: const EdgeInsets.fromLTRB(20, 20, 20, 20), padding: const EdgeInsets.fromLTRB(20, 20, 20, 20), decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.all(Radius.circular(18)) ), child: Column( children: <Widget> [ const Text("Patient Details" , style: TextStyle(fontSize: 40, color: Colors.white)), Row( children: [ const Text("Medicare:" , style: TextStyle(fontSize: 30, height: 2, color: Colors.white)), Text(userData.medicare ?? "暂无数据" , style: TextStyle(fontSize: 30, height: 2, color: Colors.black)), ]), Row( children: [ const Text("Home Address:" , style: TextStyle(fontSize: 30, height: 2, color: Colors.white)), ]), Row( children: [ Text(userData.address ?? "暂无数据" , style: TextStyle(fontSize: 30, height: 2, color: Colors.black)), ]), Row( children: [ const Text("Mobile:" , style: TextStyle(fontSize: 30, height: 2, color: Colors.white)), Text(userData.mobile ?? "暂无数据" , style: TextStyle(fontSize: 30, height: 2, color: Colors.black)), ]), // 其余按钮和布局保持不变 ] ), ), // 健康信息部分同理替换硬编码为userData对应字段 ], ); } else { // 无数据状态 return const Center(child: Text('暂无用户信息')); } }, ), ); }
方法2:在initState中加载数据并更新状态
如果不想用FutureBuilder,可以在initState里调用异步请求,获取数据后更新状态:
- 修改状态类的变量:
class _MyStatefulWidgetState extends State<MyStatefulWidget> { final _formKey = GlobalKey<FormState>(); StorageService storage = StorageService(); userInfo? info; // 改为可空类型 bool isLoading = true; String? errorMessage; @override void initState() { super.initState(); _loadUserInfo(); // 初始化时加载数据 } Future<void> _loadUserInfo() async { try { final userData = await fetchUserInfo(); setState(() { info = userData; isLoading = false; }); } catch (e) { setState(() { errorMessage = e.toString(); isLoading = false; }); } } // 保留原fetchUserInfo方法 }
- 修改
build方法:
@override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( primary: Colors.green, fixedSize: const Size(200, 50), textStyle: const TextStyle(fontSize: 20), ); if (isLoading) { return const Center(child: CircularProgressIndicator()); } if (errorMessage != null) { return Center(child: Text('加载失败: $errorMessage')); } final userData = info!; return Padding( padding: EdgeInsets.all(0), child: ListView( children: <Widget>[ // 替换所有硬编码字段为userData的对应属性,比如: Row( children: [ const Text("Medicare:" , style: TextStyle(fontSize: 30, height: 2, color: Colors.white)), Text(userData.medicare ?? "暂无数据" , style: TextStyle(fontSize: 30, height: 2, color: Colors.black)), ]), // 其余布局同理修改 ], ), ); }
注意点
- 移除
build方法里重新声明的userInfo info = userInfo(0, 0,"","", "", "", "");,避免覆盖状态变量 - 处理字段为空的情况,用
??提供默认值避免报错 - 两种方法都能解决异步数据无法在同步build中访问的问题:
FutureBuilder适合单次请求,initState加载适合需要后续更新数据的场景
内容的提问来源于stack exchange,提问作者jed101
相关产品推荐
相关产品推荐

