Forked from hawkkiller/cool_animated_switcher.dart
Created
November 13, 2025 14:52
-
-
Save MillerAdulu/47952377b2855b17b3db82e04ca9cdbd to your computer and use it in GitHub Desktop.
This is a cool sliding transition for widgets. It's inspired by the wolt_modal_sheet package, which has the same animation but is coupled with the Bottom Sheet/Pages API. This implementation should be also more performant and less fragile.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'package:flutter/material.dart'; | |
| /// A widget that transitions between two children using a fade and slide animation. | |
| class PageTransitionSwitcher extends StatelessWidget { | |
| const PageTransitionSwitcher({ | |
| required this.child, | |
| this.isForwardMove = true, | |
| super.key, | |
| }); | |
| /// The child widget to transition between. | |
| final Widget child; | |
| final bool isForwardMove; | |
| static const _duration = Duration(milliseconds: 350); | |
| static const _sizeCurve = Interval(0 / 350, 300 / 350, curve: Curves.fastOutSlowIn); | |
| static const _opacityIncomingCurve = Interval(150 / 350, 1); | |
| static const _opacityOutgoingCurve = Interval(50 / 350, 150 / 350); | |
| static const _slideCurve = Interval(50 / 350, 1, curve: Curves.fastOutSlowIn); | |
| Key? get _currentChildKey => child.key; | |
| @override | |
| Widget build(BuildContext context) { | |
| return AnimatedSize( | |
| alignment: Alignment.topCenter, | |
| duration: _duration, | |
| curve: _sizeCurve, | |
| child: AnimatedSwitcher( | |
| duration: _duration, | |
| layoutBuilder: (currentChild, previousChildren) => Stack( | |
| clipBehavior: Clip.none, | |
| children: [ | |
| ...previousChildren.map((child) => Positioned(top: 0, left: 0, right: 0, child: child)), | |
| if (currentChild != null) currentChild, | |
| ], | |
| ), | |
| transitionBuilder: (child, animation) => _transitionBuilder( | |
| child, | |
| animation, | |
| currentChildKey: _currentChildKey, | |
| isForwardMove: isForwardMove, | |
| textDirection: Directionality.of(context), | |
| ), | |
| child: child, | |
| ), | |
| ); | |
| } | |
| } | |
| enum _TransitionState { incoming, outgoing } | |
| Widget _transitionBuilder( | |
| Widget child, | |
| Animation<double> animation, { | |
| required Key? currentChildKey, | |
| required bool isForwardMove, | |
| required TextDirection textDirection, | |
| }) { | |
| final isIncoming = child.key == currentChildKey; | |
| final opacityCurve = isIncoming | |
| ? PageTransitionSwitcher._opacityIncomingCurve | |
| : PageTransitionSwitcher._opacityOutgoingCurve; | |
| final reverseOpacityCurve = isIncoming | |
| ? PageTransitionSwitcher._opacityOutgoingCurve | |
| : PageTransitionSwitcher._opacityIncomingCurve; | |
| final state = isIncoming ? _TransitionState.incoming : _TransitionState.outgoing; | |
| return FadeTransition( | |
| opacity: CurvedAnimation( | |
| parent: animation, | |
| curve: opacityCurve, | |
| reverseCurve: reverseOpacityCurve, | |
| ), | |
| child: SlideTransition( | |
| position: _slidePosition( | |
| animation, | |
| state: state, | |
| textDirection: textDirection, | |
| isForwardMove: isForwardMove, | |
| ), | |
| child: child, | |
| ), | |
| ); | |
| } | |
| Animation<Offset> _slidePosition( | |
| Animation<double> animation, { | |
| required _TransitionState state, | |
| required TextDirection textDirection, | |
| required bool isForwardMove, | |
| }) { | |
| final directionMultiplier = | |
| (textDirection == TextDirection.ltr ? 1 : -1) * (isForwardMove ? 1 : -1); | |
| switch (state) { | |
| case _TransitionState.incoming: | |
| final incomingBeginOffset = Offset(.3 * directionMultiplier, 0); | |
| return Tween<Offset>(begin: incomingBeginOffset, end: Offset.zero).animate( | |
| CurvedAnimation( | |
| parent: animation, | |
| curve: PageTransitionSwitcher._slideCurve, | |
| ), | |
| ); | |
| case _TransitionState.outgoing: | |
| final outgoingEndOffset = Offset(.3 * -directionMultiplier, 0); | |
| return Tween<Offset>(begin: outgoingEndOffset, end: Offset.zero).animate( | |
| CurvedAnimation( | |
| parent: animation, | |
| curve: PageTransitionSwitcher._slideCurve, | |
| ), | |
| ); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment