davi

Creator: coderz1093

Last updated:

Add to Cart

Description:

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

License

For personal and professional use. You cannot resell or redistribute these repositories in their original state.

Customer Reviews

There are no reviews.