Flutter点击FloatingActionButton时从Firestore更新本地Map数据
Flutter点击按钮拉取Firestore Map数据更新本地对象实现方案
你当前的Firestore数据库结构如下:
要实现点击浮动按钮拉取数据更新本地Map的需求,按以下方式修改代码即可,同时修正了你现有代码里Firebase初始化、状态管理的两个隐性问题。
前置问题修正
你现有代码存在两个会导致功能异常的问题:
- main函数中没有等待Firebase初始化完成就启动应用,后续调用Firestore接口会抛出未初始化错误
- 顶层定义的
Brain是全局变量,直接修改不会触发Flutter页面重绘,需要将其放到页面State中作为状态变量管理
核心实现逻辑
- 修正main函数初始化逻辑,确保Firebase初始化完成后再启动App
- 编写异步拉取方法:Firestore存储的Map键默认是字符串类型,和你本地用int作为键的结构不匹配,拉取后需要手动做类型转换
- 将拉取方法绑定到浮动按钮的点击事件,拉取成功后调用
setState更新状态、刷新页面 - 代码中集合名、文档ID、字段名已经对应你截图里的配置,直接复制即可运行,如果后续改了路径自行替换即可
完整可运行代码
主逻辑代码(main.dart)
import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; // 本地Brain默认初始值 Map<int, Map<int, String>> initialBrain = { 0: { 0: '0-0', 1: '0-1', 2: '0-2', 3: '0-3', 4: '0-4', }, 1: { 0: '1-0', 1: '1-1', 2: '1-2', 3: '1-3', 4: '1-4', }, }; Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); // 等待Firebase初始化完成再启动应用 await Firebase.initializeApp(); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { // 将Brain作为页面状态变量管理 late Map<int, Map<int, String>> brain; final FirebaseFirestore _firestore = FirebaseFirestore.instance; @override void initState() { super.initState(); // 页面初始化时加载默认值 brain = Map.from(initialBrain); } // 拉取Firestore数据并更新本地Map的方法 Future<void> fetchAndUpdateBrain() async { try { // 对应你截图里的集合、文档路径 DocumentSnapshot doc = await _firestore .collection('Brain') .doc('pGQraR6uS0b03V2a3eqs') .get(); if (!doc.exists) return; // 读取test字段存储的Map,Firestore返回的Map键默认是String类型 Map<String, dynamic> rawData = Map<String, dynamic>.from(doc.get('test') as Map); Map<int, Map<int, String>> convertedData = {}; // 遍历做类型转换,适配本地int键的结构 rawData.forEach((outerKey, outerVal) { int outerKeyInt = int.parse(outerKey); Map<String, dynamic> rawInnerMap = Map<String, dynamic>.from(outerVal as Map); Map<int, String> convertedInnerMap = {}; rawInnerMap.forEach((innerKey, innerVal) { convertedInnerMap[int.parse(innerKey)] = innerVal.toString(); }); convertedData[outerKeyInt] = convertedInnerMap; }); // 更新状态触发页面刷新 setState(() { brain = convertedData; }); } catch (e) { debugPrint('数据拉取异常: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('数据拉取失败,请检查网络和权限配置')) ); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Text(brain.toString()), ), floatingActionButton: FloatingActionButton( onPressed: fetchAndUpdateBrain, tooltip: '拉取最新数据', child: const Icon(Icons.add), ), ); } }
补充说明
- 测试阶段请先确认Firestore安全规则允许读写,避免权限报错,正式上线前必须配置严格的权限规则,不要长期用全量开放的规则
- 如果你是通过代码写入的Map数据,且写入时键就是int类型,可以跳过类型转换步骤直接赋值;但通过Firestore控制台手动录入的数据键默认都是字符串,类型转换步骤不能少
- 如果需要打开页面就自动加载一次数据,直接在
initState方法里调用一次fetchAndUpdateBrain()即可 - 如果后续需要数据实时同步不需要手动点击,可以把
get()方法换成snapshots()流监听,配合StreamBuilder实现自动更新
内容的提问来源于stack exchange,提问作者CastoldiG
相关产品推荐
相关产品推荐

