map: hero fixes
This commit is contained in:
parent
9aeb0a1fc3
commit
d96f9768f9
8 changed files with 275 additions and 191 deletions
|
@ -142,28 +142,28 @@ class AvesAppBar extends StatelessWidget {
|
|||
static Widget _flightShuttleBuilder(
|
||||
BuildContext flightContext,
|
||||
Animation<double> animation,
|
||||
HeroFlightDirection direction,
|
||||
BuildContext fromHero,
|
||||
BuildContext toHero,
|
||||
HeroFlightDirection flightDirection,
|
||||
BuildContext fromHeroContext,
|
||||
BuildContext toHeroContext,
|
||||
) {
|
||||
final pushing = direction == HeroFlightDirection.push;
|
||||
final pushing = flightDirection == 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,
|
||||
style: DefaultTextStyle.of(toHeroContext).style,
|
||||
child: Stack(
|
||||
children: [
|
||||
AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: pushing ? popBuilder : pushBuilder,
|
||||
child: fromHero.widget,
|
||||
child: fromHeroContext.widget,
|
||||
),
|
||||
AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: pushing ? pushBuilder : popBuilder,
|
||||
child: toHero.widget,
|
||||
child: toHeroContext.widget,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/theme/text.dart';
|
||||
import 'package:aves/widgets/aves_app.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
|
@ -5,6 +6,7 @@ import 'package:aves/widgets/viewer/info/common.dart';
|
|||
import 'package:aves_map/aves_map.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class Attribution extends StatelessWidget {
|
||||
final EntryMapStyle? style;
|
||||
|
@ -33,17 +35,40 @@ class Attribution extends StatelessWidget {
|
|||
|
||||
Widget _buildOsmAttributionMarkdown(BuildContext context, String data) {
|
||||
final theme = Theme.of(context);
|
||||
Widget child = MarkdownBody(
|
||||
data: '${context.l10n.mapAttributionOsmData}${AText.separator}$data',
|
||||
selectable: true,
|
||||
styleSheet: MarkdownStyleSheet(
|
||||
a: TextStyle(color: theme.colorScheme.primary),
|
||||
p: theme.textTheme.bodySmall!.merge(const TextStyle(fontSize: InfoRowGroup.fontSize)),
|
||||
),
|
||||
onTapLink: (text, href, title) => AvesApp.launchUrl(href),
|
||||
);
|
||||
|
||||
final animate = context.select<Settings, bool>((v) => v.animate);
|
||||
if (animate) {
|
||||
child = Hero(
|
||||
tag: 'map-attribution',
|
||||
flightShuttleBuilder: (flightContext, animation, flightDirection, fromHeroContext, toHeroContext) {
|
||||
return DefaultTextStyle(
|
||||
style: DefaultTextStyle.of(toHeroContext).style,
|
||||
child: MediaQuery.removeViewPadding(
|
||||
context: context,
|
||||
removeLeft: true,
|
||||
removeTop: true,
|
||||
removeRight: true,
|
||||
removeBottom: true,
|
||||
child: toHeroContext.widget,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: MarkdownBody(
|
||||
data: '${context.l10n.mapAttributionOsmData}${AText.separator}$data',
|
||||
selectable: true,
|
||||
styleSheet: MarkdownStyleSheet(
|
||||
a: TextStyle(color: theme.colorScheme.primary),
|
||||
p: theme.textTheme.bodySmall!.merge(const TextStyle(fontSize: InfoRowGroup.fontSize)),
|
||||
),
|
||||
onTapLink: (text, href, title) => AvesApp.launchUrl(href),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'package:aves/widgets/common/map/buttons/button.dart';
|
|||
import 'package:aves/widgets/common/map/buttons/coordinate_filter.dart';
|
||||
import 'package:aves/widgets/common/map/compass.dart';
|
||||
import 'package:aves/widgets/common/map/map_action_delegate.dart';
|
||||
import 'package:aves/widgets/common/providers/map_theme_provider.dart';
|
||||
import 'package:aves_map/aves_map.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -52,46 +53,12 @@ class _MapButtonPanelState extends State<MapButtonPanel> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final iconTheme = IconTheme.of(context);
|
||||
final iconSize = Size.square(iconTheme.size!);
|
||||
|
||||
Widget? navigationButton;
|
||||
switch (context.select<MapThemeData, MapNavigationButton>((v) => v.navigationButton)) {
|
||||
case MapNavigationButton.back:
|
||||
if (!settings.useTvLayout) {
|
||||
navigationButton = MapOverlayButton.icon(
|
||||
icon: const BackButtonIcon(),
|
||||
onPressed: () => Navigator.maybeOf(context)?.pop(),
|
||||
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
|
||||
);
|
||||
}
|
||||
case MapNavigationButton.close:
|
||||
navigationButton = MapOverlayButton.icon(
|
||||
icon: const CloseButtonIcon(),
|
||||
onPressed: SystemNavigator.pop,
|
||||
tooltip: MaterialLocalizations.of(context).closeButtonTooltip,
|
||||
);
|
||||
case MapNavigationButton.map:
|
||||
final _openMapPage = widget.openMapPage;
|
||||
if (_openMapPage != null) {
|
||||
navigationButton = MapOverlayButton.icon(
|
||||
icon: const Icon(AIcons.showFullscreenCorners),
|
||||
onPressed: () => _openMapPage.call(context),
|
||||
tooltip: context.l10n.openMapPageTooltip,
|
||||
);
|
||||
}
|
||||
case MapNavigationButton.none:
|
||||
break;
|
||||
}
|
||||
|
||||
final showCoordinateFilter = context.select<MapThemeData, bool>((v) => v.showCoordinateFilter);
|
||||
final visualDensity = context.select<MapThemeData, VisualDensity>((v) => v.visualDensity);
|
||||
final double padding = 8 + visualDensity.horizontal * 2;
|
||||
|
||||
final actions = [
|
||||
MapAction.openMapApp,
|
||||
MapAction.addShortcut,
|
||||
].where((action) => _actionDelegate.isVisible(context, action)).toList();
|
||||
Widget? topLeftButton = _buildNavigationButton(context);
|
||||
Widget? topRightButton = _buildTopRightButton(context);
|
||||
|
||||
return Positioned.fill(
|
||||
child: TooltipTheme(
|
||||
|
@ -113,38 +80,11 @@ class _MapButtonPanelState extends State<MapButtonPanel> {
|
|||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (navigationButton != null) ...[
|
||||
navigationButton,
|
||||
if (topLeftButton != null) ...[
|
||||
topLeftButton,
|
||||
SizedBox(height: padding),
|
||||
],
|
||||
ValueListenableBuilder<ZoomedBounds>(
|
||||
valueListenable: widget.boundsNotifier,
|
||||
builder: (context, bounds, child) {
|
||||
final degrees = bounds.rotation;
|
||||
final opacity = degrees == 0 ? .0 : 1.0;
|
||||
return IgnorePointer(
|
||||
ignoring: opacity == 0,
|
||||
child: AnimatedOpacity(
|
||||
opacity: opacity,
|
||||
duration: context.select<DurationsData, Duration>((v) => v.viewerOverlayAnimation),
|
||||
child: MapOverlayButton.icon(
|
||||
icon: Transform(
|
||||
origin: iconSize.center(Offset.zero),
|
||||
transform: Matrix4.rotationZ(degToRadian(degrees)),
|
||||
child: CustomPaint(
|
||||
painter: CompassPainter(
|
||||
color: iconTheme.color!,
|
||||
),
|
||||
size: iconSize,
|
||||
),
|
||||
),
|
||||
onPressed: widget.controller.resetRotation,
|
||||
tooltip: context.l10n.mapPointNorthUpTooltip,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
_buildCompass(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -161,30 +101,10 @@ class _MapButtonPanelState extends State<MapButtonPanel> {
|
|||
// key is expected by test driver
|
||||
child: Column(
|
||||
children: [
|
||||
if (actions.length == 1) _buildActionButton(context, actions.first),
|
||||
if (actions.length > 1)
|
||||
MapOverlayButton(builder: (context, visualDensity, child) {
|
||||
final animations = context.read<Settings>().accessibilityAnimations;
|
||||
return PopupMenuButton<MapAction>(
|
||||
itemBuilder: (context) => actions
|
||||
.map((action) => PopupMenuItem(
|
||||
value: action,
|
||||
child: MenuRow(
|
||||
text: action.getText(context),
|
||||
icon: action.getIcon(),
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
onSelected: (action) async {
|
||||
// wait for the popup menu to hide before proceeding with the action
|
||||
await Future.delayed(animations.popUpAnimationDelay * timeDilation);
|
||||
_actionDelegate.onActionSelected(context, action);
|
||||
},
|
||||
iconSize: MapOverlayButton.iconSize(visualDensity),
|
||||
popUpAnimationStyle: animations.popUpAnimationStyle,
|
||||
);
|
||||
}),
|
||||
SizedBox(height: padding),
|
||||
if (topRightButton != null) ...[
|
||||
topRightButton,
|
||||
SizedBox(height: padding),
|
||||
],
|
||||
// key is expected by test driver
|
||||
_buildActionButton(context, MapAction.selectStyle, buttonKey: const Key('map-menu-layers')),
|
||||
],
|
||||
|
@ -212,10 +132,133 @@ class _MapButtonPanelState extends State<MapButtonPanel> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButton(BuildContext context, MapAction action, {Key? buttonKey}) => MapOverlayButton.icon(
|
||||
buttonKey: buttonKey,
|
||||
icon: action.getIcon(),
|
||||
onPressed: () => _actionDelegate.onActionSelected(context, action),
|
||||
tooltip: action.getText(context),
|
||||
);
|
||||
Widget? _buildNavigationButton(BuildContext context) {
|
||||
Widget? child;
|
||||
switch (context.select<MapThemeData, MapNavigationButton>((v) => v.navigationButton)) {
|
||||
case MapNavigationButton.back:
|
||||
if (!settings.useTvLayout) {
|
||||
child = MapOverlayButton.icon(
|
||||
icon: const BackButtonIcon(),
|
||||
onPressed: () => Navigator.maybeOf(context)?.pop(),
|
||||
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
|
||||
);
|
||||
}
|
||||
case MapNavigationButton.close:
|
||||
child = MapOverlayButton.icon(
|
||||
icon: const CloseButtonIcon(),
|
||||
onPressed: SystemNavigator.pop,
|
||||
tooltip: MaterialLocalizations.of(context).closeButtonTooltip,
|
||||
);
|
||||
case MapNavigationButton.map:
|
||||
final _openMapPage = widget.openMapPage;
|
||||
if (_openMapPage != null) {
|
||||
child = MapOverlayButton.icon(
|
||||
icon: const Icon(AIcons.showFullscreenCorners),
|
||||
onPressed: () => _openMapPage.call(context),
|
||||
tooltip: context.l10n.openMapPageTooltip,
|
||||
);
|
||||
}
|
||||
case MapNavigationButton.none:
|
||||
break;
|
||||
}
|
||||
if (child != null) {
|
||||
child = _heroify(context, 'top-left', child);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
Widget? _buildTopRightButton(BuildContext context) {
|
||||
const heroTag = 'top-right';
|
||||
|
||||
final actions = [
|
||||
MapAction.openMapApp,
|
||||
MapAction.addShortcut,
|
||||
].where((action) => _actionDelegate.isVisible(context, action)).toList();
|
||||
|
||||
Widget? child;
|
||||
if (actions.length == 1) {
|
||||
child = _buildActionButton(context, actions.first, heroTag: heroTag);
|
||||
} else if (actions.length > 1) {
|
||||
child = MapOverlayButton(builder: (context, visualDensity, child) {
|
||||
final animations = context.read<Settings>().accessibilityAnimations;
|
||||
return PopupMenuButton<MapAction>(
|
||||
itemBuilder: (context) => actions
|
||||
.map((action) => PopupMenuItem(
|
||||
value: action,
|
||||
child: MenuRow(
|
||||
text: action.getText(context),
|
||||
icon: action.getIcon(),
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
onSelected: (action) async {
|
||||
// wait for the popup menu to hide before proceeding with the action
|
||||
await Future.delayed(animations.popUpAnimationDelay * timeDilation);
|
||||
_actionDelegate.onActionSelected(context, action);
|
||||
},
|
||||
iconSize: MapOverlayButton.iconSize(visualDensity),
|
||||
popUpAnimationStyle: animations.popUpAnimationStyle,
|
||||
);
|
||||
});
|
||||
child = _heroify(context, heroTag, child);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
Widget _buildCompass(BuildContext context) {
|
||||
final iconTheme = IconTheme.of(context);
|
||||
final iconSize = Size.square(iconTheme.size!);
|
||||
return ValueListenableBuilder<ZoomedBounds>(
|
||||
valueListenable: widget.boundsNotifier,
|
||||
builder: (context, bounds, child) {
|
||||
final degrees = bounds.rotation;
|
||||
final opacity = degrees == 0 ? .0 : 1.0;
|
||||
return IgnorePointer(
|
||||
ignoring: opacity == 0,
|
||||
child: AnimatedOpacity(
|
||||
opacity: opacity,
|
||||
duration: context.select<DurationsData, Duration>((v) => v.viewerOverlayAnimation),
|
||||
child: MapOverlayButton.icon(
|
||||
icon: Transform(
|
||||
origin: iconSize.center(Offset.zero),
|
||||
transform: Matrix4.rotationZ(degToRadian(degrees)),
|
||||
child: CustomPaint(
|
||||
painter: CompassPainter(
|
||||
color: iconTheme.color!,
|
||||
),
|
||||
size: iconSize,
|
||||
),
|
||||
),
|
||||
onPressed: widget.controller.resetRotation,
|
||||
tooltip: context.l10n.mapPointNorthUpTooltip,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButton(BuildContext context, MapAction action, {Key? buttonKey, String? heroTag}) {
|
||||
final child = MapOverlayButton.icon(
|
||||
buttonKey: buttonKey,
|
||||
icon: action.getIcon(),
|
||||
onPressed: () => _actionDelegate.onActionSelected(context, action),
|
||||
tooltip: action.getText(context),
|
||||
);
|
||||
return _heroify(context, heroTag ?? action.name, child);
|
||||
}
|
||||
|
||||
Widget _heroify(BuildContext context, String? tag, Widget child) {
|
||||
if (tag != null) {
|
||||
final animate = context.select<Settings, bool>((v) => v.animate);
|
||||
if (animate) {
|
||||
return Hero(
|
||||
tag: 'map-button-$tag',
|
||||
flightShuttleBuilder: MapTheme.heroFlightShuttleBuilder,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/widgets/common/fx/borders.dart';
|
||||
import 'package:aves/widgets/common/providers/map_theme_provider.dart';
|
||||
import 'package:aves_map/aves_map.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class MapDecorator extends StatelessWidget {
|
||||
final Widget? child;
|
||||
final Widget child;
|
||||
|
||||
static const mapBorderRadius = BorderRadius.all(Radius.circular(24)); // to match button circles
|
||||
static const mapBackground = Color(0xFFDBD5D3);
|
||||
|
@ -12,11 +14,45 @@ class MapDecorator extends StatelessWidget {
|
|||
|
||||
const MapDecorator({
|
||||
super.key,
|
||||
this.child,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget _child = ClipRRect(
|
||||
borderRadius: mapBorderRadius,
|
||||
child: Container(
|
||||
color: mapBackground,
|
||||
foregroundDecoration: BoxDecoration(
|
||||
border: AvesBorder.border(context),
|
||||
borderRadius: mapBorderRadius,
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
const GridPaper(
|
||||
color: mapLoadingGrid,
|
||||
interval: 10,
|
||||
divisions: 1,
|
||||
subdivisions: 1,
|
||||
child: CustomPaint(
|
||||
size: Size.infinite,
|
||||
),
|
||||
),
|
||||
child,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final animate = context.select<Settings, bool>((v) => v.animate);
|
||||
if (animate) {
|
||||
_child = Hero(
|
||||
tag: 'map-canvas',
|
||||
flightShuttleBuilder: MapTheme.heroFlightShuttleBuilder,
|
||||
child: _child,
|
||||
);
|
||||
}
|
||||
|
||||
final interactive = context.select<MapThemeData, bool>((v) => v.interactive);
|
||||
return GestureDetector(
|
||||
onScaleStart: interactive
|
||||
|
@ -25,30 +61,7 @@ class MapDecorator extends StatelessWidget {
|
|||
// absorb scale gesture here to prevent scrolling
|
||||
// and triggering by mistake a move to the image page above
|
||||
},
|
||||
child: ClipRRect(
|
||||
borderRadius: mapBorderRadius,
|
||||
child: Container(
|
||||
color: mapBackground,
|
||||
foregroundDecoration: BoxDecoration(
|
||||
border: AvesBorder.border(context),
|
||||
borderRadius: mapBorderRadius,
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
const GridPaper(
|
||||
color: mapLoadingGrid,
|
||||
interval: 10,
|
||||
divisions: 1,
|
||||
subdivisions: 1,
|
||||
child: CustomPaint(
|
||||
size: Size.infinite,
|
||||
),
|
||||
),
|
||||
if (child != null) child!,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
child: _child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import 'package:aves/widgets/common/map/attribution.dart';
|
|||
import 'package:aves/widgets/common/map/buttons/panel.dart';
|
||||
import 'package:aves/widgets/common/map/decorator.dart';
|
||||
import 'package:aves/widgets/common/map/leaflet/map.dart';
|
||||
import 'package:aves/widgets/common/providers/map_theme_provider.dart';
|
||||
import 'package:aves/widgets/common/thumbnail/image.dart';
|
||||
import 'package:aves/widgets/dialogs/selection_dialogs/common.dart';
|
||||
import 'package:aves/widgets/dialogs/selection_dialogs/single_selection.dart';
|
||||
|
@ -242,50 +241,6 @@ class _GeoMapState extends State<GeoMap> {
|
|||
child = _decorateMap(context, overlay);
|
||||
}
|
||||
|
||||
child = Hero(
|
||||
tag: 'map',
|
||||
flightShuttleBuilder: (flightContext, animation, flightDirection, fromHeroContext, toHeroContext) {
|
||||
final pushing = flightDirection == HeroFlightDirection.push;
|
||||
final fromMediaQuery = MediaQuery.of(fromHeroContext);
|
||||
final toMediaQuery = MediaQuery.of(toHeroContext);
|
||||
final fromRenderBox = fromHeroContext.findRenderObject()! as RenderBox;
|
||||
final toRenderBox = toHeroContext.findRenderObject()! as RenderBox;
|
||||
final fromTheme = fromHeroContext.read<MapThemeData>();
|
||||
final toTheme = toHeroContext.read<MapThemeData>();
|
||||
|
||||
return DefaultTextStyle(
|
||||
style: DefaultTextStyle.of(toHeroContext).style,
|
||||
child: AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: (context, child) {
|
||||
final t = pushing ? animation.value : 1 - animation.value;
|
||||
return MapTheme(
|
||||
interactive: false,
|
||||
showCoordinateFilter: false,
|
||||
navigationButton: toTheme.navigationButton,
|
||||
visualDensity: VisualDensity.lerp(fromTheme.visualDensity, toTheme.visualDensity, t),
|
||||
child: MediaQuery(
|
||||
data: toMediaQuery.copyWith(
|
||||
padding: EdgeInsets.lerp(fromMediaQuery.padding, toMediaQuery.padding, t),
|
||||
viewPadding: EdgeInsets.lerp(fromMediaQuery.viewPadding, toMediaQuery.viewPadding, t),
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: SizedBox.fromSize(
|
||||
size: Size.lerp(fromRenderBox.size, toRenderBox.size, t),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: toHeroContext.widget,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
|
||||
final mapHeight = context.select<MapThemeData, double?>((v) => v.mapHeight);
|
||||
child = Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
@ -319,7 +274,9 @@ class _GeoMapState extends State<GeoMap> {
|
|||
}
|
||||
Widget replacement = Stack(
|
||||
children: [
|
||||
const MapDecorator(),
|
||||
const MapDecorator(
|
||||
child: SizedBox(),
|
||||
),
|
||||
_buildButtonPanel(context),
|
||||
],
|
||||
);
|
||||
|
@ -550,7 +507,7 @@ class _GeoMapState extends State<GeoMap> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _decorateMap(BuildContext context, Widget? child) => MapDecorator(child: child);
|
||||
Widget _decorateMap(BuildContext context, Widget? child) => MapDecorator(child: child!);
|
||||
|
||||
Widget _buildButtonPanel(BuildContext context) {
|
||||
if (settings.useTvLayout) return const SizedBox();
|
||||
|
|
|
@ -41,4 +41,50 @@ class MapTheme extends StatelessWidget {
|
|||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
static Widget heroFlightShuttleBuilder(
|
||||
BuildContext flightContext,
|
||||
Animation<double> animation,
|
||||
HeroFlightDirection flightDirection,
|
||||
BuildContext fromHeroContext,
|
||||
BuildContext toHeroContext,
|
||||
) {
|
||||
final pushing = flightDirection == HeroFlightDirection.push;
|
||||
final fromMediaQuery = MediaQuery.of(fromHeroContext);
|
||||
final toMediaQuery = MediaQuery.of(toHeroContext);
|
||||
final fromRenderBox = fromHeroContext.findRenderObject()! as RenderBox;
|
||||
final toRenderBox = toHeroContext.findRenderObject()! as RenderBox;
|
||||
final fromTheme = fromHeroContext.read<MapThemeData>();
|
||||
final toTheme = toHeroContext.read<MapThemeData>();
|
||||
|
||||
return DefaultTextStyle(
|
||||
style: DefaultTextStyle.of(toHeroContext).style,
|
||||
child: AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: (context, child) {
|
||||
final t = pushing ? animation.value : 1 - animation.value;
|
||||
return MapTheme(
|
||||
interactive: false,
|
||||
showCoordinateFilter: false,
|
||||
navigationButton: toTheme.navigationButton,
|
||||
visualDensity: VisualDensity.lerp(fromTheme.visualDensity, toTheme.visualDensity, t),
|
||||
child: MediaQuery(
|
||||
data: toMediaQuery.copyWith(
|
||||
padding: EdgeInsets.lerp(fromMediaQuery.padding, toMediaQuery.padding, t),
|
||||
viewPadding: EdgeInsets.lerp(fromMediaQuery.viewPadding, toMediaQuery.viewPadding, t),
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: SizedBox.fromSize(
|
||||
size: Size.lerp(fromRenderBox.size, toRenderBox.size, t),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: toHeroContext.widget,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -269,7 +269,7 @@ class _ThumbnailImageState extends State<ThumbnailImage> {
|
|||
final backgroundColor = background.isColor ? background.color : null;
|
||||
image = Hero(
|
||||
tag: heroTag,
|
||||
flightShuttleBuilder: (flight, animation, direction, fromHero, toHero) {
|
||||
flightShuttleBuilder: (flightContext, animation, flightDirection, fromHeroContext, toHeroContext) {
|
||||
Widget child = TransitionImage(
|
||||
image: entry.bestCachedThumbnail,
|
||||
animation: animation,
|
||||
|
@ -304,11 +304,11 @@ class _ThumbnailImageState extends State<ThumbnailImage> {
|
|||
if (animate && heroTag != null) {
|
||||
child = Hero(
|
||||
tag: heroTag,
|
||||
flightShuttleBuilder: (flight, animation, direction, fromHero, toHero) {
|
||||
flightShuttleBuilder: (flightContext, animation, flightDirection, fromHeroContext, toHeroContext) {
|
||||
return MediaQueryDataProvider(
|
||||
child: DefaultTextStyle(
|
||||
style: DefaultTextStyle.of(toHero).style,
|
||||
child: toHero.widget,
|
||||
style: DefaultTextStyle.of(toHeroContext).style,
|
||||
child: toHeroContext.widget,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
@ -104,11 +104,11 @@ class _AppBottomNavBarState extends State<AppBottomNavBar> {
|
|||
if (animate) {
|
||||
child = Hero(
|
||||
tag: 'nav-bar',
|
||||
flightShuttleBuilder: (flight, animation, direction, fromHero, toHero) {
|
||||
flightShuttleBuilder: (flightContext, animation, flightDirection, fromHeroContext, toHeroContext) {
|
||||
return MediaQuery.removeViewInsets(
|
||||
context: context,
|
||||
removeBottom: true,
|
||||
child: toHero.widget,
|
||||
child: toHeroContext.widget,
|
||||
);
|
||||
},
|
||||
child: child,
|
||||
|
|
Loading…
Reference in a new issue