diff --git a/lib/theme/durations.dart b/lib/theme/durations.dart index 4fa368f36..8e03c5a52 100644 --- a/lib/theme/durations.dart +++ b/lib/theme/durations.dart @@ -32,6 +32,7 @@ class ADurations { // search animations static const filterRowExpandAnimation = Duration(milliseconds: 300); + static const searchBodyTransition = Duration(milliseconds: 300); // viewer animations static const thumbnailScrollerScrollAnimation = Duration(milliseconds: 200); diff --git a/lib/widgets/aves_app.dart b/lib/widgets/aves_app.dart index 055674ecb..38b15a934 100644 --- a/lib/widgets/aves_app.dart +++ b/lib/widgets/aves_app.dart @@ -665,6 +665,9 @@ class _AvesAppState extends State 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 { @override Widget buildOverscrollIndicator(BuildContext context, Widget child, ScrollableDetails details) { @@ -674,11 +677,7 @@ class AvesScrollBehavior extends MaterialScrollBehavior { axisDirection: details.direction, child: child, ) - : GlowingOverscrollIndicator( - axisDirection: details.direction, - color: Colors.white, - child: child, - ); + : child; } } diff --git a/lib/widgets/common/search/delegate.dart b/lib/widgets/common/search/delegate.dart index edd04390b..14d6a5a81 100644 --- a/lib/widgets/common/search/delegate.dart +++ b/lib/widgets/common/search/delegate.dart @@ -5,6 +5,7 @@ import 'package:aves/widgets/common/search/route.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:provider/provider.dart'; abstract class AvesSearchDelegate extends SearchDelegate { final String routeName; @@ -38,12 +39,13 @@ abstract class AvesSearchDelegate extends SearchDelegate { // use a property instead of checking `Navigator.canPop(context)` // because the navigator state changes as soon as we press back // so the leading may mistakenly switch to the close button + final animate = context.read().animate; return canPop ? IconButton( - icon: AnimatedIcon( + icon: animate ? AnimatedIcon( icon: AnimatedIcons.menu_arrow, progress: transitionAnimation, - ), + ): const Icon(Icons.arrow_back), onPressed: () => goBack(context), tooltip: MaterialLocalizations.of(context).backButtonTooltip, ) diff --git a/lib/widgets/common/search/page.dart b/lib/widgets/common/search/page.dart index 6bbf50f8f..fa267e629 100644 --- a/lib/widgets/common/search/page.dart +++ b/lib/widgets/common/search/page.dart @@ -1,3 +1,4 @@ +import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/themes.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:flutter/material.dart'; import 'package:flutter/scheduler.dart'; +import 'package:provider/provider.dart'; class SearchPage extends StatefulWidget { static const routeName = '/search'; @@ -103,7 +105,24 @@ class _SearchPageState extends State { @override Widget build(BuildContext 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) { case SearchBody.suggestions: body = KeyedSubtree( @@ -116,34 +135,31 @@ class _SearchPageState extends State { child: widget.delegate.buildResults(context), ); case null: - break; + body = const SizedBox(); } + + final animate = context.select((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( appBar: AppBar( - leading: Hero( - tag: AvesAppBar.leadingHeroTag, - 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), - ), - ), - ), + leading: leading, + title: title, actions: widget.delegate.buildActions(context), ), body: AvesPopScope( @@ -151,10 +167,7 @@ class _SearchPageState extends State { tvNavigationPopHandler, doubleBackPopHandler, ], - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: body, - ), + child: body, ), ); } diff --git a/lib/widgets/common/search/route.dart b/lib/widgets/common/search/route.dart index 8b5d15452..6813b6892 100644 --- a/lib/widgets/common/search/route.dart +++ b/lib/widgets/common/search/route.dart @@ -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/page.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; // adapted from Flutter `_SearchBody` in `/material/search.dart` enum SearchBody { suggestions, results } @@ -42,10 +44,13 @@ class SearchPageRoute extends PageRoute { ) { // a simple fade is usually more fitting for a search page, // instead of the `pageTransitionsTheme` used by the rest of the app - return FadeTransition( - opacity: animation, - child: child, - ); + final animate = context.read().animate; + return animate + ? FadeTransition( + opacity: animation, + child: child, + ) + : child; } @override