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

Flutter中如何让每个用户卡片的DropdownButton选中项独立?

问题解决:用户角色选择器同步变化的修复

核心问题是所有用户卡片共用了同一个全局角色变量,加上下拉菜单的状态没有和单个用户绑定,导致选一个角色所有菜单跟着变。下面是具体的修复方案和修改后的代码:

问题根源

  1. 全局变量String role = '1'被所有用户共享,修改时会触发所有依赖该变量的组件刷新
  2. RoleMenu的初始选中值为空,没有和用户当前的角色关联,且回调会修改全局变量,导致所有菜单同步更新
  3. 每个用户的待修改角色没有独立存储,共用状态导致互相干扰

修复方案

  1. 移除全局role变量,改为为每个用户维护独立的待修改角色状态
  2. 重构RoleMenu,让它接收用户当前角色作为初始值,选中后直接回传结果给父组件
  3. 在用户卡片的构建逻辑中,为每个用户保存独立的待修改角色,避免交叉影响

修改后的完整代码

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

final user = FirebaseAuth.instance.currentUser!;

Stream<QuerySnapshot> readUsers() =>
    FirebaseFirestore.instance.collection('users').snapshots();

Future accDialog(BuildContext context) => showDialog(
      context: context,
      builder: (context) => StatefulBuilder(
        builder: (BuildContext context, setState) => Dialog(
          child: Container(
            margin: const EdgeInsets.all(24),
            child: StreamBuilder(
              stream: readUsers(),
              builder: (BuildContext context,
                  AsyncSnapshot<QuerySnapshot> snapshot) {
                // 为每个用户存储独立的待修改角色,用文档ID作为key
                final Map<String, String> pendingRoles = {};

                if (snapshot.hasError) {
                  return const Text('Algo ha salido mal!');
                }
                if (snapshot.connectionState == ConnectionState.waiting) {
                  return const Text("Cargando");
                }

                return ListView(
                  shrinkWrap: true,
                  children: snapshot.data!.docs
                      .map((DocumentSnapshot document) {
                        Map<String, dynamic> data =
                            document.data()! as Map<String, dynamic>;
                        // 初始化当前用户的待修改角色为其现有角色
                        pendingRoles.putIfAbsent(document.id, () => data['role']);

                        return Card(
                          margin: const EdgeInsets.all(8),
                          child: Padding(
                            padding: const EdgeInsets.all(8),
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.start,
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: <Widget>[
                                Text(
                                  data['name'],
                                  style:
                                      Theme.of(context).textTheme.titleMedium,
                                ),
                                Text('Correo: ${data['email']}'),
                                Text('Rol Actual: ${data['role']}'),
                                Row(
                                  children: [
                                    Padding(
                                        padding: const EdgeInsets.all(8.0),
                                        child: RoleMenu(
                                          initialValue: data['role'],
                                          onRoleChanged: (newRole) {
                                            setState(() {
                                              // 更新当前用户的待修改角色
                                              pendingRoles[document.id] = newRole;
                                            });
                                          },
                                        )),
                                    const Spacer(),
                                    ElevatedButton(
                                        onPressed: () {
                                          final targetRole = pendingRoles[document.id];
                                          if (targetRole == null) return;
                                          // 使用当前用户的待修改角色更新数据库
                                          final setRole = <String, String>{
                                            "role": targetRole,
                                          };
                                          FirebaseFirestore.instance
                                              .collection('users')
                                              .doc(document.id)
                                              .set(setRole,
                                                  SetOptions(merge: true));
                                        },
                                        child: const Text('Guardar'))
                                  ],
                                )
                              ],
                            ),
                          ),
                        );
                      })
                      .toList()
                      .cast(),
                );
              },
            ),
          ),
        ),
      ),
    );

class RoleMenu extends StatefulWidget {
  // 接收用户当前的角色作为初始选中值
  final String initialValue;
  // 回调函数,回传选中的角色给父组件
  final Function(String) onRoleChanged;
  const RoleMenu({super.key, required this.initialValue, required this.onRoleChanged});

  @override
  State<RoleMenu> createState() => _RoleMenuState();
}

class _RoleMenuState extends State<RoleMenu> {
  late String selected;

  @override
  void initState() {
    super.initState();
    // 初始化选中值为用户当前的角色
    selected = widget.initialValue;
  }

  // 当父组件的initialValue变化时更新选中值(比如用户角色被成功修改后)
  @override
  void didUpdateWidget(covariant RoleMenu oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.initialValue != widget.initialValue) {
      setState(() {
        selected = widget.initialValue;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return DropdownButtonHideUnderline(
      child: DropdownButton<String>(
        enableFeedback: true,
        dropdownColor: ElevationOverlay.applySurfaceTint(
            Theme.of(context).colorScheme.surface,
            Theme.of(context).colorScheme.surfaceTint,
            2),
        iconEnabledColor: Theme.of(context).colorScheme.onSurfaceVariant,
        style: Theme.of(context).textTheme.labelLarge,
        items: const [
          DropdownMenuItem<String>(
            value: 'User',
            child: Text('Usuario'),
          ),
          DropdownMenuItem<String>(
            value: 'Admin',
            child: Text('Admin'),
          ),
        ],
        value: selected,
        onChanged: (value) {
          if (value == null) return;
          setState(() {
            selected = value;
          });
          // 通知父组件选中的角色
          widget.onRoleChanged(value);
        },
      ),
    );
  }
}

关键修改说明

  • 新增pendingRoles Map,用用户文档ID作为key,存储每个用户独立的待修改角色,避免全局变量的干扰
  • RoleMenu新增initialValue参数,初始化时显示用户当前的角色,更符合交互逻辑
  • 移除全局role变量,改用局部的pendingRoles管理每个用户的待修改状态
  • 给RoleMenu添加didUpdateWidget方法,确保当用户角色在数据库中被修改后,下拉菜单能同步更新显示最新值

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 12:45:35