changed navigation history handling

This commit is contained in:
Thibault Deckers 2020-11-23 19:17:23 +09:00
parent ad2d9b3552
commit 5898c9052a
12 changed files with 75 additions and 85 deletions

View file

@ -198,14 +198,6 @@ class Settings extends ChangeNotifier {
set searchHistory(List<CollectionFilter> newValue) => setAndNotify(searchHistoryKey, newValue.map((filter) => filter.toJson()).toList());
// utils
// `RoutePredicate`
RoutePredicate navRemoveRoutePredicate(String pushedRouteName) {
final home = homePage.routeName;
return (route) => pushedRouteName != home && route.settings?.name == home;
}
// convenience methods
// ignore: avoid_positional_boolean_parameters

View file

@ -374,7 +374,8 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
MaterialPageRoute(
settings: RouteSettings(name: StatsPage.routeName),
builder: (context) => StatsPage(
collection: collection,
source: source,
parentCollection: collection,
),
),
);

View file

@ -111,7 +111,7 @@ class _AvesFilterChipState extends State<AvesFilterChip> {
mainAxisSize: MainAxisSize.min,
children: [
content,
widget.details,
Flexible(child: widget.details),
],
);
}

View file

@ -51,7 +51,7 @@ class CollectionNavTile extends StatelessWidget {
sortFactor: settings.collectionSortFactor,
)),
),
settings.navRemoveRoutePredicate(CollectionPage.routeName),
(route) => false,
);
}
}

View file

@ -1,6 +1,5 @@
import 'dart:ui';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/utils/flutter_utils.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -52,7 +51,7 @@ class NavTile extends StatelessWidget {
Navigator.pushAndRemoveUntil(
context,
route,
settings.navRemoveRoutePredicate(routeName),
(route) => false,
);
} else {
Navigator.push(context, route);

View file

@ -1,5 +1,4 @@
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/enums.dart';
import 'package:aves/widgets/common/aves_selection_dialog.dart';
@ -52,11 +51,7 @@ abstract class ChipSetActionDelegate {
MaterialPageRoute(
settings: RouteSettings(name: StatsPage.routeName),
builder: (context) => StatsPage(
collection: CollectionLens(
source: source,
groupFactor: settings.collectionGroupFactor,
sortFactor: settings.collectionSortFactor,
),
),
),
);

View file

@ -64,21 +64,8 @@ class DecoratedFilterChip extends StatelessWidget {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedSwitcher(
duration: Durations.chipDecorationAnimation,
switchInCurve: Curves.easeInOutCubic,
switchOutCurve: Curves.easeInOutCubic,
transitionBuilder: (child, animation) => FadeTransition(
opacity: animation,
child: SizeTransition(
axis: Axis.horizontal,
sizeFactor: animation,
axisAlignment: 1.0,
child: child,
),
),
child: pinned
? Padding(
AnimatedCrossFade(
firstChild: Padding(
padding: EdgeInsets.only(right: 8),
child: DecoratedIcon(
AIcons.pin,
@ -86,8 +73,12 @@ class DecoratedFilterChip extends StatelessWidget {
shadows: [Constants.embossShadow],
size: 16,
),
)
: SizedBox.shrink(),
),
secondChild: SizedBox.shrink(),
sizeCurve: Curves.easeInOutCubic,
alignment: AlignmentDirectional.centerEnd,
crossFadeState: pinned ? CrossFadeState.showFirst : CrossFadeState.showSecond,
duration: Durations.chipDecorationAnimation,
),
if (filter is AlbumFilter && androidFileUtils.isOnRemovableStorage(filter.album))
Padding(

View file

@ -76,7 +76,7 @@ class FilterNavigationPage extends StatelessWidget {
return sourceState != SourceState.loading && emptyBuilder != null ? emptyBuilder() : SizedBox.shrink();
},
),
onTap: (filter) => Navigator.pushAndRemoveUntil(
onTap: (filter) => Navigator.push(
context,
MaterialPageRoute(
settings: RouteSettings(name: CollectionPage.routeName),
@ -87,7 +87,6 @@ class FilterNavigationPage extends StatelessWidget {
sortFactor: settings.collectionSortFactor,
)),
),
settings.navRemoveRoutePredicate(CollectionPage.routeName),
),
onLongPress: AvesApp.mode == AppMode.main ? (filter, tapPosition) => _showMenu(context, filter, tapPosition) : null,
);

View file

@ -296,7 +296,7 @@ class FullscreenBodyState extends State<FullscreenBody> with SingleTickerProvide
settings: RouteSettings(name: CollectionPage.routeName),
builder: (context) => CollectionPage(collection.derive(filter)),
),
settings.navRemoveRoutePredicate(CollectionPage.routeName),
(route) => false,
);
}

