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

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里调用异步请求,获取数据后更新状态:

  1. 修改状态类的变量:
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方法
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 23:50:43