Flutter:使用来自 Firestore 的多集合流创建动态更新的 DataTable

Ada*_*ian 5 listview dart flutter google-cloud-firestore

因此,我(尝试)创建一个 DataTable 来根据 Firestore 数据动态显示一堆包裹(例如 Amazon 或 UPS 包裹)。复杂的是:我的数据存储在数据库中的多个集合中,“队列”、“内部”和“学生”。

我已经使用 ListView.builder 和 ListViewTiles 创建了我想要的版本。目前它工作得很好,当新数据添加到队列时显示新的图块。我不想使用 ListView,而是想显示表(DataTable)中的所有数据;但是,我无法以与 DataTable 配合使用的方式来适应我已有的内容(FutureBuilders 和 StreamBuilders)。

我还有一个占位符数据表,它以我想要的方式显示预定义列表中的一堆虚拟包。

我已经研究过使用 Provider 向 DataTable 小部件提供重建列表...但是当尝试同时从多个集合中读取时,我似乎无法将这些点连接起来。

这是我的 ListView 的代码...

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

class QueueDisplay extends StatefulWidget {
  @override
  _QueueDisplayState createState() => _QueueDisplayState();
}

class _QueueDisplayState extends State<QueueDisplay> {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: StreamBuilder(
        stream: Firestore.instance.collection('queue').snapshots(),
        builder:(context, snapshot){
          if (!snapshot.hasData){
            return (Center(child: CircularProgressIndicator()));
          } else {
            return ListView.builder(
              itemCount: snapshot.data.documents.length,
              itemBuilder: (context, index) {
                DocumentSnapshot queueDoc = snapshot.data.documents[index];
                return Column(
                  children: <Widget>[
                    FutureBuilder(
                      future: Firestore.instance.collection('students').document('${queueDoc.data['owner']}').get(),
                      builder: (BuildContext context, AsyncSnapshot studentSnap) {
                        return FutureBuilder(
                          future: Firestore.instance.collection('inhouse').where('owner', isEqualTo: queueDoc.data['owner']).getDocuments(),
                          builder: (BuildContext context, AsyncSnapshot packSnap) {
                            if (packSnap.hasData){
                              if(packSnap.data!=null){
                                return ListTile(
                                    title: Row(
                                      children: <Widget>[
                                        Text('${queueDoc.data['owner']}' + '     '),
                                        Text('${studentSnap.data['name']}' + '     '),
                                        Text('${packSnap.data.documents[0].documentID}'),
                                      ],
                                    )
                                );
                              }
                            } else {
                              return Center(child: CircularProgressIndicator());
                            }
                            return Center(child: CircularProgressIndicator());
                          },
                        );
                      },
                    )
                  ],
                );
              },
            );
          }
        },
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

ListView 功能截图

这是占位符数据表的代码......

class QueueTable extends StatefulWidget {
  @override
  _QueueTableState createState() => _QueueTableState();
}

class _QueueTableState extends State<QueueTable> {
  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: Container(
        //color: Colors.grey[200],
        child: DataTable(
          // Table Displaying Queued Packages
            columnSpacing: 8,
            columns: [
              DataColumn(label: Text("Name"),numeric: false, onSort: (i, b) {}),
              DataColumn(label: Text("Location"),numeric: false, onSort: (i, b) {}),
              DataColumn(label: Text("Tracking"),numeric: false, onSort: (i, b) {}),
              DataColumn(label: Text("Carrier"),numeric: false, onSort: (i, b) {}),
              DataColumn(label: Text("Notes"),numeric: false, onSort: (i, b) {}),
              DataColumn(label: Text("Received"),numeric: false, onSort: (i, b) {}),
            ],
            rows: packages.map((package) => DataRow(
                onSelectChanged: (b) {},
                cells: [
                  DataCell(Text(package.owner.name), onTap: (){}),
                  DataCell(Text(package.location), onTap: (){}),
                  DataCell(Text(package.tracking)),
                  DataCell(Text(package.carrier)),
                  DataCell(Text(package.notes), onTap: (){}),
                  DataCell(Text(package.rDate)),
            ])).toList()
        ),
      ),
    );
  }
}

Student adam =
    Student('0000004028', 'Adam G', 'adamg@email.net');
var packages = [
  Package(adam, 'Large', 'USPS', '1/15/2020', '123456', 'Great Dude!'),
  Package(adam, 'Small', 'UPS', '1/16/2020', '1234567', 'Great Guy!'),
  Package(adam, 'Medium', 'FedEx', '1/18/2020', '12345678', 'Great Bro!'),
  Package(adam, 'Floor', 'Amazon', '1/5/2020', '123456789', 'Great Person!'),
];
Run Code Online (Sandbox Code Playgroud)

占位符数据表屏幕截图

提供的两段代码都应该可以正常工作;我似乎无法将它们组合到一个工作的动态数据表中......

有什么建议么?谢谢你!

更新:

所以这是迄今为止我最接近的尝试......

我创建了一个 List 流,并使用 StreamProvider 将流提供给创建 DataTable 的小部件。

数据库.dart

仅返回 null 包的提供者

看起来好像起作用;如果我能让 _queueListFromSnapshot 返回包 p 的正确值就好了。由于嵌套的 Future.then(){} 语句,我还不知道如何返回正确的值。(在database.dart的注释中显示)

更新2:

这是使用 async/await 的一个稍微更好的尝试。这需要我更改函数的返回类型,这会导致进一步的不匹配,因为我仍然想将完整的列表返回到 StreamProvider...对吗?

