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

Flutter Web中如何通过StreamBuilder将Firestore数据展示在PaginatedDataTable或DataTable

问题解决方法

报错根因

你遇到的报错是因为修改后的UsersData数据源类需要接收Firestore返回的文档数据作为入参,你在实例化时未传入对应参数,同时没有处理空安全校验和Firestore数据类型转换导致的。

完整实现代码

下面的代码同时支持Stream实时监听Firestore数据、行选中、行点击功能,更换集合时仅需修改Stream对应的集合路径即可:

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

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

  @override
  _AdminDashboardState createState() => _AdminDashboardState();
}

class _AdminDashboardState extends State<AdminDashboard> {
  // 更换集合时仅需修改此处的集合名称即可
  final CollectionReference _usersCollection = FirebaseFirestore.instance.collection('users');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: StreamBuilder<QuerySnapshot>(
          stream: _usersCollection.snapshots(),
          builder: (context, snapshot) {
            // 加载状态
            if (snapshot.connectionState == ConnectionState.waiting) {
              return const Center(child: CircularProgressIndicator());
            }
            // 错误状态
            if (snapshot.hasError) {
              return Center(child: Text('加载失败:${snapshot.error}'));
            }
            // 无数据状态
            if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
              return const Center(child: Text('暂无用户数据'));
            }
            // 有数据时初始化数据源
            final userDocs = snapshot.data!.docs;
            final UsersData usersData = UsersData(userDocs: userDocs);
            return PaginatedDataTable(
              header: const Text("用户列表"),
              rowsPerPage: 9,
              showCheckboxColumn: true, // 显示行选中复选框
              columns: const [
                DataColumn(label: Text('Uid')),
                DataColumn(label: Text('Name')),
                DataColumn(label: Text('Email')),
                DataColumn(label: Text('Role'))
              ],
              source: usersData,
            );
          },
        ),
      ),
    );
  }
}

class UsersData extends DataTableSource {
  final List<QueryDocumentSnapshot> userDocs;
  // 存储选中的行索引
  final Set<int> _selectedRows = {};

  UsersData({required this.userDocs});

  @override
  DataRow getRow(int index) {
    final user = userDocs[index].data() as Map<String, dynamic>;
    return DataRow.byIndex(
      index: index,
      selected: _selectedRows.contains(index),
      // 行选中/点击逻辑
      onSelectChanged: (value) {
        if (value != null) {
          _selectedRows.clear();
          if (value) {
            _selectedRows.add(index);
            // 此处添加行点击后要执行的逻辑,比如打开详情页
            debugPrint('选中的用户数据:${user.toString()}');
          }
          notifyListeners();
        }
      },
      cells: [
        DataCell(Text(user['uid'] ?? '')),
        DataCell(Text(user['name'] ?? '')),
        DataCell(Text(user['email'] ?? '')),
        DataCell(Text(user['role'] ?? '')),
      ],
    );
  }

  @override
  bool get isRowCountApproximate => false;

  @override
  int get rowCount => userDocs.length;

  @override
  int get selectedRowCount => _selectedRows.length;
}

可选适配方案

如果不需要实时数据更新,仅需单次拉取,可将StreamBuilder替换为FutureBuilder,将stream参数替换为_usersCollection.get()即可。
如果要使用普通DataTable而非分页表格,直接在snapshot.hasData分支中遍历userDocs生成DataRow列表传给DataTable的rows参数即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 11:54:04