Flutter响应式表格插件responsive_table的使用
Flutter响应式表格插件responsive_table的使用
示例代码
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:responsive_table/responsive_table.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: '/',
routes: {
'/': (_) => DataPage(),
},
);
}
}
class DataPage extends StatefulWidget {
DataPage({Key? key}) : super(key: key);
[@override](/user/override)
_DataPageState createState() => _DataPageState();
}
class _DataPageState extends State<DataPage> {
late List<DatatableHeader> _headers;
List<int> _perPages = [10, 20, 50, 100];
int _total = 100;
int? _currentPerPage = 10;
List<bool>? _expanded;
String? _searchKey = "id";
int _currentPage = 1;
bool _isSearch = false;
List<Map<String, dynamic>> _sourceOriginal = [];
List<Map<String, dynamic>> _sourceFiltered = [];
List<Map<String, dynamic>> _source = [];
List<Map<String, dynamic>> _selecteds = [];
// ignore: unused_field
String _selectableKey = "id";
String? _sortColumn;
bool _sortAscending = true;
bool _isLoading = true;
bool _showSelect = true;
var random = new Random();
List<Map<String, dynamic>> _generateData({int n: 100}) {
final List source = List.filled(n, Random.secure());
List<Map<String, dynamic>> temps = [];
var i = 1;
print(i);
// ignore: unused_local_variable
for (var data in source) {
temps.add({
"id": i,
"sku": "$i\000$i",
"name": "Product $i",
"category": "Category-$i",
"price": i * 10.00,
"cost": "20.00",
"margin": "${i}0.20",
"in_stock": "${i}0",
"alert": "5",
"received": [i + 20, 150]
});
i++;
}
return temps;
}
_initializeData() async {
_mockPullData();
}
_mockPullData() async {
_expanded = List.generate(_currentPerPage!, (index) => false);
setState(() => _isLoading = true);
Future.delayed(Duration(seconds: 3)).then((value) {
_sourceOriginal.clear();
_sourceOriginal.addAll(_generateData(n: random.nextInt(10000)));
_sourceFiltered = _sourceOriginal;
_total = _sourceFiltered.length;
_source = _sourceFiltered.getRange(0, _currentPerPage!).toList();
setState(() => _isLoading = false);
});
}
_resetData({start: 0}) async {
setState(() => _isLoading = true);
var _expandedLen =
_total - start < _currentPerPage! ? _total - start : _currentPerPage;
Future.delayed(Duration(seconds: 0)).then((value) {
_expanded = List.generate(_expandedLen as int, (index) => false);
_source.clear();
_source = _sourceFiltered.getRange(start, start + _expandedLen).toList();
setState(() => _isLoading = false);
});
}
_filterData(value) {
setState(() => _isLoading = true);
try {
if (value == "" || value == null) {
_sourceFiltered = _sourceOriginal;
} else {
_sourceFiltered = _sourceOriginal
.where((data) => data[_searchKey!]
.toString()
.toLowerCase()
.contains(value.toString().toLowerCase()))
.toList();
}
_total = _sourceFiltered.length;
var _rangeTop = _total < _currentPerPage! ? _total : _currentPerPage!;
_expanded = List.generate(_rangeTop, (index) => false);
_source = _sourceFiltered.getRange(0, _rangeTop).toList();
} catch (e) {
print(e);
}
setState(() => _isLoading = false);
}
[@override](/user/override)
void initState() {
super.initState();
/// 设置表头
_headers = [
DatatableHeader(
text: "ID",
value: "id",
show: true,
sortable: true,
textAlign: TextAlign.center),
DatatableHeader(
text: "Name",
value: "name",
show: true,
flex: 2,
sortable: true,
editable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "SKU",
value: "sku",
show: true,
sortable: true,
textAlign: TextAlign.center),
DatatableHeader(
text: "Category",
value: "category",
show: true,
sortable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "Price",
value: "price",
show: true,
sortable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "Margin",
value: "margin",
show: true,
sortable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "In Stock",
value: "in_stock",
show: true,
sortable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "Alert",
value: "alert",
show: true,
sortable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "Received",
value: "received",
show: true,
sortable: false,
sourceBuilder: (value, row) {
List list = List.from(value);
return Container(
child: Column(
children: [
Container(
width: 85,
child: LinearProgressIndicator(
value: list.first / list.last,
),
),
Text("${list.first} of ${list.last}")
],
),
);
},
textAlign: TextAlign.center),
];
_initializeData();
}
[@override](/user/override)
void dispose() {
super.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("RESPONSIVE DATA TABLE"),
actions: [
IconButton(
onPressed: _initializeData,
icon: Icon(Icons.refresh_sharp),
),
],
),
drawer: Drawer(
child: ListView(
children: [
ListTile(
leading: Icon(Icons.home),
title: Text("home"),
onTap: () {},
),
ListTile(
leading: Icon(Icons.storage),
title: Text("data"),
onTap: () {},
)
],
),
),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(0),
constraints: BoxConstraints(
maxHeight: 700,
),
child: Card(
elevation: 1,
shadowColor: Colors.black,
clipBehavior: Clip.none,
child: ResponsiveDatatable(
title: TextButton.icon(
onPressed: () {},
icon: Icon(Icons.add),
label: Text("new item"),
),
reponseScreenSizes: [ScreenSize.xs],
actions: [
if (_isSearch)
Expanded(
child: TextField(
decoration: InputDecoration(
hintText: 'Enter search term based on ' +
_searchKey!
.replaceAll(new RegExp('[\\W_]+'), ' ')
.toUpperCase(),
prefixIcon: IconButton(
icon: Icon(Icons.cancel),
onPressed: () {
setState(() {
_isSearch = false;
});
_initializeData();
}),
suffixIcon: IconButton(
icon: Icon(Icons.search), onPressed: () {})),
onSubmitted: (value) {
_filterData(value);
},
)),
if (!_isSearch)
IconButton(
icon: Icon(Icons.search),
onPressed: () {
setState(() {
_isSearch = true;
});
})
],
headers: _headers,
source: _source,
selecteds: _selecteds,
showSelect: _showSelect,
autoHeight: false,
dropContainer: (data) {
if (int.tryParse(data['id'].toString())!.isEven) {
return Text("is Even");
}
return _DropDownContainer(data: data);
},
onChangedRow: (value, header) {
/// print(value);
/// print(header);
},
onSubmittedRow: (value, header) {
/// print(value);
/// print(header);
},
onTabRow: (data) {
print(data);
},
onSort: (value) {
setState(() => _isLoading = true);
setState(() {
_sortColumn = value;
_sortAscending = !_sortAscending;
if (_sortAscending) {
_sourceFiltered.sort((a, b) =>
b["$_sortColumn"].compareTo(a["$_sortColumn"]));
} else {
_sourceFiltered.sort((a, b) =>
a["$_sortColumn"].compareTo(b["$_sortColumn"]));
}
var _rangeTop = _currentPerPage! < _sourceFiltered.length
? _currentPerPage!
: _sourceFiltered.length;
_source = _sourceFiltered.getRange(0, _rangeTop).toList();
_searchKey = value;
_isLoading = false;
});
},
expanded: _expanded,
sortAscending: _sortAscending,
sortColumn: _sortColumn,
isLoading: _isLoading,
onSelect: (value, item) {
print("$value $item ");
if (value!) {
setState(() => _selecteds.add(item));
} else {
setState(
() => _selecteds.removeAt(_selecteds.indexOf(item)));
}
},
onSelectAll: (value) {
if (value!) {
setState(() => _selecteds =
_source.map((entry) => entry).toList().cast());
} else {
setState(() => _selecteds.clear());
}
},
footers: [
Container(
padding: EdgeInsets.symmetric(horizontal: 15),
child: Text("Rows per page:"),
),
if (_perPages.isNotEmpty)
Container(
padding: EdgeInsets.symmetric(horizontal: 15),
child: DropdownButton<int>(
value: _currentPerPage,
items: _perPages
.map((e) => DropdownMenuItem<int>(
child: Text("$e"),
value: e,
))
.toList(),
onChanged: (dynamic value) {
setState(() {
_currentPerPage = value;
_currentPage = 1;
_resetData();
});
},
isExpanded: false,
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 15),
child:
Text("$_currentPage - $_currentPerPage of $_total"),
),
IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: 16,
),
onPressed: _currentPage == 1
? null
: () {
var _nextSet = _currentPage - _currentPerPage!;
setState(() {
_currentPage = _nextSet > 1 ? _nextSet : 1;
_resetData(start: _currentPage - 1);
});
},
padding: EdgeInsets.symmetric(horizontal: 15),
),
IconButton(
icon: Icon(Icons.arrow_forward_ios, size: 16),
onPressed: _currentPage + _currentPerPage! - 1 > _total
? null
: () {
var _nextSet = _currentPage + _currentPerPage!;
setState(() {
_currentPage = _nextSet < _total
? _nextSet
: _total - _currentPerPage!;
_resetData(start: _nextSet - 1);
});
},
padding: EdgeInsets.symmetric(horizontal: 15),
)
],
headerDecoration: BoxDecoration(
color: Colors.grey,
border: Border(
bottom: BorderSide(color: Colors.red, width: 1))),
selectedDecoration: BoxDecoration(
border: Border(
bottom:
BorderSide(color: Colors.green[300]!, width: 1)),
color: Colors.green,
),
headerTextStyle: TextStyle(color: Colors.white),
rowTextStyle: TextStyle(color: Colors.green),
selectedTextStyle: TextStyle(color: Colors.white),
),
),
),
])),
);
}
}
class _DropDownContainer extends StatelessWidget {
final Map<String, dynamic> data;
const _DropDownContainer({Key? key, required this.data}) : super(key: key);
[@override](/user/override)
Widget build(BuildContext context) {
List<Widget> _children = data.entries.map<Widget>((entry) {
Widget w = Row(
children: [
Text(entry.key.toString()),
Spacer(),
Text(entry.value.toString()),
],
);
return w;
}).toList();
return Container(
child: Column(
children: _children,
),
);
}
}
方法
onSelect( bool selected, dynamic key);
onSelectAll( bool selected );
onTabRow( dynamic row );
onSort( dynamic value );
属性
title: Widget
headers: List<DatatableHeader>
actions: List<Widget>
source: List<Map<String, dynamic>>
selecteds: List<Map<String, dynamic>>
showSelect: bool
footers: List<Widget>
sortColumn: String
sortAscending: bool
isLoading: bool,
autoHeight: bool,
使用说明
本示例展示了如何在 Flutter 应用程序中使用 responsive_table
插件来创建一个响应式的表格。通过此插件,你可以轻松地添加分页、搜索、排序和选择功能。
初始化数据
首先,初始化数据并设置表格头部:
[@override](/user/override)
void initState() {
super.initState();
/// 设置表头
_headers = [
DatatableHeader(
text: "ID",
value: "id",
show: true,
sortable: true,
textAlign: TextAlign.center),
DatatableHeader(
text: "Name",
value: "name",
show: true,
flex: 2,
sortable: true,
editable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "SKU",
value: "sku",
show: true,
sortable: true,
textAlign: TextAlign.center),
DatatableHeader(
text: "Category",
value: "category",
show: true,
sortable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "Price",
value: "price",
show: true,
sortable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "Margin",
value: "margin",
show: true,
sortable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "In Stock",
value: "in_stock",
show: true,
sortable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "Alert",
value: "alert",
show: true,
sortable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "Received",
value: "received",
show: true,
sortable: false,
sourceBuilder: (value, row) {
List list = List.from(value);
return Container(
child: Column(
children: [
Container(
width: 85,
child: LinearProgressIndicator(
value: list.first / list.last,
),
),
Text("${list.first} of ${list.last}")
],
),
);
},
textAlign: TextAlign.center),
];
_initializeData();
}
构建表格
在 build
方法中构建表格:
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("RESPONSIVE DATA TABLE"),
actions: [
IconButton(
onPressed: _initializeData,
icon: Icon(Icons.refresh_sharp),
),
],
),
drawer: Drawer(
child: ListView(
children: [
ListTile(
leading: Icon(Icons.home),
title: Text("home"),
onTap: () {},
),
ListTile(
leading: Icon(Icons.storage),
title: Text("data"),
onTap: () {},
)
],
),
),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(0),
constraints: BoxConstraints(
maxHeight: 700,
),
child: Card(
elevation: 1,
shadowColor: Colors.black,
clipBehavior: Clip.none,
child: ResponsiveDatatable(
title: TextButton.icon(
onPressed: () {},
icon: Icon(Icons.add),
label: Text("new item"),
),
reponseScreenSizes: [ScreenSize.xs],
actions: [
if (_isSearch)
Expanded(
child: TextField(
decoration: InputDecoration(
hintText: 'Enter search term based on ' +
_searchKey!
.replaceAll(new RegExp('[\\W_]+'), ' ')
.toUpperCase(),
prefixIcon: IconButton(
icon: Icon(Icons.cancel),
onPressed: () {
setState(() {
_isSearch = false;
});
_initializeData();
}),
suffixIcon: IconButton(
icon: Icon(Icons.search), onPressed: () {})),
onSubmitted: (value) {
_filterData(value);
},
)),
if (!_isSearch)
IconButton(
icon: Icon(Icons.search),
onPressed: () {
setState(() {
_isSearch = true;
});
})
],
headers: _headers,
source: _source,
selecteds: _selecteds,
showSelect: _showSelect,
autoHeight: false,
dropContainer: (data) {
if (int.tryParse(data['id'].toString())!.isEven) {
return Text("is Even");
}
return _DropDownContainer(data: data);
},
onChangedRow: (value, header) {
/// print(value);
/// print(header);
},
onSubmittedRow: (value, header) {
/// print(value);
/// print(header);
},
onTabRow: (data) {
print(data);
},
onSort: (value) {
setState(() => _isLoading = true);
setState(() {
_sortColumn = value;
_sortAscending = !_sortAscending;
if (_sortAscending) {
_sourceFiltered.sort((a, b) =>
b["$_sortColumn"].compareTo(a["$_sortColumn"]));
} else {
_sourceFiltered.sort((a, b) =>
a["$_sortColumn"].compareTo(b["$_sortColumn"]));
}
var _rangeTop = _currentPerPage! < _sourceFiltered.length
? _currentPerPage!
: _sourceFiltered.length;
_source = _sourceFiltered.getRange(0, _rangeTop).toList();
_searchKey = value;
_isLoading = false;
});
},
expanded: _expanded,
sortAscending: _sortAscending,
sortColumn: _sortColumn,
isLoading: _isLoading,
onSelect: (value, item) {
print("$value $item ");
if (value!) {
setState(() => _selecteds.add(item));
} else {
setState(
() => _selecteds.removeAt(_selecteds.indexOf(item)));
}
},
onSelectAll: (value) {
if (value!) {
setState(() => _selecteds =
_source.map((entry) => entry).toList().cast());
} else {
setState(() => _selecteds.clear());
}
},
footers: [
Container(
padding: EdgeInsets.symmetric(horizontal: 15),
child: Text("Rows per page:"),
),
if (_perPages.isNotEmpty)
Container(
padding: EdgeInsets.symmetric(horizontal: 15),
child: DropdownButton<int>(
value: _currentPerPage,
items: _perPages
.map((e) => DropdownMenuItem<int>(
child: Text("$e"),
value: e,
))
.toList(),
onChanged: (dynamic value) {
setState(() {
_currentPerPage = value;
_currentPage = 1;
_resetData();
});
},
isExpanded: false,
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 15),
child:
Text("$_currentPage - $_currentPerPage of $_total"),
),
IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: 16,
),
onPressed: _currentPage == 1
? null
: () {
var _nextSet = _currentPage - _currentPerPage!;
setState(() {
_currentPage = _nextSet > 1 ? _nextSet : 1;
_resetData(start: _currentPage - 1);
});
},
padding: EdgeInsets.symmetric(horizontal: 15),
),
IconButton(
icon: Icon(Icons.arrow_forward_ios, size: 16),
onPressed: _currentPage + _currentPerPage! - 1 > _total
? null
: () {
var _nextSet = _currentPage + _currentPerPage!;
setState(() {
_currentPage = _nextSet < _total
? _nextSet
: _total - _currentPerPage!;
_resetData(start: _nextSet - 1);
});
},
padding: EdgeInsets.symmetric(horizontal: 15),
)
],
headerDecoration: BoxDecoration(
color: Colors.grey,
border: Border(
bottom: BorderSide(color: Colors.red, width: 1))),
selectedDecoration: BoxDecoration(
border: Border(
bottom:
BorderSide(color: Colors.green[300]!, width: 1)),
color: Colors.green,
),
headerTextStyle: TextStyle(color: Colors.white),
rowTextStyle: TextStyle(color: Colors.green),
selectedTextStyle: TextStyle(color: Colors.white),
),
),
),
])),
);
}
更多关于Flutter响应式表格插件responsive_table的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter响应式表格插件responsive_table的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何使用Flutter中的responsive_table
插件来创建响应式表格的示例代码。responsive_table
插件允许你创建在不同屏幕尺寸上都能很好显示的表格。
首先,你需要在pubspec.yaml
文件中添加responsive_table
依赖:
dependencies:
flutter:
sdk: flutter
responsive_table: ^0.3.0 # 请检查最新版本号
然后运行flutter pub get
来安装依赖。
接下来是一个完整的Flutter应用示例,展示如何使用responsive_table
插件:
import 'package:flutter/material.dart';
import 'package:responsive_table/responsive_table.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Responsive Table Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
final List<Map<String, String>> data = [
{"Name": "Alice", "Age": "24", "Country": "USA"},
{"Name": "Bob", "Age": "30", "Country": "UK"},
{"Name": "Charlie", "Age": "29", "Country": "Canada"},
{"Name": "David", "Age": "22", "Country": "Australia"},
];
final List<String> columns = ["Name", "Age", "Country"];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Responsive Table Demo'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: ResponsiveTable(
columns: columns,
data: data,
columnWidths: {
"Name": FlexibleColumnWidth(flex: 2),
"Age": FlexibleColumnWidth(flex: 1),
"Country": FlexibleColumnWidth(flex: 2),
},
cellDecorators: {
"Name": (value) => Text(value, style: TextStyle(fontWeight: FontWeight.bold)),
"Age": (value) => Text(value, style: TextStyle(color: Colors.red)),
"Country": (value) => Text(value, style: TextStyle(decoration: TextDecoration.underline)),
},
headerStyle: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
dataRowStyle: TextStyle(fontSize: 18),
),
),
);
}
}
代码解释
-
依赖引入:在
pubspec.yaml
中引入responsive_table
插件。 -
数据准备:在
MyHomePage
类中,我们准备了一些示例数据data
和一个列名列表columns
。 -
ResponsiveTable组件:
columns
:指定表格的列名。data
:提供表格的数据。columnWidths
:定义每列的宽度,这里使用了FlexibleColumnWidth
来根据flex值分配宽度。cellDecorators
:允许你对每个单元格的内容进行自定义装饰,例如改变文本样式。headerStyle
:定义表头的样式。dataRowStyle
:定义数据行的样式。
运行这个示例应用,你将看到一个响应式的表格,它在不同屏幕尺寸上都能很好地显示,并且每列的宽度和内容样式都可以根据需要进行自定义。
希望这个示例能帮助你理解如何使用responsive_table
插件来创建响应式表格!