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

如何将TextFormField列表数据写入Firestore并实现读写更新

解决方案:从TextFormField获取列表并完成Firestore的增读改操作

1. 定义数据模型

先封装列表项数据类,方便后续序列化和操作:

class DataItem {
  final String id;
  String amount;

  DataItem({required this.id, required this.amount});

  // 转成Firestore兼容的Map格式
  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'amount1': amount,
    };
  }

  // 从Firestore的Map数据还原为DataItem对象
  factory DataItem.fromMap(Map<String, dynamic> map) {
    return DataItem(
      id: map['id'],
      amount: map['amount1'],
    );
  }
}

2. 实现完整功能的页面组件

用StatefulWidget维护输入状态、本地列表数据与Firestore交互逻辑:

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

class FirestoreListPage extends StatefulWidget {
  const FirestoreListPage({super.key});

  @override
  State<FirestoreListPage> createState() => _FirestoreListPageState();
}

class _FirestoreListPageState extends State<FirestoreListPage> {
  final _firestore = FirebaseFirestore.instance;
  final _idController = TextEditingController();
  final _amountController = TextEditingController();
  List<DataItem> _items = [];

  // 页面初始化时自动读取Firestore数据
  @override
  void initState() {
    super.initState();
    _fetchData();
  }

  // 从Firestore读取列表数据
  Future<void> _fetchData() async {
    try {
      final docSnapshot = await _firestore.collection('data').doc('one').get();
      if (docSnapshot.exists) {
        final listData = docSnapshot.data()?['list'] as List;
        setState(() {
          _items = listData.map((item) => DataItem.fromMap(item)).toList();
        });
      }
    } catch (e) {
      print('读取数据失败: $e');
    }
  }

  // 从TextFormField获取输入,添加新项到本地列表
  void _addItem() {
    if (_idController.text.isNotEmpty && _amountController.text.isNotEmpty) {
      setState(() {
        _items.add(DataItem(
          id: _idController.text,
          amount: _amountController.text,
        ));
      });
      _idController.clear();
      _amountController.clear();
    }
  }

  // 修改本地列表中指定项的金额
  void _updateLocalItem(int index, String newAmount) {
    setState(() {
      _items[index].amount = newAmount;
    });
  }

  // 将本地列表数据写入/更新到Firestore
  Future<void> _saveToFirestore() async {
    try {
      final docData = {
        'list': _items.map((item) => item.toMap()).toList(),
      };
      await _firestore.collection('data').doc('one').set(docData);
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('数据保存成功')),
      );
    } catch (e) {
      print('写入数据失败: $e');
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('保存失败: $e')),
      );
    }
  }

  // 优化:单独更新Firestore中的单个项(避免全量更新)
  Future<void> _updateSingleItem(String itemId, String newAmount) async {
    try {
      final targetItem = _items.firstWhere((e) => e.id == itemId);
      final docRef = _firestore.collection('data').doc('one');
      
      // 先移除旧数据,再添加更新后的数据
      await docRef.update({
        'list': FieldValue.arrayRemove([targetItem.toMap()])
      });
      await docRef.update({
        'list': FieldValue.arrayUnion([
          {'id': itemId, 'amount1': newAmount}
        ])
      });

      // 更新本地列表
      setState(() {
        targetItem.amount = newAmount;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('项已更新')),
      );
    } catch (e) {
      print('更新单个项失败: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Firestore 列表操作')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            // 输入新项的区域
            Row(
              children: [
                Expanded(
                  child: TextFormField(
                    controller: _idController,
                    decoration: const InputDecoration(labelText: 'ID'),
                  ),
                ),
                const SizedBox(width: 16),
                Expanded(
                  child: TextFormField(
                    controller: _amountController,
                    decoration: const InputDecoration(labelText: '金额'),
                    keyboardType: TextInputType.number,
                  ),
                ),
                IconButton(
                  icon: const Icon(Icons.add),
                  onPressed: _addItem,
                ),
              ],
            ),
            const SizedBox(height: 20),
            // 展示列表数据的区域
            Expanded(
              child: ListView.builder(
                itemCount: _items.length,
                itemBuilder: (context, index) {
                  final item = _items[index];
                  return ListTile(
                    title: Text(item.id),
                    subtitle: Text('金额: ${item.amount}'),
                    trailing: SizedBox(
                      width: 100,
                      child: Row(
                        children: [
                          IconButton(
                            icon: const Icon(Icons.edit),
                            onPressed: () {
                              showDialog(
                                context: context,
                                builder: (context) => AlertDialog(
                                  title: const Text('修改金额'),
                                  content: TextFormField(
                                    initialValue: item.amount,
                                    keyboardType: TextInputType.number,
                                    onFieldSubmitted: (value) {
                                      // 可选:用全量更新则调用_updateLocalItem,用单条更新则调用_updateSingleItem
                                      _updateSingleItem(item.id, value);
                                      Navigator.pop(context);
                                    },
                                  ),
                                ),
                              );
                            },
                          ),
                          IconButton(
                            icon: const Icon(Icons.delete),
                            onPressed: () {
                              setState(() {
                                _items.removeAt(index);
                              });
                            },
                          ),
                        ],
                      ),
                    ),
                  );
                },
              ),
            ),
            // 保存按钮
            ElevatedButton(
              onPressed: _saveToFirestore,
              child: const Text('保存到Firestore'),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _idController.dispose();
    _amountController.dispose();
    super.dispose();
  }
}

核心功能说明

  • TextFormField数据获取:通过TextEditingController绑定输入框,点击添加按钮时将输入内容转为DataItem加入本地列表。
  • Firestore数据读取:页面初始化时自动拉取指定文档的列表数据,转为DataItem后更新UI展示。
  • 屏幕展示:用ListView.builder遍历本地列表,展示每个项的ID和金额,同时提供编辑、删除操作入口。
  • 更新操作:支持两种更新方式——全量更新(将整个列表重新写入Firestore)和单条更新(仅修改指定项,减少数据传输)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 14:10:28