Add scrollbars to tables

This commit is contained in:
Hillel Coren 2021-11-14 01:23:55 +02:00
parent 7c54561161
commit ae1b5bb63a
2 changed files with 107 additions and 84 deletions

View File

@ -8,6 +8,7 @@ import 'package:flutter/material.dart' hide DataRow, DataCell, DataColumn;
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/gestures.dart' show DragStartBehavior; import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'package:invoiceninja_flutter/ui/app/app_scrollbar.dart';
import 'package:invoiceninja_flutter/ui/app/tables/app_data_table.dart'; import 'package:invoiceninja_flutter/ui/app/tables/app_data_table.dart';
import 'package:invoiceninja_flutter/ui/app/tables/app_data_table_source.dart'; import 'package:invoiceninja_flutter/ui/app/tables/app_data_table_source.dart';
@ -223,11 +224,13 @@ class AppPaginatedDataTableState extends State<AppPaginatedDataTable> {
int _firstRowIndex; int _firstRowIndex;
int _rowCount; int _rowCount;
bool _rowCountApproximate; bool _rowCountApproximate;
ScrollController _controller;
final Map<int, DataRow> _rows = <int, DataRow>{}; final Map<int, DataRow> _rows = <int, DataRow>{};
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_controller = ScrollController();
_firstRowIndex = PageStorage.of(context)?.readState(context) as int ?? _firstRowIndex = PageStorage.of(context)?.readState(context) as int ??
widget.initialFirstRowIndex ?? widget.initialFirstRowIndex ??
0; 0;
@ -247,6 +250,7 @@ class AppPaginatedDataTableState extends State<AppPaginatedDataTable> {
@override @override
void dispose() { void dispose() {
_controller.dispose();
widget.source.removeListener(_handleDataSourceChanged); widget.source.removeListener(_handleDataSourceChanged);
super.dispose(); super.dispose();
} }
@ -470,23 +474,27 @@ class AppPaginatedDataTableState extends State<AppPaginatedDataTable> {
), ),
), ),
*/ */
SingleChildScrollView( Scrollbar(
scrollDirection: Axis.horizontal, controller: _controller,
dragStartBehavior: widget.dragStartBehavior, child: SingleChildScrollView(
child: ConstrainedBox( controller: _controller,
constraints: BoxConstraints(minWidth: constraints.minWidth), scrollDirection: Axis.horizontal,
child: AppDataTable( dragStartBehavior: widget.dragStartBehavior,
key: _tableKey, child: ConstrainedBox(
columns: widget.columns, constraints: BoxConstraints(minWidth: constraints.minWidth),
sortColumnIndex: widget.sortColumnIndex, child: AppDataTable(
sortAscending: widget.sortAscending, key: _tableKey,
onSelectAll: widget.onSelectAll, columns: widget.columns,
dataRowHeight: widget.dataRowHeight, sortColumnIndex: widget.sortColumnIndex,
headingRowHeight: widget.headingRowHeight, sortAscending: widget.sortAscending,
horizontalMargin: widget.horizontalMargin, onSelectAll: widget.onSelectAll,
columnSpacing: widget.columnSpacing, dataRowHeight: widget.dataRowHeight,
showCheckboxColumn: widget.showCheckboxColumn, headingRowHeight: widget.headingRowHeight,
rows: _getRows(_firstRowIndex, widget.rowsPerPage), horizontalMargin: widget.horizontalMargin,
columnSpacing: widget.columnSpacing,
showCheckboxColumn: widget.showCheckboxColumn,
rows: _getRows(_firstRowIndex, widget.rowsPerPage),
),
), ),
), ),
), ),

View File

