Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add optional parameter to specify type of animation #38

Merged
merged 6 commits into from
Aug 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 50 additions & 10 deletions lib/tree_view/tree_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,16 @@ final class TreeViewController<Data, Tree extends ITreeNode<Data>> {
const TreeViewController(this._animatedListController);

/// Method for programmatically scrolling to an [index] in the flat list of the [TreeView].
Future scrollToIndex(int index) async =>
_animatedListController.expansionBehaviourController.scrollToIndex(index);
Future scrollToIndex(int index,
[Duration duration = scrollAnimationDuration]) async =>
_animatedListController.expansionBehaviourController
.scrollToIndex(index, duration);

/// Method for programmatically scrolling to a [node] in the [TreeView].
Future scrollToItem(Tree node) async =>
_animatedListController.expansionBehaviourController.scrollToItem(node);
Future scrollToItem(Tree node,
[Duration duration = scrollAnimationDuration]) async =>
_animatedListController.expansionBehaviourController
.scrollToItem(node, duration);

/// Method for programmatically toggling the expansion state of a [TreeNode].
/// If the [TreeNode] is in expanded state, then it will be collapsed.
Expand Down Expand Up @@ -373,6 +377,9 @@ final class TreeView<Data, Tree extends ITreeNode<Data>>
/// For more information see the [AnimatedList.shrinkWrap]
final bool shrinkWrap;

/// An optional animation for AnimatedList. If no animation is provided, AnimatedList falls back on its default.
final Animation<double>? animation;

const TreeView._({
super.key,
super.expansionBehavior,
Expand All @@ -388,6 +395,7 @@ final class TreeView<Data, Tree extends ITreeNode<Data>>
this.shrinkWrap = false,
super.showRootNode,
super.onTreeReady,
this.animation,
super.focusToNewNode,
});

Expand Down Expand Up @@ -426,6 +434,7 @@ final class TreeView<Data, Tree extends ITreeNode<Data>>
bool showRootNode = true,
bool focusToNewNode = true,
TreeReadyCallback<Data, TreeNode<Data>>? onTreeReady,
Animation<double>? animation,
}) =>
TreeView._(
key: key,
Expand All @@ -444,6 +453,7 @@ final class TreeView<Data, Tree extends ITreeNode<Data>>
showRootNode: showRootNode,
onTreeReady: onTreeReady,
focusToNewNode: focusToNewNode,
animation: animation,
);

/// Use the typed constructor if you are extending the [TreeNode] instead of
Expand Down Expand Up @@ -483,6 +493,7 @@ final class TreeView<Data, Tree extends ITreeNode<Data>>
bool showRootNode = true,
bool focusToNewNode = true,
TreeReadyCallback<Data, Tree>? onTreeReady,
Animation<double>? animation,
}) =>
TreeView._(
key: key,
Expand All @@ -501,6 +512,7 @@ final class TreeView<Data, Tree extends ITreeNode<Data>>
showRootNode: showRootNode,
onTreeReady: onTreeReady,
focusToNewNode: focusToNewNode,
animation: animation,
);

/// The alternate implementation of [TreeView] uses an [IndexedNode] internally,
Expand Down Expand Up @@ -536,6 +548,7 @@ final class TreeView<Data, Tree extends ITreeNode<Data>>
bool showRootNode = true,
bool focusToNewNode = true,
TreeReadyCallback<Data, IndexedTreeNode<Data>>? onTreeReady,
Animation<double>? animation,
}) =>
TreeView._(
key: key,
Expand All @@ -554,6 +567,7 @@ final class TreeView<Data, Tree extends ITreeNode<Data>>
showRootNode: showRootNode,
onTreeReady: onTreeReady,
focusToNewNode: focusToNewNode,
animation: animation,
);

/// Use the typed constructor if you are extending the [IndexedTreeNode] instead
Expand Down Expand Up @@ -592,6 +606,7 @@ final class TreeView<Data, Tree extends ITreeNode<Data>>
bool showRootNode = true,
bool focusToNewNode = true,
TreeReadyCallback<Data, Tree>? onTreeReady,
Animation<double>? animation,
}) =>
TreeView._(
key: key,
Expand All @@ -610,21 +625,26 @@ final class TreeView<Data, Tree extends ITreeNode<Data>>
showRootNode: showRootNode,
onTreeReady: onTreeReady,
focusToNewNode: focusToNewNode,
animation: animation,
);

