diff --git a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/expanding_sub_list_item.dart b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/expanding_sub_list_item.dart index 39f45d589..f2cb410d3 100644 --- a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/expanding_sub_list_item.dart +++ b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/expanding_sub_list_item.dart @@ -6,6 +6,7 @@ import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart'; import 'package:stackwallet/widgets/expandable.dart'; class ExpandingSubListItem extends StatefulWidget { @@ -14,11 +15,17 @@ class ExpandingSubListItem extends StatefulWidget { required this.title, required this.entities, required this.initialState, - }) : super(key: key); + double? animationDurationMultiplier, + this.curve = Curves.easeInOutCubicEmphasized, + }) : animationDurationMultiplier = + animationDurationMultiplier ?? entities.length * 0.11, + super(key: key); final String title; final List entities; final ExpandableState initialState; + final double animationDurationMultiplier; + final Curve curve; @override State createState() => _ExpandingSubListItemState(); @@ -28,31 +35,40 @@ class _ExpandingSubListItemState extends State { final isDesktop = Util.isDesktop; late final ExpandableController _controller; - - late bool _expandedState; + late final RotateIconController _rotateIconController; @override void initState() { - _expandedState = widget.initialState == ExpandableState.expanded; _controller = ExpandableController(); + _rotateIconController = RotateIconController(); WidgetsBinding.instance.addPostFrameCallback((_) { - if (_expandedState) { + if (widget.initialState == ExpandableState.expanded) { _controller.toggle?.call(); } }); super.initState(); } + @override + void dispose() { + _controller.toggle = null; + _rotateIconController.forward = null; + _rotateIconController.reverse = null; + super.dispose(); + } + @override Widget build(BuildContext context) { return Expandable( - animationDurationMultiplier: 0.1 * widget.entities.length, - curve: Curves.easeInOutCubicEmphasized, + animationDurationMultiplier: widget.animationDurationMultiplier, + curve: widget.curve, controller: _controller, - onExpandChanged: (state) { - setState(() { - _expandedState = state == ExpandableState.expanded; - }); + onExpandWillChange: (state) { + if (state == ExpandableState.expanded) { + _rotateIconController.forward?.call(); + } else { + _rotateIconController.reverse?.call(); + } }, header: Container( color: Colors.transparent, @@ -76,13 +92,18 @@ class _ExpandingSubListItemState extends State { : STextStyles.smallMed12(context), textAlign: TextAlign.left, ), - SvgPicture.asset( - _expandedState ? Assets.svg.chevronUp : Assets.svg.chevronDown, - width: 12, - height: 6, - color: Theme.of(context) - .extension()! - .textFieldActiveSearchIconRight, + RotateIcon( + icon: SvgPicture.asset( + Assets.svg.chevronDown, + width: 12, + height: 6, + color: Theme.of(context) + .extension()! + .textFieldActiveSearchIconRight, + ), + curve: widget.curve, + animationDurationMultiplier: widget.animationDurationMultiplier, + controller: _rotateIconController, ), ], ), diff --git a/lib/widgets/animated_widgets/rotate_icon.dart b/lib/widgets/animated_widgets/rotate_icon.dart new file mode 100644 index 000000000..c818628d0 --- /dev/null +++ b/lib/widgets/animated_widgets/rotate_icon.dart @@ -0,0 +1,72 @@ +import 'package:flutter/widgets.dart'; + +class RotateIconController { + VoidCallback? forward; + VoidCallback? reverse; +} + +class RotateIcon extends StatefulWidget { + const RotateIcon({ + Key? key, + required this.icon, + required this.curve, + this.controller, + this.animationDurationMultiplier = 1.0, + this.rotationPercent = 0.5, + }) : super(key: key); + + final Widget icon; + final Curve curve; + final RotateIconController? controller; + final double animationDurationMultiplier; + final double rotationPercent; + + @override + State createState() => _RotateIconState(); +} + +class _RotateIconState extends State + with SingleTickerProviderStateMixin { + late final AnimationController animationController; + late final Animation animation; + late final Duration duration; + + @override + void initState() { + duration = Duration( + milliseconds: (500 * widget.animationDurationMultiplier).toInt(), + ); + animationController = AnimationController( + vsync: this, + duration: duration, + ); + animation = Tween( + begin: 0.0, + end: widget.rotationPercent, + ).animate( + CurvedAnimation( + curve: widget.curve, + parent: animationController, + ), + ); + + widget.controller?.forward = animationController.forward; + widget.controller?.reverse = animationController.reverse; + + super.initState(); + } + + @override + void dispose() { + animationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return RotationTransition( + turns: animation, + child: widget.icon, + ); + } +} diff --git a/lib/widgets/expandable.dart b/lib/widgets/expandable.dart index c5f669d4e..aa438e34f 100644 --- a/lib/widgets/expandable.dart +++ b/lib/widgets/expandable.dart @@ -19,6 +19,7 @@ class Expandable extends StatefulWidget { this.animation, this.animationDurationMultiplier = 1.0, this.onExpandChanged, + this.onExpandWillChange, this.controller, this.expandOverride, this.curve = Curves.easeInOut, @@ -30,6 +31,7 @@ class Expandable extends StatefulWidget { final Animation? animation; final double animationDurationMultiplier; final void Function(ExpandableState)? onExpandChanged; + final void Function(ExpandableState)? onExpandWillChange; final ExpandableController? controller; final VoidCallback? expandOverride; final Curve curve; @@ -48,10 +50,12 @@ class _ExpandableState extends State with TickerProviderStateMixin { Future toggle() async { if (animation.isDismissed) { + widget.onExpandWillChange?.call(ExpandableState.expanded); await animationController.forward(); _toggleState = ExpandableState.expanded; widget.onExpandChanged?.call(_toggleState); } else if (animation.isCompleted) { + widget.onExpandWillChange?.call(ExpandableState.collapsed); await animationController.reverse(); _toggleState = ExpandableState.collapsed; widget.onExpandChanged?.call(_toggleState);