Improve animation

This commit is contained in:
2025-01-29 20:52:37 +08:00
parent 3e5ae0a39a
commit a3e758831b
2 changed files with 161 additions and 102 deletions

View File

@@ -20,6 +20,7 @@ class AppPageRoute<T> extends PageRoute<T> with _AppRouteTransitionMixin {
super.barrierDismissible = false, super.barrierDismissible = false,
this.enableIOSGesture = true, this.enableIOSGesture = true,
this.preventRebuild = true, this.preventRebuild = true,
this.isRoot = false,
}) { }) {
assert(opaque); assert(opaque);
} }
@@ -44,6 +45,9 @@ class AppPageRoute<T> extends PageRoute<T> with _AppRouteTransitionMixin {
@override @override
final bool preventRebuild; final bool preventRebuild;
@override
final bool isRoot;
static void updateBackButton() { static void updateBackButton() {
Future.delayed(const Duration(milliseconds: 300), () { Future.delayed(const Duration(milliseconds: 300), () {
StateController.findOrNull(tag: "back_button")?.update(); StateController.findOrNull(tag: "back_button")?.update();
@@ -77,6 +81,8 @@ mixin _AppRouteTransitionMixin<T> on PageRoute<T> {
Widget? _child; Widget? _child;
bool get isRoot;
@override @override
Widget buildPage( Widget buildPage(
BuildContext context, BuildContext context,
@@ -115,6 +121,22 @@ mixin _AppRouteTransitionMixin<T> on PageRoute<T> {
@override @override
Widget buildTransitions(BuildContext context, Animation<double> animation, Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) { Animation<double> secondaryAnimation, Widget child) {
if (isRoot) {
return EntrancePageTransition(
animation: CurvedAnimation(
parent: animation,
curve: FluentTheme.of(context).animationCurve,
),
child: enableIOSGesture && App.isIOS
? IOSBackGestureDetector(
gestureWidth: _kBackGestureWidth,
enabledCallback: () => _isPopGestureEnabled<T>(this),
onStartPopGesture: () => _startPopGesture(this),
child: child)
: child,
);
}
return DrillInPageTransition( return DrillInPageTransition(
animation: CurvedAnimation( animation: CurvedAnimation(
parent: animation, parent: animation,
@@ -388,3 +410,35 @@ class SideBarRoute<T> extends PopupRoute<T> {
return IOSBackGestureController(route.controller!, route.navigator!); return IOSBackGestureController(route.controller!, route.navigator!);
} }
} }
class EntrancePageTransition extends StatelessWidget {
/// Creates an entrance page transition
const EntrancePageTransition({
super.key,
required this.child,
required this.animation,
});
/// The widget to be animated
final Widget child;
/// The animation to drive this transition
final Animation<double> animation;
@override
Widget build(BuildContext context) {
return ColoredBox(
color: FluentTheme.of(context).micaBackgroundColor,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 0.1),
end: Offset.zero,
).animate(animation),
child: FadeTransition(
opacity: animation,
child: child,
),
),
);
}
}

View File

@@ -132,112 +132,112 @@ class _MainPageState extends State<MainPage>
return DefaultSelectionStyle.merge( return DefaultSelectionStyle.merge(
selectionColor: FluentTheme.of(context).selectionColor.toOpacity(0.4), selectionColor: FluentTheme.of(context).selectionColor.toOpacity(0.4),
child: NavigationView( child: NavigationView(
appBar: buildAppBar(context, navigatorKey), appBar: buildAppBar(context, navigatorKey),
pane: NavigationPane( pane: NavigationPane(
selected: index, selected: index,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
index = value; index = value;
}); });
navigate(value); navigate(value);
}, },
items: [ items: [
UserPane(), UserPane(),
PaneItem( PaneItem(
icon: const Icon( icon: const Icon(
MdIcons.search, MdIcons.search,
size: 20, size: 20,
),
title: Text('Search'.tl),
body: const SizedBox.shrink(),
), ),
PaneItem( title: Text('Search'.tl),
icon: const Icon( body: const SizedBox.shrink(),
MdIcons.downloading, ),
size: 20, PaneItem(
), icon: const Icon(
title: Text('Downloading'.tl), MdIcons.downloading,
body: const SizedBox.shrink(), size: 20,
), ),
PaneItem( title: Text('Downloading'.tl),
icon: const Icon( body: const SizedBox.shrink(),
MdIcons.download, ),
size: 20, PaneItem(
), icon: const Icon(
title: Text('Downloaded'.tl), MdIcons.download,
body: const SizedBox.shrink(), size: 20,
), ),
PaneItemSeparator(), title: Text('Downloaded'.tl),
PaneItemHeader( body: const SizedBox.shrink(),
header: Text('${"Illustrations".tl}/${"Manga".tl}') ),
.paddingBottom(4) PaneItemSeparator(),
.paddingLeft(8)), PaneItemHeader(
PaneItem( header: Text('${"Illustrations".tl}/${"Manga".tl}')
icon: const Icon( .paddingBottom(4)
MdIcons.explore_outlined, .paddingLeft(8)),
size: 20, PaneItem(
), icon: const Icon(
title: Text('Explore'.tl), MdIcons.explore_outlined,
body: const SizedBox.shrink(), size: 20,
), ),
PaneItem( title: Text('Explore'.tl),
icon: const Icon(MdIcons.bookmark_outline, size: 20), body: const SizedBox.shrink(),
title: Text('Bookmarks'.tl), ),
body: const SizedBox.shrink(), PaneItem(
), icon: const Icon(MdIcons.bookmark_outline, size: 20),
PaneItem( title: Text('Bookmarks'.tl),
icon: const Icon(MdIcons.interests_outlined, size: 20), body: const SizedBox.shrink(),
title: Text('Following'.tl), ),
body: const SizedBox.shrink(), PaneItem(
), icon: const Icon(MdIcons.interests_outlined, size: 20),
PaneItem( title: Text('Following'.tl),
icon: const Icon(MdIcons.history, size: 20), body: const SizedBox.shrink(),
title: Text('History'.tl), ),
body: const SizedBox.shrink(), PaneItem(
), icon: const Icon(MdIcons.history, size: 20),
PaneItem( title: Text('History'.tl),
icon: const Icon(MdIcons.leaderboard_outlined, size: 20), body: const SizedBox.shrink(),
title: Text('Ranking'.tl), ),
body: const SizedBox.shrink(), PaneItem(
), icon: const Icon(MdIcons.leaderboard_outlined, size: 20),
PaneItemSeparator(), title: Text('Ranking'.tl),
PaneItemHeader( body: const SizedBox.shrink(),
header: Text("Novel".tl).paddingBottom(4).paddingLeft(8)), ),
PaneItem( PaneItemSeparator(),
icon: const Icon(MdIcons.featured_play_list_outlined, size: 20), PaneItemHeader(
title: Text('Recommendation'.tl), header: Text("Novel".tl).paddingBottom(4).paddingLeft(8)),
body: const SizedBox.shrink(), PaneItem(
), icon: const Icon(MdIcons.featured_play_list_outlined, size: 20),
PaneItem( title: Text('Recommendation'.tl),
icon: body: const SizedBox.shrink(),
const Icon(MdIcons.collections_bookmark_outlined, size: 20), ),
title: Text('Bookmarks'.tl), PaneItem(
body: const SizedBox.shrink(), icon: const Icon(MdIcons.collections_bookmark_outlined, size: 20),
), title: Text('Bookmarks'.tl),
PaneItem( body: const SizedBox.shrink(),
icon: const Icon(MdIcons.leaderboard_outlined, size: 20), ),
title: Text('Ranking'.tl), PaneItem(
body: const SizedBox.shrink(), icon: const Icon(MdIcons.leaderboard_outlined, size: 20),
), title: Text('Ranking'.tl),
PaneItemSeparator(), body: const SizedBox.shrink(),
PaneItem( ),
icon: const Icon(MdIcons.settings_outlined, size: 20), PaneItemSeparator(),
title: Text('Settings'.tl), PaneItem(
body: const SizedBox.shrink(), icon: const Icon(MdIcons.settings_outlined, size: 20),
), title: Text('Settings'.tl),
], body: const SizedBox.shrink(),
),
],
),
paneBodyBuilder: (pane, child) => MediaQuery.removePadding(
context: context,
removeTop: true,
child: Navigator(
key: navigatorKey,
onGenerateRoute: (settings) => AppPageRoute(
isRoot: true,
builder: (context) => pageBuilders.elementAtOrNull(index)!(),
),
), ),
paneBodyBuilder: (pane, child) => MediaQuery.removePadding( ),
context: context, ),
removeTop: true,
child: Navigator(
key: navigatorKey,
onGenerateRoute: (settings) => AppPageRoute(
builder: (context) =>
pageBuilders.elementAtOrNull(index)!(),
),
),
)),
); );
} }
@@ -263,7 +263,12 @@ class _MainPageState extends State<MainPage>
child: Text("Invalid Page: $index"), child: Text("Invalid Page: $index"),
); );
navigatorKey.currentState!.pushAndRemoveUntil( navigatorKey.currentState!.pushAndRemoveUntil(
AppPageRoute(builder: (context) => page()), (route) => false); AppPageRoute(
builder: (context) => page(),
isRoot: true,
),
(route) => false,
);
} }
NavigationAppBar buildAppBar( NavigationAppBar buildAppBar(