0 purchases
davi
Ready for a large number of data. Building cells on demand.
Focused on Web/Desktop Applications.
Bidirectional scroll bars.
Resizable.
Highly customized.
Pinned columns.
Multiple sort.
Infinite scroll.
Usage #
Get started
Model
Column
Columns fit
Stretchable column
Column style
Pinned column
Row
Row color
Row cursor
Row callbacks
Row hover listener
Infinite scroll
Cell
Cell style
Custom cell widget
Cell edit
Sort
Multiple sort
Sort callback
Server-side sorting
Always sorted
Theme
Dividers thickness and color
Header
Hidden header
Row
Row color
Row zebra color
Row hover background
Row hover foreground
Row fill height
Scrollbar
Scrollbar always visible
Cell
Null value color
Support this project
Get started #
DaviModel<Person>? _model;
@override
void initState() {
super.initState();
_model = DaviModel<Person>(rows: [
Person('Landon', 19),
Person('Sari', 22),
Person('Julian', 37),
Person('Carey', 39),
Person('Cadu', 43),
Person('Delmar', 72)
], columns: [
DaviColumn(name: 'Name', stringValue: (row) => row.name),
DaviColumn(name: 'Age', intValue: (row) => row.age)
]);
}
@override
Widget build(BuildContext context) {
return Davi<Person>(_model);
}
copied to clipboard
Model #
Column #
Columns fit
All columns will fit in the available width.
_model = DaviModel<Person>(rows: rows, columns: [
DaviColumn(name: 'Name', grow: 2, stringValue: (row) => row.name),
DaviColumn(name: 'Age', grow: 1, intValue: (row) => row.age)
]);
copied to clipboard
Davi<Person>(_model, columnWidthBehavior: ColumnWidthBehavior.fit);
copied to clipboard
Stretchable column
The remaining width will be distributed to the columns according to the value of the grow attribute.
_model = DaviModel<Person>(rows: rows, columns: [
DaviColumn(name: 'Name', grow: 1, stringValue: (row) => row.name),
DaviColumn(name: 'Age', intValue: (row) => row.age)
]);
copied to clipboard
Davi<Person>(_model);
copied to clipboard
Column style
_model = DaviModel<Person>(rows: rows, columns: [
DaviColumn(name: 'Name', stringValue: (row) => row.name),
DaviColumn(
name: 'Age',
intValue: (row) => row.age,
headerTextStyle: TextStyle(color: Colors.blue[900]!),
headerAlignment: Alignment.center,
cellAlignment: Alignment.center,
cellTextStyle: TextStyle(color: Colors.blue[700]!),
cellBackground: (data) => Colors.blue[50])
]);
copied to clipboard
Pinned column
_model = DaviModel(rows: persons, columns: [
DaviColumn(
pinStatus: PinStatus.left,
width: 30,
cellBuilder: (BuildContext context, DaviRow<Person> row) {
return InkWell(
child: const Icon(Icons.edit, size: 16),
onTap: () => _onEdit(row.data));
}),
DaviColumn(name: 'Name', stringValue: (row) => row.name),
DaviColumn(name: 'Age', intValue: (row) => row.age)
]);
copied to clipboard
Row #
Row color
_model = DaviModel<Person>(rows: rows, columns: [
DaviColumn(name: 'Name', stringValue: (row) => row.name),
DaviColumn(name: 'Age', intValue: (row) => row.age)
]);
copied to clipboard
@override
Widget build(BuildContext context) {
return Davi<Person>(_model, rowColor: _rowColor);
}
Color? _rowColor(DaviRow<Person> row) {
if (row.data.age < 20) {
return Colors.green[50]!;
} else if (row.data.age > 30 && row.data.age < 50) {
return Colors.orange[50]!;
}
return null;
}
copied to clipboard
Row cursor
DaviTheme(
data: const DaviThemeData(
row: RowThemeData(cursorOnTapGesturesOnly: false)),
child: Davi<Person>(_model,
rowCursor: (row) =>
row.data.age < 20 ? SystemMouseCursors.forbidden : null));
copied to clipboard
Row callbacks
@override
Widget build(BuildContext context) {
return Davi<Person>(_model,
onRowTap: (person) => _onRowTap(context, person),
onRowSecondaryTap: (person) => _onRowSecondaryTap(context, person),
onRowSecondaryTapUp: (person, detail) => _onRowSecondaryTapUp(context, person, detail),
onRowDoubleTap: (person) => _onRowDoubleTap(context, person));
}
void _onRowTap(BuildContext context, Person person) {
...
}
void _onRowSecondaryTap(BuildContext context, Person person) {
...
}
void _onRowSecondaryTapUp(BuildContext context, Person person, TapUpDetail detail) {
...
}
void _onRowDoubleTap(BuildContext context, Person person) {
...
}
copied to clipboard
Row hover listener
Davi<Person>(_model, onHover: _onHover);
void _onHover(int? rowIndex) {
...
}
copied to clipboard
Infinite scroll
DaviModel<Value>? _model;
bool _loading = false;
@override
void initState() {
super.initState();
List<Value> rows = List.generate(30, (index) => Value(index));
_model = DaviModel<Value>(rows: rows, columns: [
DaviColumn(name: 'Index', intValue: (row) => row.index),
DaviColumn(name: 'Random 1', stringValue: (row) => row.random1),
DaviColumn(name: 'Random 2', stringValue: (row) => row.random2)
]);
}
@override
Widget build(BuildContext context) {
return Davi<Value>(_model,
lastRowWidget: const LoadingWidget(),
onLastRowWidget: _onLastRowWidget);
}
void _onLastRowWidget(bool visible) {
if (visible && !_loading) {
setState(() {
_loading = true;
});
Future.delayed(const Duration(seconds: 2), () {
setState(() {
_loading = false;
List<Value> newValues =
List.generate(15, (index) => Value(_model!.rowsLength + index));
_model!.addRows(newValues);
});
});
}
}
copied to clipboard
Cell #
Cell style
_model = DaviModel<Person>(rows: rows, columns: [
DaviColumn(name: 'Name', stringValue: (row) => row.name),
DaviColumn(
name: 'Age',
intValue: (row) => row.age,
cellStyleBuilder: (row) => row.data.age >= 30 && row.data.age < 40
? CellStyle(
background: Colors.blue[800],
alignment: Alignment.center,
textStyle: const TextStyle(color: Colors.white))
: null)
]);
copied to clipboard
Custom cell widget
_model = DaviModel<Person>(rows: rows, columns: [
DaviColumn(name: 'Name', stringValue: (row) => row.name),
DaviColumn(
name: 'Rate',
width: 150,
cellBuilder: (context, row) => StarsWidget(stars: row.data.stars))
]);
copied to clipboard
Cell edit
class Person {
Person(this.name, this.value);
final String name;
final int value;
bool _valid = true;
bool get valid => _valid;
String _editable = '';
String get editable => _editable;
set editable(String value) {
_editable = value;
_valid = _editable.length < 6;
}
}
class MainWidgetState extends State<MainWidget> {
DaviModel<Person>? _model;
@override
void initState() {
super.initState();
List<Person> rows = [
Person('Landon', 1),
Person('Sari', 0),
Person('Julian', 2),
Person('Carey', 4),
Person('Cadu', 5),
Person('Delmar', 2)
];
_model = DaviModel<Person>(rows: rows, columns: [
DaviColumn(name: 'Name', stringValue: (row) => row.name),
DaviColumn(name: 'Value', intValue: (row) => row.value),
DaviColumn(
name: 'Editable',
cellBuilder: _buildField,
cellBackground: (row) => row.data.valid ? null : Colors.red[800])
]);
}
Widget _buildField(BuildContext context, DaviRow<Person> row) {
return TextFormField(
initialValue: row.data.editable,
style: TextStyle(color: row.data.valid ? Colors.black : Colors.white),
onChanged: (value) => _onFieldChange(value, row.data));
}
void _onFieldChange(String value, Person person) {
final wasValid = person.valid;
person.editable = value;
if (wasValid != person.valid) {
setState(() {
// rebuild
});
}
}
@override
Widget build(BuildContext context) {
return Davi<Person>(_model);
}
}
copied to clipboard
Sort #
Multiple sort
DaviModel<Person>(
rows: rows,
columns: [
DaviColumn(name: 'Name', stringValue: (row) => row.name),
DaviColumn(name: 'Age', intValue: (row) => row.age),
DaviColumn(
name: 'Weight', width: 120, doubleValue: (row) => row.weight)
],
multiSortEnabled: true);
copied to clipboard
Sort callback
_model = DaviModel<Person>(
rows: rows,
columns: [
DaviColumn(name: 'Name', stringValue: (row) => row.name),
DaviColumn(name: 'Age', intValue: (row) => row.age)
],
onSort: _onSort);
copied to clipboard
void _onSort(List<DaviColumn<Person>> sortedColumns) {
...
}
copied to clipboard
Server-side sorting
Ignoring sorting functions from the model.
Simulating the server-side sorting when loading data.
class Person {
Person(this.name, this.age);
final String name;
final int age;
}
enum ColumnId { name, age }
class MainWidgetState extends State<MainWidget> {
late DaviModel<Person> _model;
bool _loading = true;
@override
void initState() {
super.initState();
_model = DaviModel<Person>(columns: [
DaviColumn(
id: ColumnId.name, name: 'Name', stringValue: (row) => row.name),
DaviColumn(id: ColumnId.age, name: 'Age', intValue: (row) => row.age)
], onSort: _onSort, ignoreDataComparators: true);
loadData();
}
void loadData([DaviSort? sort]) {
Future<List<Person>>.delayed(const Duration(seconds: 1), () {
List<Person> rows = [
Person('Linda', 33),
Person('Pamela', 22),
Person('Steven', 21),
Person('James', 37),
Person('Amanda', 43),
Person('Cadu', 35)
];
if (sort != null) {
final DaviSortDirection direction = sort.direction;
rows.sort((a, b) {
switch (sort.columnId) {
case ColumnId.name:
return direction == DaviSortDirection.ascending
? a.name.compareTo(b.name)
: b.name.compareTo(a.name);
case ColumnId.age:
return direction == DaviSortDirection.ascending
? a.age.compareTo(b.age)
: b.age.compareTo(a.age);
}
return 0;
});
}
return rows;
}).then((list) {
if (mounted) {
setState(() {
_loading = false;
_model.replaceRows(list);
});
}
});
}
void _onSort(List<DaviColumn<Person>> sortedColumns) {
setState(() {
_loading = true;
_model.removeRows();
});
loadData(sortedColumns.isNotEmpty ? sortedColumns.first.sort : null);
}
@override
Widget build(BuildContext context) {
return Davi(_model,
tapToSortEnabled: !_loading,
lastRowWidget:
_loading ? const Center(child: Text('Loading...')) : null);
}
}
copied to clipboard
Always sorted
Some sortable column will always be sorted.
_model = DaviModel<Person>(
rows: rows,
columns: [
DaviColumn(name: 'Name', stringValue: (row) => row.name),
DaviColumn(name: 'Age', intValue: (row) => row.age),
DaviColumn(
name: 'Weight', width: 120, doubleValue: (row) => row.weight)
],
alwaysSorted: true);
copied to clipboard
Theme #
Dividers thickness and color #
DaviTheme(
data: const DaviThemeData(
columnDividerThickness: 4,
columnDividerColor: Colors.blue,
header: HeaderThemeData(columnDividerColor: Colors.purple),
row: RowThemeData(dividerThickness: 4, dividerColor: Colors.green),
scrollbar:
TableScrollbarThemeData(columnDividerColor: Colors.orange)),
child: Davi<Person>(_model));
copied to clipboard
Header #
DaviTheme(
data: DaviThemeData(
header: HeaderThemeData(
color: Colors.green[50],
bottomBorderHeight: 4,
bottomBorderColor: Colors.blue),
headerCell: HeaderCellThemeData(
height: 40,
alignment: Alignment.center,
textStyle: const TextStyle(
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold,
color: Colors.blue),
resizeAreaWidth: 10,
resizeAreaHoverColor: Colors.blue.withOpacity(.5),
sortIconColors: SortIconColors.all(Colors.green),
expandableName: false)),
child: Davi<Person>(_model));
copied to clipboard
Hidden header
DaviTheme(
data: const DaviThemeData(header: HeaderThemeData(visible: false)),
child: Davi<Person>(_model));
copied to clipboard
Row #
Theme Row color
DaviTheme(
data: DaviThemeData(
row: RowThemeData(color: (rowIndex) => Colors.green[50])),
child: Davi<Person>(_model));
copied to clipboard
Row zebra color
DaviTheme(
data:
DaviThemeData(row: RowThemeData(color: RowThemeData.zebraColor())),
child: Davi<Person>(_model));
copied to clipboard
Row hover background
DaviTheme(
data: DaviThemeData(
row: RowThemeData(hoverBackground: (rowIndex) => Colors.blue[50])),
child: Davi<Person>(_model));
copied to clipboard
Row hover foreground
DaviTheme(
data: DaviThemeData(
row: RowThemeData(
hoverForeground: (rowIndex) => Colors.blue.withOpacity(.2))),
child: Davi<Person>(_model));
copied to clipboard
Row fill height
DaviTheme(
data: DaviThemeData(
row: RowThemeData(
fillHeight: true, color: RowThemeData.zebraColor())),
child: Davi<Person>(_model));
copied to clipboard
Scrollbar #
DaviTheme(
data: const DaviThemeData(
scrollbar: TableScrollbarThemeData(
thickness: 16,
thumbColor: Colors.black,
pinnedHorizontalColor: Colors.yellow,
unpinnedHorizontalColor: Colors.green,
verticalColor: Colors.blue,
borderThickness: 8,
pinnedHorizontalBorderColor: Colors.orange,
unpinnedHorizontalBorderColor: Colors.purple,
verticalBorderColor: Colors.pink)),
child: Davi<Person>(_model));
copied to clipboard
Scrollbar always visible
DaviTheme(
data: const DaviThemeData(
scrollbar: TableScrollbarThemeData(
horizontalOnlyWhenNeeded: false,
verticalOnlyWhenNeeded: false)),
child: Davi<Person>(_model));
copied to clipboard
Cell #
Null value color
_model = DaviModel<Person>(rows: [
Person('Landon', '+321 321-432-543'),
Person('Sari', '+123 456-789-012'),
Person('Julian', null),
Person('Carey', '+111 222-333-444'),
Person('Cadu', null),
Person('Delmar', '+22 222-222-222')
], columns: [
DaviColumn(name: 'Name', stringValue: (row) => row.name),
DaviColumn(name: 'Mobile', width: 150, stringValue: (row) => row.mobile)
]);
copied to clipboard
DaviTheme(
data: DaviThemeData(
cell: CellThemeData(
nullValueColor: ((rowIndex, hovered) => Colors.grey[300]))),
child: Davi<Person>(_model));
copied to clipboard
TODO #
Collapsed rows
Header grouping
Row selection
Column reorder
Cell merge
Pinned column on right
Filter
And everything else, the sky is the limit
Support this project #
Bitcoin #
bc1qhqy84y45gya58gtfkvrvass38k4mcyqnav803h
Ethereum (ERC-20) or Binance Smart Chain (BEP-20) #
0x9eB815FD4c88A53322304143A9Aa8733D3369985
Solana #
7vp45LoQXtLYFXXKx8wQGnzYmhcnKo1TmfqUgMX45Ad8
Helium #
13A2fDqoApT9VnoxFjHWcy8kPQgVFiVnzps32MRAdpTzvs3rq68
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.