Flutter Firestore 集合相关的下拉菜单

Posted

技术标签:

【中文标题】Flutter Firestore 集合相关的下拉菜单【英文标题】:Flutter Firestore Collection Dependent Drop Down Menus 【发布时间】:2021-12-21 02:45:08 【问题描述】:

我正在尝试使用 Flutter 和 Firestore 作为后端来实现级联下拉菜单。到目前为止,这是我的代码,它通过独立加载所有类别和子类别来工作。

class AddProductScreen extends StatefulWidget 
  final ProductData? data;
  AddProductScreen(this.data);
  @override
  AddProductScreenState createState() => AddProductScreenState();


class AddProductScreenState extends State<AddProductScreen> 

  AsyncMemoizer categoryMemoizer = AsyncMemoizer<List<CategoryData>>();
  AsyncMemoizer subCategoryMemoizer = AsyncMemoizer<List<SubCategoryData>>();

  CategoryData? selectedCategory;
  SubCategoryData? selectedSubCategory;
  // CategoryData selectedSubCategory;

  List<CategoryData> categories = [];
  List<SubCategoryData> subCategories = [];

  @override
  void initState() 
    super.initState();
    init();
  

  Future<void> init() async 
    categories = await categoryService.categoriesFuture();
    subCategories = await subCategoryService.categoriesFuture();
    setState(() );
  

  @override
  void setState(fn) 
    if (mounted) super.setState(fn);
  

  @override
  Widget build(BuildContext context) 

    return Scaffold(
      backgroundColor: white,
      appBar: AppBar(
        backgroundColor: white,
        elevation: 0.0,
        title: Text('Title'),
        actions: [
          isUpdate
              ? IconButton(
                  icon: Icon(Icons.delete_forever, color: black),
                  onPressed: () 
                    _showMyDialog();
                  ,
                ).paddingOnly(right: 8)
              : SizedBox(),
        ],
      ),
      body: SingleChildScrollView(
        child: Form(
          key: formKey,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              if (categories.isNotEmpty)
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('Select Category', style: boldTextStyle(size: 18)),
                    8.height,
                    Container(
                      width: context.width() * 0.45,
                      decoration: BoxDecoration(
                          borderRadius: radius(), color: Colors.grey.shade200),
                      padding:
                          EdgeInsets.symmetric(horizontal: 16, vertical: 4),
                      child: DropdownButton(
                        underline: Offstage(),
                        items: categories.map((e) 
                          return DropdownMenuItem(
                              child: Text(e.name.validate()), value: e);
                        ).toList(),
                        isExpanded: true,
                        value: selectedCategory,
                        onChanged: (dynamic c) 
                          selectedCategory = c;
                          setState(() 
                          );
                        ,
                      ),
                    ),
                  ],
                ),
              if (subCategories.isNotEmpty)
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('Select Sub Category', style: boldTextStyle(size: 18)),
                    8.height,
                    Container(
                      width: context.width() * 0.45,
                      decoration: BoxDecoration(
                          borderRadius: radius(), color: Colors.grey.shade200),
                      padding:
                          EdgeInsets.symmetric(horizontal: 16, vertical: 4),
                      child: DropdownButton(
                        underline: Offstage(),
                        items: subCategories.map((e) 
                          return DropdownMenuItem(
                              child: Text(e.name.validate()), value: e);
                        ).toList(),
                        isExpanded: true,
                        value: selectedSubCategory,
                        onChanged: (dynamic c) 
                          selectedSubCategory = c;
                          setState(() );
                        ,
                      ),
                    ),
                  ],
                ),
            ],
          ).paddingAll(16),
        ),
      ),
    ).cornerRadiusWithClipRRect(16);
  

这里是firestore调用

  Future<List<SubCategoryData>> categoriesFuture() async 
    return await ref!.get().then((x) => x.docs
        .map((y) => SubCategoryData.fromJson(y.data() as Map<String, dynamic>))
        .toList());
  

  Future<List<SubCategoryData>> categoriesFutureById(String? doc) async 
    DocumentReference categoryRef = db.doc('categories/' + doc.toString());
    return await ref!
        .where(SubCategoryKeys.categoryRef, isEqualTo: categoryRef)
        .get()
        .then((x) => x.docs
            .map((y) =>
                SubCategoryData.fromJson(y.data() as Map<String, dynamic>))
            .toList());
  

第一次down的onchanged方法被调用怎么办?

【问题讨论】:

【参考方案1】:

我终于通过修改first drop down的onChanged方法解决了,如下。

onChanged: (dynamic c) 
  selectedCategory = c;
  if (selectedCategory!.id != null) 
    loadSubcategories(selectedCategory!.id);
  
  setState(() );
,

Future<void> loadSubcategories(String? docId) async 
    DocumentReference categoryRef = db.doc('categories/' + docId.toString());

    subCategoryService.categoriesFutureById(categoryRef).then((value) 
      // isLoading = false;
      log(value);
      subCategories.clear();
      subCategories.addAll(value);
      selectedSubCategory = subCategories.first;

      setState(() );
    ).catchError((e) 
      //isLoading = false;
      setState(() );
      toast(e.toString());
    );

【讨论】:

以上是关于Flutter Firestore 集合相关的下拉菜单的主要内容,如果未能解决你的问题,请参考以下文章

在 Flutter 中监听 Firestore 集合及其子集合

使用 Flutter 的 Firestore 组集合查询

Flutter 使用 Stream 检索 Firestore 集合

在 Firestore 中删除集合 - Flutter/Android

如何在 Firestore -Flutter 中将所有文档从一个集合复制到另一个集合?

Flutter Web - 获取 Firestore 集合