首页 > 解决方案 > 如何在颤动的可编辑表中添加搜索和排序?

问题描述

由于我是 Flutter 的新手,所以我找不到如何在 Flutter 中的可编辑数据表上添加搜索和排序的解决方案。为了创建一个可编辑的数据表,我editable: ^1.1.1在我的项目中使用包。我怎样才能做到这一点?请任何人帮助我!

下面是我的代码。

可编辑数据.dart

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

class TablePage extends StatefulWidget {
  @override
  _TablePageState createState() => _TablePageState();
}

class _TablePageState extends State<TablePage> {
  final _editableKey = GlobalKey<EditableState>();

  List rows = [
    {'name': 'Abhi', 'age': '23'},
    {'name': 'Sipun', 'age': '19'},
    {'name': 'Lipun', 'age': '12'},
  ];

  List headers = [
  {'title': 'Name', 'index': 1, 'key':'name'},
  {'title': 'Age', 'index' : 2, 'key': 'age'},
  ];

  void _printEditedRows() {
    List editedRows = _editableKey.currentState.editedRows;
    print(editedRows);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: FlatButton(
                onPressed: () => _printEditedRows(),
                child: Text('Print Edited Rows',
                    style: TextStyle(fontWeight: FontWeight.bold))),
          )
        ],
      ),
      body: Padding(
        padding: const EdgeInsets.all(100.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Editable(
              key: _editableKey,
              columns: headers,
              rows: rows,
              tdStyle: TextStyle(fontSize: 20),
              showSaveIcon: false,
              borderColor: Colors.grey.shade300,
            ),
          ],
        ),
      ),
    );
  }
}

标签: flutterdart

解决方案


我查看了您的问题,但在我更改其行属性时拒绝更新表格内容的可编辑包遇到了困难。所以,我想,用以下基本块来实现你的要求有多难?

所以,这是我在 140 行代码中的解决方案。

在此处输入图像描述


1.成员模型类

我们保持它非常基本,只是添加一个uid用于适当的状态管理。

@freezed
abstract class Member with _$Member {
  factory Member({String uid, String name, int age}) = _Member;
}

使用Mockaroo生成的一些测试数据:

final List<Member> testData = [
  Member(name: "Adrian Andreuzzi", uid: "gi768157", age: 84),
  Member(name: "Angil Aglione", uid: "uc772005", age: 26),
  Member(name: "Bald Geertsen", uid: "ng269401", age: 99),
  Member(name: "Bree Minshull", uid: "rg692522", age: 10),
  Member(name: "Daniella Giacobini", uid: "tk828689", age: 94),
  Member(name: "Darn Kennion", uid: "zm299734", age: 44),
  Member(name: "Derron Rault", uid: "ax854756", age: 21),
  Member(name: "Ester Stoffersen", uid: "ia343485", age: 92),
  Member(name: "Fulvia Maher", uid: "hy669111", age: 43),
  Member(name: "Glennie Rogers", uid: "ma744931", age: 88),
  Member(name: "Jannelle Rubinsztein", uid: "je802620", age: 44),
  Member(name: "Jerrylee MacConnell", uid: "ad466446", age: 85),
  Member(name: "Nappy Dewan", uid: "zd804411", age: 55),
  Member(name: "Oberon Pudge", uid: "mz474307", age: 08),
  Member(name: "Petunia Dany", uid: "pt098116", age: 20),
  Member(name: "Rodolfo Gipp", uid: "cs063868", age: 61),
  Member(name: "Rolando Teese", uid: "mq696713", age: 54),
  Member(name: "Tiena Strute", uid: "rj867066", age: 14),
  Member(name: "Tove McDonell", uid: "sk682172", age: 36),
  Member(name: "Verney Bryenton", uid: "he816728", age: 66),
];

2.状态管理

2.1 会员提供者

此提供程序用作内存中的持久性。您必须将其连接到本地和/或远程数据源。

它提供了成员以及更新成员的方法。可以使用相同的方法添加新成员,使用新的uid.

final membersProvider = StateNotifierProvider<MembersStateNotifier>(
  (ref) => MembersStateNotifier(),
);

class MembersStateNotifier extends StateNotifier<List<Member>> {
  MembersStateNotifier([List<Member> state]) : super(state ?? testData);

  void update(Member updatedMember) {
    state = [
      ...state.where((member) => member.uid != updatedMember.uid),
      updatedMember,
    ].sorted((a, b) => a.name.compareTo(b.name));
    print(state.where((item) => item.uid == updatedMember.uid));
  }
}

2.2 搜索引擎提供商

它提供了每次更新成员列表时都会重置的模糊搜索引擎。这也是我定义搜索引擎选项的地方,例如使用哪些键和权重。

final searchEngineProvider = StateNotifierProvider<SearchEngineNotifier>((ref) {
  final members = ref.watch(membersProvider.state);
  return SearchEngineNotifier(members);
});

class SearchEngineNotifier extends StateNotifier<Fuzzy<Member>> {
  SearchEngineNotifier(List<Member> members) : super(null) {
    _resetEngine(members);
  }