@override
State<StatefulWidget> createState() => TreeViewState<Data, Tree>();
State<StatefulWidget> createState() => TreeViewState<Data, Tree>(animation);
}

class TreeViewState<Data, Tree extends ITreeNode<Data>>
extends State<TreeView<Data, Tree>>
with _TreeViewState<Data, Tree, TreeView<Data, Tree>> {
TreeViewState(Animation<double>? animation) : _animation = animation;

static const _errorMsg =
"Animated list state not found from GlobalKey<AnimatedListState>";

late final GlobalKey<AnimatedListState> _listKey =
GlobalKey<AnimatedListState>();

final Animation<double>? _animation;

@override
void insertItem(int index, {Duration duration = animationDuration}) {
if (_listKey.currentState == null) throw Exception(_errorMsg);
Expand All @@ -637,7 +657,8 @@ class TreeViewState<Data, Tree extends ITreeNode<Data>>
if (_listKey.currentState == null) throw Exception(_errorMsg);
_listKey.currentState!.removeItem(
index,
(context, animation) => _removedItemBuilder(context, item, animation),
(context, animation) =>
_removedItemBuilder(context, item, _animation ?? animation),
duration: duration,
);
}
Expand All @@ -653,7 +674,8 @@ class TreeViewState<Data, Tree extends ITreeNode<Data>>
physics: widget.physics,
padding: widget.padding,
shrinkWrap: widget.shrinkWrap,
itemBuilder: _insertedItemBuilder,
itemBuilder: (context, index, animation) =>
_insertedItemBuilder(context, index, _animation ?? animation),
);
}
}
Expand Down Expand Up @@ -682,6 +704,9 @@ class TreeViewState<Data, Tree extends ITreeNode<Data>>
/// e.g. for path './.level1/level2', complexity is simply O(2).
final class SliverTreeView<Data, Tree extends ITreeNode<Data>>
extends _TreeView<Data, Tree> {
/// An optional animation for AnimatedList. If no animation is provided, AnimatedList falls back on its default.
final Animation<double>? animation;

const SliverTreeView._({
super.key,
required super.builder,
Expand All @@ -695,6 +720,7 @@ final class SliverTreeView<Data, Tree extends ITreeNode<Data>>
super.showRootNode,
super.onTreeReady,
super.focusToNewNode,
this.animation,
}) : assert(
expansionBehavior == ExpansionBehavior.none ||
scrollController != null,
Expand All @@ -703,7 +729,8 @@ final class SliverTreeView<Data, Tree extends ITreeNode<Data>>
"For more info see example/lib/samples/sliver_treeview/sliver_treeview_sample.dart\n\n");

@override
State<StatefulWidget> createState() => SliverTreeViewState<Data, Tree>();
State<StatefulWidget> createState() =>
SliverTreeViewState<Data, Tree>(animation);

/// The default implementation of [SliverTreeView] that uses a [TreeNode] internally,
/// which is based on the [Map] data structure for maintaining the children states.
Expand Down Expand Up @@ -742,6 +769,7 @@ final class SliverTreeView<Data, Tree extends ITreeNode<Data>>
bool showRootNode = true,
bool focusToNewNode = true,
TreeReadyCallback<Data, TreeNode<Data>>? onTreeReady,
Animation<double>? animation,
}) =>
SliverTreeView._(
key: key,
Expand All @@ -757,6 +785,7 @@ final class SliverTreeView<Data, Tree extends ITreeNode<Data>>
showRootNode: showRootNode,
onTreeReady: onTreeReady,
focusToNewNode: focusToNewNode,
animation: animation,
);

/// Use the typed constructor if you are extending the [TreeNode] instead of
Expand Down Expand Up @@ -799,6 +828,7 @@ final class SliverTreeView<Data, Tree extends ITreeNode<Data>>
bool showRootNode = true,
bool focusToNewNode = true,
TreeReadyCallback<Data, Tree>? onTreeReady,
Animation<double>? animation,
}) =>
SliverTreeView._(
key: key,
Expand All @@ -814,6 +844,7 @@ final class SliverTreeView<Data, Tree extends ITreeNode<Data>>
showRootNode: showRootNode,
onTreeReady: onTreeReady,
focusToNewNode: focusToNewNode,
animation: animation,
);