@ -10,6 +10,7 @@ import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/ui/pref_state.dart'; import 'package:invoiceninja_flutter/redux/ui/pref_state.dart';
import 'package:invoiceninja_flutter/ui/app/app_border.dart'; import 'package:invoiceninja_flutter/ui/app/app_border.dart';
import 'package:invoiceninja_flutter/ui/app/app_scrollbar.dart';
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart'; import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart';
import 'package:invoiceninja_flutter/ui/app/forms/save_cancel_buttons.dart'; import 'package:invoiceninja_flutter/ui/app/forms/save_cancel_buttons.dart';
import 'package:invoiceninja_flutter/ui/app/help_text.dart'; import 'package:invoiceninja_flutter/ui/app/help_text.dart';
@ -60,11 +61,14 @@ class _EntityListState extends State<EntityList> {
EntityDataTableSource dataTableSource; EntityDataTableSource dataTableSource;
int _firstRowIndex = 0; int _firstRowIndex = 0;
ScrollController _controller;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_controller = ScrollController();
final entityType = widget.entityType; final entityType = widget.entityType;
final state = widget.state; final state = widget.state;
final entityList = widget.entityList; final entityList = widget.entityList;
@ -107,6 +111,12 @@ class _EntityListState extends State<EntityList> {
dataTableSource.notifyListeners(); dataTableSource.notifyListeners();
} }
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final store = StoreProvider.of<AppState>(context); final store = StoreProvider.of<AppState>(context);
@ -229,74 +239,79 @@ class _EntityListState extends State<EntityList> {
}, },
), ),
Expanded( Expanded(
child: SingleChildScrollView( child: AppScrollbar(
child: Padding( controller: _controller,
padding: const EdgeInsets.symmetric(vertical: 16), child: SingleChildScrollView(
child: AppPaginatedDataTable( controller: _controller,
onSelectAll: (value) { child: Padding(
final startIndex = padding: const EdgeInsets.symmetric(vertical: 16),
min(_firstRowIndex, entityList.length - 1); child: AppPaginatedDataTable(
final endIndex = onSelectAll: (value) {
min(_firstRowIndex + rowsPerPage, entityList.length); final startIndex =
final entities = entityList min(_firstRowIndex, entityList.length - 1);
.sublist(startIndex, endIndex) final endIndex = min(
.map<BaseEntity>( _firstRowIndex + rowsPerPage, entityList.length);
(String entityId) => entityMap[entityId]) final entities = entityList
.where((invoice) => .sublist(startIndex, endIndex)
value != listUIState.isSelected(invoice.id)) .map<BaseEntity>(
.toList(); (String entityId) => entityMap[entityId])
handleEntitiesActions( .where((invoice) =>
entities, EntityAction.toggleMultiselect); value != listUIState.isSelected(invoice.id))
}, .toList();
columns: [ handleEntitiesActions(
if (!isInMultiselect) DataColumn(label: SizedBox()), entities, EntityAction.toggleMultiselect);
...widget.tableColumns.map((field) { },
String label = columns: [
AppLocalization.of(context).lookup(field); if (!isInMultiselect) DataColumn(label: SizedBox()),
if (field.startsWith('custom')) { ...widget.tableColumns.map((field) {
final key = field.replaceFirst( String label =
'custom', entityType.snakeCase); AppLocalization.of(context).lookup(field);
label = state.company.getCustomFieldLabel(key); if (field.startsWith('custom')) {
} final key = field.replaceFirst(
var maxWidth = kTableColumnWidthMax; 'custom', entityType.snakeCase);
var minWidth = kTableColumnWidthMin; label = state.company.getCustomFieldLabel(key);
if (field == ProductFields.description) { }
maxWidth *= 5; var maxWidth = kTableColumnWidthMax;
minWidth *= 5; var minWidth = kTableColumnWidthMin;
} if (field == ProductFields.description) {
return DataColumn( maxWidth *= 5;
label: Container( minWidth *= 5;
constraints: BoxConstraints( }
minWidth: minWidth, return DataColumn(
maxWidth: maxWidth, label: Container(
constraints: BoxConstraints(
minWidth: minWidth,
maxWidth: maxWidth,
),
child: Text(
label,
overflow: TextOverflow.ellipsis,
),
), ),
child: Text( onSort: (int columnIndex, bool ascending) {
label, widget.onSortColumn(field);
overflow: TextOverflow.ellipsis, });
), }),
), ],
onSort: (int columnIndex, bool ascending) { source: dataTableSource,
widget.onSortColumn(field); sortColumnIndex: widget.tableColumns
}); .contains(listUIState.sortField)
}), ? widget.tableColumns.indexOf(listUIState.sortField)
], : 0,
source: dataTableSource, sortAscending: listUIState.sortAscending,
sortColumnIndex: rowsPerPage: state.prefState.rowsPerPage,
widget.tableColumns.contains(listUIState.sortField) onPageChanged: (row) => _firstRowIndex = row,
? widget.tableColumns.indexOf(listUIState.sortField) initialFirstRowIndex: _firstRowIndex,
: 0, availableRowsPerPage: [
sortAscending: listUIState.sortAscending, 10,
rowsPerPage: state.prefState.rowsPerPage, 25,
onPageChanged: (row) => _firstRowIndex = row, 50,
initialFirstRowIndex: _firstRowIndex, ],
availableRowsPerPage: [ onRowsPerPageChanged: (value) {
10, store.dispatch(
25, UpdateUserPreferences(rowsPerPage: value));
50, },
], ),
onRowsPerPageChanged: (value) {
store.dispatch(UpdateUserPreferences(rowsPerPage: value));
},
), ),
), ),
), ),