paged_datatable 2.2.0-dev1 copy "paged_datatable: ^2.2.0-dev1" to clipboard
paged_datatable: ^2.2.0-dev1 copied to clipboard

A brand new way of creating paginated DataTables in Flutter with sorting and filters

example/lib/main.dart

import 'dart:math';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:paged_datatable/paged_datatable.dart';
import 'package:paged_datatable_example/post.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // await initializeDateFormatting("en");

  PostsRepository.generate(500);

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: const [
        GlobalMaterialLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        PagedDataTableLocalization.delegate
      ],
      supportedLocales: const [
        Locale("es"),
        Locale("en"),
        Locale("de"),
        Locale("it"),
        Locale("ru"),
        Locale("fr"),
      ],
      locale: const Locale("en"),
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        textTheme: kIsWeb ? GoogleFonts.robotoTextTheme() : null,
      ),
      home: const MainView(),
    );
  }
}

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

  @override
  State<StatefulWidget> createState() => _MainViewState();
}

class _MainViewState extends State<MainView> {
  final simple = const SimplePagedDataTable();
  final expansible = const ExpansiblePagedDataTable();

  bool isSimple = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        color: const Color.fromARGB(255, 208, 208, 208),
        padding: const EdgeInsets.all(20.0),
        child: Column(
          children: [
            TextButton(
              child: Text("Switch to ${isSimple ? 'expansible' : 'simple'}"),
              onPressed: () {
                setState(() {
                  isSimple = !isSimple;
                });
              },
            ),
            Expanded(
              child: isSimple ? simple : expansible,
            ),
          ],
        ),
      ),
    );
  }

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

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

  @override
  State<SimplePagedDataTable> createState() => _SimplePagedDataTableState();
}

class _SimplePagedDataTableState extends State<SimplePagedDataTable> {
  final tableController = PagedDataTableController<String, Post>();
  @override
  Widget build(BuildContext context) {
    return PagedDataTableTheme(
      data: PagedDataTableThemeData(
        selectedRow: const Color(0xFFCE93D8),
        rowColor: (index) => index.isEven ? Colors.purple[50] : null,
      ),
      child: PagedDataTable<String, Post>(
        controller: tableController,
        initialPageSize: 100,
        configuration: const PagedDataTableConfiguration(),
        pageSizes: const [10, 20, 50, 100],
        fetcher: (pageSize, sortModel, filterModel, pageToken) async {
          final data = await PostsRepository.getPosts(
            pageSize: pageSize,
            pageToken: pageToken,
            sortBy: sortModel?.fieldName,
            sortDescending: sortModel?.descending ?? false,
            gender: filterModel["authorGender"],
            searchQuery: filterModel["content"],
          );
          return (data.items, data.nextPageToken);
        },
        filters: [
          TextTableFilter(
            id: "content",
            chipFormatter: (value) => 'Content has "$value"',
            name: "Content",
          ),
          DropdownTableFilter<Gender>(
            items: Gender.values
                .map((e) => DropdownMenuItem(value: e, child: Text(e.name)))
                .toList(growable: false),
            chipFormatter: (value) => 'Author is ${value.name.toLowerCase()}',
            id: "authorGender",
            name: "Author's Gender",
          ),
          DateTimePickerTableFilter(
            id: "1",
            name: "Date picker",
            chipFormatter: (date) => "Date is $date",
            initialValue: DateTime.now(),
            firstDate: DateTime.now().subtract(const Duration(days: 30)),
            lastDate: DateTime.now(),
            dateFormat: DateFormat.yMd(),
          ),
          DateRangePickerTableFilter(
            id: "2",
            name: "DateRange picker",
            chipFormatter: (date) => "Date is $date",
            initialValue: null,
            firstDate: DateTime.now().subtract(const Duration(days: 30)),
            lastDate: DateTime.now(),
            formatter: (range) => "${range.start} - ${range.end}",
          ),
        ],
        filterBarChild: PopupMenuButton(
          icon: const Icon(Icons.more_vert_outlined),
          itemBuilder: (context) => <PopupMenuEntry>[
            PopupMenuItem(
              child: const Text("Print selected rows"),
              onTap: () {
                debugPrint(tableController.selectedRows.toString());
                debugPrint(tableController.selectedItems.toString());
              },
            ),
            PopupMenuItem(
              child: const Text("Select random row"),
              onTap: () {
                final index = Random().nextInt(tableController.totalItems);
                tableController.selectRow(index);
              },
            ),
            PopupMenuItem(
              child: const Text("Select all rows"),
              onTap: () {
                tableController.selectAllRows();
              },
            ),
            PopupMenuItem(
              child: const Text("Unselect all rows"),
              onTap: () {
                tableController.unselectAllRows();
              },
            ),
            const PopupMenuDivider(),
            PopupMenuItem(
              child: const Text("Remove first row"),
              onTap: () {
                tableController.removeRowAt(0);
              },
            ),
            PopupMenuItem(
              child: const Text("Remove last row"),
              onTap: () {
                tableController.removeRowAt(tableController.totalItems - 1);
              },
            ),
            PopupMenuItem(
              child: const Text("Remove random row"),
              onTap: () {
                final index = Random().nextInt(tableController.totalItems);
                tableController.removeRowAt(index);
              },
            ),
          ],
        ),
        fixedColumnCount: 2,
        columns: [
          RowSelectorColumn(),
          TableColumn(
            title: const Text("Id"),
            cellBuilder: (context, item, index) => Text(item.id.toString()),
            size: const FixedColumnSize(100),
          ),
          TableColumn(
            title: const Text("Author"),
            cellBuilder: (context, item, index) => Text(item.author),
            sortable: true,
            id: "author",
            size: const FractionalColumnSize(.15),
          ),
          DropdownTableColumn(
            title: const Text("Enabled"),
            // cellBuilder: (context, item, index) => Text(item.isEnabled ? "Yes" : "No"),
            items: const <DropdownMenuItem<bool>>[
              DropdownMenuItem(value: true, child: Text("Yes")),
              DropdownMenuItem(value: false, child: Text("No")),
            ],
            size: const FixedColumnSize(100),
            getter: (item, index) => item.isEnabled,
            setter: (item, newValue, index) async {
              await Future.delayed(const Duration(seconds: 2));
              item.isEnabled = newValue;
              return true;
            },
          ),
          TableColumn(
            title: const Text("Author Gender"),
            cellBuilder: (context, item, index) => Text(item.authorGender.name),
            sortable: true,
            id: "authorGender",
            size: const MaxColumnSize(
                FractionalColumnSize(.2), FixedColumnSize(100)),
          ),
          LargeTextTableColumn(
            title: const Text("Content"),
            size: const RemainingColumnSize(),
            getter: (item, index) => item.content,
            fieldLabel: "Content",
            setter: (item, newValue, index) async {
              await Future.delayed(const Duration(seconds: 2));
              item.content = newValue;
              return true;
            },
          ),
          TextTableColumn(
            title: const Text("Number"),
            format: const NumericColumnFormat(),
            // cellBuilder: (context, item, index) => Text(item.number.toString()),
            size: const MaxColumnSize(
                FixedColumnSize(100), FractionalColumnSize(.1)),
            getter: (item, index) => item.number.toString(),
            inputFormatters: [FilteringTextInputFormatter.digitsOnly],
            setter: (item, newValue, index) async {
              await Future.delayed(const Duration(seconds: 2));
              item.number = int.parse(newValue);
              return true;
            },
          ),
        ],
      ),
    );
  }
}

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

  @override
  State<ExpansiblePagedDataTable> createState() =>
      _ExpansiblePagedDataTableState();
}

