diff --git a/CHANGELOG.md b/CHANGELOG.md index 92e21d8..165d7be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.3.0 +* Update + - `ExpansionTileGroup` with new parameters `keepState`. + - Avoid column main axis use all available space. Thanks for [#53](https://github.com/congthanhng/Expansion-Tile-Group/pull/53) + ## 2.2.0 * New Feature: - Only trailing can toggle the state: `isOnlyTrailingDoToggle` [#42](https://github.com/congthanhng/Expansion-Tile-Group/pull/42) diff --git a/doc/group-features.md b/doc/group-features.md index ee26c97..9877acb 100644 --- a/doc/group-features.md +++ b/doc/group-features.md @@ -77,13 +77,14 @@ class ExpansionGroupExample extends StatelessWidget { That's it! ### ExpansionTileGroup parameters -| Parameter | Description | -|--------------------------|--------------------------------------------------------------| -| `key` | Controls how one widget replaces another widget in the tree. | -| `children`* | The children in this group, `ExpansionTileItem` | -| `toggleType` | Provide the behaviors of items in this group, it's `enum` | -| `spaceBetweenItem` | The gap space between item in the group | -| `onExpansionItemChanged` | Listen the changed behavior of any item in the group | +| Parameter | Description | +|--------------------------|----------------------------------------------------------------------------------------| +| `key` | Controls how one widget replaces another widget in the tree. | +| `children`* | The children in this group, `ExpansionTileItem` | +| `toggleType` | Provide the behaviors of items in this group, it's `enum` | +| `spaceBetweenItem` | The gap space between item in the group | +| `keepState` | Will keep the state of the expansion tile whenever the parent's node widget is rebuilt | +| `onExpansionItemChanged` | Listen the changed behavior of any item in the group | ## Let implement with Option 2: Using `ExansionGroupController` Why do we have option 2 while option 1 work well? diff --git a/doc/item-features.md b/doc/item-features.md index 903cdbd..a8c3b0b 100644 --- a/doc/item-features.md +++ b/doc/item-features.md @@ -169,6 +169,7 @@ Now you can change default trailing icon by calling `trailingIcon`, it is retrie | `isEnableExpanded` | Force item expand or NOT | | `isDefaultVerticalPadding` | Remove completely default vertical title padding | | `isHideSubtitleOnExpanded` | Hide Subtitle when view is expanded | +| `isOnlyTrailingDoToggle` | Only trailing can toggle the state [#42](https://github.com/congthanhng/Expansion-Tile-Group/pull/42) | | `trailingIcon` | Change default trailing icon with keeping rotate animation | ## Additional ExpansionTileLeaf parameters diff --git a/example/README.md b/example/README.md index 29e2944..014c661 100644 --- a/example/README.md +++ b/example/README.md @@ -1,6 +1,6 @@ # example -A new Flutter project. +An example Flutter project to demonstrate the `expansion_tile_group` package. ## Getting Started @@ -25,3 +25,7 @@ dart pub global activate dhttpd dart pub global run dhttpd --path doc/api ``` ## Build demo web +flutter build web --release + +## Public package +dart pub publish --dry-run \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index caa2044..18d6159 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,6 +1,7 @@ import 'package:expansion_tile_group_example/route_named.dart'; import 'package:expansion_tile_group_example/use_cases/fantasy/fantasy_page.dart'; import 'package:expansion_tile_group_example/use_cases/group/custom_group_with_controller.dart'; +import 'package:expansion_tile_group_example/use_cases/group/keep_state_page.dart'; import 'package:flutter/material.dart'; import 'use_cases/use_cases.dart'; @@ -55,6 +56,9 @@ class _MyAppState extends State { builder: (context) => const CustomGroupWithController()); case RouteNamed.fantasyPage: return MaterialPageRoute(builder: (context) => const FantasyPage()); + case RouteNamed.keepStatePage: + return MaterialPageRoute( + builder: (context) => const KeepStatePage()); default: return MaterialPageRoute(builder: (context) => const HomePage()); } @@ -166,6 +170,12 @@ class HomePage extends StatelessWidget { routeName: RouteNamed.listenGroupItemChanged, ), const Divider(), + _buildRow( + context, + title: 'Keep state Example', + routeName: RouteNamed.keepStatePage, + ), + const Divider(), const Text( 'CUSTOM GROUP', style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold), diff --git a/example/lib/route_named.dart b/example/lib/route_named.dart index 3749ccf..7a459a8 100644 --- a/example/lib/route_named.dart +++ b/example/lib/route_named.dart @@ -12,4 +12,5 @@ class RouteNamed { static const String ignoreBehavior = '/ignore_behavior'; static const String hideSubtitle = '/hide_subtitle'; static const String fantasyPage = '/fantasy_page'; + static const String keepStatePage = '/keep_state_page'; } diff --git a/example/lib/use_cases/group/keep_state_page.dart b/example/lib/use_cases/group/keep_state_page.dart new file mode 100644 index 0000000..f4414f7 --- /dev/null +++ b/example/lib/use_cases/group/keep_state_page.dart @@ -0,0 +1,118 @@ +import 'package:expansion_tile_group/expansion_tile_group.dart'; +import 'package:flutter/material.dart'; + +class KeepStatePage extends StatefulWidget { + const KeepStatePage({super.key}); + + @override + State createState() => _KeepStatePageState(); +} + +class _KeepStatePageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Keep State Example'), + ), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Row( + children: [ + _buildGroupLayout( + context, + title: 'Group 1: keepState: true', + child: ExpansionTileGroup( + spaceBetweenItem: 16, + keepState: true, + children: [ + _buildExpansionItem(context), + _buildExpansionItem(context), + _buildExpansionItem(context), + ], + ), + ), + const SizedBox( + width: 16, + ), + _buildGroupLayout( + context, + title: 'Group 2: keepState: false', + child: ExpansionTileGroup( + spaceBetweenItem: 16, + keepState: false, + children: [ + _buildExpansionItem(context), + _buildExpansionItem(context), + _buildExpansionItem(context), + ], + ), + ), + ], + ), + const SizedBox( + height: 16, + ), + const Text( + 'Try to expand some items of each group and then tap to below button, you can see the different', + textAlign: TextAlign.center, + ), + const SizedBox( + height: 16, + ), + ElevatedButton( + onPressed: () { + setState(() {}); + }, + child: const Text('Rebuild this Page')), + ], + ), + ), + ), + ); + } + + ExpansionTileItem _buildExpansionItem(BuildContext context, + {bool isExpanded = false}) { + return ExpansionTileItem.outlined( + initiallyExpanded: isExpanded, + title: const Text('ExpansionTile Item'), + children: [ + Material( + child: InkWell( + onTap: () {}, + child: const Text( + ''' Nullam eleifend ultrices tortor, sit amet gravida sapien cursus vitae. Duis rutrum convallis erat et ultrices. Morbi a luctus ligula, at varius ligula. Nam mollis sapien ac nunc hendrerit consequat. Cras posuere metus felis, at pellentesque sem ornare id. Praesent ut nunc aliquam, dictum felis eu, congue metus. Nunc vitae elit eros. In eu dui pharetra, varius metus a, efficitur eros.'''), + ), + ), + ], + ); + } + + Widget _buildGroupLayout(BuildContext context, + {required String title, required Widget child}) { + return Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + title, + style: const TextStyle(fontSize: 20), + ), + const SizedBox( + height: 16, + ), + Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: child, + ), + ), + ], + ), + ); + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock index d117b10..7160ff8 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,42 +5,42 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" cupertino_icons: dependency: "direct main" description: @@ -60,10 +60,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" flutter: dependency: "direct main" description: flutter @@ -86,18 +86,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "10.0.8" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: @@ -118,10 +118,10 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -134,71 +134,71 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.16.0" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.4" vector_math: dependency: transitive description: @@ -211,10 +211,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.3.1" sdks: - dart: ">=3.3.0 <4.0.0" + dart: ">=3.7.0-0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/lib/src/core/expansion_tile_group.dart b/lib/src/core/expansion_tile_group.dart index 8df9bf4..e2d13a6 100644 --- a/lib/src/core/expansion_tile_group.dart +++ b/lib/src/core/expansion_tile_group.dart @@ -3,20 +3,33 @@ import 'package:expansion_tile_group/src/core/utils.dart'; import 'package:flutter/material.dart'; class ExpansionTileGroup extends StatefulWidget { - const ExpansionTileGroup( - {Key? key, - required this.children, - this.toggleType = ToggleType.none, - this.onItemChanged, - this.spaceBetweenItem = 0.0}) - : assert(spaceBetweenItem >= 0.0, + const ExpansionTileGroup({ + Key? key, + required this.children, + this.toggleType = ToggleType.none, + this.onItemChanged, + this.spaceBetweenItem = 0.0, + this.keepState = false, + }) : assert(spaceBetweenItem >= 0.0, '[Error] ExpansionTileGroup: The spaceBetweenItem must be >= 0'), super(key: key); + + /// The children of this group final List children; + + /// The type of toggle final ToggleType toggleType; + + /// The callback that is called when the state of the expansion tile is changed final Function(int, bool)? onItemChanged; + + /// The space between each expansion tile final double spaceBetweenItem; + /// If `true`, will keep the state of the expansion tile whenever the parent's node widget is rebuilt + /// By Default, it is `false` + final bool keepState; + @override State createState() => _ExpansionTileGroupState(); } @@ -68,6 +81,7 @@ class _ExpansionTileGroupState extends State { @override void didUpdateWidget(ExpansionTileGroup oldWidget) { + if (widget.keepState) return; _generateKey(isClear: true); _modifyChildren(isClear: true); super.didUpdateWidget(oldWidget); @@ -76,13 +90,18 @@ class _ExpansionTileGroupState extends State { @override Widget build(BuildContext context) { return Column( + mainAxisSize: MainAxisSize.min, children: [...modifiedChildren].separatedBy(SizedBox( height: widget.spaceBetweenItem, )), ); } - void _handleBehaviors(int index, bool isExpanded, bool isDefaultExpand) { + void _handleBehaviors( + int index, + bool isExpanded, + bool isDefaultExpand, + ) { switch (widget.toggleType) { case ToggleType.expandOnlyCurrent: _onExpandOne(index, isExpanded, isDefaultExpand); @@ -104,7 +123,11 @@ class _ExpansionTileGroupState extends State { } } - void _onExpandOne(int index, bool isExpanded, bool isDefaultExpand) { + void _onExpandOne( + int index, + bool isExpanded, + bool isDefaultExpand, + ) { _isTransforming = true; if (isExpanded) { keysMap[index]?.currentState?.expand(); @@ -124,7 +147,10 @@ class _ExpansionTileGroupState extends State { } void _onExpandAlwaysCurrent( - int index, bool isExpanded, bool isDefaultExpand) { + int index, + bool isExpanded, + bool isDefaultExpand, + ) { _isTransforming = true; keysMap[index]?.currentState?.expand(); for (int i = 0; i < keysMap.length; i++) { @@ -139,7 +165,11 @@ class _ExpansionTileGroupState extends State { _isTransforming = false; } - void _onCollapseAll(int index, bool isExpanded, bool isDefaultExpand) { + void _onCollapseAll( + int index, + bool isExpanded, + bool isDefaultExpand, + ) { _isTransforming = true; if (!isExpanded) { for (int i = 0; i < keysMap.length; i++) { @@ -149,7 +179,11 @@ class _ExpansionTileGroupState extends State { _isTransforming = false; } - void _onExpandAll(int index, bool isExpanded, bool isDefaultExpand) { + void _onExpandAll( + int index, + bool isExpanded, + bool isDefaultExpand, + ) { _isTransforming = true; if (isExpanded) { for (int i = 0; i < keysMap.length; i++) { @@ -159,7 +193,11 @@ class _ExpansionTileGroupState extends State { _isTransforming = false; } - void _onExpandAllOrCollapseAll(int index, bool isExpanded, bool keepState) { + void _onExpandAllOrCollapseAll( + int index, + bool isExpanded, + bool keepState, + ) { _isTransforming = true; if (isExpanded) { for (int i = 0; i < keysMap.length; i++) { diff --git a/pubspec.yaml b/pubspec.yaml index a885747..93eaad4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,7 +13,7 @@ topics: - widget - expansion-tile-group -version: 2.2.0 +version: 2.3.0 environment: sdk: '>=2.18.0 <4.0.0' flutter: ">=1.17.0"