小智 0

            import 'dart:io';
            import 'package:flutter/material.dart';
            import 'package:flutter/rendering.dart';
            import 'package:test4/sqlite/Sqlite.dart';

            class DataColumnEx {
              bool show;
              String label;
              String tooltip;
              bool numeric;
              bool editable;
              TextInputType editableTextInputType = TextInputType.text;
              DataColumnEx(
                  {@required this.show,
                  this.label = "",
                  this.tooltip = "",
                  this.numeric = false,
                  this.editable = false,
                  this.editableTextInputType = TextInputType.text});
            }

            class DataTableWidget extends StatefulWidget {
              final String dbName;
              final String sqlScript;
              final Map<String, DataColumnEx> columns;
              DataTableWidget(
                  {@required this.dbName,
                  @required this.sqlScript,
                  @required this.columns});

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

            class _DataTableWidgetState extends State<DataTableWidget> {
              bool _sortAscending = true;
              int _sortColumnIndex = 0;
              List<Map<String, dynamic>> _snapshot;

              List<Map<String, dynamic>> _getsnapshot() {
                return _snapshot;
              }

              _setsnapshot(AsyncSnapshot<List<Map<String, dynamic>>> snapshot) {
                _snapshot = List<Map<String, dynamic>>.from(snapshot.data);
              }

              _sort() {
                String columnName = _getsnapshot().first.keys.elementAt(_sortColumnIndex);
                if (_sortAscending)
                  _getsnapshot().sort((a, b) => (b[columnName]).compareTo(a[columnName]));
                else
                  _getsnapshot().sort((a, b) => (a[columnName]).compareTo(b[columnName]));
              }

              DataColumnEx getDataColumnEx(String columnName) {
                return widget.columns.containsKey(columnName)
                    ? widget.columns[columnName]
                    : DataColumnEx(
                        show: true,
                        label: columnName,
                        tooltip: columnName,
                        numeric: false,
                        editable: false,
                        editableTextInputType: TextInputType.name);
              }

              List<DataColumn> _getDataColumn() {
                List<DataColumn> listDataColumn = <DataColumn>[];

                for (var i = 0; i < _getsnapshot()[0].keys.length; i++) {
                  String columnName = _getsnapshot()[0].keys.elementAt(i).toString();

                  DataColumnEx column = getDataColumnEx(columnName);

                  if (false == column.show) continue;

                  listDataColumn.add(DataColumn(
                      onSort: (columnIndex, ascending) {
                        setState(() {
                          _sortColumnIndex = columnIndex;
                          _sortAscending = ascending;
                        });
                      },
                      tooltip: column.tooltip,
                      label: Text(column.label, textAlign: TextAlign.center)));
                }
                return listDataColumn;
              }

              List<DataRow> _getDataRow() {
                List<DataRow> listDataRow = <DataRow>[];

                for (var rowIdx = 0; rowIdx < _getsnapshot().length; rowIdx++) {
                  List<DataCell> ldc = <DataCell>[];
                  for (var ceilIdx = 0;
                      ceilIdx < _getsnapshot()[0].keys.length;
                      ceilIdx++) {
                    String columnName =
                        _getsnapshot().first.keys.elementAt(ceilIdx).toString();

                    DataColumnEx column = getDataColumnEx(columnName);
                    if (false == column.show) continue;

                    String ceilText = _getsnapshot()[rowIdx][columnName].toString();

                    ldc.add(column.editable
                        ? DataCell(
                            TextFormField(
                              controller: TextEditingController(text: ceilText),
                              //initialValue: ceilText,
                              keyboardType: column.editableTextInputType,
                              onFieldSubmitted: (val) {
                                print('onSubmited $val');
                              },
                            ),
                            showEditIcon: true,
                          )
                        : DataCell(Text(ceilText)));
                  }

                  listDataRow.add(DataRow(
                    // color: MaterialStateColor.resolveWith((states) {
                    //   return rowIdx % 2 == 0 ? Colors.red : Colors.black; //make tha magic!
                    // }),
                    // color: MaterialStateProperty.resolveWith<Color>(
                    //     (Set<MaterialState> states) {
                    //   if (states.contains(MaterialState.selected))
                    //     return Theme.of(context).colorScheme.primary.withOpacity(0.58);
                    //   return null; // Use the default value.
                    // }),
                    // selected: true,
                    // onSelectChanged: (value) {
                    //   setState(() {});
                    // },
                    cells: ldc,
                  ));
                }
                return listDataRow;
              }

              @override
              Widget build(BuildContext context) {
                final size = MediaQuery.of(context).size;
                final width = size.width;
                final height = size.height;
                return Container(
                  width: width,
                  height: height,
                  child: SingleChildScrollView(
                    scrollDirection: Axis.vertical,
                    child: SingleChildScrollView(
                      scrollDirection: Axis.horizontal,
                      child: FutureBuilder<List<Map<String, dynamic>>>(
                        future: SqliteUtils(widget.dbName).executeReader(widget.sqlScript),
                        builder: (BuildContext c,
                            AsyncSnapshot<List<Map<String, dynamic>>> aspsh) {
                          debugPrint("build");

                          if (!aspsh.hasData)
                            return (Center(child: CircularProgressIndicator()));

                          _setsnapshot(aspsh);
                          //sleep(Duration(seconds: 5));
                          _sort();

                          return DataTable(
                            //headingRowColor:
                            //    MaterialStateColor.resolveWith((states) => Colors.blue),
                            sortAscending: _sortAscending,
                            sortColumnIndex: _sortColumnIndex,
                            showCheckboxColumn: false,
                            columns: _getDataColumn(),
                            rows: _getDataRow(),
                          );
                        },
                      ),
                    ),
                  ),
                );
              }
            }
Run Code Online (Sandbox Code Playgroud)