Support proxies search Support svg display Optimize config persistence Add some scenes auto close connections Update core Optimize more details
316 lines
8.7 KiB
Dart
316 lines
8.7 KiB
Dart
import 'package:fl_clash/common/common.dart';
|
|
import 'package:fl_clash/enum/enum.dart';
|
|
import 'package:fl_clash/models/models.dart';
|
|
import 'package:fl_clash/state.dart';
|
|
import 'package:fl_clash/widgets/dialog.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
class BaseNavigator {
|
|
static Future<T?> push<T>(BuildContext context, Widget child) async {
|
|
if (globalState.appState.viewMode != ViewMode.mobile) {
|
|
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,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
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 MaterialPageRoute<T> {
|
|
CommonRoute({
|
|
required super.builder,
|
|
});
|
|
|
|
@override
|
|
Duration get transitionDuration => const Duration(milliseconds: 500);
|
|
|
|
@override
|
|
Duration get reverseTransitionDuration => const Duration(milliseconds: 500);
|
|
}
|
|
|
|
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 Animation<Offset> delegatedPositionAnimation = CurvedAnimation(
|
|
parent: secondaryAnimation,
|
|
curve: Curves.linearToEaseOut,
|
|
reverseCurve: Curves.easeInToLinear,
|
|
).drive(_kMiddleLeftTween);
|
|
|
|
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>[
|
|
widget.context.colorScheme.inverseSurface.withValues(alpha: 0.02),
|
|
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 = 1 * 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);
|
|
}
|
|
}
|
|
}
|