  void _resetEngine(List<Member> members) {
    print('RESET $members');
    state = Fuzzy<Member>(
      members,
      options: FuzzyOptions(
        keys: [
          WeightedKey(getter: (item) => item.name, weight: 1, name: 'name'),
        ],
        threshold: .6,
        shouldSort: true,
      ),
    );
  }
}

2.3 搜索词提供者

当前搜索词的基本提供者。

final searchTermProvider = StateProvider<String>((ref) => '');

2.4 过滤成员提供者

此 Providers 组合所有提供程序以提供过滤后的成员列表:

  • searchTermProvider
  • membersProvider
  • searchEngineProvider

请注意,我阅读了StateNotifier 类中的membersProviderand searchEngineProvider,以便仅在更改搜索词时才获得新的 StateNotifier ,而不是在编辑成员时。

final filteredMembersProvider = StateNotifierProvider<FilteredMembersNotifier>(
  (ref) {
    final searchTerm = ref.watch(searchTermProvider).state;
    return FilteredMembersNotifier(searchTerm, ref.read);
  },
);

class FilteredMembersNotifier extends StateNotifier<List<Member>> {
  final Reader read;
  final String searchTerm;

  FilteredMembersNotifier(this.searchTerm, this.read) : super(null) {
    state = searchTerm.isEmpty
        ? read(membersProvider.state)
        : read(searchEngineProvider)
            .state
            .search(searchTerm)
            .map((result) => result.item)
            .toList();
    print('STATE: $state');
  }
}

小部件

我们的基本应用程序将使用三个小部件:

  • MembersPage, 我们的脚手架
  • SearchBox
  • MembersDataTable

虽然MembersPage是访问 Providers 的 HookWidget,但两者SearchBox都是MembersDataTable非常基本的 StatelessWidget。

3.1 会员页面

状态管理:

  • 从中获取过滤后的成员filteredMembersProvider
  • 更改更新searchTermProviderSearchBox
  • 更改更新membersProviderMembersDataTable
class MembersPage extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final _members = useProvider(filteredMembersProvider.state);
    return Scaffold(
      appBar: AppBar(
        title: Text('Members'),
        actions: [
          Padding(
            padding: EdgeInsets.all(8.0),
            child: SearchBox(
                onSearch: (searchTerm) =>
                    context.read(searchTermProvider).state = searchTerm),
          ),
        ],
      ),
      body: Center(
        child: _members.isEmpty
            ? Text('No members found.')
            : SingleChildScrollView(
                child: MembersDataTable(
                  members: _members,
                  onChanged: context.read(membersProvider).update,
                ),
              ),
      ),
    );
  }
}

3.2 搜索框

class SearchBox extends StatelessWidget {
  final ValueChanged<String> onSearch;

  const SearchBox({
    Key key,
    @required this.onSearch,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 150,
      child: TextFormField(
        initialValue: '',
        style: TextStyle(color: Colors.white),
        cursorColor: Colors.white,
        decoration: InputDecoration(
          hintText: "Search",
          hintStyle: TextStyle(fontSize: 16, color: Colors.white),
          isDense: true,
          suffixIcon: Icon(Icons.search, color: Colors.white),
          focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(16),
            borderSide: BorderSide(
              color: Colors.white,
              width: 2.0,
            ),
          ),
          enabledBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(16),
            borderSide: BorderSide(style: BorderStyle.none),
          ),
          filled: true,
          fillColor: Colors.lightBlue.shade200,
        ),
        onChanged: onSearch,
      ),
    );
  }
}

3.3 成员数据表

class MembersDataTable extends StatelessWidget {
  final List<Member> members;
  final ValueChanged<Member> onChanged;

  const MembersDataTable({
    Key key,
    this.members,
    this.onChanged,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return DataTable(
      columns: [
        DataColumn(label: Text('Name')),
        DataColumn(label: Text('Age')),
      ],
      rows: members.map(
        (member) {
          return DataRow(
            cells: [
              DataCell(
                TextFormField(
                  controller: TextEditingController(text: member.name),
                  decoration: InputDecoration(border: InputBorder.none),
                  onChanged: (value) =>
                      onChanged?.call(member.copyWith(name: value)),
                ),
              ),
              DataCell(
                TextFormField(
                  controller:
                      TextEditingController(text: member.age.toString()),
                  decoration: InputDecoration(border: InputBorder.none),
                  onChanged: (value) =>
                      onChanged?.call(member.copyWith(age: int.parse(value))),
                ),
              ),
            ],
          );
        },
      ).toList(),
    );
  }
}

4. 申请

void main() {
  runApp(
    ProviderScope(
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'Members DataTable',
        home: MembersPage(),
      ),
    ),
  );
}

缺少功能

当然,这个解决方案还远未完成。这更像是一次有趣的探索练习。

以下是缺失功能的非详尽列表:

  • 远程和/或本地数据库持久性
  • 添加/删除成员的可能性
  • 可以对表格的列进行排序

瞧!如果您发现任何问题或改进此基本解决方案的方法,请告诉我,我将更新我的答案。



推荐阅读