diff --git a/example/lib/main.dart b/example/lib/main.dart index d9e8a8d..00f05ed 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -49,11 +49,12 @@ class _DataPageState extends State { List> _selecteds = []; // ignore: unused_field String _selectableKey = "id"; - + String _sortText = "SORT BY"; String? _sortColumn; bool _sortAscending = true; bool _isLoading = true; bool _showSelect = true; + bool _showSort = true; var random = new Random(); List> _generateData({int n: 100}) { @@ -100,7 +101,8 @@ class _DataPageState extends State { _resetData({start: 0}) async { setState(() => _isLoading = true); - var _expandedLen = _total - start < _currentPerPage! ? _total - start : _currentPerPage; + 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(); @@ -116,7 +118,12 @@ class _DataPageState extends State { if (value == "" || value == null) { _sourceFiltered = _sourceOriginal; } else { - _sourceFiltered = _sourceOriginal.where((data) => data[_searchKey!].toString().toLowerCase().contains(value.toString().toLowerCase())).toList(); + _sourceFiltered = _sourceOriginal + .where((data) => data[_searchKey!] + .toString() + .toLowerCase() + .contains(value.toString().toLowerCase())) + .toList(); } _total = _sourceFiltered.length; @@ -142,11 +149,22 @@ class _DataPageState extends State { sortable: true, textAlign: TextAlign.center, format: DataTableFormat.list, - items: ['1dasdasdasdasdasdasdasdasdas asdasdasd adasdasd asdasdasdas', '2'], + items: [ + '1dasdasdasdasdasdasdasdasdas asdasdasd adasdasd asdasdasdas', + '2' + ], editable: true, ), DatatableHeader( - text: "Name", value: "name", show: true, flex: 2, sortable: true, editable: true, textAlign: TextAlign.left, format: DataTableFormat.number, textInputFormatter: []), + text: "Name", + value: "name", + show: true, + flex: 2, + sortable: true, + editable: true, + textAlign: TextAlign.left, + format: DataTableFormat.number, + textInputFormatter: []), DatatableHeader( text: "SKU", value: "sku", @@ -156,11 +174,36 @@ class _DataPageState extends State { editable: true, format: DataTableFormat.number, ), - 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: "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", @@ -222,185 +265,206 @@ class _DataPageState extends State { ), ), body: SingleChildScrollView( - child: Column(mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [ - Container( - margin: const EdgeInsets.all(10), - padding: const EdgeInsets.all(0), - constraints: const BoxConstraints( - maxHeight: 700, - ), - child: Card( - elevation: 1, - shadowColor: Colors.black, - clipBehavior: Clip.none, - child: XDataTable( - timeToSubtract: const Duration(days: 6), - title: TextButton.icon( - onPressed: () => {}, - icon: const Icon(Icons.add), - label: const Text("new item"), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Container( + margin: const EdgeInsets.all(10), + padding: const EdgeInsets.all(0), + constraints: const BoxConstraints( + maxHeight: 700, ), - reponseScreenSizes: [ScreenSize.xs], - actions: [ - if (_isSearch) - Expanded( - child: TextField( - decoration: InputDecoration( - hintText: 'Enter search term based on ${_searchKey!.replaceAll(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); + child: Card( + elevation: 1, + shadowColor: Colors.black, + clipBehavior: Clip.none, + child: XDataTable( + timeToSubtract: const Duration(days: 6), + title: TextButton.icon( + onPressed: () => {}, + icon: const Icon(Icons.add), + label: const Text("new item"), + ), + reponseScreenSizes: [ScreenSize.xs], + actions: [ + if (_isSearch) + Expanded( + child: TextField( + decoration: InputDecoration( + hintText: + 'Enter search term based on ${_searchKey!.replaceAll(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, + showSort: _showSort, + sortText: _sortText, + 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; + 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( - value: _currentPerPage, - items: _perPages - .map((e) => DropdownMenuItem( - child: Text("$e"), - value: e, - )) - .toList(), - onChanged: (dynamic value) { - setState(() { - _currentPerPage = value; - _currentPage = 1; - _resetData(); - }); - }, - isExpanded: false, + _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( + value: _currentPerPage, + items: _perPages + .map((e) => DropdownMenuItem( + 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, ), - 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), + headerTextStyle: TextStyle(color: Colors.white), + rowTextStyle: TextStyle(color: Colors.green), + selectedTextStyle: TextStyle(color: Colors.white), ), - 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), ), - ), - ), - ])), + ])), ); } } diff --git a/lib/src/flutter_datatable.dart b/lib/src/flutter_datatable.dart index e9cc6dd..3565cc9 100644 --- a/lib/src/flutter_datatable.dart +++ b/lib/src/flutter_datatable.dart @@ -23,7 +23,8 @@ class XDataTable extends StatefulWidget { final bool hideUnderline; final bool commonMobileView; final bool isExpandRows; - + final String? sortText; + final bool showSort; final List? expanded; final Widget Function(Map value)? dropContainer; final Function(Map value, DatatableHeader header)? @@ -78,6 +79,8 @@ class XDataTable extends StatefulWidget { this.onSelect, this.onTabRow, this.onSort, + this.sortText = 'SORT BY', + this.showSort = false, this.headers = const [], this.source, this.selecteds, @@ -114,52 +117,59 @@ class XDataTable extends StatefulWidget { } class _XDataTableState extends State { - Widget mobileHeader() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Checkbox( - value: widget.selecteds!.length == widget.source!.length && - widget.source != null && - widget.source!.isNotEmpty, - onChanged: (value) { - if (widget.onSelectAll != null) widget.onSelectAll!(value); - }, - ), - PopupMenuButton( - tooltip: "SORT BY", - initialValue: widget.sortColumn, - itemBuilder: (_) => widget.headers - .where((header) => header.show == true && header.sortable == true) - .toList() - .map((header) => PopupMenuItem( - value: header.value, - child: Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Text( - header.text, - textAlign: header.textAlign, + Widget mobileHeader({required showSelect, required showSort}) { + if (showSelect || showSort) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (showSelect) + Checkbox( + value: widget.selecteds!.length == widget.source!.length && + widget.source != null && + widget.source!.isNotEmpty, + onChanged: (value) { + if (widget.onSelectAll != null) widget.onSelectAll!(value); + }, + ), + if (showSort) + PopupMenuButton( + tooltip: widget.sortText, + initialValue: widget.sortColumn, + itemBuilder: (_) => widget.headers + .where((header) => + header.show == true && header.sortable == true) + .toList() + .map((header) => PopupMenuItem( + value: header.value, + child: Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Text( + header.text, + textAlign: header.textAlign, + ), + if (widget.sortColumn != null && + widget.sortColumn == header.value) + widget.sortAscending! + ? const Icon(Icons.arrow_downward, size: 15) + : const Icon(Icons.arrow_upward, size: 15) + ], ), - if (widget.sortColumn != null && - widget.sortColumn == header.value) - widget.sortAscending! - ? const Icon(Icons.arrow_downward, size: 15) - : const Icon(Icons.arrow_upward, size: 15) - ], - ), - )) - .toList(), - onSelected: (dynamic value) { - if (widget.onSort != null) widget.onSort!(value); - }, - child: Container( - padding: const EdgeInsets.all(15), - child: const Text("SORT BY"), - ), - ) - ], - ); + )) + .toList(), + onSelected: (dynamic value) { + if (widget.onSort != null) widget.onSort!(value); + }, + child: Container( + padding: const EdgeInsets.all(15), + child: Text(widget.sortText ?? "SORT BY"), + ), + ) + ], + ); + } else { + return const SizedBox.shrink(); + } } List mobileList() { @@ -229,7 +239,7 @@ class _XDataTableState extends State { : Expanded( child: Text( "${data[header.value]}", - textAlign: header.textAlign, + textAlign: TextAlign.end, style: widget.selecteds!.contains(data) ? widget.selectedTextStyle @@ -428,8 +438,10 @@ class _XDataTableState extends State { if (widget.autoHeight) Column( children: [ - if (widget.showSelect && widget.selecteds != null) - mobileHeader(), + mobileHeader( + showSelect: + (widget.showSelect && widget.selecteds != null), + showSort: (widget.showSort && widget.onSort != null)), if (widget.isLoading) const LinearProgressIndicator(), ...mobileList(), ], @@ -439,8 +451,10 @@ class _XDataTableState extends State { child: ListView( /// itemCount: source.length, children: [ - if (widget.showSelect && widget.selecteds != null) - mobileHeader(), + mobileHeader( + showSelect: + (widget.showSelect && widget.selecteds != null), + showSort: (widget.showSort && widget.onSort != null)), if (widget.isLoading) const LinearProgressIndicator(), /// mobileList