I'm creating a responsive MaterialPageRoute
, that combines the current MaterialPageRoute
for small screens, and a dialog for big screens. Basically I want to create a new type of route that accepts a parameter called breakingWidth
, and changes it's behavior based on that width.
- On mobile devices it will behave like the current
MaterialPageRoute
with it's correspondent animation for iOS and Android - On tablet and desktop it will behave like a
showDialog
, with a Dialog on the middle of the screen
I have copied some code of the current implementation of the MaterialPageRoute
, and done some changes to adapt it to the behavior that I want.
This is the code that I currently have. I have removed big comment blocks and imports to make the code block smaller.
class ResponsiveMaterialPageRoute<T> extends PageRoute<T> with MaterialRouteTransitionMixin<T> {
/// Construct a MaterialPageRoute whose contents are defined by [builder].
ResponsiveMaterialPageRoute({
required this.breakingWidth,
required this.builder,
this.maintainState = true,
});
final WidgetBuilder builder;
@override
final double breakingWidth;
@override
final bool opaque = false;
@override
Widget buildContent(BuildContext context) {
final width = MediaQuery.of(context).size.width;
if (width > breakingWidth) {
return Dialog(
child: ClipRRect(
borderRadius: BorderRadius.circular(28),
child: builder(context)
),
);
}
else {
return builder(context);
}
}
@override
final bool maintainState;
@override
String get debugLabel => '${super.debugLabel}(${settings.name})';
}
mixin MaterialRouteTransitionMixin<T> on PageRoute<T> {
/// Builds the primary contents of the route.
@protected
Widget buildContent(BuildContext context);
@override
Duration get transitionDuration => const Duration(milliseconds: 300);
@override
Color? get barrierColor => null;
@override
String? get barrierLabel => null;
late final double breakingWidth;
@override
bool canTransitionTo(TransitionRoute<dynamic> nextRoute) {
// Don't perform outgoing animation if the next route is a fullscreen dialog.
return (nextRoute is MaterialRouteTransitionMixin && !nextRoute.fullscreenDialog)
|| (nextRoute is CupertinoRouteTransitionMixin && !nextRoute.fullscreenDialog);
}
@override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
final Widget result = buildContent(context);
return Semantics(
scopesRoute: true,
explicitChildNodes: true,
child: result,
);
}
@override
Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
if (MediaQuery.of(context).size.width > breakingWidth) {
return FadeTransition(
opacity: CurvedAnimation(
parent: animation,
curve: Curves.easeOut,
),
child: child,
);
}
else {
final PageTransitionsTheme theme = Theme.of(context).pageTransitionsTheme;
return theme.buildTransitions<T>(this, context, animation, secondaryAnimation, child);
}
}
}
Here you have an example of the current behavior. The only thing that's remaining to do is to add the translucent overlay behind the route, but I don't know how to do it. Hopefully someone can help me. example