diff --git a/lib/widgets/aves_app.dart b/lib/widgets/aves_app.dart index 9947415ae..2ea40a119 100644 --- a/lib/widgets/aves_app.dart +++ b/lib/widgets/aves_app.dart @@ -126,7 +126,11 @@ class _AvesAppState extends State with WidgetsBindingObserver { // - `FadeUpwardsPageTransitionsBuilder` on Oreo / API 27 and below // - `OpenUpwardsPageTransitionsBuilder` on Pie / API 28 // - `ZoomPageTransitionsBuilder` on Android 10 / API 29 and above (default in Flutter v3.0.0) - ? const PageTransitionsTheme() + ? const PageTransitionsTheme( + builders: { + TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(), + }, + ) // strip page transitions used by `MaterialPageRoute` : const DirectPageTransitionsTheme(); diff --git a/lib/widgets/common/identity/aves_app_bar.dart b/lib/widgets/common/identity/aves_app_bar.dart index a3199e672..0af883226 100644 --- a/lib/widgets/common/identity/aves_app_bar.dart +++ b/lib/widgets/common/identity/aves_app_bar.dart @@ -13,6 +13,9 @@ class AvesAppBar extends StatelessWidget { final Widget? bottom; final Object? transitionKey; + static const leadingHeroTag = 'appbar-leading'; + static const titleHeroTag = 'appbar-title'; + const AvesAppBar({ super.key, required this.contentHeight, @@ -49,17 +52,27 @@ class AvesAppBar extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 4), - child: leading, + child: Hero( + tag: leadingHeroTag, + flightShuttleBuilder: _flightShuttleBuilder, + transitionOnUserGestures: true, + child: leading, + ), ), Expanded( - child: AnimatedSwitcher( - duration: context.read().iconAnimation, - child: Row( - key: ValueKey(transitionKey), - children: [ - Expanded(child: title), - ...actions, - ], + child: Hero( + tag: titleHeroTag, + flightShuttleBuilder: _flightShuttleBuilder, + transitionOnUserGestures: true, + child: AnimatedSwitcher( + duration: context.read().iconAnimation, + child: Row( + key: ValueKey(transitionKey), + children: [ + Expanded(child: title), + ...actions, + ], + ), ), ), ), @@ -77,6 +90,38 @@ class AvesAppBar extends StatelessWidget { ); } + static Widget _flightShuttleBuilder( + BuildContext flightContext, + Animation animation, + HeroFlightDirection direction, + BuildContext fromHero, + BuildContext toHero, + ) { + final pushing = direction == HeroFlightDirection.push; + Widget popBuilder(context, child) => Opacity(opacity: 1 - animation.value, child: child); + Widget pushBuilder(context, child) => Opacity(opacity: animation.value, child: child); + return Material( + type: MaterialType.transparency, + child: DefaultTextStyle( + style: DefaultTextStyle.of(toHero).style, + child: Stack( + children: [ + AnimatedBuilder( + animation: animation, + builder: pushing ? popBuilder : pushBuilder, + child: fromHero.widget, + ), + AnimatedBuilder( + animation: animation, + builder: pushing ? pushBuilder : popBuilder, + child: toHero.widget, + ), + ], + ), + ), + ); + } + static double appBarHeightForContentHeight(double contentHeight) => AvesFloatingBar.margin.vertical + contentHeight; } diff --git a/lib/widgets/search/search_page.dart b/lib/widgets/search/search_page.dart index 937eba50c..cf4056862 100644 --- a/lib/widgets/search/search_page.dart +++ b/lib/widgets/search/search_page.dart @@ -3,8 +3,10 @@ import 'dart:ui'; import 'package:aves/theme/durations.dart'; import 'package:aves/utils/debouncer.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:aves/widgets/common/identity/aves_app_bar.dart'; import 'package:aves/widgets/search/search_delegate.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; class SearchPage extends StatefulWidget { static const routeName = '/search'; @@ -51,7 +53,10 @@ class _SearchPageState extends State { return; } widget.animation.removeStatusListener(_onAnimationStatusChanged); - _focusNode.requestFocus(); + Future.delayed(Durations.pageTransitionAnimation * timeDilation).then((_) { + if (!mounted) return; + _focusNode.requestFocus(); + }); } @override @@ -110,19 +115,27 @@ class _SearchPageState extends State { } return Scaffold( appBar: AppBar( - leading: widget.delegate.buildLeading(context), - title: DefaultTextStyle.merge( - style: const TextStyle(fontFeatures: [FontFeature.disable('smcp')]), - child: TextField( - controller: widget.delegate.queryTextController, - focusNode: _focusNode, - style: theme.textTheme.headline6, - textInputAction: TextInputAction.search, - onSubmitted: (_) => widget.delegate.showResults(context), - decoration: InputDecoration( - border: InputBorder.none, - hintText: context.l10n.searchCollectionFieldHint, - hintStyle: theme.inputDecorationTheme.hintStyle, + 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: _focusNode, + decoration: InputDecoration( + border: InputBorder.none, + hintText: context.l10n.searchCollectionFieldHint, + hintStyle: theme.inputDecorationTheme.hintStyle, + ), + textInputAction: TextInputAction.search, + style: theme.textTheme.headline6, + onSubmitted: (_) => widget.delegate.showResults(context), ), ), ), diff --git a/lib/widgets/settings/settings_page.dart b/lib/widgets/settings/settings_page.dart index bad9172a3..a8e20d6e5 100644 --- a/lib/widgets/settings/settings_page.dart +++ b/lib/widgets/settings/settings_page.dart @@ -96,7 +96,6 @@ class _SettingsPageState extends State with FeedbackMixin { ), ), ], - titleSpacing: 0, ), body: GestureAreaProtectorStack( child: SafeArea( diff --git a/lib/widgets/viewer/info/info_app_bar.dart b/lib/widgets/viewer/info/info_app_bar.dart index 603c93975..7d3fb8ea0 100644 --- a/lib/widgets/viewer/info/info_app_bar.dart +++ b/lib/widgets/viewer/info/info_app_bar.dart @@ -74,7 +74,6 @@ class InfoAppBar extends StatelessWidget { ), ), ], - titleSpacing: 0, floating: true, ); }