From 0aa67cd646b2b75868f05d962b2b48af5716fbf1 Mon Sep 17 00:00:00 2001
From: congthanhng <“thanhcong.uit@gmail.com”>
Date: Sun, 22 Sep 2024 11:34:24 +0700
Subject: [PATCH] release 2.1.0
---
README.md | 20 +-
doc/group-features.md | 215 ++++++++++++------
example/README.md | 11 +
example/lib/main.dart | 8 +-
example/lib/route_named.dart | 3 +-
.../group/custom_group_with_controller.dart | 11 +-
example/pubspec.lock | 2 +-
lib/src/core/expansion_group_controller.dart | 9 +-
8 files changed, 193 insertions(+), 86 deletions(-)
diff --git a/README.md b/README.md
index 5d25c55..a9bd294 100644
--- a/README.md
+++ b/README.md
@@ -29,20 +29,20 @@ Migrate with [CHANGE LOG](https://pub.dev/packages/expansion_tile_group/changelo
## Features
Item features:
-* `ExpansionTileItem` fully `extends` standard [ExpansionTile](https://api.flutter.dev/flutter/material/ExpansionTile-class.html) functionality.
-* Supported many commonly type of `ExpansionTileItem` UIs: `ExpansionTileOutlined`, `ExpansionTileFlat`, `ExpansionTileLeaf`, `ExpansionTileCard`.
-* Can easily control behaviors of an `ExpansionTileItem` from anywhere.
+* `ExpansionTileItem` fully `extends` standard of [ExpansionTile](https://api.flutter.dev/flutter/material/ExpansionTile-class.html) functionality.
+* Supported many commonly types of `ExpansionTileItem` UIs: `ExpansionTileOutlined`, `ExpansionTileFlat`, `ExpansionTileLeaf`, `ExpansionTileCard`.
+* Can easily control behaviors of an `ExpansionTileItem` from anywhere (expand or collapse from anywhere).
* Can easily custom or add decoration into an `ExpansionTileItem` with `Border`, `BorderRadius`, `Shadow`, or `BoxDecoration` and more.
-* Can lock to extend behavior of an `ExpansionTileItem` until a task is completed.
-* Can remove completely the `trailing`, included: area, arrow icon. With that the `title` can be extended the width.
-* Can remove completely default vertical title padding of ExpansionTile.
-* Can hide the subtitle of an `ExpansionTileItem` whenever extend the view.
-* Can change the default Trailing Icon with keeping default rotate animation.
+* Can prevent an `ExpansionTileItem` expand until a task is completed.
+* Can remove completely the `trailing` included: area, arrow icon that `ExpansionTile` can NOT. With that the `title` can be extended the fully width.
+* Can remove completely default title's vertical padding of an `ExpansionTile`.
+* Can hide the subtitle of an `ExpansionTileItem` whenever it expanded.
+* Can change the default Trailing Icon to a custom widget with keeping default rotate animation.
Group features:
* Can group `ExpansionTileItem`s together and manage them.
-* Can manage interactions of items in group with supported types: `ExpandOnlyCurrent`, `ExpandAll`, `CollapseAll`, `ExpandAllOrCollapseAll`, `expandAlwaysCurrent`.
-* Can listen to any changed behavior of any item in the group.
+* Supporting type's behaviors of interactions of items in group: `ExpandOnlyCurrent`, `ExpandAll`, `CollapseAll`, `ExpandAllOrCollapseAll`, `expandAlwaysCurrent`.
+* Can listen to behaviors' changed of items in the group.
* Can add the space between items in the group.
## Documentation
diff --git a/doc/group-features.md b/doc/group-features.md
index 09a9bc6..ee26c97 100644
--- a/doc/group-features.md
+++ b/doc/group-features.md
@@ -4,9 +4,34 @@ Check out [Example](https://congthanhng.github.io/ExpansionTileGroupExample/)
## Get Started
-You have defined a list of `ExpansionTileItem`, to group them just simply wrap them into `ExpansionTileGroup` widget.
+To group and manage `ExpansionTileItem`, there are 2 options to do that:
+- Option 1: Wrap them inside `ExpansionTileGroup` widget.
+- Option 2: Using `ExansionGroupController`
-For example:
+## Behaviors between items in the group
+Before we start you should to know the type of behaviors between items in group.
+We defined an enum to manage them `ToggleType`. This enum contains almost common case you will be met:
+
+| `toggleType` type | Description |
+|--------------------------|----------------------------------------------------------------------------------------------------------|
+| `none` | It's default. Do nothing if an item in group changed behavior. |
+| `expandOnlyCurrent` | When an item in group is expanded, would collapse all the others. |
+| `collapseAll` | Collapse all items if any item in group is collapsed. |
+| `expandAll` | Expanded all items if any item in group is expanded. |
+| `expandAllOrCollapseAll` | Expanded/Collapsed all items if any item in group is Expanded/Collapsed. |
+| `expandAlwaysCurrent` | Expand the tapped item and collapse all others, but still remain expanding when tapping this item again. |
+
+
+| | |
+|--------------------------------------------------------------------|------------------------------------------------------------------------------|
+| **ToggleType.expandOnlyCurrent**
![Image][ExpandedCurrent] | **ToggleType.collapseAll**
![Image][CollapseAll] |
+| **ToggleType.expandAll**
![Image][ExpandedAll] | **ToggleType.expandAllOrCollapseAll**
![Image][ExpandedAndCollapsedAll] |
+
+
+## Let implement with Option 1: Wrap them inside `ExpansionTileGroup` widget.
+You have a list of `ExpansionTileItem` and want to group them, just simply wrap them into `ExpansionTileGroup`
+
+Fully code example:
```dart
class ExpansionGroupExample extends StatelessWidget {
@@ -14,6 +39,14 @@ class ExpansionGroupExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ExpansionTileGroup(
+ toggleType: ToggleType.expandOnlyCurrent,
+ onExpansionItemChanged: (index, isExpanded) {
+ //index: the position of item that just changed state,
+ //isExpanded: present current behavior:
+ //- true: the item is expanding,
+ //- false: the item is collapsing
+ },
+ spaceBetweenItem: 16,
children: [
const ExpansionTileItem(
title: Text('ExpansionTile item 1'),
@@ -41,88 +74,142 @@ class ExpansionGroupExample extends StatelessWidget {
}
}
```
+That's it!
-## Behaviors between items in the group
+### 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 |
+
+## Let implement with Option 2: Using `ExansionGroupController`
+Why do we have option 2 while option 1 work well?
+
+Well, the Option 1 only works with List of `ExpansionTileItem`.
+
+In a case, you have a list of Widget and each widget contains one or many `ExpansionTileItem` inside. That the Option 1 can't handle this case.
+
+So we have the Option 2 by using `ExansionGroupController`
+
+Step by Step:
-You can control behaviors between items in the group by adding `toggleType` parameter to `ExpansionTileGroup`like this:
+Step 1: Define `ExpansionGroupController`
```dart
-class ExpansionGroupExample extends StatelessWidget {
- const ExpansionGroupExample({Key? key}) : super(key: key);
- @override
- Widget build(BuildContext context) {
- return ExpansionTileGroup(
- toggleType: ToggleType.expandOnlyCurrent,
- children: []
- );
- }
-}
+controller = ExpansionGroupController(
+ length: 6, toggleType: ToggleType.expandAllOrCollapseAll);
```
+The `lenth` is required.
-There are many types of `toggleType`, it support almost common case you will be met:
-
-| `toggleType` type | Description |
-|--------------------------|----------------------------------------------------------------------------------------------|
-| `none` | It's default. Do nothing if an item changed behavior |
-| `expandOnlyCurrent` | When an item in group is expanded, would collapse all the others |
-| `collapseAll` | Collapse all items if any item in group is collapsed |
-| `expandAll` | Expanded all items if any item in group is expanded |
-| `expandAllOrCollapseAll` | Expanded/Collapsed all items if any item in group is Expanded/Collapsed |
-| `expandAlwaysCurrent` | Expand tapped item and collapse all others, but not collapse the expanded one when tap again |
+The `toggleType` as mentioned above. Default is `ToggleType.none`
+Step 2: Pass `controller` and `index` in each `ExpansionTileItem`
-| | |
-|--------------------------------------------------------------------|------------------------------------------------------------------------------|
-| **ToggleType.expandOnlyCurrent**
![Image][ExpandedCurrent] | **ToggleType.collapseAll**
![Image][CollapseAll] |
-| **ToggleType.expandAll**
![Image][ExpandedAll] | **ToggleType.expandAllOrCollapseAll**
![Image][ExpandedAndCollapsedAll] |
+```dart
+ExpansionTileOutlined(
+ controller: controller,
+ index: 0,
+ title: Text('HELLO, I\'m index: 0'),
+ children: [])
+```
-## Listen the changed of any item in the group
+Make sure the index is always less than the `length` because `index` always counted from `0`.
-Adding the `onExpansionItemChanged` parameter into `ExpansionTileGroup` to listen the changed of any item in the group:
+To list itemChanged in group just add this listener;
```dart
-class ExpansionGroupExample extends StatelessWidget {
- const ExpansionGroupExample({Key? key}) : super(key: key);
- @override
- Widget build(BuildContext context) {
- return ExpansionTileGroup(
- onExpansionItemChanged: (index, isExpanded) {
- //index: the position of item that just changed state,
- //isExpanded: present current behavior:
- //- true: the item is expanding,
- //- false: the item is collapsing
- },
- children: []
- );
- }
-}
+controller.addItemChangedListener(
+ (index, isExpanded) {
+ debugPrint('index: $index, isExpanded: $isExpanded');
+ },
+ );
```
-## Adding space between items
+Finally, don't forget to dispose the controller
+```dart
+controller.dispose();
+```
-Adding the `spaceBetweenItem` parameter into `ExpansionTileGroup` to spacing between items:
+Below example code shows you a List of `ExpansionTileItem` wrapped by a ListView and a `ExpansionTileItem` wrapped by a Container.
```dart
-class ExpansionGroupExample extends StatelessWidget {
- const ExpansionGroupExample({Key? key}) : super(key: key);
- @override
- Widget build(BuildContext context) {
- return ExpansionTileGroup(
- spaceBetweenItem: 16,
- children: []
- );
- }
+import 'package:expansion_tile_group/expansion_tile_group.dart';
+import 'package:flutter/material.dart';
+
+class CustomGroupWithController extends StatefulWidget {
+ const CustomGroupWithController({Key? key}) : super(key: key);
+
+ @override
+ State createState() =>
+ _CustomGroupWithControllerState();
+}
+
+class _CustomGroupWithControllerState extends State {
+ late final ExpansionGroupController controller;
+
+ @override
+ void initState() {
+ controller = ExpansionGroupController(
+ length: 6, initialIndex: 0, toggleType: ToggleType.expandAllOrCollapseAll);
+
+ controller.addItemChangedListener(
+ (index, isExpanded) {
+ debugPrint('index: $index, isExpanded: $isExpanded');
+ },
+ );
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ controller.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ children: [
+ ListView.separated(
+ separatorBuilder: (context, index) => const SizedBox(
+ height: 8,
+ ),
+ shrinkWrap: true,
+ itemBuilder: (context, index) => ExpansionTileOutlined(
+ controller: controller,
+ index: index,
+ title: Text('HELLO, I\'m index: $index of ListView'),
+ children: [sampleContent]),
+ itemCount: 5),
+ Container(
+ color: Colors.yellow,
+ width: 250,
+ child: ExpansionTileFlat(
+ controller: controller,
+ index: 5,
+ title:
+ const Text('HELLO, I\'m index: 5 and wrap by Container'),
+ children: [sampleContent]),
+ )
+ ],
+ );
+ }
+
+ Widget get sampleContent => 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.'''),
+ ),
+ );
}
+
```
-## 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 behavior 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 |
+That's it! Hope you work well!
[ExpansionTile]: https://api.flutter.dev/flutter/material/ExpansionTile-class.html
diff --git a/example/README.md b/example/README.md
index 2b3fce4..29e2944 100644
--- a/example/README.md
+++ b/example/README.md
@@ -14,3 +14,14 @@ A few resources to get you started if this is your first Flutter project:
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
+
+## Generate Document
+```shell
+dart doc .
+```
+## Preview Document in localhost
+```shell
+dart pub global activate dhttpd
+dart pub global run dhttpd --path doc/api
+```
+## Build demo web
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 091775f..caa2044 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -50,7 +50,7 @@ class _MyAppState extends State {
case RouteNamed.expandAlwaysCurrent:
return MaterialPageRoute(
builder: (context) => const ExpandAlwaysCurrentPage());
- case RouteNamed.customGroupWithController:
+ case RouteNamed.customGroupWithController:
return MaterialPageRoute(
builder: (context) => const CustomGroupWithController());
case RouteNamed.fantasyPage:
@@ -165,9 +165,11 @@ class HomePage extends StatelessWidget {
title: 'Listen changed of any item in group',
routeName: RouteNamed.listenGroupItemChanged,
),
-
const Divider(),
- const Text('CUSTOM GROUP', style: TextStyle(color:Colors.red, fontWeight: FontWeight.bold),),
+ const Text(
+ 'CUSTOM GROUP',
+ style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
+ ),
_buildRow(
context,
title: 'Custom Group with controller',
diff --git a/example/lib/route_named.dart b/example/lib/route_named.dart
index a149f89..3749ccf 100644
--- a/example/lib/route_named.dart
+++ b/example/lib/route_named.dart
@@ -4,7 +4,8 @@ class RouteNamed {
static const String listenGroupItemChanged = '/listen_group_item_changed';
static const String expandOnlyCurrent = '/expand_only_current';
static const String expandAlwaysCurrent = '/expand_always_current';
- static const String customGroupWithController = '/custom_group_with_controller';
+ static const String customGroupWithController =
+ '/custom_group_with_controller';
static const String collapseAll = '/collapse_all';
static const String expandAll = '/expand_all';
static const String expandOrCollapseAll = '/expand_or_collapse_all';
diff --git a/example/lib/use_cases/group/custom_group_with_controller.dart b/example/lib/use_cases/group/custom_group_with_controller.dart
index faeb463..e70b82d 100644
--- a/example/lib/use_cases/group/custom_group_with_controller.dart
+++ b/example/lib/use_cases/group/custom_group_with_controller.dart
@@ -15,7 +15,7 @@ class _CustomGroupWithControllerState extends State {
@override
void initState() {
controller = ExpansionGroupController(
- length: 6, initialIndex: 0, toggleType: ToggleType.expandAllOrCollapseAll);
+ length: 6, toggleType: ToggleType.expandAllOrCollapseAll);
controller.addItemChangedListener(
(index, isExpanded) {
@@ -25,11 +25,18 @@ class _CustomGroupWithControllerState extends State {
super.initState();
}
+ @override
+ void dispose() {
+ controller.dispose();
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
- title: const Text('By Using Controller, you can customize your items and behaviors anywhere'),
+ title: const Text(
+ 'By Using Controller, you can customize your items and behaviors anywhere'),
),
body: SingleChildScrollView(
child: Center(
diff --git a/example/pubspec.lock b/example/pubspec.lock
index c48dce3..e15574b 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -55,7 +55,7 @@ packages:
path: ".."
relative: true
source: path
- version: "2.0.0"
+ version: "2.1.0"
fake_async:
dependency: transitive
description:
diff --git a/lib/src/core/expansion_group_controller.dart b/lib/src/core/expansion_group_controller.dart
index 4364c41..246e626 100644
--- a/lib/src/core/expansion_group_controller.dart
+++ b/lib/src/core/expansion_group_controller.dart
@@ -4,16 +4,15 @@ import 'expansion_tile_core.dart';
import 'toggle_type.dart';
class ExpansionGroupController with ChangeNotifier {
- ExpansionGroupController(
- {this.toggleType = ToggleType.none,
- required this.length,
- this.initialIndex = 0}) {
+ ExpansionGroupController({
+ this.toggleType = ToggleType.none,
+ required this.length,
+ }) {
_generateKeys();
}
final ToggleType toggleType;
final int length;
- final int initialIndex;
final _keysMap = >{};
final _keysMapReverse = , int>{};