View file

@ -22,8 +22,8 @@ import 'package:flutter/services.dart';
class ImageSearchDelegate {
final CollectionSource source;
final ValueNotifier<String> expandedSectionNotifier = ValueNotifier(null);
final CollectionLens parentCollection;
final ValueNotifier<String> expandedSectionNotifier = ValueNotifier(null);
static const searchHistoryCount = 10;
@ -188,14 +188,12 @@ class ImageSearchDelegate {
if (parentCollection != null) {
_applyToParentCollectionPage(context, filter);
} else {
_goToCollectionPage(context, filter);
_jumpToCollectionPage(context, filter);
}
}
void _applyToParentCollectionPage(BuildContext context, CollectionFilter filter) {
if (filter != null) {
parentCollection.addFilter(filter);
}
// we post closing the search page after applying the filter selection
// so that hero animation target is ready in the `FilterBar`,
// even when the target is a child of an `AnimatedList`
@ -209,7 +207,7 @@ class ImageSearchDelegate {
Navigator.pop(context);
}
void _goToCollectionPage(BuildContext context, CollectionFilter filter) {
void _jumpToCollectionPage(BuildContext context, CollectionFilter filter) {
_clean();
Navigator.pushAndRemoveUntil(
context,
@ -222,7 +220,7 @@ class ImageSearchDelegate {
sortFactor: settings.collectionSortFactor,
)),
),
settings.navRemoveRoutePredicate(CollectionPage.routeName),
(route) => false,
);
}

View file

@ -1,9 +1,6 @@
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/utils/color_utils.dart';
import 'package:aves/utils/constants.dart';
import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/common/aves_filter_chip.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
@ -11,14 +8,16 @@ import 'package:intl/intl.dart';
import 'package:percent_indicator/linear_percent_indicator.dart';
class FilterTable extends StatelessWidget {
final CollectionLens collection;
final int totalEntryCount;
final Map<String, int> entryCountMap;
final CollectionFilter Function(String key) filterBuilder;
final FilterCallback onFilterSelection;
const FilterTable({
@required this.collection,
@required this.totalEntryCount,
@required this.entryCountMap,
@required this.filterBuilder,
@required this.onFilterSelection,
});
static const chipWidth = AvesFilterChip.maxChipWidth;
@ -27,7 +26,6 @@ class FilterTable extends StatelessWidget {
@override
Widget build(BuildContext context) {
final maxCount = collection.entryCount;
final sortedEntries = entryCountMap.entries.toList()
..sort((kv1, kv2) {
final c = kv2.value.compareTo(kv1.value);
@ -47,7 +45,7 @@ class FilterTable extends StatelessWidget {
final filter = filterBuilder(kv.key);
final label = filter.label;
final count = kv.value;
final percent = count / maxCount;
final percent = count / totalEntryCount;
return TableRow(
children: [
Container(
@ -58,7 +56,7 @@ class FilterTable extends StatelessWidget {
alignment: AlignmentDirectional.centerStart,
child: AvesFilterChip(
filter: filter,
onTap: (filter) => _goToCollection(context, filter),
onTap: onFilterSelection,
),
),
if (showPercentIndicator)
@ -92,16 +90,4 @@ class FilterTable extends StatelessWidget {
),
);
}
void _goToCollection(BuildContext context, CollectionFilter filter) {
if (collection == null) return;
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
settings: RouteSettings(name: CollectionPage.routeName),
builder: (context) => CollectionPage(collection.derive(filter)),
),
settings.navRemoveRoutePredicate(CollectionPage.routeName),
);
}
}

View file

@ -8,6 +8,7 @@ import 'package:aves/model/image_entry.dart';
import 'package:aves/model/mime_types.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/utils/color_utils.dart';
import 'package:aves/utils/constants.dart';
import 'package:aves/widgets/collection/collection_page.dart';
@ -25,14 +26,18 @@ import 'package:percent_indicator/linear_percent_indicator.dart';
class StatsPage extends StatelessWidget {
static const routeName = '/collection/stats';
final CollectionLens collection;
final CollectionSource source;
final CollectionLens parentCollection;
final Map<String, int> entryCountPerCountry = {}, entryCountPerPlace = {}, entryCountPerTag = {};
List<ImageEntry> get entries => collection.sortedEntries;
List<ImageEntry> get entries => parentCollection?.sortedEntries ?? source.rawEntries;
static const mimeDonutMinWidth = 124.0;
StatsPage({this.collection}) {
StatsPage({
@required this.source,
this.parentCollection,
}) : assert(source != null) {
entries.forEach((entry) {
if (entry.isLocated) {
final address = entry.addressDetails;
@ -55,7 +60,7 @@ class StatsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
Widget child;
if (collection.isEmpty) {
if (entries.isEmpty) {
child = EmptyContent(
icon: AIcons.image,
text: 'No images',
@ -75,7 +80,7 @@ class StatsPage extends StatelessWidget {
final catalogued = entries.where((entry) => entry.isCatalogued);
final withGps = catalogued.where((entry) => entry.hasGps);
final withGpsPercent = withGps.length / collection.entryCount;
final withGpsPercent = withGps.length / entries.length;
final textScaleFactor = MediaQuery.textScaleFactorOf(context);
final lineHeight = 16 * textScaleFactor;
final locationIndicator = Padding(
@ -105,9 +110,9 @@ class StatsPage extends StatelessWidget {
children: [
mimeDonuts,
locationIndicator,
..._buildTopFilters('Top Countries', entryCountPerCountry, (s) => LocationFilter(LocationLevel.country, s)),
..._buildTopFilters('Top Places', entryCountPerPlace, (s) => LocationFilter(LocationLevel.place, s)),
..._buildTopFilters('Top Tags', entryCountPerTag, (s) => TagFilter(s)),
..._buildTopFilters(context, 'Top Countries', entryCountPerCountry, (s) => LocationFilter(LocationLevel.country, s)),
..._buildTopFilters(context, 'Top Places', entryCountPerPlace, (s) => LocationFilter(LocationLevel.place, s)),
..._buildTopFilters(context, 'Top Tags', entryCountPerTag, (s) => TagFilter(s)),
],
);
}
@ -178,7 +183,7 @@ class StatsPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: seriesData
.map((d) => GestureDetector(
onTap: () => _goToCollection(context, MimeFilter(d.mimeType)),
onTap: () => _onFilterSelection(context, MimeFilter(d.mimeType)),
child: Text.rich(
TextSpan(
children: [
@ -218,6 +223,7 @@ class StatsPage extends StatelessWidget {
}
List<Widget> _buildTopFilters(
BuildContext context,
String title,
Map<String, int> entryCountMap,
CollectionFilter Function(String key) filterBuilder,
@ -233,22 +239,45 @@ class StatsPage extends StatelessWidget {
),
),
FilterTable(
collection: collection,
totalEntryCount: entries.length,
entryCountMap: entryCountMap,
filterBuilder: filterBuilder,
onFilterSelection: (filter) => _onFilterSelection(context, filter),
),
];
}
void _goToCollection(BuildContext context, CollectionFilter filter) {
if (collection == null) return;
void _onFilterSelection(BuildContext context, CollectionFilter filter) {
if (parentCollection != null) {
_applyToParentCollectionPage(context, filter);
} else {
_jumpToCollectionPage(context, filter);
}
}
void _applyToParentCollectionPage(BuildContext context, CollectionFilter filter) {
parentCollection.addFilter(filter);
// we post closing the search page after applying the filter selection
// so that hero animation target is ready in the `FilterBar`,
// even when the target is a child of an `AnimatedList`
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pop(context);
});
}
void _jumpToCollectionPage(BuildContext context, CollectionFilter filter) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
settings: RouteSettings(name: CollectionPage.routeName),
builder: (context) => CollectionPage(collection.derive(filter)),
builder: (context) => CollectionPage(CollectionLens(
source: source,
filters: [filter],
groupFactor: settings.collectionGroupFactor,
sortFactor: settings.collectionSortFactor,
)),
),
settings.navRemoveRoutePredicate(CollectionPage.routeName),
(route) => false,
);
}
}