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

Flutter:如何通过PopupMenuButton预填充表单数据

解决PopupMenuButton传参预填充表单的问题

核心思路和你之前用InkWell的逻辑一致:只要拿到当前操作的账号实例/ID,就能复用原来的跳转和预填充逻辑。下面分两种场景给出具体实现:


场景1:PopupMenuButton在AccountView组件内部

因为你通过ChangeNotifierProvider.value把accounts[index]传入了AccountView,所以在AccountView里可以直接获取当前账号实例:

// 在AccountView组件中
Widget build(BuildContext context) {
  // 获取当前账号实例
  final currentAccount = Provider.of<Account>(context);

  return Row(
    mainAxisAlignment: MainAxisAlignment.spaceBetween,
    children: [
      // 原有账号信息展示部分
      ...,
      // 弹出菜单按钮
      PopupMenuButton<AccountOptions>(
        onSelected: (selection) {
          if (selection == AccountOptions.editAccount) {
            // 和InkWell一样传ID跳转
            Navigator.of(context).pushNamed(
              EditSettings.routeName,
              arguments: currentAccount.id
            );
          } else if (selection == AccountOptions.passVisible) {
            setState(() => _passwordVisible = true);
          } else if (selection == AccountOptions.passInvisible) {
            setState(() => _passwordVisible = false);
          }
        },
        itemBuilder: (context) => [
          const PopupMenuItem(
            value: AccountOptions.passVisible,
            child: Text('显示密码'),
          ),
          const PopupMenuItem(
            value: AccountOptions.passInvisible,
            child: Text('隐藏密码'),
          ),
          const PopupMenuItem(
            value: AccountOptions.editAccount,
            child: Text('编辑账号'),
          ),
        ],
      )
    ],
  );
}

场景2:PopupMenuButton在ListView的itemBuilder层级

如果弹出菜单和InkWell同级别,直接在构建时缓存当前账号即可:

itemBuilder: ((context, index) {
  // 缓存当前索引对应的账号
  final currentAccount = accounts[index];
  
  return Container(
    child: Row(
      children: [
        InkWell(
          onTap: () => Navigator.of(context)
              .pushNamed(EditSettings.routeName, arguments: currentAccount.id),
          child: ChangeNotifierProvider.value(
            value: currentAccount,
            // 注意:key要用单个账号的唯一标识,比如ID,不是整个accounts列表
            key: ValueKey(currentAccount.id),
            child: const AccountView(),
          ),
        ),
        PopupMenuButton<AccountOptions>(
          onSelected: (selection) {
            setState(() {
              if (selection == AccountOptions.editAccount) {
                // 直接用缓存的账号ID跳转
                Navigator.of(context).pushNamed(
                  EditSettings.routeName,
                  arguments: currentAccount.id
                );
              } else if (selection == AccountOptions.passVisible) {
                _passwordVisible = true;
              } else if (selection == AccountOptions.passInvisible) {
                _passwordVisible = false;
              }
            });
          },
          itemBuilder: (context) => [
            const PopupMenuItem(value: AccountOptions.passVisible, child: Text('显示密码')),
            const PopupMenuItem(value: AccountOptions.passInvisible, child: Text('隐藏密码')),
            const PopupMenuItem(value: AccountOptions.editAccount, child: Text('编辑账号')),
          ],
        )
      ],
    ),
  );
})

预填充表单的复用逻辑

跳转后的EditSettings页面不需要修改,依然用原来的方式获取ID并匹配账号数据:

class EditSettings extends StatelessWidget {
  static const routeName = '/edit-settings';

  @override
  Widget build(BuildContext context) {
    final accountId = ModalRoute.of(context)!.settings.arguments as String;
    // 从Provider或账号列表中找到对应数据
    final targetAccount = Provider.of<AccountProvider>(context).findById(accountId);

    // 用账号数据初始化表单控制器
    final nameCtrl = TextEditingController(text: targetAccount.name);
    final urlCtrl = TextEditingController(text: targetAccount.url);
    final usernameCtrl = TextEditingController(text: targetAccount.username);
    final passwordCtrl = TextEditingController(text: targetAccount.password);

    return Scaffold(
      appBar: AppBar(title: const Text('编辑账号')),
      body: Form(
        child: Column(
          children: [
            TextFormField(controller: nameCtrl, decoration: const InputDecoration(labelText: '名称')),
            TextFormField(controller: urlCtrl, decoration: const InputDecoration(labelText: '网址')),
            TextFormField(controller: usernameCtrl, decoration: const InputDecoration(labelText: '用户名')),
            TextFormField(controller: passwordCtrl, decoration: const InputDecoration(labelText: '密码')),
            ElevatedButton(onPressed: () {/* 保存逻辑 */}, child: const Text('保存'))
          ],
        ),
      ),
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 21:05:35