! Don't put the widgets inside unbounded parents. You don't need scrollables anymore (e.g. SingleChildScrollView
) - widgets handle scrolling by theirselves. If you need a widget inside a Column()
, wrap it into Expanded()
or Flexible()
.
In-place substitute for Flutter's stock DataTable and PaginatedDataTable widgets with fixed/sticky header/top rows and left columns. A few useful features missing in the originals were added.
DataTable2 and PaginatedDataTable2 widgets are based on the sources of Flutter's originals, mimic the API and provide seamless integration.
If you've been using (or considered using) standard Flutter's widgets for displaying tables or data grids and missed the sticky headers (or vertical borders, 'No rows' placeholder, straightforward async data source API etc.) - you've come to the right place. No need to learn yet another API of a new control, just stick to well described DataTable and PaginatedDataTable.
Please check the example folder which demonstrates various features of the widgets. There's also a DataGrid Sample in separate repo which is based on DataTable2
.
PaginatedDataTable2
)DataTable2.fixedTopRows
DataTable2.fixedLeftColumns
autoRowsToHeight
property on PaginatedDataTable2 allows to auto calculate page size depending on how much rows fit the height and makes vertical scrolling unnecessaryisVerticalScrollBarVisible
and isHorizontalScrollBarVisible
DataColumn
definitions with DataColumn2
(which is a descendant of DataColumn). The class provides size
property which can be set to one of 3 relative sizes (S, M and L)smRatio
and lmRatio
paramsfixedWidth
parameter allows to define absolute value for column widthminWidth
property) which is useful in portrait orientations with multiple columns not fitting the screenbottomMargin
property) to allow slight over-scrollFlexible
and SingleScrollView
widgets to allow widget to fill parent container and be scrollablescrollController
property. See example 'DataTable2 - Scroll-up' which shows 'up' button when scrolling down and allows to jump to the top of the tablePaginatedDataTable2.fit
property controls whether the paginator sticks to the bottom and leaves a gap to data rows aboveDataRow2
alternative to stock DataRow
which provides row level tap events (including right clicks)PaginatedDataTable2.renderEmptyRowsInTheEnd
property changes the default Flutter way of rendering pages with empty rowsDataRow2.specificRowHeight
allows overriding default height for any rowsortArrowIcon
and sortArrowAnimationDuration
properties, custom arrow builder with sortArrowBuilder
empty
property which allows defining a placeholder widget to be displayed when data source is emptyborder
allows drawing inner and outer vertical and horizontal borders (e.g. outlining individual cells) - stock widgets only allow drawing horizontal row splittersPaginatorController
allows to externally control PaginatedDataTable2
state (e.g. switch pages, change page size etc.)AsynPaginatedDataTable2
widget built for asynchronous scenarios (such a requesting data from a web service) relying on AsyncDataTableSource
returning rows in a Future
NOTE:* don't put the widgets into any unconstrained parents with infinite width or height (e.g. scrollables such as SingleChildScrollView, Column etc.). The widgets are designed to stretch and fill all available space within parent and have a number of own scrollables inside to address fixed rows/columns feature. Putting it inside unconstrained parent break widgets.
Add reference to pubspec.yaml.
Import:
import 'package:data_table_2/data_table_2.dart';
import 'package:data_table_2/data_table_2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
/// Example without a datasource
class DataTable2SimpleDemo extends StatelessWidget {
const DataTable2SimpleDemo();
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: DataTable2(
columnSpacing: 12,
horizontalMargin: 12,
minWidth: 600,
columns: [
DataColumn2(
label: Text('Column A'),
size: ColumnSize.L,
),
DataColumn(
label: Text('Column B'),
),
DataColumn(
label: Text('Column C'),
),
DataColumn(
label: Text('Column D'),
),
DataColumn(
label: Text('Column NUMBERS'),
numeric: true,
),
],
rows: List<DataRow>.generate(
100,
(index) => DataRow(cells: [
DataCell(Text('A' * (10 - index % 10))),
DataCell(Text('B' * (10 - (index + 5) % 10))),
DataCell(Text('C' * (15 - (index + 5) % 10))),
DataCell(Text('D' * (15 - (index + 10) % 10))),
DataCell(Text(((index + 0.1) * 25.4).toString()))
]))),
);
}
}
If you're already using the standard widgets you can reference the package and add '2' to the names of stock widgets (making them DataTable2 or PaginatedDataTable2) and that is it.
minWidth
), data rows width are predefined by constructor params. Content that doesn't fit a cell gets clippedDataRow.onSelectChanged
event handlerDataTable2.showCheckboxColumn
set to true AND there must be some rows with onSelectChanged
event handler being not nullrenderEmptyRowsInTheEnd
Run this command:
With Flutter:
$ flutter pub add data_table_2
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
data_table_2: ^2.5.4
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:data_table_2/data_table_2.dart';
import 'package:example/screens/data_table2_fixed_nm.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'nav_helper.dart';
import 'screens/async_paginated_data_table2.dart';
import 'screens/data_table.dart';
import 'screens/data_table2.dart';
import 'screens/data_table2_scrollup.dart';
import 'screens/data_table2_simple.dart';
import 'screens/data_table2_tests.dart';
import 'screens/paginated_data_table.dart';
import 'screens/paginated_data_table2.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
void main() {
runApp(MyApp());
// Add import
// import 'package:data_table_2/data_table_2.dart';
// and uncomment below line to remove widgets' logs
//dataTableShowLogs = false;
}
const String initialRoute = '/datatable2';
Scaffold _getScaffold(BuildContext context, Widget body,
[List<String>? options]) {
var defaultOption = getCurrentRouteOption(context);
if (defaultOption.isEmpty && options != null && options.isNotEmpty) {
defaultOption = options[0];
}
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey[200],
shadowColor: Colors.transparent,
automaticallyImplyLeading: false,
title: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Container(
padding: const EdgeInsets.fromLTRB(12, 4, 12, 4),
color: Colors.grey[850],
//screen selection
child: DropdownButton<String>(
icon: const Icon(Icons.arrow_forward),
dropdownColor: Colors.grey[800],
style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(color: Colors.white),
value: _getCurrentRoute(context),
onChanged: (v) {
Navigator.of(context).pushNamed(v!);
},
items: const [
DropdownMenuItem(
value: '/datatable2',
child: Text('DataTable2'),
),
DropdownMenuItem(
value: '/datatable2simple',
child: Text('Simple'),
),
DropdownMenuItem(
value: '/datatable2scrollup',
child: Text('Scroll-up/Scroll-left'),
),
DropdownMenuItem(
value: '/datatable2fixedmn',
child: Text('Fixed Rows/Cols'),
),
DropdownMenuItem(
value: '/paginated2',
child: Text('PaginatedDataTable2'),
),
DropdownMenuItem(
value: '/asyncpaginated2',
child: Text('AsyncPaginatedDataTable2'),
),
DropdownMenuItem(
value: '/datatable',
child: Text('DataTable'),
),
DropdownMenuItem(
value: '/paginated',
child: Text('PaginatedDataTable'),
),
if (kDebugMode)
DropdownMenuItem(
value: '/datatable2tests',
child: Text('Unit Tests Preview'),
),
],
)),
options != null && options.isNotEmpty
? Flexible(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 4, 0, 4),
child: DropdownButton<String>(
icon: const SizedBox(),
dropdownColor: Colors.grey[300],
style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(color: Colors.black),
value: defaultOption,
onChanged: (v) {
var r = _getCurrentRoute(context);
Navigator.of(context).pushNamed(r, arguments: v);
},
items: options
.map<DropdownMenuItem<String>>(
(v) => DropdownMenuItem<String>(
value: v,
child: Text(v),
))
.toList()))))
: const SizedBox()
]),
),
body: body,
);
}
String _getCurrentRoute(BuildContext context) {
return ModalRoute.of(context) != null &&
ModalRoute.of(context)!.settings.name != null
? ModalRoute.of(context)!.settings.name!
: initialRoute;
}
// ignore: use_key_in_widget_constructors
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
restorationScopeId: 'main',
title: 'Flutter Demo',
theme: ThemeData(
primaryColor: Colors.grey[300],
),
initialRoute: initialRoute,
routes: {
'/datatable2': (context) => _getScaffold(
context, const DataTable2Demo(), getOptionsForRoute('/datatable2')),
'/datatable2simple': (context) =>
_getScaffold(context, const DataTable2SimpleDemo()),
'/datatable2scrollup': (context) =>
_getScaffold(context, const DataTable2ScrollupDemo()),
'/datatable2fixedmn': (context) => _getScaffold(
context,
const DataTable2FixedNMDemo(),
getOptionsForRoute('/datatable2fixedmn')),
'/paginated2': (context) => _getScaffold(context,
const PaginatedDataTable2Demo(), getOptionsForRoute('/paginated2')),
'/asyncpaginated2': (context) => _getScaffold(
context,
const AsyncPaginatedDataTable2Demo(),
getOptionsForRoute('/asyncpaginated2')),
'/datatable': (context) => _getScaffold(context, const DataTableDemo()),
'/paginated': (context) =>
_getScaffold(context, const PaginatedDataTableDemo()),
'/datatable2tests': (context) =>
_getScaffold(context, const DataTable2Tests()),
},
localizationsDelegates: const [GlobalMaterialLocalizations.delegate],
supportedLocales: const [
Locale('en', ''),
Locale('be', ''),
Locale('ru', ''),
Locale('fr', ''),
],
// change to see how PaginatedDataTable2 controls (e.g. Rows per page) get translated
locale: const Locale('en', ''),
);
}
}
Download details:
Author: saplin.blogspot.com
Source: https://github.com/maxim-saplin/data_table_2
#flutter #android #ios #web-development #ui #data #grid #table