Skip to content

Commit

Permalink
release 2.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
congthanhng committed Sep 22, 2024
1 parent 526f45a commit 0aa67cd
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 86 deletions.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
215 changes: 151 additions & 64 deletions doc/group-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,49 @@ 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** <br/> ![Image][ExpandedCurrent] | **ToggleType.collapseAll** <br/>![Image][CollapseAll] |
| **ToggleType.expandAll** <br/>![Image][ExpandedAll] | **ToggleType.expandAllOrCollapseAll** <br/>![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 {
const ExpansionGroupExample({Key? key}) : super(key: key);
@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'),
Expand Down Expand Up @@ -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** <br/> ![Image][ExpandedCurrent] | **ToggleType.collapseAll** <br/>![Image][CollapseAll] |
| **ToggleType.expandAll** <br/>![Image][ExpandedAll] | **ToggleType.expandAllOrCollapseAll** <br/>![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<CustomGroupWithController> createState() =>
_CustomGroupWithControllerState();
}
class _CustomGroupWithControllerState extends State<CustomGroupWithController> {
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
Expand Down
11 changes: 11 additions & 0 deletions example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 5 additions & 3 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class _MyAppState extends State<MyApp> {
case RouteNamed.expandAlwaysCurrent:
return MaterialPageRoute(
builder: (context) => const ExpandAlwaysCurrentPage());
case RouteNamed.customGroupWithController:
case RouteNamed.customGroupWithController:
return MaterialPageRoute(
builder: (context) => const CustomGroupWithController());
case RouteNamed.fantasyPage:
Expand Down Expand Up @@ -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',
Expand Down
3 changes: 2 additions & 1 deletion example/lib/route_named.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
11 changes: 9 additions & 2 deletions example/lib/use_cases/group/custom_group_with_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class _CustomGroupWithControllerState extends State<CustomGroupWithController> {
@override
void initState() {
controller = ExpansionGroupController(
length: 6, initialIndex: 0, toggleType: ToggleType.expandAllOrCollapseAll);
length: 6, toggleType: ToggleType.expandAllOrCollapseAll);

controller.addItemChangedListener(
(index, isExpanded) {
Expand All @@ -25,11 +25,18 @@ class _CustomGroupWithControllerState extends State<CustomGroupWithController> {
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(
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ packages:
path: ".."
relative: true
source: path
version: "2.0.0"
version: "2.1.0"
fake_async:
dependency: transitive
description:
Expand Down
9 changes: 4 additions & 5 deletions lib/src/core/expansion_group_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <int, GlobalKey<ExpansionTileCoreState>>{};
final _keysMapReverse = <GlobalKey<ExpansionTileCoreState>, int>{};
Expand Down

0 comments on commit 0aa67cd

Please sign in to comment.