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

Flutter下拉菜单使用Firebase数据时出现重复值断言错误

问题回顾

硬编码Dropdown选项时功能正常,但使用Firebase获取的分类列表时,出现断言错误:DropdownButton的值应对应唯一项,且控制台显示原本5组键值对变为10组,存在同一key对应不同value的重复项问题。

核心错误分析

  • 职责混淆导致数据重复:Category类同时承担数据模型和StateNotifier的职责,每次调用getCategoryLabels时,循环中每次添加新分类都会扩展旧state,重复调用就会导致数据累加翻倍(比如两次调用后变成10项)。
  • Value值类型不匹配:DropdownButtonFormField的value绑定了productCategory(整个state列表),但实际需要的是选中的单个分类ID,类型不匹配触发断言失败。
  • 数据加载逻辑低效:getCategoryLabels在循环遍历Firebase文档时,每次迭代都更新state,应该一次性收集所有分类后再赋值,避免多次不必要的状态更新。
  • 选中事件未生效:categoryOnChanged方法仅返回name,未更新选中状态,导致Dropdown无法同步选中值。

修复方案

1. 拆分数据模型与状态管理

将数据模型和StateNotifier分离,明确职责:

// 独立的分类数据模型
class CategoryModel {
  final String id;
  final String name;
  final String imageUrl;

  CategoryModel({
    required this.id,
    required this.name,
    required this.imageUrl,
  });
}

// 状态管理类
final categoryProvider = StateNotifierProvider<CategoryNotifier, List<CategoryModel>>(
  (ref) => CategoryNotifier(),
);

class CategoryNotifier extends StateNotifier<List<CategoryModel>> {
  String? selectedCategoryId;

  CategoryNotifier() : super([]);

  String? get selectedId => selectedCategoryId;

  void updateSelectedCategory(String? id) {
    selectedCategoryId = id;
    // 触发UI更新
    state = [...state];
  }

  Future getCategoryLabels() async {
    final snapshot = await FirebaseFirestore.instance.collection('categories').get();
    // 一次性转换所有文档为模型,替换state而非累加
    final categories = snapshot.docs.map((doc) => CategoryModel(
      id: doc.get('categoryId'),
      name: doc.get('name'),
      imageUrl: doc.get('image'),
    )).toList();
    state = categories;
  }
}

2. 修正DropdownButtonFormField实现

绑定正确的选中值,修复选项生成逻辑:

child: Consumer<CategoryNotifier>(
  builder: (context, notifier, child) {
    return DropdownButtonFormField<String>(
      dropdownColor: Colors.deepOrange,
      hint: Text(
        "Category",
        style: TextStyle(
          color: Colors.white,
          fontSize: Device.screenType == ScreenType.mobile ? 16.sp : 18.sp,
        ),
      ),
      // 绑定选中的分类ID
      value: notifier.selectedId,
      decoration: const InputDecoration.collapsed(hintText: ''),
      icon: const Icon(Icons.arrow_drop_down, color: Colors.white),
      iconSize: Device.screenType == ScreenType.mobile ? 18.sp : 22.sp,
      elevation: 16,
      validator: (value) => value == null ? 'Please select product category' : null,
      items: notifier.state.map((cat) {
        return DropdownMenuItem<String>(
          value: cat.id,
          child: Row(
            children: [
              Icon(
                Icons.check,
                color: Colors.white,
                size: Device.screenType == ScreenType.mobile ? 16.sp : 20.sp,
              ),
              SizedBox(width: 3.w),
              Text(
                cat.name,
                style: TextStyle(
                  color: Colors.white,
                  fontSize: Device.screenType == ScreenType.mobile ? 16.sp : 20.sp,
                ),
              ),
            ],
          ),
        );
      }).toList(),
      onChanged: (dropdownValue) {
        notifier.updateSelectedCategory(dropdownValue);
      },
    );
  },
),

3. 修正手动遍历逻辑(若需保留)

确保基于去重后的分类列表生成选项:

// 从状态管理获取正确的分类列表
final categoryList = ref.watch(categoryProvider);

Map<String, Key> dropdownKeys = {};
for (var cat in categoryList) {
  // 避免重复生成Key
  dropdownKeys.putIfAbsent(cat.id, () => UniqueKey());
}

List<DropdownMenuItem<String>> dropdownMenuItems = categoryList.map((cat) {
  return DropdownMenuItem<String>(
    key: dropdownKeys[cat.id],
    value: cat.id,
    child: Row(
      children: [
        Icon(
          Icons.check,
          color: Colors.white,
          size: Device.screenType == ScreenType.mobile ? 16.sp : 20.sp,
        ),
        SizedBox(width: 3.w),
        Text(
          cat.name,
          style: TextStyle(
            color: Colors.white,
            fontSize: Device.screenType == ScreenType.mobile ? 16.sp : 20.sp,
          ),
        ),
      ],
    ),
  );
}).toList();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 09:45:06