/// The alternate implementation of [SliverTreeView] uses an [IndexedNode]
Expand Down Expand Up @@ -851,6 +882,7 @@ final class SliverTreeView<Data, Tree extends ITreeNode<Data>>
bool showRootNode = true,
bool focusToNewNode = true,
TreeReadyCallback<Data, IndexedTreeNode<Data>>? onTreeReady,
Animation<double>? animation,
}) =>
SliverTreeView._(
key: key,
Expand All @@ -866,6 +898,7 @@ final class SliverTreeView<Data, Tree extends ITreeNode<Data>>
showRootNode: showRootNode,
onTreeReady: onTreeReady,
focusToNewNode: focusToNewNode,
animation: animation,
);

/// Use the typed constructor if you are extending the [IndexedTreeNode] instead
Expand Down Expand Up @@ -911,6 +944,7 @@ final class SliverTreeView<Data, Tree extends ITreeNode<Data>>
bool showRootNode = true,
bool focusToNewNode = true,
TreeReadyCallback<Data, Tree>? onTreeReady,
Animation<double>? animation,
}) =>
SliverTreeView._(
key: key,
Expand All @@ -926,18 +960,22 @@ final class SliverTreeView<Data, Tree extends ITreeNode<Data>>
showRootNode: showRootNode,
onTreeReady: onTreeReady,
focusToNewNode: focusToNewNode,
animation: animation,
);
}

class SliverTreeViewState<Data, Tree extends ITreeNode<Data>>
extends State<SliverTreeView<Data, Tree>>
with _TreeViewState<Data, Tree, SliverTreeView<Data, Tree>> {
SliverTreeViewState(Animation<double>? animation) : _animation = animation;
static const _errorMsg =
"Sliver Animated list state not found from GlobalKey<SliverAnimatedListState>";

late final GlobalKey<SliverAnimatedListState> _listKey =
GlobalKey<SliverAnimatedListState>();

final Animation<double>? _animation;

@override
void insertItem(int index, {Duration duration = animationDuration}) {
if (_listKey.currentState == null) throw Exception(_errorMsg);
Expand All @@ -950,7 +988,8 @@ class SliverTreeViewState<Data, Tree extends ITreeNode<Data>>
if (_listKey.currentState == null) throw Exception(_errorMsg);
_listKey.currentState!.removeItem(
index,
(context, animation) => _removedItemBuilder(context, item, animation),
(context, animation) =>
_removedItemBuilder(context, item, _animation ?? animation),
duration: duration,
);
}
Expand All @@ -961,7 +1000,8 @@ class SliverTreeViewState<Data, Tree extends ITreeNode<Data>>
key: _listKey,
initialItemCount:
_treeViewEventHandler.animatedListStateController.list.length,
itemBuilder: _insertedItemBuilder,
itemBuilder: (context, index, animation) =>
_insertedItemBuilder(context, index, _animation ?? animation),
);
}
}
15 changes: 8 additions & 7 deletions lib/tree_view/tree_view_state_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -230,13 +230,14 @@ class TreeViewExpansionBehaviourController<Data> {
required this.animatedListStateController,
});

Future scrollToIndex(int index) async => await scrollController.scrollToIndex(
index,
preferPosition: AutoScrollPosition.begin,
);

Future scrollToItem(ITreeNode<Data> item) async =>
await scrollToIndex(animatedListStateController.indexOf(item));
Future scrollToIndex(int index,
[Duration duration = scrollAnimationDuration]) async =>
await scrollController.scrollToIndex(index,
preferPosition: AutoScrollPosition.begin, duration: duration);

Future scrollToItem(ITreeNode<Data> item,
[Duration duration = scrollAnimationDuration]) async =>
await scrollToIndex(animatedListStateController.indexOf(item), duration);

Future<void> collapseNode(ITreeNode<Data> item) async {
final removeItems = animatedListStateController.list.where((element) =>
Expand Down
1 change: 0 additions & 1 deletion test/node/node_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:animated_tree_view/animated_tree_view.dart';
import 'package:collection/collection.dart';
import 'package:flutter_test/flutter_test.dart';

import '../mocks/node_mocks.dart';
Expand Down