collection: prevent hero when navigating from drawer

This commit is contained in:
Thibault Deckers 2021-02-03 13:00:43 +09:00
parent 104373a186
commit accfb2c57b
8 changed files with 37 additions and 21 deletions

View file

@ -21,6 +21,7 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel
EntryGroupFactor groupFactor; EntryGroupFactor groupFactor;
EntrySortFactor sortFactor; EntrySortFactor sortFactor;
final AChangeNotifier filterChangeNotifier = AChangeNotifier(); final AChangeNotifier filterChangeNotifier = AChangeNotifier();
int id;
bool listenToSource; bool listenToSource;
List<AvesEntry> _filteredEntries; List<AvesEntry> _filteredEntries;
@ -33,10 +34,12 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel
Iterable<CollectionFilter> filters, Iterable<CollectionFilter> filters,
@required EntryGroupFactor groupFactor, @required EntryGroupFactor groupFactor,
@required EntrySortFactor sortFactor, @required EntrySortFactor sortFactor,
this.id,
this.listenToSource = true, this.listenToSource = true,
}) : filters = {if (filters != null) ...filters.where((f) => f != null)}, }) : filters = {if (filters != null) ...filters.where((f) => f != null)},
groupFactor = groupFactor ?? EntryGroupFactor.month, groupFactor = groupFactor ?? EntryGroupFactor.month,
sortFactor = sortFactor ?? EntrySortFactor.date { sortFactor = sortFactor ?? EntrySortFactor.date {
id ??= hashCode;
if (listenToSource) { if (listenToSource) {
_subscriptions.add(source.eventBus.on<EntryAddedEvent>().listen((e) => _refresh())); _subscriptions.add(source.eventBus.on<EntryAddedEvent>().listen((e) => _refresh()));
_subscriptions.add(source.eventBus.on<EntryRemovedEvent>().listen((e) => onEntryRemoved(e.entries))); _subscriptions.add(source.eventBus.on<EntryRemovedEvent>().listen((e) => onEntryRemoved(e.entries)));

View file

@ -54,16 +54,20 @@ class InteractiveThumbnail extends StatelessWidget {
context, context,
TransparentMaterialPageRoute( TransparentMaterialPageRoute(
settings: RouteSettings(name: EntryViewerPage.routeName), settings: RouteSettings(name: EntryViewerPage.routeName),
pageBuilder: (c, a, sa) => EntryViewerPage( pageBuilder: (c, a, sa) {
collection: CollectionLens( final viewerCollection = CollectionLens(
source: collection.source, source: collection.source,
filters: collection.filters, filters: collection.filters,
groupFactor: collection.groupFactor, groupFactor: collection.groupFactor,
sortFactor: collection.sortFactor, sortFactor: collection.sortFactor,
id: collection.id,
listenToSource: false, listenToSource: false,
), );
initialEntry: entry, return EntryViewerPage(
), collection: viewerCollection,
initialEntry: entry,
);
},
), ),
); );
} }

View file

@ -27,17 +27,21 @@ class DecoratedThumbnail extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// hero tag should include a collection identifier, so that it animates
// between different views of the entry in the same collection (e.g. thumbnails <-> viewer)
// but not between different collection instances, even with the same attributes (e.g. reloading collection page via drawer)
final heroTag = hashValues(collection?.id, entry);
var child = entry.isSvg var child = entry.isSvg
? VectorImageThumbnail( ? VectorImageThumbnail(
entry: entry, entry: entry,
extent: extent, extent: extent,
canHero: true, heroTag: heroTag,
) )
: RasterImageThumbnail( : RasterImageThumbnail(
entry: entry, entry: entry,
extent: extent, extent: extent,
isScrollingNotifier: isScrollingNotifier, isScrollingNotifier: isScrollingNotifier,
canHero: true, heroTag: heroTag,
); );
child = Stack( child = Stack(

View file

@ -10,14 +10,14 @@ class RasterImageThumbnail extends StatefulWidget {
final AvesEntry entry; final AvesEntry entry;
final double extent; final double extent;
final ValueNotifier<bool> isScrollingNotifier; final ValueNotifier<bool> isScrollingNotifier;
final bool canHero; final Object heroTag;
const RasterImageThumbnail({ const RasterImageThumbnail({
Key key, Key key,
@required this.entry, @required this.entry,
@required this.extent, @required this.extent,
this.isScrollingNotifier, this.isScrollingNotifier,
this.canHero = false, this.heroTag,
}) : super(key: key); }) : super(key: key);
@override @override
@ -124,9 +124,9 @@ class _RasterImageThumbnailState extends State<RasterImageThumbnail> {
height: extent, height: extent,
fit: BoxFit.cover, fit: BoxFit.cover,
); );
return widget.canHero return widget.heroTag != null
? Hero( ? Hero(
tag: entry, tag: widget.heroTag,
flightShuttleBuilder: (flight, animation, direction, fromHero, toHero) { flightShuttleBuilder: (flight, animation, direction, fromHero, toHero) {
return TransitionImage( return TransitionImage(
image: entry.getBestThumbnail(extent), image: entry.getBestThumbnail(extent),

View file

@ -10,13 +10,13 @@ import 'package:provider/provider.dart';
class VectorImageThumbnail extends StatelessWidget { class VectorImageThumbnail extends StatelessWidget {
final AvesEntry entry; final AvesEntry entry;
final double extent; final double extent;
final bool canHero; final Object heroTag;
const VectorImageThumbnail({ const VectorImageThumbnail({
Key key, Key key,
@required this.entry, @required this.entry,
@required this.extent, @required this.extent,
this.canHero = false, this.heroTag,
}) : super(key: key); }) : super(key: key);
@override @override
@ -63,9 +63,9 @@ class VectorImageThumbnail extends StatelessWidget {
); );
}, },
); );
return canHero return heroTag != null
? Hero( ? Hero(
tag: entry, tag: heroTag,
transitionOnUserGestures: true, transitionOnUserGestures: true,
child: child, child: child,
) )

View file

@ -77,7 +77,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with SingleTickerPr
super.initState(); super.initState();
final entry = widget.initialEntry; final entry = widget.initialEntry;
// opening hero, with viewer as target // opening hero, with viewer as target
_heroInfoNotifier.value = HeroInfo(entry); _heroInfoNotifier.value = HeroInfo(collection?.id, entry);
_entryNotifier.value = entry; _entryNotifier.value = entry;
_currentHorizontalPage = max(0, entries.indexOf(entry)); _currentHorizontalPage = max(0, entries.indexOf(entry));
_currentVerticalPage = ValueNotifier(imagePage); _currentVerticalPage = ValueNotifier(imagePage);
@ -421,7 +421,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with SingleTickerPr
} }
// closing hero, with viewer as source // closing hero, with viewer as source
final heroInfo = HeroInfo(_entryNotifier.value); final heroInfo = HeroInfo(collection?.id, _entryNotifier.value);
if (_heroInfoNotifier.value != heroInfo) { if (_heroInfoNotifier.value != heroInfo) {
_heroInfoNotifier.value = heroInfo; _heroInfoNotifier.value = heroInfo;
// we post closing the viewer page so that hero animation source is ready // we post closing the viewer page so that hero animation source is ready

View file

@ -1,16 +1,21 @@
import 'package:aves/model/entry.dart'; import 'package:aves/model/entry.dart';
import 'package:flutter/widgets.dart';
class HeroInfo { class HeroInfo {
// hero tag should include a collection identifier, so that it animates
// between different views of the entry in the same collection (e.g. thumbnails <-> viewer)
// but not between different collection instances, even with the same attributes (e.g. reloading collection page via drawer)
final int collectionId;
final AvesEntry entry; final AvesEntry entry;
const HeroInfo(this.entry); const HeroInfo(this.collectionId, this.entry);
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false; if (other.runtimeType != runtimeType) return false;
return other is HeroInfo && other.entry == entry; return other is HeroInfo && other.collectionId == collectionId && other.entry == entry;
} }
@override @override
int get hashCode => entry.hashCode; int get hashCode => hashValues(collectionId, entry);
} }

View file

@ -145,7 +145,7 @@ class _EntryPageViewState extends State<EntryPageView> {
return Consumer<HeroInfo>( return Consumer<HeroInfo>(
builder: (context, info, child) => Hero( builder: (context, info, child) => Hero(
tag: info?.entry == mainEntry ? mainEntry : hashCode, tag: info?.entry == mainEntry ? hashValues(info.collectionId, mainEntry) : hashCode,
transitionOnUserGestures: true, transitionOnUserGestures: true,
child: child, child: child,
), ),