diff --git a/CHANGELOG.md b/CHANGELOG.md index e5cc9dc..ef02bac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Flutter Expandable Table +## [2.1.0] - 2024-07-03 +#### [@RichiB20](https://github.com/RichiB20) +- Fixed vertical Scrollbar and added horizontal Scrollbar. +- Added `trackVisibilityScrollbar`,`thumbVisibilityScrollbar` and `expanded` parameters. +- Fixed table size. + ## [2.0.1] - 2023-09-04 #### [@rickypid](https://github.com/rickypid) - Update flutter_scroll_shadow dependency, add GitHub Actions. diff --git a/README.md b/README.md index 528fa5b..95ce6c2 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ Here is an example: ### ExpandableTable Properties * `firstHeaderCell`: Is the top left cell, i.e. the first header cell. * `headers`: contains the list of all column headers, each one of these can contain a list of further headers, this allows you to create nested and expandable columns. -* `rows`: ontains the list of all the rows of the table, each of these can contain a list of further rows, this allows you to create nested and expandable rows. +* `rows`: contains the list of all the rows of the table, each of these can contain a list of further rows, this allows you to create nested and expandable rows. * `headerHeight`: is the height of each column header, i.e. the first row. * `firstColumnWidth`: determines first Column width size. * `defaultsColumnWidth`: defines the default width of all columns, it is possible to redefine it for each individual column. @@ -103,7 +103,10 @@ Here is an example: * `scrollShadowFadeOutCurve`: determines rendered curve animation of shadows disappearance. * `scrollShadowColor`: determines rendered color of shadows. * `scrollShadowSize`: determines size of shadows. -* `visibleScrollbar`: determines visibility of scrollbar. +* `visibleScrollbar`: determines visibility of horizontal and vertical scrollbars. +* `trackVisibilityScrollbar`: indicates that the scrollbar track should be visible. +* `thumbVisibilityScrollbar`: indicates that the scrollbar thumb should be visible, even when a scroll is not underway. +* `expanded`: indicates that the table expands, so it fills the available space along the horizontal and vertical axes.   diff --git a/analysis_options.yaml b/analysis_options.yaml index 90dc9c3..598e178 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,9 +1,18 @@ include: package:flutter_lints/flutter.yaml -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options linter: rules: - public_member_api_docs + - recursive_getters + - always_use_package_imports + - prefer_expression_function_bodies + - prefer_single_quotes + - always_declare_return_types + - avoid_unused_constructor_parameters + - prefer_final_fields + - prefer_final_locals + - sort_pub_dependencies - type_annotate_public_apis - - recursive_getters \ No newline at end of file + - unawaited_futures + - use_named_constants + - use_super_parameters diff --git a/doc/.media/screenshot.png b/doc/.media/screenshot.png new file mode 100644 index 0000000..f0566ab Binary files /dev/null and b/doc/.media/screenshot.png differ diff --git a/example/README.md b/example/README.md index 5f6b7db..8dd735c 100644 --- a/example/README.md +++ b/example/README.md @@ -91,7 +91,7 @@ Here is an example: ### ExpandableTable Properties * `firstHeaderCell`: Is the top left cell, i.e. the first header cell. * `headers`: contains the list of all column headers, each one of these can contain a list of further headers, this allows you to create nested and expandable columns. -* `rows`: ontains the list of all the rows of the table, each of these can contain a list of further rows, this allows you to create nested and expandable rows. +* `rows`: contains the list of all the rows of the table, each of these can contain a list of further rows, this allows you to create nested and expandable rows. * `headerHeight`: is the height of each column header, i.e. the first row. * `firstColumnWidth`: determines first Column width size. * `defaultsColumnWidth`: defines the default width of all columns, it is possible to redefine it for each individual column. @@ -103,7 +103,10 @@ Here is an example: * `scrollShadowFadeOutCurve`: determines rendered curve animation of shadows disappearance. * `scrollShadowColor`: determines rendered color of shadows. * `scrollShadowSize`: determines size of shadows. -* `visibleScrollbar`: determines visibility of scrollbar. +* `visibleScrollbar`: determines visibility of horizontal and vertical scrollbars. +* `trackVisibilityScrollbar`: indicates that the scrollbar track should be visible. +* `thumbVisibilityScrollbar`: indicates that the scrollbar thumb should be visible, even when a scroll is not underway. +* `expanded`: indicates that the table expands, so it fills the available space along the horizontal and vertical axes.   diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index a5744c1..598e178 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -1,4 +1,18 @@ include: package:flutter_lints/flutter.yaml -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options +linter: + rules: + - public_member_api_docs + - recursive_getters + - always_use_package_imports + - prefer_expression_function_bodies + - prefer_single_quotes + - always_declare_return_types + - avoid_unused_constructor_parameters + - prefer_final_fields + - prefer_final_locals + - sort_pub_dependencies + - type_annotate_public_apis + - unawaited_futures + - use_named_constants + - use_super_parameters diff --git a/example/devtools_options.yaml b/example/devtools_options.yaml new file mode 100644 index 0000000..7e7e7f6 --- /dev/null +++ b/example/devtools_options.yaml @@ -0,0 +1 @@ +extensions: diff --git a/example/lib/main.dart b/example/lib/main.dart index b55fcde..b58c705 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -4,109 +4,102 @@ import 'package:flutter/material.dart'; // ignore: depend_on_referenced_packages import 'package:flutter_expandable_table/flutter_expandable_table.dart'; -const Color primaryColor = Color(0xFF1e2f36); //corner -const Color accentColor = Color(0xFF0d2026); //background -const TextStyle textStyle = TextStyle(color: Colors.white); -const TextStyle textStyleSubItems = TextStyle(color: Colors.grey); +const Color _primaryColor = Color(0xFF1e2f36); //corner +const Color _accentColor = Color(0xFF0d2026); //background +const TextStyle _textStyle = TextStyle(color: Colors.white); +// const TextStyle _textStyleSubItems = TextStyle(color: Colors.grey); -void main() => runApp(const MyApp()); +void main() => runApp(const _MyApp()); -class MyApp extends StatelessWidget { - const MyApp({super.key}); +class _MyApp extends StatelessWidget { + const _MyApp(); @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'ExpandableTable Example', - theme: ThemeData(primarySwatch: Colors.grey), - home: const MyHomePage(), - scrollBehavior: AppCustomScrollBehavior(), - ); - } + Widget build(BuildContext context) => MaterialApp( + title: 'ExpandableTable Example', + theme: ThemeData(primarySwatch: Colors.grey), + home: const _MyHomePage(), + scrollBehavior: _AppCustomScrollBehavior(), + ); } -class MyHomePage extends StatefulWidget { - const MyHomePage({Key? key}) : super(key: key); +class _MyHomePage extends StatefulWidget { + const _MyHomePage(); @override - State createState() => _MyHomePageState(); + State<_MyHomePage> createState() => _MyHomePageState(); } -class DefaultCellCard extends StatelessWidget { +class _DefaultCellCard extends StatelessWidget { final Widget child; - const DefaultCellCard({ - Key? key, + const _DefaultCellCard({ required this.child, - }) : super(key: key); + }); @override - Widget build(BuildContext context) { - return Container( - color: primaryColor, - margin: const EdgeInsets.all(1), - child: child, - ); - } + Widget build(BuildContext context) => Container( + color: _primaryColor, + margin: const EdgeInsets.all(1), + child: child, + ); } -class _MyHomePageState extends State { - _buildCell(String content, {CellBuilder? builder}) { - return ExpandableTableCell( - child: builder != null - ? null - : DefaultCellCard( - child: Center( - child: Text( - content, - style: textStyle, +class _MyHomePageState extends State<_MyHomePage> { + ExpandableTableCell _buildCell(String content, {CellBuilder? builder}) => + ExpandableTableCell( + child: builder != null + ? null + : _DefaultCellCard( + child: Center( + child: Text( + content, + style: _textStyle, + ), ), ), - ), - builder: builder, - ); - } - - ExpandableTableCell _buildFirstRowCell() { - return ExpandableTableCell( - builder: (context, details) => DefaultCellCard( - child: Padding( - padding: const EdgeInsets.only(left: 16.0), - child: Row( - children: [ - SizedBox( - width: 24 * details.row!.address.length.toDouble(), - child: details.row?.children != null - ? Align( - alignment: Alignment.centerRight, - child: AnimatedRotation( - duration: const Duration(milliseconds: 500), - turns: - details.row?.childrenExpanded == true ? 0.25 : 0, - child: const Icon( - Icons.keyboard_arrow_right, - color: Colors.white, + builder: builder, + ); + + ExpandableTableCell _buildFirstRowCell() => ExpandableTableCell( + builder: (context, details) => _DefaultCellCard( + child: Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Row( + children: [ + SizedBox( + width: 24 * details.row!.address.length.toDouble(), + child: details.row?.children != null + ? Align( + alignment: Alignment.centerRight, + child: AnimatedRotation( + duration: const Duration(milliseconds: 500), + turns: details.row?.childrenExpanded == true + ? 0.25 + : 0, + child: const Icon( + Icons.keyboard_arrow_right, + color: Colors.white, + ), ), - ), - ) - : null, - ), - Text( - '${details.row!.address.length > 1 ? details.row!.address.skip(1).map((e) => 'Sub ').join() : ''}Row ${details.row!.address.last}', - style: textStyle, - ), - ], + ) + : null, + ), + Text( + '${details.row!.address.length > 1 ? details.row!.address.skip(1).map((e) => 'Sub ').join() : ''}Row ${details.row!.address.last}', + style: _textStyle, + ), + ], + ), ), ), - ), - ); - } + ); ExpandableTable _buildSimpleTable() { const int columnsCount = 20; const int rowsCount = 20; //Creation header - List headers = List.generate( + final List headers = List.generate( columnsCount - 1, (index) => ExpandableTableHeader( width: index % 2 == 0 ? 200 : 150, @@ -114,7 +107,7 @@ class _MyHomePageState extends State { ), ); //Creation rows - List rows = List.generate( + final List rows = List.generate( rowsCount, (rowIndex) => ExpandableTableRow( height: rowIndex % 2 == 0 ? 50 : 70, @@ -129,8 +122,11 @@ class _MyHomePageState extends State { return ExpandableTable( firstHeaderCell: _buildCell('Simple\nTable'), headers: headers, - scrollShadowColor: accentColor, + scrollShadowColor: _accentColor, rows: rows, + visibleScrollbar: true, + trackVisibilityScrollbar: true, + thumbVisibilityScrollbar: true, ); } @@ -141,7 +137,7 @@ class _MyHomePageState extends State { static const int totalColumns = columnsCount + subColumnsCount; List _generateRows(int quantity, {int depth = 0}) { - bool generateLegendRow = (depth == 0 || depth == 2); + final bool generateLegendRow = (depth == 0 || depth == 2); return List.generate( quantity, (rowIndex) => ExpandableTableRow( @@ -156,14 +152,14 @@ class _MyHomePageState extends State { ) : null, legend: generateLegendRow && (rowIndex == 3 || rowIndex == 2) - ? const DefaultCellCard( + ? const _DefaultCellCard( child: Align( alignment: FractionalOffset.centerLeft, child: Padding( padding: EdgeInsets.only(left: 24.0), child: Text( 'This is row legend', - style: textStyle, + style: _textStyle, ), ), ), @@ -175,7 +171,7 @@ class _MyHomePageState extends State { ExpandableTable _buildExpandableTable() { //Creation header - List subHeader = List.generate( + final List subHeader = List.generate( subColumnsCount, (index) => ExpandableTableHeader( cell: _buildCell('Sub Column $index'), @@ -183,7 +179,7 @@ class _MyHomePageState extends State { ); //Creation header - List headers = List.generate( + final List headers = List.generate( columnsCount, (index) => ExpandableTableHeader( cell: _buildCell( @@ -198,42 +194,43 @@ class _MyHomePageState extends State { defaultsRowHeight: 60, defaultsColumnWidth: 150, firstColumnWidth: 250, - scrollShadowColor: accentColor, + scrollShadowColor: _accentColor, + visibleScrollbar: true, + expanded: false, ); } @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text( - ' Simple Table | Expandable Table'), - centerTitle: true, - ), - body: Container( - color: accentColor, - child: Row( - children: [ - Expanded( - child: Padding( - padding: const EdgeInsets.all(20.0), - child: _buildSimpleTable(), + Widget build(BuildContext context) => Scaffold( + appBar: AppBar( + title: const Text( + ' Simple Table | Expandable Table'), + centerTitle: true, + ), + body: Container( + color: _accentColor, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: _buildSimpleTable(), + ), ), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.all(20.0), - child: _buildExpandableTable(), + Expanded( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: _buildExpandableTable(), + ), ), - ), - ], + ], + ), ), - ), - ); - } + ); } -class AppCustomScrollBehavior extends MaterialScrollBehavior { +class _AppCustomScrollBehavior extends MaterialScrollBehavior { @override Set get dragDevices => { PointerDeviceKind.touch, diff --git a/example/pubspec.lock b/example/pubspec.lock index a0d8237..cc2052f 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: args - sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440" + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.2" async: dependency: transitive description: @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" fake_async: dependency: transitive description: @@ -68,23 +68,23 @@ packages: path: ".." relative: true source: path - version: "2.0.1" + version: "2.1.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "4.0.0" flutter_scroll_shadow: dependency: transitive description: name: flutter_scroll_shadow - sha256: a822a2ddd5dba4e76e8958b8d64e1a8436429a5236df93ce93f28d0968731aa4 + sha256: c0509c642c5077654301fab1fb2260adc94c82a407c60e64162974b4366e7874 url: "https://pub.dev" source: hosted - version: "1.2.3" + version: "1.2.4" flutter_test: dependency: "direct dev" description: flutter @@ -98,6 +98,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.6.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" linked_scroll_controller: dependency: transitive description: @@ -110,34 +134,34 @@ packages: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "4.0.0" matcher: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.12.0" nested: dependency: transitive description: @@ -150,18 +174,18 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" provider: dependency: transitive description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.2" sky_engine: dependency: transitive description: flutter @@ -179,18 +203,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -211,10 +235,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.7.0" tint: dependency: transitive description: @@ -231,22 +255,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "14.2.1" yaml: dependency: transitive description: name: yaml - sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" - flutter: ">=1.17.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 15c62a3..6997ceb 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_expandable_table_example description: A Flutter widget for create an expandable table with header and first column fixed. -version: 2.0.1 +version: 2.1.0 environment: sdk: ">=2.18.0 <4.0.0" @@ -10,11 +10,12 @@ dependencies: sdk: flutter dev_dependencies: + flutter_expandable_table: + path: ../ + flutter_lints: ^4.0.0 flutter_test: sdk: flutter - flutter_lints: ^2.0.1 import_sorter: ^4.6.0 - flutter_expandable_table: - path: ../ + flutter: uses-material-design: true \ No newline at end of file diff --git a/lib/src/class/controller.dart b/lib/src/class/controller.dart index 0db2eeb..e6d5077 100644 --- a/lib/src/class/controller.dart +++ b/lib/src/class/controller.dart @@ -11,6 +11,7 @@ class ExpandableTableController extends ChangeNotifier { /// [firstHeaderCell] is the top left cell, i.e. the first header cell. /// `required` ExpandableTableCell get firstHeaderCell => _firstHeaderCell; + set firstHeaderCell(ExpandableTableCell value) { _firstHeaderCell = value; notifyListeners(); @@ -90,7 +91,7 @@ class ExpandableTableController extends ChangeNotifier { bool _visibleScrollbar = false; - /// [visibleScrollbar] determines visibility of scrollbar. + /// [visibleScrollbar] determines visibility of horizontal and vertical scrollbars. /// /// Default: [false] bool get visibleScrollbar => _visibleScrollbar; @@ -100,6 +101,42 @@ class ExpandableTableController extends ChangeNotifier { notifyListeners(); } + bool? _trackVisibilityScrollbar; + + /// [trackVisibilityScrollbar] indicates that the scrollbar track should be visible. + /// + /// 'optional' + bool? get trackVisibilityScrollbar => _trackVisibilityScrollbar; + + set trackVisibilityScrollbar(bool? value) { + _trackVisibilityScrollbar = value; + notifyListeners(); + } + + bool? _thumbVisibilityScrollbar; + + /// [thumbVisibilityScrollbar] indicates that the scrollbar thumb should be visible, even when a scroll is not underway. + /// + /// 'optional' + bool? get thumbVisibilityScrollbar => _thumbVisibilityScrollbar; + + set thumbVisibilityScrollbar(bool? value) { + _thumbVisibilityScrollbar = value; + notifyListeners(); + } + + bool _expanded = true; + + /// [expanded] indicates that the table expands, so it fills the available space along the horizontal and vertical axes. + /// + /// Default: [true] + bool get expanded => _expanded; + + set expanded(bool value) { + _expanded = value; + notifyListeners(); + } + /// [duration] determines duration rendered animation of Rows/Columns expansion. /// /// Default: [500ms] @@ -147,6 +184,10 @@ class ExpandableTableController extends ChangeNotifier { required ExpandableTableCell firstHeaderCell, required List headers, required List rows, + bool visibleScrollbar = false, + bool? trackVisibilityScrollbar, + bool? thumbVisibilityScrollbar, + bool expanded = true, this.duration = const Duration(milliseconds: 500), this.curve = Curves.fastOutSlowIn, this.scrollShadowDuration = const Duration(milliseconds: 500), @@ -166,6 +207,10 @@ class ExpandableTableController extends ChangeNotifier { _defaultsRowHeight = defaultsRowHeight; _headers = headers; _rows = rows; + _visibleScrollbar = visibleScrollbar; + _trackVisibilityScrollbar = trackVisibilityScrollbar; + _thumbVisibilityScrollbar = thumbVisibilityScrollbar; + _expanded = expanded; _addHeadersListener(); _addRowsListener(); } @@ -203,7 +248,7 @@ class ExpandableTableController extends ChangeNotifier { super.dispose(); } - _listener() => notifyListeners(); + void _listener() => notifyListeners(); /// [allHeaders] returns all table headers, visible and not, including nested ones. List get allHeaders => _getAllHeaders(headers); @@ -219,7 +264,7 @@ class ExpandableTableController extends ChangeNotifier { List _getAllHeaders( List headers) { - List cells = []; + final List cells = []; for (var header in headers) { cells.add(header); if (header.children != null) { @@ -242,7 +287,7 @@ class ExpandableTableController extends ChangeNotifier { .fold(0, (a, b) => a + b); List _getAllRows(List rows) { - List rowsTmp = []; + final List rowsTmp = []; for (var row in rows) { rowsTmp.add(row); if (row.children != null) { diff --git a/lib/src/class/header.dart b/lib/src/class/header.dart index 2443d53..48c7ba0 100644 --- a/lib/src/class/header.dart +++ b/lib/src/class/header.dart @@ -109,7 +109,7 @@ class ExpandableTableHeader extends ChangeNotifier { super.dispose(); } - _listener() => notifyListeners(); + void _listener() => notifyListeners(); /// [columnsCount] returns the number of columns, this one and /// all those nested within it. diff --git a/lib/src/class/row.dart b/lib/src/class/row.dart index 50ca127..d5dcd1c 100644 --- a/lib/src/class/row.dart +++ b/lib/src/class/row.dart @@ -124,7 +124,7 @@ class ExpandableTableRow extends ChangeNotifier { super.dispose(); } - _listener() => notifyListeners(); + void _listener() => notifyListeners(); /// [rowsCount] returns the number of rows, this one and /// all those nested within it. diff --git a/lib/src/widget/table.dart b/lib/src/widget/table.dart index f40b429..8862f10 100644 --- a/lib/src/widget/table.dart +++ b/lib/src/widget/table.dart @@ -81,11 +81,26 @@ class ExpandableTable extends StatefulWidget { /// Default: [10] final double scrollShadowSize; - /// [visibleScrollbar] determines visibility of scrollbar. + /// [visibleScrollbar] determines visibility of horizontal and vertical scrollbars. /// /// Default: [false] final bool visibleScrollbar; + /// [trackVisibilityScrollbar] indicates that the scrollbar track should be visible. + /// + /// 'optional' + final bool? trackVisibilityScrollbar; + + /// [thumbVisibilityScrollbar] indicates that the scrollbar thumb should be visible, even when a scroll is not underway. + /// + /// 'optional' + final bool? thumbVisibilityScrollbar; + + /// [expanded] indicates that the table expands, so it fills the available space along the horizontal and vertical axes. + /// + /// Default: [true] + final bool expanded; + /// [controller] specifies the external controller of the table, allows /// you to dynamically manage the data in the table externally. /// Do not use if [firstHeaderCell], [headers] and [rows] are passed @@ -107,7 +122,7 @@ class ExpandableTable extends StatefulWidget { /// ); /// ``` const ExpandableTable({ - Key? key, + super.key, this.firstHeaderCell, this.headers, this.rows, @@ -123,9 +138,13 @@ class ExpandableTable extends StatefulWidget { this.scrollShadowColor = Colors.transparent, this.scrollShadowSize = 10, this.visibleScrollbar = false, - }) : assert((firstHeaderCell != null && rows != null && headers != null) || - controller != null), - super(key: key); + this.trackVisibilityScrollbar, + this.thumbVisibilityScrollbar, + this.expanded = true, + }) : assert(((firstHeaderCell != null && rows != null && headers != null) || + controller != null) && + !(thumbVisibilityScrollbar == false && + (trackVisibilityScrollbar ?? false))); @override State createState() => _ExpandableTableState(); @@ -135,13 +154,13 @@ class _ExpandableTableState extends State { @override void initState() { if (widget.controller == null) { - int totalColumns = + final int totalColumns = widget.headers!.map((e) => e.columnsCount).fold(0, (a, b) => a + b); for (int i = 0; i < widget.rows!.length; i++) { if (widget.rows![i].cellsCount != null && widget.rows![i].cellsCount != totalColumns) { throw FormatException( - "Row $i cells count ${widget.rows![i].cellsCount} <> $totalColumns header cell count."); + 'Row $i cells count ${widget.rows![i].cellsCount} <> $totalColumns header cell count.'); } } } @@ -149,29 +168,32 @@ class _ExpandableTableState extends State { } @override - Widget build(BuildContext context) { - return widget.controller != null - ? ChangeNotifierProvider.value( - value: widget.controller!, - builder: (context, child) => const InternalTable(), - ) - : ChangeNotifierProvider( - create: (context) => ExpandableTableController( - firstHeaderCell: widget.firstHeaderCell!, - headers: widget.headers!, - rows: widget.rows!, - duration: widget.duration, - curve: widget.curve, - scrollShadowDuration: widget.scrollShadowDuration, - scrollShadowFadeInCurve: widget.scrollShadowCurve, - scrollShadowColor: widget.scrollShadowColor, - scrollShadowSize: widget.scrollShadowSize, - firstColumnWidth: widget.firstColumnWidth, - defaultsColumnWidth: widget.defaultsColumnWidth, - defaultsRowHeight: widget.defaultsRowHeight, - headerHeight: widget.headerHeight, - ), - builder: (context, child) => const InternalTable(), - ); - } + Widget build(BuildContext context) => widget.controller != null + ? ChangeNotifierProvider.value( + value: widget.controller!, + builder: (context, child) => const InternalTable(), + ) + : ChangeNotifierProvider( + create: (context) => ExpandableTableController( + firstHeaderCell: widget.firstHeaderCell!, + headers: widget.headers!, + rows: widget.rows!, + duration: widget.duration, + curve: widget.curve, + scrollShadowDuration: widget.scrollShadowDuration, + scrollShadowFadeInCurve: widget.scrollShadowCurve, + scrollShadowFadeOutCurve: widget.scrollShadowCurve, + scrollShadowColor: widget.scrollShadowColor, + scrollShadowSize: widget.scrollShadowSize, + firstColumnWidth: widget.firstColumnWidth, + defaultsColumnWidth: widget.defaultsColumnWidth, + defaultsRowHeight: widget.defaultsRowHeight, + headerHeight: widget.headerHeight, + visibleScrollbar: widget.visibleScrollbar, + trackVisibilityScrollbar: widget.trackVisibilityScrollbar, + thumbVisibilityScrollbar: widget.thumbVisibilityScrollbar, + expanded: widget.expanded, + ), + builder: (context, child) => const InternalTable(), + ); } diff --git a/lib/src/widget_internal/cell.dart b/lib/src/widget_internal/cell.dart index d742e54..e3df99d 100644 --- a/lib/src/widget_internal/cell.dart +++ b/lib/src/widget_internal/cell.dart @@ -39,24 +39,22 @@ class ExpandableTableCellWidget extends StatelessWidget { }); @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onTap, - child: AnimatedContainer( - duration: context.watch().duration, - curve: context.watch().curve, - width: header?.visible == false ? 0 : width, - height: row?.visible == false ? 0 : height, - child: builder( - context, - CellDetails( - headerParent: header?.parent, - rowParent: row?.parent, - header: header, - row: row, + Widget build(BuildContext context) => GestureDetector( + onTap: onTap, + child: AnimatedContainer( + duration: context.watch().duration, + curve: context.watch().curve, + width: header?.visible == false ? 0 : width, + height: row?.visible == false ? 0 : height, + child: builder( + context, + CellDetails( + headerParent: header?.parent, + rowParent: row?.parent, + header: header, + row: row, + ), ), ), - ), - ); - } + ); } diff --git a/lib/src/widget_internal/table.dart b/lib/src/widget_internal/table.dart index 8935083..7e10356 100644 --- a/lib/src/widget_internal/table.dart +++ b/lib/src/widget_internal/table.dart @@ -14,8 +14,8 @@ import 'package:flutter_expandable_table/src/widget_internal/cell.dart'; class InternalTable extends StatefulWidget { /// [InternalTable] constructor. const InternalTable({ - Key? key, - }) : super(key: key); + super.key, + }); @override InternalTableState createState() => InternalTableState(); @@ -23,50 +23,49 @@ class InternalTable extends StatefulWidget { /// [InternalTable] state. class InternalTableState extends State { - late LinkedScrollControllerGroup _verticalLinkedControllers; - late ScrollController _headController; - late ScrollController _bodyController; late LinkedScrollControllerGroup _horizontalLinkedControllers; + late ScrollController _headController; + late ScrollController _horizontalBodyController; + late LinkedScrollControllerGroup _verticalLinkedControllers; late ScrollController _firstColumnController; late ScrollController _restColumnsController; @override void initState() { super.initState(); - _verticalLinkedControllers = LinkedScrollControllerGroup(); - _headController = _verticalLinkedControllers.addAndGet(); - _bodyController = _verticalLinkedControllers.addAndGet(); _horizontalLinkedControllers = LinkedScrollControllerGroup(); - _restColumnsController = _horizontalLinkedControllers.addAndGet(); - _firstColumnController = _horizontalLinkedControllers.addAndGet(); + _headController = _horizontalLinkedControllers.addAndGet(); + _horizontalBodyController = _horizontalLinkedControllers.addAndGet(); + _verticalLinkedControllers = LinkedScrollControllerGroup(); + _firstColumnController = _verticalLinkedControllers.addAndGet(); + _restColumnsController = _verticalLinkedControllers.addAndGet(); } @override void dispose() { _headController.dispose(); - _bodyController.dispose(); + _horizontalBodyController.dispose(); _restColumnsController.dispose(); _firstColumnController.dispose(); super.dispose(); } - List _buildHeaderCells(ExpandableTableController data) { - return data.allHeaders - .map( - (e) => ExpandableTableCellWidget( - height: data.headerHeight, - width: e.width ?? data.defaultsColumnWidth, - header: e, - onTap: () { - if (!e.disableDefaultOnTapExpansion) { - e.toggleExpand(); - } - }, - builder: e.cell.build, - ), - ) - .toList(); - } + List _buildHeaderCells(ExpandableTableController data) => + data.allHeaders + .map( + (e) => ExpandableTableCellWidget( + height: data.headerHeight, + width: e.width ?? data.defaultsColumnWidth, + header: e, + onTap: () { + if (!e.disableDefaultOnTapExpansion) { + e.toggleExpand(); + } + }, + builder: e.cell.build, + ), + ) + .toList(); Widget _buildRowCells( ExpandableTableController data, ExpandableTableRow row) { @@ -95,155 +94,205 @@ class InternalTableState extends State { } } - Widget _buildBody(ExpandableTableController data) { - return Row( - children: [ - SizedBox( - width: data.firstColumnWidth, - child: ScrollShadow( - size: data.scrollShadowSize, - color: data.scrollShadowColor, - fadeInCurve: data.scrollShadowFadeInCurve, - fadeOutCurve: data.scrollShadowFadeOutCurve, - duration: data.scrollShadowDuration, - child: Builder( - builder: (context) { - Widget child = ListView( - controller: _firstColumnController, - physics: const ClampingScrollPhysics(), - children: data.allRows - .map( - (e) => ChangeNotifierProvider.value( - value: e, - builder: (context, child) => - ExpandableTableCellWidget( - row: context.watch(), - height: - context.watch().height ?? - data.defaultsRowHeight, - width: data.firstColumnWidth, - builder: context - .watch() - .firstCell - .build, - onTap: () { - if (!e.disableDefaultOnTapExpansion) { - e.toggleExpand(); - } - }, - ), + Widget _buildBody(ExpandableTableController data) => Row( + children: [ + Builder( + builder: (context) { + final Widget child = ListView( + controller: _firstColumnController, + physics: const ClampingScrollPhysics(), + children: data.allRows + .map( + (e) => ChangeNotifierProvider.value( + value: e, + builder: (context, child) => ExpandableTableCellWidget( + row: context.watch(), + height: context.watch().height ?? + data.defaultsRowHeight, + width: data.firstColumnWidth, + builder: context + .watch() + .firstCell + .build, + onTap: () { + if (!e.disableDefaultOnTapExpansion) { + e.toggleExpand(); + } + }, ), - ) - .toList(), - ); - return data.visibleScrollbar - ? Scrollbar( - thumbVisibility: true, - controller: _firstColumnController, - child: child, - ) - : child; - }, - ), + ), + ) + .toList(), + ); + return SizedBox( + width: data.firstColumnWidth, + child: ScrollConfiguration( + behavior: ScrollConfiguration.of(context) + .copyWith(scrollbars: false), + child: ScrollShadow( + size: data.scrollShadowSize, + color: data.scrollShadowColor, + fadeInCurve: data.scrollShadowFadeInCurve, + fadeOutCurve: data.scrollShadowFadeOutCurve, + duration: data.scrollShadowDuration, + child: data.visibleScrollbar + ? Scrollbar( + controller: _firstColumnController, + thumbVisibility: data.thumbVisibilityScrollbar, + trackVisibility: data.trackVisibilityScrollbar, + scrollbarOrientation: ScrollbarOrientation.left, + child: child, + ) + : child, + ), + ), + ); + }, ), - ), - Expanded( - child: ScrollShadow( - size: data.scrollShadowSize, - color: data.scrollShadowColor, - fadeInCurve: data.scrollShadowFadeInCurve, - fadeOutCurve: data.scrollShadowFadeOutCurve, - duration: data.scrollShadowDuration, - child: SingleChildScrollView( - controller: _bodyController, - scrollDirection: Axis.horizontal, - physics: const ClampingScrollPhysics(), - child: AnimatedContainer( - width: data.visibleHeadersWidth, - duration: data.duration, - curve: data.curve, - child: ScrollShadow( - size: data.scrollShadowSize, - color: data.scrollShadowColor, - fadeInCurve: data.scrollShadowFadeInCurve, - fadeOutCurve: data.scrollShadowFadeOutCurve, - duration: data.scrollShadowDuration, - child: Builder( - builder: (context) { - Widget child = ListView( - controller: _restColumnsController, - physics: const ClampingScrollPhysics(), - children: data.allRows - .map( - (e) => _buildRowCells(data, e), - ) - .toList(), - ); - return data.visibleScrollbar - ? ScrollConfiguration( - behavior: ScrollConfiguration.of(context) - .copyWith(scrollbars: false), - child: child, - ) - : child; - }, + Builder( + builder: (context) { + final Widget child = SingleChildScrollView( + controller: _horizontalBodyController, + scrollDirection: Axis.horizontal, + physics: const ClampingScrollPhysics(), + child: AnimatedContainer( + width: data.visibleHeadersWidth, + duration: data.duration, + curve: data.curve, + child: ScrollShadow( + size: data.scrollShadowSize, + color: data.scrollShadowColor, + fadeInCurve: data.scrollShadowFadeInCurve, + fadeOutCurve: data.scrollShadowFadeOutCurve, + duration: data.scrollShadowDuration, + child: ListView( + controller: _restColumnsController, + physics: const ClampingScrollPhysics(), + children: data.allRows + .map( + (e) => _buildRowCells(data, e), + ) + .toList(), + ), ), ), - ), - ), + ); + + return Expanded( + child: ScrollConfiguration( + behavior: ScrollConfiguration.of(context) + .copyWith(scrollbars: false), + child: ScrollShadow( + size: data.scrollShadowSize, + color: data.scrollShadowColor, + fadeInCurve: data.scrollShadowFadeInCurve, + fadeOutCurve: data.scrollShadowFadeOutCurve, + duration: data.scrollShadowDuration, + child: data.visibleScrollbar + ? Scrollbar( + controller: _horizontalBodyController, + thumbVisibility: data.thumbVisibilityScrollbar, + trackVisibility: data.trackVisibilityScrollbar, + child: child, + ) + : child, + ), + ), + ); + }, ), - ), - ], - ); - } + ], + ); + + double _computeTableWidth({required ExpandableTableController data}) => + data.firstColumnWidth + + (data.headers + .map((e) => + (e.width ?? data.defaultsColumnWidth) + + _computeChildrenWidth( + expandableTableHeader: e, + defaultsColumnWidth: data.defaultsColumnWidth)) + .reduce((value, element) => value + element)); + + double _computeTableHeight({required ExpandableTableController data}) => + data.headerHeight + + (data.rows + .map((e) => + (e.height ?? data.defaultsRowHeight) + + _computeChildrenHeight( + expandableTableRow: e, + defaultsRowHeight: data.defaultsRowHeight)) + .reduce((value, element) => value + element)); + + double _computeChildrenHeight({ + required ExpandableTableRow expandableTableRow, + required double defaultsRowHeight, + }) => + expandableTableRow.childrenExpanded + ? expandableTableRow.children! + .map((e) => + (e.height ?? defaultsRowHeight) + + _computeChildrenHeight( + expandableTableRow: e, + defaultsRowHeight: defaultsRowHeight)) + .reduce((value, element) => value + element) + : 0; + + double _computeChildrenWidth({ + required ExpandableTableHeader expandableTableHeader, + required double defaultsColumnWidth, + }) => + expandableTableHeader.childrenExpanded + ? expandableTableHeader.children! + .map((e) => + (e.width ?? defaultsColumnWidth) + + _computeChildrenWidth( + expandableTableHeader: e, + defaultsColumnWidth: defaultsColumnWidth)) + .reduce((value, element) => value + element) + : 0; @override Widget build(BuildContext context) { - ExpandableTableController data = context.watch(); - return Column( - children: [ - SizedBox( - height: data.headerHeight, - child: Row( - children: [ - ExpandableTableCellWidget( - height: data.headerHeight, - width: data.firstColumnWidth, - builder: data.firstHeaderCell.build, - ), - Expanded( - child: ScrollShadow( - size: data.scrollShadowSize, - color: data.scrollShadowColor, - fadeInCurve: data.scrollShadowFadeInCurve, - fadeOutCurve: data.scrollShadowFadeOutCurve, - duration: data.scrollShadowDuration, - child: Builder( - builder: (context) { - Widget child = ListView( - controller: _headController, - physics: const ClampingScrollPhysics(), - scrollDirection: Axis.horizontal, - children: _buildHeaderCells(data), - ); - return data.visibleScrollbar - ? Scrollbar( - thumbVisibility: true, - controller: _headController, - child: child, - ) - : child; - }, + final ExpandableTableController data = + context.watch(); + return SizedBox( + width: data.expanded ? null : _computeTableWidth(data: data), + height: data.expanded ? null : _computeTableHeight(data: data), + child: Column( + children: [ + SizedBox( + height: data.headerHeight, + child: Row( + children: [ + ExpandableTableCellWidget( + height: data.headerHeight, + width: data.firstColumnWidth, + builder: data.firstHeaderCell.build, + ), + Expanded( + child: ScrollShadow( + size: data.scrollShadowSize, + color: data.scrollShadowColor, + fadeInCurve: data.scrollShadowFadeInCurve, + fadeOutCurve: data.scrollShadowFadeOutCurve, + duration: data.scrollShadowDuration, + child: ListView( + controller: _headController, + physics: const ClampingScrollPhysics(), + scrollDirection: Axis.horizontal, + children: _buildHeaderCells(data), + ), ), ), - ), - ], + ], + ), + ), + Expanded( + child: _buildBody(data), ), - ), - Expanded( - child: _buildBody(data), - ), - ], + ], + ), ); } } diff --git a/pubspec.lock b/pubspec.lock index 94fa03f..7510d84 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: args - sha256: c372bb384f273f0c2a8aaaa226dad84dc27c8519a691b888725dec59518ad53a + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" async: dependency: transitive description: @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" fake_async: dependency: transitive description: @@ -66,18 +66,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "4.0.0" flutter_scroll_shadow: dependency: "direct main" description: name: flutter_scroll_shadow - sha256: a822a2ddd5dba4e76e8958b8d64e1a8436429a5236df93ce93f28d0968731aa4 + sha256: c0509c642c5077654301fab1fb2260adc94c82a407c60e64162974b4366e7874 url: "https://pub.dev" source: hosted - version: "1.2.3" + version: "1.2.4" flutter_test: dependency: "direct dev" description: flutter @@ -91,6 +91,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.6.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" linked_scroll_controller: dependency: "direct main" description: @@ -103,34 +127,34 @@ packages: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "4.0.0" matcher: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.11.0" nested: dependency: transitive description: @@ -143,18 +167,18 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" provider: dependency: "direct main" description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.2" sky_engine: dependency: transitive description: flutter @@ -172,18 +196,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -204,10 +228,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" tint: dependency: transitive description: @@ -224,14 +248,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "13.0.0" yaml: dependency: transitive description: @@ -241,5 +265,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0-0 <4.0.0" flutter: ">=1.17.0" diff --git a/pubspec.yaml b/pubspec.yaml index 13e8ecf..0e19270 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_expandable_table description: A Flutter widget for create an expandable table with header and first column fixed. -version: 2.0.1 +version: 2.1.0 repository: https://github.com/rickypid/flutter_expandable_table issue_tracker: https://github.com/rickypid/flutter_expandable_table/issues @@ -11,14 +11,17 @@ environment: dependencies: flutter: sdk: flutter + flutter_scroll_shadow: ^1.2.4 linked_scroll_controller: ^0.2.0 - flutter_scroll_shadow: ^1.2.3 - provider: ^6.0.5 + provider: ^6.1.2 dev_dependencies: + flutter_lints: ^4.0.0 flutter_test: sdk: flutter - flutter_lints: ^2.0.1 import_sorter: ^4.6.0 -flutter: \ No newline at end of file +flutter: +screenshots: + - description: 'Expandable Table - screenshot 1' + path: doc/.media/screenshot.png \ No newline at end of file diff --git a/test/my_app_test.dart b/test/my_app_test.dart index 8a88059..08ece91 100644 --- a/test/my_app_test.dart +++ b/test/my_app_test.dart @@ -18,18 +18,16 @@ class MyApp extends StatelessWidget { const MyApp({super.key}); @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'ExpandableTable Example', - theme: ThemeData(primarySwatch: Colors.grey), - home: const MyHomePage(), - scrollBehavior: AppCustomScrollBehavior(), - ); - } + Widget build(BuildContext context) => MaterialApp( + title: 'ExpandableTable Example', + theme: ThemeData(primarySwatch: Colors.grey), + home: const MyHomePage(), + scrollBehavior: AppCustomScrollBehavior(), + ); } class MyHomePage extends StatefulWidget { - const MyHomePage({Key? key}) : super(key: key); + const MyHomePage({super.key}); @override State createState() => _MyHomePageState(); @@ -39,77 +37,73 @@ class DefaultCellCard extends StatelessWidget { final Widget child; const DefaultCellCard({ - Key? key, + super.key, required this.child, - }) : super(key: key); + }); @override - Widget build(BuildContext context) { - return Container( - color: primaryColor, - margin: const EdgeInsets.all(1), - child: child, - ); - } + Widget build(BuildContext context) => Container( + color: primaryColor, + margin: const EdgeInsets.all(1), + child: child, + ); } class _MyHomePageState extends State { - _buildCell(String content, {CellBuilder? builder}) { - return ExpandableTableCell( - child: builder != null - ? null - : DefaultCellCard( - child: Center( - child: Text( - content, - style: textStyle, + ExpandableTableCell _buildCell(String content, {CellBuilder? builder}) => + ExpandableTableCell( + child: builder != null + ? null + : DefaultCellCard( + child: Center( + child: Text( + content, + style: textStyle, + ), ), ), - ), - builder: builder, - ); - } - - ExpandableTableCell _buildFirstRowCell() { - return ExpandableTableCell( - builder: (context, details) => DefaultCellCard( - child: Padding( - padding: const EdgeInsets.only(left: 16.0), - child: Row( - children: [ - SizedBox( - width: 24 * details.row!.address.length.toDouble(), - child: details.row?.children != null - ? Align( - alignment: Alignment.centerRight, - child: AnimatedRotation( - duration: const Duration(milliseconds: 500), - turns: - details.row?.childrenExpanded == true ? 0.25 : 0, - child: const Icon( - Icons.keyboard_arrow_right, - color: Colors.white, + builder: builder, + ); + + ExpandableTableCell _buildFirstRowCell() => ExpandableTableCell( + builder: (context, details) => DefaultCellCard( + child: Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Row( + children: [ + SizedBox( + width: 24 * details.row!.address.length.toDouble(), + child: details.row?.children != null + ? Align( + alignment: Alignment.centerRight, + child: AnimatedRotation( + duration: const Duration(milliseconds: 500), + turns: details.row?.childrenExpanded == true + ? 0.25 + : 0, + child: const Icon( + Icons.keyboard_arrow_right, + color: Colors.white, + ), ), - ), - ) - : null, - ), - Text( - '${details.row!.address.length > 1 ? details.row!.address.skip(1).map((e) => '&Sub ').join() : ''}&Row ${details.row!.address.last}', - style: textStyle, - ), - ], + ) + : null, + ), + Text( + '${details.row!.address.length > 1 ? details.row!.address.skip(1).map((e) => '&Sub ').join() : ''}&Row ${details.row!.address.last}', + style: textStyle, + ), + ], + ), ), ), - ), - ); - } + ); ExpandableTable _buildSimpleTable() { const int columnsCount = 20; const int rowsCount = 20; //Creation header - List headers = List.generate( + final List headers = List.generate( columnsCount - 1, (index) => ExpandableTableHeader( width: index % 2 == 0 ? 200 : 150, @@ -117,7 +111,7 @@ class _MyHomePageState extends State { ), ); //Creation rows - List rows = List.generate( + final List rows = List.generate( rowsCount, (rowIndex) => ExpandableTableRow( height: rowIndex % 2 == 0 ? 50 : 70, @@ -144,7 +138,7 @@ class _MyHomePageState extends State { static const int totalColumns = columnsCount + subColumnsCount; List _generateRows(int quantity, {int depth = 0}) { - bool generateLegendRow = (depth == 0 || depth == 2); + final bool generateLegendRow = (depth == 0 || depth == 2); return List.generate( quantity, (rowIndex) => ExpandableTableRow( @@ -178,7 +172,7 @@ class _MyHomePageState extends State { ExpandableTable _buildExpandableTable() { //Creation header - List subHeader = List.generate( + final List subHeader = List.generate( subColumnsCount, (index) => ExpandableTableHeader( cell: _buildCell('&Sub Column $index'), @@ -186,7 +180,7 @@ class _MyHomePageState extends State { ); //Creation header - List headers = List.generate( + final List headers = List.generate( columnsCount, (index) => ExpandableTableHeader( cell: _buildCell( @@ -206,34 +200,32 @@ class _MyHomePageState extends State { } @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text( - ' Simple Table | Expandable Table'), - centerTitle: true, - ), - body: Container( - color: accentColor, - child: Row( - children: [ - Expanded( - child: Padding( - padding: const EdgeInsets.all(20.0), - child: _buildSimpleTable(), + Widget build(BuildContext context) => Scaffold( + appBar: AppBar( + title: const Text( + ' Simple Table | Expandable Table'), + centerTitle: true, + ), + body: Container( + color: accentColor, + child: Row( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: _buildSimpleTable(), + ), ), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.all(20.0), - child: _buildExpandableTable(), + Expanded( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: _buildExpandableTable(), + ), ), - ), - ], + ], + ), ), - ), - ); - } + ); } class AppCustomScrollBehavior extends MaterialScrollBehavior {