анимация circular reveal при смене темы
This commit is contained in:
+94
-2
@@ -1,4 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -31,6 +33,7 @@ import 'frontend/debug/fps_overlay_layer.dart';
|
||||
import 'frontend/screens/auth/login_screen.dart';
|
||||
import 'frontend/screens/chats/chat_list_screen.dart';
|
||||
import 'frontend/widgets/custom_notification.dart';
|
||||
import 'frontend/widgets/theme_reveal.dart';
|
||||
|
||||
final api = Api();
|
||||
final accountModule = AccountModule(api);
|
||||
@@ -128,9 +131,15 @@ class KometApp extends StatefulWidget {
|
||||
State<KometApp> createState() => KometAppState();
|
||||
}
|
||||
|
||||
class KometAppState extends State<KometApp> with WidgetsBindingObserver {
|
||||
class KometAppState extends State<KometApp>
|
||||
with WidgetsBindingObserver, TickerProviderStateMixin {
|
||||
static const _fallbackSeed = Color(0xFFC1C4FF);
|
||||
|
||||
final GlobalKey _captureBoundaryKey = GlobalKey();
|
||||
OverlayEntry? _revealEntry;
|
||||
AnimationController? _revealController;
|
||||
ui.Image? _revealImage;
|
||||
|
||||
late Locale _locale;
|
||||
late String _fontId;
|
||||
bool _isLoggingOut = false;
|
||||
@@ -239,6 +248,7 @@ class KometAppState extends State<KometApp> with WidgetsBindingObserver {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_finishReveal();
|
||||
_sessionExpiredSub?.cancel();
|
||||
_loginStatusSub?.cancel();
|
||||
_vpnBypassSub?.cancel();
|
||||
@@ -321,10 +331,89 @@ class KometAppState extends State<KometApp> with WidgetsBindingObserver {
|
||||
await AppThemeModeConfig.save(mode);
|
||||
}
|
||||
|
||||
void applyThemeModeWithReveal(AppThemeMode mode, Offset center) {
|
||||
if (AppThemeModeConfig.current.value == mode) return;
|
||||
_runThemeReveal(center, () => AppThemeModeConfig.save(mode));
|
||||
}
|
||||
|
||||
Future<void> applyAmoled(bool value) async {
|
||||
await AppAmoled.save(value);
|
||||
}
|
||||
|
||||
void applyAmoledWithReveal(bool value, Offset center) {
|
||||
if (AppAmoled.current.value == value) return;
|
||||
_runThemeReveal(center, () => AppAmoled.save(value));
|
||||
}
|
||||
|
||||
void _runThemeReveal(Offset center, Future<void> Function() apply) {
|
||||
final overlay = KometApp.navigatorKey.currentState?.overlay;
|
||||
final ctx = _captureBoundaryKey.currentContext;
|
||||
if (overlay == null || ctx == null) {
|
||||
apply();
|
||||
return;
|
||||
}
|
||||
if (MediaQuery.disableAnimationsOf(ctx)) {
|
||||
apply();
|
||||
return;
|
||||
}
|
||||
final renderObject = ctx.findRenderObject();
|
||||
if (renderObject is! RenderRepaintBoundary) {
|
||||
apply();
|
||||
return;
|
||||
}
|
||||
|
||||
final ui.Image snapshot;
|
||||
try {
|
||||
final dpr = math.min(MediaQuery.of(ctx).devicePixelRatio, 2.0);
|
||||
snapshot = renderObject.toImageSync(pixelRatio: dpr);
|
||||
} catch (_) {
|
||||
apply();
|
||||
return;
|
||||
}
|
||||
|
||||
_finishReveal();
|
||||
|
||||
final controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 650),
|
||||
);
|
||||
final entry = ThemeRevealOverlay.build(
|
||||
snapshot: snapshot,
|
||||
center: center,
|
||||
animation: controller,
|
||||
);
|
||||
|
||||
_revealController = controller;
|
||||
_revealEntry = entry;
|
||||
_revealImage = snapshot;
|
||||
|
||||
overlay.insert(entry);
|
||||
apply();
|
||||
|
||||
WidgetsBinding.instance.endOfFrame.then((_) {
|
||||
if (_revealController != controller) return;
|
||||
controller.forward().then(
|
||||
(_) {
|
||||
if (_revealController != controller) return;
|
||||
_finishReveal();
|
||||
},
|
||||
onError: (_) {},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void _finishReveal() {
|
||||
_revealEntry?.remove();
|
||||
_revealEntry = null;
|
||||
_revealController?.dispose();
|
||||
_revealController = null;
|
||||
final img = _revealImage;
|
||||
_revealImage = null;
|
||||
if (img != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => img.dispose());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> applyThemeSchedule(ThemeSchedule schedule) async {
|
||||
await AppThemeSchedule.save(schedule);
|
||||
}
|
||||
@@ -553,7 +642,10 @@ class KometAppState extends State<KometApp> with WidgetsBindingObserver {
|
||||
fit: StackFit.expand,
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
sChild!,
|
||||
RepaintBoundary(
|
||||
key: _captureBoundaryKey,
|
||||
child: sChild!,
|
||||
),
|
||||
if (fpsOn) const FpsOverlayLayer(),
|
||||
],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user