respect disableAnimations flag

This commit is contained in:
Fabian Freund
2026-03-09 18:03:34 +01:00
parent a815a3529a
commit 0a8348b324
8 changed files with 102 additions and 19 deletions
@@ -76,8 +76,10 @@ class _AnimatedToolbar extends HookWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final disableAnimations = MediaQuery.disableAnimationsOf(context);
final controller = useAnimationController( final controller = useAnimationController(
duration: _kAnimationDuration, duration: disableAnimations ? Duration.zero : _kAnimationDuration,
initialValue: visible ? 1.0 : 0.0, initialValue: visible ? 1.0 : 0.0,
); );
@@ -618,10 +618,7 @@ class _PinTopSiteTile extends HookConsumerWidget {
} else { } else {
await ref await ref
.read(topSiteRepositoryProvider.notifier) .read(topSiteRepositoryProvider.notifier)
.addPinnedSite( .addPinnedSite(title: tabState.titleOrAuthority, url: url);
title: tabState.titleOrAuthority,
url: url,
);
if (context.mounted) { if (context.mounted) {
ui_helper.showInfoMessage(context, 'Pinned to Top Sites'); ui_helper.showInfoMessage(context, 'Pinned to Top Sites');
} }
@@ -1932,19 +1929,21 @@ class _SyncTile extends HookConsumerWidget {
); );
final isSyncing = syncStarted || syncInfo?.syncing == true; final isSyncing = syncStarted || syncInfo?.syncing == true;
final disableAnimations = MediaQuery.disableAnimationsOf(context);
final controller = useAnimationController( final controller = useAnimationController(
duration: const Duration(seconds: 2), duration: disableAnimations ? Duration.zero : const Duration(seconds: 2),
); );
useEffect(() { useEffect(() {
if (isSyncing) { if (isSyncing && !disableAnimations) {
unawaited(controller.repeat()); unawaited(controller.repeat());
} else { } else {
controller.stop(); controller.stop();
controller.reset(); controller.reset();
} }
return null; return null;
}, [isSyncing]); }, [isSyncing, disableAnimations]);
return ListTile( return ListTile(
leading: RotationTransition( leading: RotationTransition(
@@ -74,6 +74,11 @@ class _DraggableScrollableHeaderState extends State<DraggableScrollableHeader>
double targetSize, { double targetSize, {
Duration? customDuration, Duration? customDuration,
}) async { }) async {
if (MediaQuery.disableAnimationsOf(context)) {
widget.controller.jumpTo(targetSize);
return;
}
if (customDuration != null) { if (customDuration != null) {
_animationController.duration = customDuration; _animationController.duration = customDuration;
} else { } else {
@@ -53,19 +53,21 @@ class SyncSettingsScreen extends HookConsumerWidget {
return 'Last synced: $formattedDate'; return 'Last synced: $formattedDate';
}, [syncInfo, isSyncing]); }, [syncInfo, isSyncing]);
final disableAnimations = MediaQuery.disableAnimationsOf(context);
final syncController = useAnimationController( final syncController = useAnimationController(
duration: const Duration(seconds: 2), duration: disableAnimations ? Duration.zero : const Duration(seconds: 2),
); );
useEffect(() { useEffect(() {
if (isSyncing) { if (isSyncing && !disableAnimations) {
unawaited(syncController.repeat()); unawaited(syncController.repeat());
} else { } else {
syncController.stop(); syncController.stop();
syncController.reset(); syncController.reset();
} }
return null; return null;
}, [isSyncing]); }, [isSyncing, disableAnimations]);
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('Firefox Sync')), appBar: AppBar(title: const Text('Firefox Sync')),
+31
View File
@@ -81,6 +81,31 @@ bool _hasBrokenSurfaceContainerColors(ColorScheme scheme) {
scheme.surfaceContainerHighest == scheme.surface; scheme.surfaceContainerHighest == scheme.surface;
} }
class _NoAnimationPageTransitionsBuilder extends PageTransitionsBuilder {
const _NoAnimationPageTransitionsBuilder();
@override
Widget buildTransitions<T>(
PageRoute<T> route,
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return child;
}
}
const _noAnimationPageTransitionsTheme = PageTransitionsTheme(
builders: {
TargetPlatform.android: _NoAnimationPageTransitionsBuilder(),
TargetPlatform.iOS: _NoAnimationPageTransitionsBuilder(),
TargetPlatform.linux: _NoAnimationPageTransitionsBuilder(),
TargetPlatform.macOS: _NoAnimationPageTransitionsBuilder(),
TargetPlatform.windows: _NoAnimationPageTransitionsBuilder(),
},
);
class _MainWidget extends HookConsumerWidget { class _MainWidget extends HookConsumerWidget {
const _MainWidget(); const _MainWidget();
@@ -257,11 +282,17 @@ class _MainWidget extends HookConsumerWidget {
theme: ThemeData( theme: ThemeData(
useMaterial3: true, useMaterial3: true,
colorScheme: lightColorScheme, colorScheme: lightColorScheme,
pageTransitionsTheme: disableAnimations
? _noAnimationPageTransitionsTheme
: null,
extensions: const <ThemeExtension<dynamic>>[AppColors.light], extensions: const <ThemeExtension<dynamic>>[AppColors.light],
), ),
darkTheme: ThemeData( darkTheme: ThemeData(
useMaterial3: true, useMaterial3: true,
colorScheme: darkColorScheme, colorScheme: darkColorScheme,
pageTransitionsTheme: disableAnimations
? _noAnimationPageTransitionsTheme
: null,
extensions: const <ThemeExtension<dynamic>>[AppColors.dark], extensions: const <ThemeExtension<dynamic>>[AppColors.dark],
), ),
themeMode: themeMode, themeMode: themeMode,
+9
View File
@@ -55,6 +55,9 @@ class MainApp extends HookConsumerWidget {
theme: theme, theme: theme,
darkTheme: darkTheme, darkTheme: darkTheme,
themeMode: themeMode, themeMode: themeMode,
themeAnimationStyle: disableAnimations
? AnimationStyle.noAnimation
: null,
builder: (context, child) { builder: (context, child) {
return _AppMediaQueryOverrides( return _AppMediaQueryOverrides(
uiScaleFactor: uiScaleFactor, uiScaleFactor: uiScaleFactor,
@@ -85,6 +88,9 @@ class MainApp extends HookConsumerWidget {
theme: theme, theme: theme,
darkTheme: darkTheme, darkTheme: darkTheme,
themeMode: themeMode, themeMode: themeMode,
themeAnimationStyle: disableAnimations
? AnimationStyle.noAnimation
: null,
routerConfig: router.value, routerConfig: router.value,
builder: (context, child) { builder: (context, child) {
return _AppMediaQueryOverrides( return _AppMediaQueryOverrides(
@@ -103,6 +109,9 @@ class MainApp extends HookConsumerWidget {
theme: theme, theme: theme,
darkTheme: darkTheme, darkTheme: darkTheme,
themeMode: themeMode, themeMode: themeMode,
themeAnimationStyle: disableAnimations
? AnimationStyle.noAnimation
: null,
builder: (context, child) { builder: (context, child) {
return _AppMediaQueryOverrides( return _AppMediaQueryOverrides(
uiScaleFactor: uiScaleFactor, uiScaleFactor: uiScaleFactor,
@@ -120,12 +120,24 @@ class _AnimateGradientShaderState extends State<AnimateGradientShader>
List<Color> primaryColors = []; List<Color> primaryColors = [];
List<Color> secondaryColors = []; List<Color> secondaryColors = [];
bool _disableAnimations = false;
@override @override
void initState() { void initState() {
_initialize(); _initialize();
super.initState(); super.initState();
} }
@override
void didChangeDependencies() {
super.didChangeDependencies();
final disableAnimations = MediaQuery.disableAnimationsOf(context);
if (disableAnimations != _disableAnimations) {
_disableAnimations = disableAnimations;
_setAnimations();
}
}
@override @override
void didUpdateWidget(AnimateGradientShader oldWidget) { void didUpdateWidget(AnimateGradientShader oldWidget) {
_initialize(); _initialize();
@@ -222,10 +234,15 @@ class _AnimateGradientShaderState extends State<AnimateGradientShader>
void _setAnimations() { void _setAnimations() {
_controller?.dispose(); _controller?.dispose();
_controller = _controller =
(widget.controller ?? widget.controller ??
AnimationController(vsync: this, duration: widget.duration)) AnimationController(vsync: this, duration: widget.duration);
// ignore: discarded_futures
..repeat(reverse: widget.reverse); if (_disableAnimations) {
_controller!.value = 0;
} else {
// ignore: discarded_futures
_controller!.repeat(reverse: widget.reverse);
}
_animation = CurvedAnimation(parent: _controller!, curve: Curves.easeInOut); _animation = CurvedAnimation(parent: _controller!, curve: Curves.easeInOut);
} }
@@ -129,12 +129,30 @@ class _AnimatedIndexedStackState extends State<AnimatedIndexedStack>
/// The entry which is currently at the top of the stack. /// The entry which is currently at the top of the stack.
_ChildEntry? _currentEntry; _ChildEntry? _currentEntry;
bool _disableAnimations = false;
Duration get _effectiveDuration =>
_disableAnimations ? Duration.zero : widget.duration;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_updateEntriesList(); _updateEntriesList();
} }
@override
void didChangeDependencies() {
super.didChangeDependencies();
final disableAnimations = MediaQuery.disableAnimationsOf(context);
if (disableAnimations != _disableAnimations) {
_disableAnimations = disableAnimations;
for (final entry in _entries) {
entry.primaryController.duration = _effectiveDuration;
entry.secondaryController.duration = _effectiveDuration;
}
}
}
@override @override
void didUpdateWidget(AnimatedIndexedStack oldWidget) { void didUpdateWidget(AnimatedIndexedStack oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
@@ -207,8 +225,8 @@ class _AnimatedIndexedStackState extends State<AnimatedIndexedStack>
// If we find an existing entry, we update its child widget and reuse it. // If we find an existing entry, we update its child widget and reuse it.
// This ensures it continues to use the same global key and animation controllers. // This ensures it continues to use the same global key and animation controllers.
existingEntry.child = child; existingEntry.child = child;
existingEntry.primaryController.duration = widget.duration; existingEntry.primaryController.duration = _effectiveDuration;
existingEntry.secondaryController.duration = widget.duration; existingEntry.secondaryController.duration = _effectiveDuration;
entry = existingEntry; entry = existingEntry;
} else { } else {
entry = _newEntry(child); entry = _newEntry(child);
@@ -299,11 +317,11 @@ class _AnimatedIndexedStackState extends State<AnimatedIndexedStack>
key: GlobalKey(), key: GlobalKey(),
child: child, child: child,
primaryController: AnimationController( primaryController: AnimationController(
duration: widget.duration, duration: _effectiveDuration,
vsync: this, vsync: this,
), ),
secondaryController: AnimationController( secondaryController: AnimationController(
duration: widget.duration, duration: _effectiveDuration,
vsync: this, vsync: this,
), ),
); );