#1102 accessibility: enable/disable more animations
This commit is contained in:
parent
fcde32d555
commit
e3f6644366
5 changed files with 61 additions and 41 deletions
|
@ -32,6 +32,7 @@ class ADurations {
|
||||||
|
|
||||||
// search animations
|
// search animations
|
||||||
static const filterRowExpandAnimation = Duration(milliseconds: 300);
|
static const filterRowExpandAnimation = Duration(milliseconds: 300);
|
||||||
|
static const searchBodyTransition = Duration(milliseconds: 300);
|
||||||
|
|
||||||
// viewer animations
|
// viewer animations
|
||||||
static const thumbnailScrollerScrollAnimation = Duration(milliseconds: 200);
|
static const thumbnailScrollerScrollAnimation = Duration(milliseconds: 200);
|
||||||
|
|
|
@ -665,6 +665,9 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flutter has various overscroll indicator implementations for Android:
|
||||||
|
// - `StretchingOverscrollIndicator`, default when using Material 3
|
||||||
|
// - `GlowingOverscrollIndicator`, default when not using Material 3
|
||||||
class AvesScrollBehavior extends MaterialScrollBehavior {
|
class AvesScrollBehavior extends MaterialScrollBehavior {
|
||||||
@override
|
@override
|
||||||
Widget buildOverscrollIndicator(BuildContext context, Widget child, ScrollableDetails details) {
|
Widget buildOverscrollIndicator(BuildContext context, Widget child, ScrollableDetails details) {
|
||||||
|
@ -674,11 +677,7 @@ class AvesScrollBehavior extends MaterialScrollBehavior {
|
||||||
axisDirection: details.direction,
|
axisDirection: details.direction,
|
||||||
child: child,
|
child: child,
|
||||||
)
|
)
|
||||||
: GlowingOverscrollIndicator(
|
: child;
|
||||||
axisDirection: details.direction,
|
|
||||||
color: Colors.white,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:aves/widgets/common/search/route.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
abstract class AvesSearchDelegate extends SearchDelegate {
|
abstract class AvesSearchDelegate extends SearchDelegate {
|
||||||
final String routeName;
|
final String routeName;
|
||||||
|
@ -38,12 +39,13 @@ abstract class AvesSearchDelegate extends SearchDelegate {
|
||||||
// use a property instead of checking `Navigator.canPop(context)`
|
// use a property instead of checking `Navigator.canPop(context)`
|
||||||
// because the navigator state changes as soon as we press back
|
// because the navigator state changes as soon as we press back
|
||||||
// so the leading may mistakenly switch to the close button
|
// so the leading may mistakenly switch to the close button
|
||||||
|
final animate = context.read<Settings>().animate;
|
||||||
return canPop
|
return canPop
|
||||||
? IconButton(
|
? IconButton(
|
||||||
icon: AnimatedIcon(
|
icon: animate ? AnimatedIcon(
|
||||||
icon: AnimatedIcons.menu_arrow,
|
icon: AnimatedIcons.menu_arrow,
|
||||||
progress: transitionAnimation,
|
progress: transitionAnimation,
|
||||||
),
|
): const Icon(Icons.arrow_back),
|
||||||
onPressed: () => goBack(context),
|
onPressed: () => goBack(context),
|
||||||
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
|
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/theme/themes.dart';
|
import 'package:aves/theme/themes.dart';
|
||||||
import 'package:aves/utils/debouncer.dart';
|
import 'package:aves/utils/debouncer.dart';
|
||||||
|
@ -10,6 +11,7 @@ import 'package:aves/widgets/common/search/delegate.dart';
|
||||||
import 'package:aves/widgets/common/search/route.dart';
|
import 'package:aves/widgets/common/search/route.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class SearchPage extends StatefulWidget {
|
class SearchPage extends StatefulWidget {
|
||||||
static const routeName = '/search';
|
static const routeName = '/search';
|
||||||
|
@ -103,7 +105,24 @@ class _SearchPageState extends State<SearchPage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
Widget? body;
|
|
||||||
|
Widget leading = Center(child: widget.delegate.buildLeading(context));
|
||||||
|
Widget title = DefaultTextStyle.merge(
|
||||||
|
style: const TextStyle(fontFeatures: [FontFeature.disable('smcp')]),
|
||||||
|
child: TextField(
|
||||||
|
controller: widget.delegate.queryTextController,
|
||||||
|
focusNode: _searchFieldFocusNode,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
hintText: widget.delegate.searchFieldLabel,
|
||||||
|
hintStyle: theme.inputDecorationTheme.hintStyle,
|
||||||
|
),
|
||||||
|
textInputAction: TextInputAction.search,
|
||||||
|
style: Themes.searchFieldStyle(context),
|
||||||
|
onSubmitted: (_) => widget.delegate.showResults(context),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Widget body;
|
||||||
switch (widget.delegate.currentBody) {
|
switch (widget.delegate.currentBody) {
|
||||||
case SearchBody.suggestions:
|
case SearchBody.suggestions:
|
||||||
body = KeyedSubtree(
|
body = KeyedSubtree(
|
||||||
|
@ -116,34 +135,31 @@ class _SearchPageState extends State<SearchPage> {
|
||||||
child: widget.delegate.buildResults(context),
|
child: widget.delegate.buildResults(context),
|
||||||
);
|
);
|
||||||
case null:
|
case null:
|
||||||
break;
|
body = const SizedBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final animate = context.select<Settings, bool>((v) => v.animate);
|
||||||
|
if (animate) {
|
||||||
|
leading = Hero(
|
||||||
|
tag: AvesAppBar.leadingHeroTag,
|
||||||
|
transitionOnUserGestures: true,
|
||||||
|
child: leading,
|
||||||
|
);
|
||||||
|
title = Hero(
|
||||||
|
tag: AvesAppBar.titleHeroTag,
|
||||||
|
transitionOnUserGestures: true,
|
||||||
|
child: title,
|
||||||
|
);
|
||||||
|
body = AnimatedSwitcher(
|
||||||
|
duration: ADurations.searchBodyTransition,
|
||||||
|
child: body,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return AvesScaffold(
|
return AvesScaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: Hero(
|
leading: leading,
|
||||||
tag: AvesAppBar.leadingHeroTag,
|
title: title,
|
||||||
transitionOnUserGestures: true,
|
|
||||||
child: Center(child: widget.delegate.buildLeading(context)),
|
|
||||||
),
|
|
||||||
title: Hero(
|
|
||||||
tag: AvesAppBar.titleHeroTag,
|
|
||||||
transitionOnUserGestures: true,
|
|
||||||
child: DefaultTextStyle.merge(
|
|
||||||
style: const TextStyle(fontFeatures: [FontFeature.disable('smcp')]),
|
|
||||||
child: TextField(
|
|
||||||
controller: widget.delegate.queryTextController,
|
|
||||||
focusNode: _searchFieldFocusNode,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: InputBorder.none,
|
|
||||||
hintText: widget.delegate.searchFieldLabel,
|
|
||||||
hintStyle: theme.inputDecorationTheme.hintStyle,
|
|
||||||
),
|
|
||||||
textInputAction: TextInputAction.search,
|
|
||||||
style: Themes.searchFieldStyle(context),
|
|
||||||
onSubmitted: (_) => widget.delegate.showResults(context),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actions: widget.delegate.buildActions(context),
|
actions: widget.delegate.buildActions(context),
|
||||||
),
|
),
|
||||||
body: AvesPopScope(
|
body: AvesPopScope(
|
||||||
|
@ -151,10 +167,7 @@ class _SearchPageState extends State<SearchPage> {
|
||||||
tvNavigationPopHandler,
|
tvNavigationPopHandler,
|
||||||
doubleBackPopHandler,
|
doubleBackPopHandler,
|
||||||
],
|
],
|
||||||
child: AnimatedSwitcher(
|
child: body,
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
child: body,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/widgets/common/search/delegate.dart';
|
import 'package:aves/widgets/common/search/delegate.dart';
|
||||||
import 'package:aves/widgets/common/search/page.dart';
|
import 'package:aves/widgets/common/search/page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
// adapted from Flutter `_SearchBody` in `/material/search.dart`
|
// adapted from Flutter `_SearchBody` in `/material/search.dart`
|
||||||
enum SearchBody { suggestions, results }
|
enum SearchBody { suggestions, results }
|
||||||
|
@ -42,10 +44,13 @@ class SearchPageRoute<T> extends PageRoute<T> {
|
||||||
) {
|
) {
|
||||||
// a simple fade is usually more fitting for a search page,
|
// a simple fade is usually more fitting for a search page,
|
||||||
// instead of the `pageTransitionsTheme` used by the rest of the app
|
// instead of the `pageTransitionsTheme` used by the rest of the app
|
||||||
return FadeTransition(
|
final animate = context.read<Settings>().animate;
|
||||||
opacity: animation,
|
return animate
|
||||||
child: child,
|
? FadeTransition(
|
||||||
);
|
opacity: animation,
|
||||||
|
child: child,
|
||||||
|
)
|
||||||
|
: child;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
Loading…
Reference in a new issue