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

Flutter:如何从Firestore获取数据生成可点击变色Chips?

问题描述

我已经实现了从本地列表生成Chips的功能,点击Chips时颜色会变为蓝色。现在希望替换本地的wordList,改为从Firestore数据库中获取数据,将集合中的"WordName"字段内容显示在Chips上。现有代码如下:

class uitry extends StatefulWidget {
  const uitry({Key? key}) : super(key: key);

  @override
  State<uitry> createState() => _uitryState();
}

class _uitryState extends State<uitry> {
  List<String> wordList = [
    'Shopping',
    'Brunch',
    'Music',
    'Road Trips',
    'Tea',
    'Trivia',
    'Comedy',
    'Clubbing',
    'Drinking',
    'Wine',
  ];

  List<String> selectedWord = [];
  List<String>? deSelectedWord = [];
  @override
  Widget build(BuildContext context) {
    double height = MediaQuery.of(context).size.height;
    double width = MediaQuery.of(context).size.width;
    return Scaffold(
      body: Container(
        decoration: const BoxDecoration(
          image: DecorationImage(
              image: AssetImage(Config.app_background4), fit: BoxFit.fill),
        ),
        child: SafeArea(
            child: Center(
                child: Column(
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(top: 14, right: 0),
              child: Column(
                children: [
                  SizedBox(
                    width: width * 0.94,
                    height: height * 0.30,
                    child: Column(
                      children: <Widget>[
                        const SizedBox(height: 16),
                        Wrap(
                          children: wordList.map(
                            (word) {
                              bool isSelected = false;
                              if (selectedWord!.contains(word)) {
                                isSelected = true;
                              }
                              return GestureDetector(
                                onTap: () {
                                  if (!selectedWord!.contains(word)) {
                                    if (selectedWord!.length < 50) {
                                      selectedWord!.add(word);
                                      deSelectedWord!.removeWhere(
                                          (element) => element == word);
                                      setState(() {});
                                      print(selectedWord);
                                    }
                                  } else {
                                    selectedWord!.removeWhere(
                                        (element) => element == word);
                                    deSelectedWord!.add(word);
                                    setState(() {
                                      // selectedHobby.remove(hobby);
                                    });
                                    print(selectedWord);
                                    print(deSelectedWord);
                                  }
                                },
                                child: Container(
                                  margin: const EdgeInsets.symmetric(
                                      horizontal: 5, vertical: 4),
                                  child: Container(
                                    padding: const EdgeInsets.symmetric(
                                        vertical: 5, horizontal: 12),
                                    decoration: BoxDecoration(
                                        color: isSelected
                                            ? HexColor('#0000FF')
                                            : HexColor('#D9D9D9'),
                                        borderRadius: BorderRadius.circular(18),
                                        border: Border.all(
                                            color: isSelected
                                                ? HexColor('#0000FF')
                                                : HexColor('#D9D9D9'),
                                            width: 2)),
                                    child: Text(
                                      word,
                                      style: TextStyle(
                                          color: isSelected
                                              ? Colors.black
                                              : Colors.black,
                                          fontSize: 14,
                                          fontWeight: FontWeight.w600),
                                    ),
                                  ),
                                ),
                              );
                            },
                          ).toList(),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ],
        ))),
      ),
    );
  }
}
解决方案

要实现从Firestore获取数据生成Chips,按以下步骤修改代码:

1. 添加Firestore依赖

在pubspec.yaml中添加Firebase相关依赖(如果未添加):

dependencies:
  flutter:
    sdk: flutter
  cloud_firestore: ^4.15.0 # 使用最新版本
  firebase_core: ^2.24.2 # 初始化Firebase必需

执行flutter pub get完成依赖安装。

2. 修改状态类,添加Firestore数据获取逻辑

更新_uitryState类,添加异步获取数据的方法,并处理加载状态:

class _uitryState extends State<uitry> {
  List<String> wordList = [];
  List<String> selectedWord = [];
  List<String> deSelectedWord = []; // 移除可空标记,直接初始化空列表
  bool isLoading = true; // 新增加载状态标识

  @override
  void initState() {
    super.initState();
    fetchWordsFromFirestore(); // 页面初始化时获取数据
  }

  // 从Firestore获取WordName字段的异步方法
  Future<void> fetchWordsFromFirestore() async {
    try {
      // 替换为你的Firestore集合名称,示例为"words"
      QuerySnapshot snapshot = await FirebaseFirestore.instance.collection("words").get();
      
      // 遍历文档,提取WordName字段
      List<String> fetchedWords = snapshot.docs.map((doc) {
        return doc["WordName"] as String;
      }).toList();

      setState(() {
        wordList = fetchedWords;
        isLoading = false; // 数据加载完成,更新状态
      });
    } catch (e) {
      print("Firestore数据获取失败: $e");
      setState(() {
        isLoading = false;
      });
    }
  }

  // 原build方法修改见下一步
}

3. 调整UI,处理加载状态

在build方法中,根据isLoading状态显示加载提示或Chips列表:

@override
Widget build(BuildContext context) {
  double height = MediaQuery.of(context).size.height;
  double width = MediaQuery.of(context).size.width;
  return Scaffold(
    body: Container(
      decoration: const BoxDecoration(
        image: DecorationImage(
            image: AssetImage(Config.app_background4), fit: BoxFit.fill),
      ),
      child: SafeArea(
          child: Center(
              child: Column(
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.only(top: 14, right: 0),
            child: Column(
              children: [
                SizedBox(
                  width: width * 0.94,
                  height: height * 0.30,
                  child: Column(
                    children: <Widget>[
                      const SizedBox(height: 16),
                      // 根据加载状态显示不同内容
                      isLoading 
                          ? const CircularProgressIndicator() // 加载中显示进度圈
                          : Wrap(
                              children: wordList.map(
                                (word) {
                                  bool isSelected = selectedWord.contains(word); // 简化选中判断
                                  return GestureDetector(
                                    onTap: () {
                                      setState(() {
                                        if (!isSelected) {
                                          if (selectedWord.length < 50) {
                                            selectedWord.add(word);
                                            deSelectedWord.remove(word);
                                          }
                                        } else {
                                          selectedWord.remove(word);
                                          deSelectedWord.add(word);
                                        }
                                        print(selectedWord);
                                        print(deSelectedWord);
                                      });
                                    },
                                    child: Container(
                                      margin: const EdgeInsets.symmetric(
                                          horizontal: 5, vertical: 4),
                                      child: Container(
                                        padding: const EdgeInsets.symmetric(
                                            vertical: 5, horizontal: 12),
                                        decoration: BoxDecoration(
                                            color: isSelected
                                                ? HexColor('#0000FF')
                                                : HexColor('#D9D9D9'),
                                            borderRadius: BorderRadius.circular(18),
                                            border: Border.all(
                                                color: isSelected
                                                    ? HexColor('#0000FF')
                                                    : HexColor('#D9D9D9'),
                                                width: 2)),
                                        child: Text(
                                          word,
                                          style: TextStyle(
                                              color: Colors.black,
                                              fontSize: 14,
                                              fontWeight: FontWeight.w600),
                                        ),
                                      ),
                                    ),
                                  );
                                },
                              ).toList(),
                            ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ],
      ))),
    ),
  );
}

4. 额外优化点

  • 移除了selectedWord和deSelectedWord的可空标记,避免不必要的空安全判断
  • 简化了isSelected的判断逻辑,直接通过selectedWord.contains(word)获取状态
  • 合并了setState调用,减少状态更新次数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 20:40:29