Files
MWClash/lib/common/navigator.dart
chen08209 dbd8fd89fe Add sqlite store
Optimize android quick action

Optimize backup and restore

Optimize more details
2026-01-26 01:31:32 +08:00

347 lines
9.5 KiB
Dart

import 'package:animations/animations.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/controller.dart';
import 'package:flutter/material.dart';
class BaseNavigator {
static Future<T?> push<T>(BuildContext context, Widget child) async {
if (!appController.isMobile) {
return await Navigator.of(
context,
).push<T>(CommonDesktopRoute(builder: (context) => child));
}
return await Navigator.of(
context,
).push<T>(CommonRoute(builder: (context) => child));
}
// static Future<T?> modal<T>(BuildContext context, Widget child) async {
// if (globalState.appState.viewMode != ViewMode.mobile) {
// return await globalState.showCommonDialog<T>(
// child: CommonModal(
// child: child,
// ),
// );
// }
// return await Navigator.of(context).push<T>(
// CommonRoute(
// builder: (context) => child,
// ),
// );
// }
}
const commonSharedXPageTransitions = SharedAxisPageTransitionsBuilder(
transitionType: SharedAxisTransitionType.horizontal,
fillColor: Colors.transparent,
);
class CommonDesktopRoute<T> extends PageRoute<T> {
final Widget Function(BuildContext context) builder;
CommonDesktopRoute({required this.builder});
@override
Color? get barrierColor => null;
@override
String? get barrierLabel => null;
@override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
final Widget result = builder(context);
return Semantics(
scopesRoute: true,
explicitChildNodes: true,
child: FadeTransition(opacity: animation, child: result),
);
}
@override
bool get maintainState => true;
@override
Duration get transitionDuration => Duration(milliseconds: 200);
@override
Duration get reverseTransitionDuration => Duration(milliseconds: 200);
}
class CommonRoute<T> extends PageRoute<T> {
final Widget Function(BuildContext context) builder;
CommonRoute({required this.builder});
@override
Color? get barrierColor => null;
@override
String? get barrierLabel => null;
@override
bool get maintainState => true;
@override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
final Widget result = builder(context);
return Semantics(
scopesRoute: true,
explicitChildNodes: true,
child: SharedAxisTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
transitionType: SharedAxisTransitionType.horizontal,
fillColor: context.colorScheme.surface,
child: result,
),
);
}
@override
Duration get transitionDuration => Duration(milliseconds: 300);
@override
Duration get reverseTransitionDuration => Duration(milliseconds: 300);
}
final Animatable<Offset> _kRightMiddleTween = Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
);
final Animatable<Offset> _kMiddleLeftTween = Tween<Offset>(
begin: Offset.zero,
end: const Offset(-1.0 / 3.0, 0.0),
);
class CommonPageTransitionsBuilder extends PageTransitionsBuilder {
const CommonPageTransitionsBuilder();
@override
Widget buildTransitions<T>(
PageRoute<T> route,
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return CommonPageTransition(
context: context,
primaryRouteAnimation: animation,
secondaryRouteAnimation: secondaryAnimation,
linearTransition: false,
child: child,
);
}
}
class CommonPageTransition extends StatefulWidget {
const CommonPageTransition({
super.key,
required this.context,
required this.primaryRouteAnimation,
required this.secondaryRouteAnimation,
required this.child,
required this.linearTransition,
});
final Widget child;
final Animation<double> primaryRouteAnimation;
final Animation<double> secondaryRouteAnimation;
final BuildContext context;
final bool linearTransition;
static Widget? delegatedTransition(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
bool allowSnapshotting,
Widget? child,
) {
final CurvedAnimation animation = CurvedAnimation(
parent: secondaryAnimation,
curve: Curves.linearToEaseOut,
reverseCurve: Curves.easeInToLinear,
);
final Animation<Offset> delegatedPositionAnimation = animation.drive(
_kMiddleLeftTween,
);
animation.dispose();
assert(debugCheckHasDirectionality(context));
final TextDirection textDirection = Directionality.of(context);
return SlideTransition(
position: delegatedPositionAnimation,
textDirection: textDirection,
transformHitTests: false,
child: child,
);
}
@override
State<CommonPageTransition> createState() => _CommonPageTransitionState();
}
class _CommonPageTransitionState extends State<CommonPageTransition> {
late Animation<Offset> _primaryPositionAnimation;
late Animation<Offset> _secondaryPositionAnimation;
late Animation<Decoration> _primaryShadowAnimation;
CurvedAnimation? _primaryPositionCurve;
CurvedAnimation? _secondaryPositionCurve;
CurvedAnimation? _primaryShadowCurve;
@override
void initState() {
super.initState();
_setupAnimation();
}
@override
void didUpdateWidget(covariant CommonPageTransition oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.primaryRouteAnimation != widget.primaryRouteAnimation ||
oldWidget.secondaryRouteAnimation != widget.secondaryRouteAnimation ||
oldWidget.linearTransition != widget.linearTransition) {
_disposeCurve();
_setupAnimation();
}
}
@override
void dispose() {
_disposeCurve();
super.dispose();
}
void _disposeCurve() {
_primaryPositionCurve?.dispose();
_secondaryPositionCurve?.dispose();
_primaryShadowCurve?.dispose();
_primaryPositionCurve = null;
_secondaryPositionCurve = null;
_primaryShadowCurve = null;
}
void _setupAnimation() {
if (!widget.linearTransition) {
_primaryPositionCurve = CurvedAnimation(
parent: widget.primaryRouteAnimation,
curve: Curves.fastEaseInToSlowEaseOut,
reverseCurve: Curves.fastEaseInToSlowEaseOut.flipped,
);
_secondaryPositionCurve = CurvedAnimation(
parent: widget.secondaryRouteAnimation,
curve: Curves.linearToEaseOut,
reverseCurve: Curves.easeInToLinear,
);
_primaryShadowCurve = CurvedAnimation(
parent: widget.primaryRouteAnimation,
curve: Curves.linearToEaseOut,
);
}
_primaryPositionAnimation =
(_primaryPositionCurve ?? widget.primaryRouteAnimation).drive(
_kRightMiddleTween,
);
_secondaryPositionAnimation =
(_secondaryPositionCurve ?? widget.secondaryRouteAnimation).drive(
_kMiddleLeftTween,
);
_primaryShadowAnimation =
(_primaryShadowCurve ?? widget.primaryRouteAnimation).drive(
DecorationTween(
begin: const _CommonEdgeShadowDecoration(),
end: _CommonEdgeShadowDecoration(<Color>[
Color(0x04000000),
Colors.transparent,
]),
),
);
}
@override
Widget build(BuildContext context) {
assert(debugCheckHasDirectionality(context));
final TextDirection textDirection = Directionality.of(context);
return SlideTransition(
position: _secondaryPositionAnimation,
textDirection: textDirection,
transformHitTests: false,
child: SlideTransition(
position: _primaryPositionAnimation,
textDirection: textDirection,
child: DecoratedBoxTransition(
decoration: _primaryShadowAnimation,
child: widget.child,
),
),
);
}
}
class _CommonEdgeShadowDecoration extends Decoration {
final List<Color>? _colors;
const _CommonEdgeShadowDecoration([this._colors]);
@override
BoxPainter createBoxPainter([VoidCallback? onChanged]) {
return _CommonEdgeShadowPainter(this, onChanged);
}
}
class _CommonEdgeShadowPainter extends BoxPainter {
_CommonEdgeShadowPainter(this._decoration, super.onChanged)
: assert(_decoration._colors == null || _decoration._colors.length > 1);
final _CommonEdgeShadowDecoration _decoration;
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
final List<Color>? colors = _decoration._colors;
if (colors == null) {
return;
}
final double shadowWidth = 0.05 * configuration.size!.width;
final double shadowHeight = configuration.size!.height;
final double bandWidth = shadowWidth / (colors.length - 1);
final TextDirection? textDirection = configuration.textDirection;
assert(textDirection != null);
final (double shadowDirection, double start) = switch (textDirection!) {
TextDirection.rtl => (1, offset.dx + configuration.size!.width),
TextDirection.ltr => (-1, offset.dx),
};
int bandColorIndex = 0;
for (int dx = 0; dx < shadowWidth; dx += 1) {
if (dx ~/ bandWidth != bandColorIndex) {
bandColorIndex += 1;
}
final Paint paint = Paint()
..color = Color.lerp(
colors[bandColorIndex],
colors[bandColorIndex + 1],
(dx % bandWidth) / bandWidth,
)!;
final double x = start + shadowDirection * dx;
canvas.drawRect(
Rect.fromLTWH(x - 1.0, offset.dy, 1.0, shadowHeight),
paint,
);
}
}
}