A Flutter Widget that create a horizontal table with fixed first column.
The following is the guide for installation with different dart sdk and flutter version.
minium dart sdk | flutter version (stable) | package version |
---|---|---|
<2.12.0 | <2.0.1 | 2.5.1 |
<2.12.0 | =2.0.1 | 2.5.2 |
>=2.12.0 (enabled null-safety) | >=2.0.1 && <3.0.0 | 3.6.2+1 |
>=2.12.0 | >=3.0.0 && <3.3.0 | 4.1.1+3 |
>=2.12.0 && < 2.18.0 | >=3.3.0 && <3.7.0 | 4.1.4 |
>=2.18.0 && <2.19.0 | >=3.7.0 | 4.2.0 |
>=2.19.0 | >=3.7.0 | latest |
Run this command to install latest:
flutter pub add horizontal_data_table
onScrollControllerReady: (vertical, horizontal) {
_verticalScrollController = vertical;
_horizontalScrollController = horizontal;
},
This shows Widget's full customizations:
HorizontalDataTable(
{
required double leftHandSideColumnWidth,
required double rightHandSideColumnWidth,
this.tableHeight,
this.isFixedHeader = false,
this.headerWidgets,
this.isFixedFooter = false,
this.footerWidgets,
IndexedWidgetBuilder? leftSideItemBuilder,
IndexedWidgetBuilder? rightSideItemBuilder,
this.itemCount = 0,
List<Widget>? leftSideChildren,
List<Widget>? rightSideChildren,
this.rowSeparatorWidget = const Divider(
color: Colors.transparent,
height: 0.0,
thickness: 0.0,
),
this.elevation = 3.0,
this.elevationColor = Colors.black54,
Color leftHandSideColBackgroundColor = Colors.white,
Color rightHandSideColBackgroundColor = Colors.white,
this.onScrollControllerReady,
this.verticalScrollbarStyle,
this.horizontalScrollbarStyle,
this.enablePullToRefresh = false,
this.refreshIndicatorHeight = 60.0,
this.htdRefreshController,
this.onRefresh,
this.refreshIndicator,
this.fixedSidePlaceHolderRefreshIndicator,
this.enablePullToLoadNewData = false,
this.onLoad,
this.loadIndicator,
this.fixedSidePlaceHolderLoadIndicator,
this.scrollPhysics,
this.horizontalScrollPhysics,
this.itemExtent,
}
)
/// right-to-left mode constructor
HorizontalDataTable.rtl({...same as the above})
The pull to refresh action is impletemented based on the 'pull-to-refresh' package code. Currently only part of the function is available.
HorizontalDataTable(
{
...
this.enablePullToRefresh = true,
this.refreshIndicator: const WaterDropHeader(),
this.fixedSidePlaceHolderRefreshIndicator,
this.onRefresh: _onRefresh,
this.enablePullToLoadNewData = true,
this.onLoad: _onLoad,
this.loadIndicator: const ClassicFooter(),
this.fixedSidePlaceHolderLoadIndicator,
this.htdRefreshController: _hdtRefreshController,
}
)
refreshIndicator is the header widget when pull to refresh. Supported refreshIndicator:
fixedSidePlaceHolderRefreshIndicator is the fixed column side part refresh indicator. This aims to synchronize the action on both side. Prefer using PlaceholderHeader.
refreshIndicatorHeight is the height of the refreshIndicator. Default is set to 60.
onRefresh is the callback from the refresh action.
loadIndicator is the header widget when pull to load. Supported refreshIndicator:
fixedSidePlaceHolderLoadIndicator is the fixed column side part load indicator. This aims to synchronize the action on both side. Prefer using PlaceholderFooter.
onLoad is the callback from the load action.
htdRefreshController is the wrapper controller for returning the refresh or load result. This is the example on how to use onRefresh and htdRefreshController.
void _onRefresh() async {
//do some network call and get the response
if(isError){
//call this when it is an error case
_hdtRefreshController.refreshFailed();
}else{
//call this when it is a success case
_hdtRefreshController.refreshCompleted();
}
},
void _onLoad() async {
//do some network call and get the response
if(isError){
//call this when it is an error case
_hdtRefreshController.loadFailed();
}else if(isNoMoreData){
//call this when it is end loading data
_hdtRefreshController.loadNoData();
}else{
//call this when it is a success case
_hdtRefreshController.loadCompleted();
}
},
All of this 4 params are required when enablePullToRefresh is set to true.
import 'package:flutter/material.dart';
import 'package:horizontal_data_table/horizontal_data_table.dart';
import 'data/user.dart';
class SimpleTablePage extends StatefulWidget {
const SimpleTablePage({
Key? key,
required this.user,
}) : super(key: key);
final User user;
@override
State<SimpleTablePage> createState() => _SimpleTablePageState();
}
class _SimpleTablePageState extends State<SimpleTablePage> {
@override
void initState() {
widget.user.initData(3000);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Simple Table')),
body: HorizontalDataTable(
leftHandSideColumnWidth: 100,
rightHandSideColumnWidth: 600,
isFixedHeader: true,
headerWidgets: _getTitleWidget(),
isFixedFooter: true,
footerWidgets: _getTitleWidget(),
leftSideItemBuilder: _generateFirstColumnRow,
rightSideItemBuilder: _generateRightHandSideColumnRow,
itemCount: widget.user.userInfo.length,
rowSeparatorWidget: const Divider(
color: Colors.black38,
height: 1.0,
thickness: 0.0,
),
leftHandSideColBackgroundColor: const Color(0xFFFFFFFF),
rightHandSideColBackgroundColor: const Color(0xFFFFFFFF),
itemExtent: 55,
),
);
}
List<Widget> _getTitleWidget() {
return [
_getTitleItemWidget('Name', 100),
_getTitleItemWidget('Status', 100),
_getTitleItemWidget('Phone', 200),
_getTitleItemWidget('Register', 100),
_getTitleItemWidget('Termination', 200),
];
}
Widget _getTitleItemWidget(String label, double width) {
return Container(
width: width,
height: 56,
padding: const EdgeInsets.fromLTRB(5, 0, 0, 0),
alignment: Alignment.centerLeft,
child: Text(label, style: const TextStyle(fontWeight: FontWeight.bold)),
);
}
Widget _generateFirstColumnRow(BuildContext context, int index) {
return Container(
width: 100,
height: 52,
padding: const EdgeInsets.fromLTRB(5, 0, 0, 0),
alignment: Alignment.centerLeft,
child: Text(widget.user.userInfo[index].name),
);
}
Widget _generateRightHandSideColumnRow(BuildContext context, int index) {
return Row(
children: <Widget>[
Container(
width: 100,
height: 52,
padding: const EdgeInsets.fromLTRB(5, 0, 0, 0),
alignment: Alignment.centerLeft,
child: Row(
children: <Widget>[
Icon(
widget.user.userInfo[index].status
? Icons.notifications_off
: Icons.notifications_active,
color: widget.user.userInfo[index].status
? Colors.red
: Colors.green),
Text(widget.user.userInfo[index].status ? 'Disabled' : 'Active')
],
),
),
Container(
width: 200,
height: 52,
padding: const EdgeInsets.fromLTRB(5, 0, 0, 0),
alignment: Alignment.centerLeft,
child: Text(widget.user.userInfo[index].phone),
),
Container(
width: 100,
height: 52,
padding: const EdgeInsets.fromLTRB(5, 0, 0, 0),
alignment: Alignment.centerLeft,
child: Text(widget.user.userInfo[index].registerDate),
),
Container(
width: 200,
height: 52,
padding: const EdgeInsets.fromLTRB(5, 0, 0, 0),
alignment: Alignment.centerLeft,
child: Text(widget.user.userInfo[index].terminationDate),
),
],
);
}
}
Run this command:
With Flutter:
$ flutter pub add horizontal_data_table
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
horizontal_data_table: ^4.3.1
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:horizontal_data_table/horizontal_data_table.dart';
import 'dart:io';
import 'package:example/data/user.dart';
import 'package:example/simple_rtl_table.dart';
import 'package:example/simple_table.dart';
import 'package:example/simple_table_keepalive_image.dart';
import 'package:example/simple_table_refresh_load.dart';
import 'package:example/simple_table_refresh_load_desktop.dart';
import 'package:example/simple_table_scroll_style.dart';
import 'package:example/simple_table_sort.dart';
import 'package:example/title_reorder_table.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final User _user = User();
@override
void initState() {
_user.initData(100);
super.initState();
}
bool get isDesktopOrWeb {
if (kIsWeb) {
return true;
}
return Platform.isLinux || Platform.isMacOS || Platform.isWindows;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
children: [
_getRouteButton(
'Simple Table',
SimpleTablePage(
user: _user,
),
),
_getRouteButton(
'RTL Table',
SimpleTableRTLPage(
user: _user,
),
),
_getRouteButton(
'Reorderable Header/Footer Table',
TitleReorderTablePage(
user: _user,
),
),
_getRouteButton(
'Pull-to-refresh Table',
isDesktopOrWeb
? SimpleTableDesktopRefreshLoadPage(
user: _user,
)
: SimpleTableRefreshLoadPage(
user: _user,
),
),
_getRouteButton(
'Customize Scroll Related Table',
SimpleTableScrollStylePage(
user: _user,
),
),
_getRouteButton(
'Sortable Table',
SimpleTableSortPage(
user: _user,
),
),
_getRouteButton(
'Keep Alive Image Table',
SimpleTableKeepAliveImagePage(
user: _user,
),
),
],
),
),
);
}
Widget _getRouteButton(String label, Widget page) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return page;
},
),
);
},
child: Text(
label,
),
),
);
}
}
Download details:
Author: MayLau-CbL
Source: https://github.com/MayLau-CbL/flutter_horizontal_data_table