I have a CustomPageRoute
for a fade animation when pushing
to another screen. That is working as expected.
However I would love to have a swipe back animation and can not make it work. I tried couple of different things, the most promising was this answer but that ist still sliding in/out the page.
For animation I only want the a fadeAnimation
, both for pushing and popping.
This is my code for the FadeTransition
:
class FadePageTransition extends Page {
final Widget page;
const FadePageTransition({
required this.page,
LocalKey? key,
String? restorationId,
}) : super(
key: key,
restorationId: restorationId,
);
@override
Route createRoute(BuildContext context) {
return FadeRoute(
child: page,
routeSettings: this,
);
}
}
class FadeRoute<T> extends PageRoute<T> {
final Widget child;
final RouteSettings routeSettings;
FadeRoute({
required this.child,
required this.routeSettings,
});
@override
RouteSettings get settings => routeSettings;
@override
Color? get barrierColor => Palette.black;
@override
String? get barrierLabel => null;
@override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return FadeTransition(
opacity: animation,
child: child,
);
}
@override
bool get maintainState => true;
@override
Duration get transitionDuration => const Duration(
milliseconds: transitionDurationInMS,
);
}
Let me know if you need any more info.
In iOS Swift it is possible, with this code:
// handle swqipe down gesture
@objc private func handlePan(gestureRecognizer:UIPanGestureRecognizer) {
// calculate the progress based on how far the user moved
let translation = panGR.translation(in: nil)
let progress = translation.y / 2 / view.bounds.height
switch panGR.state {
case .began:
// begin the transition as normal
self.dismissView()
break
case .changed:
Hero.shared.update(progress)
default:
// finish or cancel the transition based on the progress and user's touch velocity
if progress + panGR.velocity(in: nil).y / view.bounds.height > 0.3 {
self.dismissView()
Hero.shared.finish()
} else {
Hero.shared.cancel()
}
}
}
I have extended CupertinoPageRoute and used buildTransitions method to get _CupertinoBackGestureDetector object which is responsible for detecting the back swipe.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return Semantics(
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
useMaterial3: false,
),
onGenerateRoute: (settings) {
if (settings.name == "/") {
return FadePageRoute(builder: (context) => const HomePage());
} else if (settings.name == "/abc") {
return FadePageRoute(builder: (context) => const SecondPage());
}
return null;
},
),
);
}
}
class FadePageRoute<T> extends CupertinoPageRoute<T> {
FadePageRoute({required super.builder});
@override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
final widget =
super.buildTransitions(context, animation, secondaryAnimation, child);
if (widget is CupertinoPageTransition) {
return FadeTransition(
opacity: animation,
child: widget.child,
);
} else {
return widget;
}
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: TextButton(
child: const Text('Next Page',
style: TextStyle(
fontSize: 48,
)),
onPressed: () {
Navigator.of(context).pushNamed('/abc');
},
),
),
);
}
}
class SecondPage extends StatelessWidget {
const SecondPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: const Center(
child: Text(
'2nd Page',
style: TextStyle(
fontSize: 48,
),
),
),
);
}
}
Output: https://youtube.com/shorts/dQpfyEH9frE
Please let me know if this is something which you wanted.