class _ExpansiblePagedDataTableState extends State<ExpansiblePagedDataTable> {
  final tableController = PagedDataTableController<String, Post>();
  @override
  Widget build(BuildContext context) {
    return PagedDataTableTheme(
      data: PagedDataTableThemeData(
        selectedRow: const Color(0xFF6DC870),
        rowColor: (index) => index.isEven ? Colors.green[50] : null,
      ),
      child: PagedDataTable<String, Post>.expansible(
        controller: tableController,
        initialPageSize: 100,
        configuration: const PagedDataTableConfiguration(),
        pageSizes: const [10, 20, 50, 100],
        fetcher: (pageSize, sortModel, filterModel, pageToken) async {
          final data = await PostsRepository.getPosts(
            pageSize: pageSize,
            pageToken: pageToken,
            sortBy: sortModel?.fieldName,
            sortDescending: sortModel?.descending ?? false,
            gender: filterModel["authorGender"],
            searchQuery: filterModel["content"],
          );

          final resultset = <Post, List<Post>?>{};
          for (final post in data.items) {
            resultset[post] = PostsRepository.getRelatedPosts(post);
          }

          return (resultset, data.nextPageToken);
        },
        filters: [
          TextTableFilter(
            id: "content",
            chipFormatter: (value) => 'Content has "$value"',
            name: "Content",
          ),
          DropdownTableFilter<Gender>(
            items: Gender.values
                .map((e) => DropdownMenuItem(value: e, child: Text(e.name)))
                .toList(growable: false),
            chipFormatter: (value) => 'Author is ${value.name.toLowerCase()}',
            id: "authorGender",
            name: "Author's Gender",
          ),
          DateTimePickerTableFilter(
            id: "1",
            name: "Date picker",
            chipFormatter: (date) => "Date is $date",
            initialValue: DateTime.now(),
            firstDate: DateTime.now().subtract(const Duration(days: 30)),
            lastDate: DateTime.now(),
            dateFormat: DateFormat.yMd(),
          ),
          DateRangePickerTableFilter(
            id: "2",
            name: "DateRange picker",
            chipFormatter: (date) => "Date is $date",
            initialValue: null,
            firstDate: DateTime.now().subtract(const Duration(days: 30)),
            lastDate: DateTime.now(),
            formatter: (range) => "${range.start} - ${range.end}",
          ),
        ],
        filterBarChild: PopupMenuButton(
          icon: const Icon(Icons.more_vert_outlined),
          itemBuilder: (context) => <PopupMenuEntry>[
            PopupMenuItem(
              child: const Text("Print selected rows"),
              onTap: () {
                debugPrint(tableController.selectedRows.toString());
                debugPrint(tableController.selectedItems.toString());
              },
            ),
            PopupMenuItem(
              child: const Text("Select random row"),
              onTap: () {
                final index = Random().nextInt(tableController.totalItems);
                tableController.selectRow(index);
              },
            ),
            PopupMenuItem(
              child: const Text("Select all rows"),
              onTap: () {
                tableController.selectAllRows();
              },
            ),
            PopupMenuItem(
              child: const Text("Unselect all rows"),
              onTap: () {
                tableController.unselectAllRows();
              },
            ),
            const PopupMenuDivider(),
            PopupMenuItem(
              child: const Text("Remove first row"),
              onTap: () {
                tableController.removeRowAt(0);
              },
            ),
            PopupMenuItem(
              child: const Text("Remove last row"),
              onTap: () {
                tableController.removeRowAt(tableController.totalItems - 1);
              },
            ),
            PopupMenuItem(
              child: const Text("Remove random row"),
              onTap: () {
                final index = Random().nextInt(tableController.totalItems);
                tableController.removeRowAt(index);
              },
            ),
            const PopupMenuDivider(),
            PopupMenuItem(
              child: const Text("Add collapsed row in first row"),
              onTap: () {
                tableController.insertCollapsed(
                  3,
                  Post(
                      id: 123456789,
                      author: "Me",
                      content: "This is the content",
                      createdAt: DateTime.now(),
                      isEnabled: true,
                      number: 9999,
                      authorGender: Gender.male),
                );
              },
            ),
            PopupMenuItem(
              child: const Text("Remove the first collapsed row"),
              onTap: () {
                tableController.removeCollapsedAt(
                    tableController.expansibleRows.first, 0);
              },
            ),
          ],
        ),
        fixedColumnCount: 2,
        columns: [
          RowSelectorColumn(),
          CollapsibleRowColumn(),
          TableColumn(
            title: const Text("Id"),
            cellBuilder: (context, item, index) => Text(item.id.toString()),
            size: const FixedColumnSize(100),
          ),
          TableColumn(
            title: const Text("Author"),
            cellBuilder: (context, item, index) => Text(item.author),
            sortable: true,
            id: "author",
            size: const FractionalColumnSize(.15),
          ),
          DropdownTableColumn(
            title: const Text("Enabled"),
            // cellBuilder: (context, item, index) => Text(item.isEnabled ? "Yes" : "No"),
            items: const <DropdownMenuItem<bool>>[
              DropdownMenuItem(value: true, child: Text("Yes")),
              DropdownMenuItem(value: false, child: Text("No")),
            ],
            size: const FixedColumnSize(100),
            getter: (item, index) => item.isEnabled,
            setter: (item, newValue, index) async {
              debugPrint(
                  "Item: [${item.id}] NewValue: [$newValue] Index: [$index]");

              await Future.delayed(const Duration(seconds: 2));
              item.isEnabled = newValue;
              return true;
            },
          ),
          TableColumn(
            title: const Text("Author Gender"),
            cellBuilder: (context, item, index) => Text(item.authorGender.name),
            sortable: true,
            id: "authorGender",
            size: const MaxColumnSize(
                FractionalColumnSize(.2), FixedColumnSize(100)),
          ),
          LargeTextTableColumn(
            title: const Text("Content"),
            size: const RemainingColumnSize(),
            getter: (item, index) => item.content,
            fieldLabel: "Content",
            setter: (item, newValue, index) async {
              await Future.delayed(const Duration(seconds: 2));
              item.content = newValue;
              return true;
            },
          ),
          TextTableColumn(
            title: const Text("Number"),
            format: const NumericColumnFormat(),
            // cellBuilder: (context, item, index) => Text(item.number.toString()),
            size: const MaxColumnSize(
                FixedColumnSize(100), FractionalColumnSize(.1)),
            getter: (item, index) => item.number.toString(),
            inputFormatters: [FilteringTextInputFormatter.digitsOnly],
            setter: (item, newValue, index) async {
              await Future.delayed(const Duration(seconds: 2));
              item.number = int.parse(newValue);
              return true;
            },
          ),
        ],
      ),
    );
  }
}
63
likes
0
pub points
90%
popularity

Publisher

verified publishertomasweigenast.me

A brand new way of creating paginated DataTables in Flutter with sorting and filters

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, flutter_localizations, intl

More

Packages that depend on paged_datatable