current route identification

This commit is contained in:
Thibault Deckers 2020-09-03 13:20:42 +09:00
parent 67f873b3f5
commit daedad57fa
27 changed files with 147 additions and 69 deletions

View file

@ -5,6 +5,8 @@ on:
branches:
- develop
# TODO TLAD run `flutter format -l 1000 .` and fail if any
jobs:
build:
name: Check code quality.

View file

@ -155,7 +155,7 @@ extension ExtraHomePageSetting on HomePageSetting {
String get name {
switch (this) {
case HomePageSetting.collection:
return 'All Media';
return 'Collection';
case HomePageSetting.albums:
return 'Albums';
default:

View file

@ -0,0 +1,5 @@
import 'package:flutter/widgets.dart';
extension ExtraContext on BuildContext {
String get currentRouteName => ModalRoute.of(this)?.settings?.name;
}

View file

@ -7,6 +7,8 @@ import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:package_info/package_info.dart';
class AboutPage extends StatelessWidget {
static const routeName = '/about';
@override
Widget build(BuildContext context) {
return Scaffold(

View file

@ -134,7 +134,10 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
Widget _buildAppBarTitle() {
if (collection.isBrowsing) {
Widget title = Text(AvesApp.mode == AppMode.pick ? 'Select' : 'Aves', key: Key('appbar-title'));
Widget title = Text(
AvesApp.mode == AppMode.pick ? 'Select' : 'Collection',
key: Key('appbar-title'),
);
if (AvesApp.mode == AppMode.main) {
title = SourceStateAwareAppBarTitle(
title: title,
@ -345,6 +348,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
return Navigator.push(
context,
MaterialPageRoute(
settings: RouteSettings(name: StatsPage.routeName),
builder: (context) => StatsPage(
collection: collection,
),

View file

@ -8,6 +8,8 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class CollectionPage extends StatelessWidget {
static const routeName = '/collection';
final CollectionLens collection;
const CollectionPage(this.collection);

View file

@ -5,7 +5,7 @@ import 'package:aves/services/viewer_service.dart';
import 'package:aves/widgets/album/grid/list_known_extent.dart';
import 'package:aves/widgets/album/grid/list_section_layout.dart';
import 'package:aves/widgets/album/thumbnail/decorated.dart';
import 'package:aves/widgets/common/transparent_material_page_route.dart';
import 'package:aves/widgets/common/routes.dart';
import 'package:aves/widgets/fullscreen/fullscreen_page.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@ -85,6 +85,7 @@ class GridThumbnail extends StatelessWidget {
Navigator.push(
context,
TransparentMaterialPageRoute(
settings: RouteSettings(name: MultiFullscreenPage.routeName),
pageBuilder: (c, a, sa) => MultiFullscreenPage(
collection: collection,
initialEntry: entry,

View file

@ -12,6 +12,7 @@ import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/location.dart';
import 'package:aves/model/source/tag.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/flutter_utils.dart';
import 'package:aves/widgets/about/about_page.dart';
import 'package:aves/widgets/album/collection_page.dart';
import 'package:aves/widgets/common/aves_logo.dart';
@ -75,10 +76,10 @@ class _AppDrawerState extends State<AppDrawer> {
),
);
final allMediaEntry = _FilteredCollectionNavTile(
final allCollectionEntry = _FilteredCollectionNavTile(
source: source,
leading: Icon(AIcons.allMedia),
title: 'All media',
leading: Icon(AIcons.allCollection),
title: 'All collection',
filter: null,
);
final videoEntry = _FilteredCollectionNavTile(
@ -99,7 +100,7 @@ class _AppDrawerState extends State<AppDrawer> {
child: ListTile(
leading: Icon(AIcons.settings),
title: Text('Preferences'),
onTap: () => _goTo((_) => SettingsPage()),
onTap: () => _goTo(SettingsPage.routeName, (_) => SettingsPage()),
),
);
final aboutEntry = SafeArea(
@ -108,20 +109,20 @@ class _AppDrawerState extends State<AppDrawer> {
child: ListTile(
leading: Icon(AIcons.info),
title: Text('About'),
onTap: () => _goTo((_) => AboutPage()),
onTap: () => _goTo(AboutPage.routeName, (_) => AboutPage()),
),
);
final drawerItems = <Widget>[
header,
allMediaEntry,
allCollectionEntry,
videoEntry,
favouriteEntry,
_buildSpecialAlbumSection(),
Divider(),
_buildRegularAlbumSection(),
_buildCountrySection(),
_buildTagSection(),
_buildAlbumListEntry(),
_buildCountryListEntry(),
_buildTagListEntry(),
Divider(),
settingsEntry,
aboutEntry,
@ -133,7 +134,7 @@ class _AppDrawerState extends State<AppDrawer> {
child: ListTile(
leading: Icon(AIcons.debug),
title: Text('Debug'),
onTap: () => _goTo((_) => DebugPage(source: source)),
onTap: () => _goTo(DebugPage.routeName, (_) => DebugPage(source: source)),
),
),
],
@ -184,7 +185,7 @@ class _AppDrawerState extends State<AppDrawer> {
builder: (context, snapshot) {
final specialAlbums = source.sortedAlbums.where((album) {
final type = androidFileUtils.getAlbumType(album);
return type != AlbumType.regular && type != AlbumType.app;
return [AlbumType.camera, AlbumType.screenshots].contains(type);
});
if (specialAlbums.isEmpty) return SizedBox.shrink();
@ -197,7 +198,7 @@ class _AppDrawerState extends State<AppDrawer> {
});
}
Widget _buildRegularAlbumSection() {
Widget _buildAlbumListEntry() {
return SafeArea(
top: false,
bottom: false,
@ -215,12 +216,12 @@ class _AppDrawerState extends State<AppDrawer> {
),
);
}),
onTap: () => _goTo((_) => AlbumListPage(source: source)),
onTap: () => _goTo(AlbumListPage.routeName, (_) => AlbumListPage(source: source)),
),
);
}
Widget _buildCountrySection() {
Widget _buildCountryListEntry() {
return SafeArea(
top: false,
bottom: false,
@ -237,12 +238,12 @@ class _AppDrawerState extends State<AppDrawer> {
),
);
}),
onTap: () => _goTo((_) => CountryListPage(source: source)),
onTap: () => _goTo(CountryListPage.routeName, (_) => CountryListPage(source: source)),
),
);
}
Widget _buildTagSection() {
Widget _buildTagListEntry() {
return SafeArea(
top: false,
bottom: false,
@ -259,14 +260,21 @@ class _AppDrawerState extends State<AppDrawer> {
),
);
}),
onTap: () => _goTo((_) => TagListPage(source: source)),
onTap: () => _goTo(TagListPage.routeName, (_) => TagListPage(source: source)),
),
);
}
void _goTo(WidgetBuilder builder) {
void _goTo(String routeName, WidgetBuilder builder) {
Navigator.pop(context);
Navigator.push(context, MaterialPageRoute(builder: builder));
if (routeName != context.currentRouteName) {
Navigator.push(
context,
MaterialPageRoute(
settings: RouteSettings(name: routeName),
builder: builder,
));
}
}
}
@ -306,6 +314,7 @@ class _FilteredCollectionNavTile extends StatelessWidget {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
settings: RouteSettings(name: CollectionPage.routeName),
builder: (context) => CollectionPage(CollectionLens(
source: source,
filters: [filter],

View file

@ -174,6 +174,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin {
Navigator.push(
context,
MaterialPageRoute(
settings: RouteSettings(name: FullscreenDebugPage.routeName),
builder: (context) => FullscreenDebugPage(entry: entry),
),
);

View file

@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
import 'package:outline_material_icons/outline_material_icons.dart';
class AIcons {
static const IconData allMedia = OMIcons.collections;
static const IconData allCollection = OMIcons.collections;
static const IconData image = OMIcons.photo;
static const IconData video = OMIcons.movie;
static const IconData vector = OMIcons.code;

View file

@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
class DirectMaterialPageRoute<T> extends PageRouteBuilder<T> {
DirectMaterialPageRoute({
RouteSettings settings,
@required WidgetBuilder builder,
}) : super(
settings: settings,
transitionDuration: Duration.zero,
pageBuilder: (c, a, sa) => builder(c),
);
@override
Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
return child;
}
}
class TransparentMaterialPageRoute<T> extends PageRouteBuilder<T> {
TransparentMaterialPageRoute({
RouteSettings settings,
@required RoutePageBuilder pageBuilder,
}) : super(settings: settings, pageBuilder: pageBuilder);
@override
bool get opaque => false;
@override
Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
final theme = Theme.of(context).pageTransitionsTheme;
return theme.buildTransitions<T>(this, context, animation, secondaryAnimation, child);
}
}

View file

@ -1,16 +0,0 @@
import 'package:flutter/material.dart';
class TransparentMaterialPageRoute<T> extends PageRouteBuilder<T> {
TransparentMaterialPageRoute({
@required RoutePageBuilder pageBuilder,
}) : super(pageBuilder: pageBuilder);
@override
bool get opaque => false;
@override
Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
final theme = Theme.of(context).pageTransitionsTheme;
return theme.buildTransitions<T>(this, context, animation, secondaryAnimation, child);
}
}

View file

@ -18,6 +18,8 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:outline_material_icons/outline_material_icons.dart';
class DebugPage extends StatefulWidget {
static const routeName = '/debug';
final CollectionSource source;
const DebugPage({this.source});

View file

@ -17,6 +17,8 @@ import 'package:flutter/scheduler.dart';
import 'package:provider/provider.dart';
class AlbumListPage extends StatelessWidget {
static const routeName = '/albums';
final CollectionSource source;
const AlbumListPage({@required this.source});

View file

@ -7,6 +7,8 @@ import 'package:aves/widgets/filter_grids/filter_grid_page.dart';
import 'package:flutter/material.dart';
class CountryListPage extends StatelessWidget {
static const routeName = '/countries';
final CollectionSource source;
const CountryListPage({@required this.source});

View file

@ -56,6 +56,7 @@ class FilterNavigationPage extends StatelessWidget {
onPressed: (filter) => Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
settings: RouteSettings(name: CollectionPage.routeName),
builder: (context) => CollectionPage(CollectionLens(
source: source,
filters: [filter],

View file

@ -7,6 +7,8 @@ import 'package:aves/widgets/filter_grids/filter_grid_page.dart';
import 'package:flutter/material.dart';
class TagListPage extends StatelessWidget {
static const routeName = '/tags';
final CollectionSource source;
const TagListPage({@required this.source});

View file

@ -10,6 +10,8 @@ import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
class FullscreenDebugPage extends StatefulWidget {
static const routeName = '/fullscreen/debug';
final ImageEntry entry;
const FullscreenDebugPage({@required this.entry});

View file

@ -275,6 +275,7 @@ class FullscreenBodyState extends State<FullscreenBody> with SingleTickerProvide
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
settings: RouteSettings(name: CollectionPage.routeName),
builder: (context) => CollectionPage(collection.derive(filter)),
),
(route) => false,
@ -323,7 +324,7 @@ class FullscreenBodyState extends State<FullscreenBody> with SingleTickerProvide
}
void _onLeave() {
if (!ModalRoute.of(context).canPop) {
if (!Navigator.canPop(context)) {
// exit app when trying to pop a fullscreen page that is a viewer for a single entry
exit(0);
}

View file

@ -5,6 +5,8 @@ import 'package:aves/widgets/fullscreen/fullscreen_body.dart';
import 'package:flutter/material.dart';
class MultiFullscreenPage extends AnimatedWidget {
static const routeName = '/fullscreen';
final CollectionLens collection;
final ImageEntry initialEntry;
@ -30,6 +32,8 @@ class MultiFullscreenPage extends AnimatedWidget {
}
class SingleFullscreenPage extends StatelessWidget {
static const routeName = '/fullscreen';
final ImageEntry entry;
const SingleFullscreenPage({

View file

@ -126,7 +126,7 @@ class _TopOverlayRow extends StatelessWidget {
children: [
OverlayButton(
scale: scale,
child: ModalRoute.of(context)?.canPop ?? true ? BackButton() : CloseButton(),
child: Navigator.canPop(context) ? BackButton() : CloseButton(),
),
Spacer(),
...quickActions.map(_buildOverlayButton),

View file

@ -7,7 +7,7 @@ import 'package:aves/services/viewer_service.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/widgets/album/collection_page.dart';
import 'package:aves/widgets/common/data_providers/media_store_collection_provider.dart';
import 'package:aves/widgets/common/icons.dart';
import 'package:aves/widgets/common/routes.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/fullscreen/fullscreen_page.dart';
import 'package:flutter/material.dart';
@ -17,6 +17,8 @@ import 'package:permission_handler/permission_handler.dart';
import 'package:screen/screen.dart';
class HomePage extends StatefulWidget {
static const routeName = '/';
const HomePage();
@override
@ -26,16 +28,18 @@ class HomePage extends StatefulWidget {
class _HomePageState extends State<HomePage> {
MediaStoreSource _mediaStore;
ImageEntry _viewerEntry;
Future<void> _appSetup;
@override
void initState() {
super.initState();
_appSetup = _setup();
_setup();
imageCache.maximumSizeBytes = 512 * (1 << 20);
Screen.keepOn(true);
}
@override
Widget build(BuildContext context) => Scaffold();
Future<void> _setup() async {
final permissions = await [
Permission.storage,
@ -80,6 +84,8 @@ class _HomePageState extends State<HomePage> {
await _mediaStore.init();
unawaited(_mediaStore.refresh());
}
unawaited(Navigator.pushReplacement(context, _getRedirectRoute()));
}
Future<ImageEntry> _initViewerEntry({@required String uri, @required String mimeType}) async {
@ -92,30 +98,36 @@ class _HomePageState extends State<HomePage> {
return entry;
}
@override
Widget build(BuildContext context) {
return FutureBuilder<void>(
future: _appSetup,
builder: (context, snapshot) {
if (snapshot.hasError) return Icon(AIcons.error);
if (snapshot.connectionState != ConnectionState.done) return Scaffold();
if (AvesApp.mode == AppMode.view) {
return SingleFullscreenPage(entry: _viewerEntry);
}
Route _getRedirectRoute() {
switch (AvesApp.mode) {
case AppMode.view:
return DirectMaterialPageRoute(
settings: RouteSettings(name: SingleFullscreenPage.routeName),
builder: (_) => SingleFullscreenPage(entry: _viewerEntry),
);
case AppMode.main:
case AppMode.pick:
if (_mediaStore != null) {
switch (settings.homePage) {
case HomePageSetting.albums:
return AlbumListPage(source: _mediaStore);
break;
return DirectMaterialPageRoute(
settings: RouteSettings(name: AlbumListPage.routeName),
builder: (_) => AlbumListPage(source: _mediaStore),
);
case HomePageSetting.collection:
return CollectionPage(CollectionLens(
return DirectMaterialPageRoute(
settings: RouteSettings(name: CollectionPage.routeName),
builder: (_) => CollectionPage(
CollectionLens(
source: _mediaStore,
groupFactor: settings.collectionGroupFactor,
sortFactor: settings.collectionSortFactor,
));
),
),
);
}
}
return SizedBox.shrink();
});
}
return null;
}
}

View file

@ -7,6 +7,8 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class SettingsPage extends StatelessWidget {
static const routeName = '/settings';
@override
Widget build(BuildContext context) {
return MediaQueryDataProvider(

View file

@ -90,6 +90,7 @@ class FilterTable extends StatelessWidget {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
settings: RouteSettings(name: CollectionPage.routeName),
builder: (context) => CollectionPage(collection.derive(filter)),
),
(route) => false,

View file

@ -20,6 +20,8 @@ import 'package:intl/intl.dart';
import 'package:percent_indicator/linear_percent_indicator.dart';
class StatsPage extends StatelessWidget {
static const routeName = '/collection/stats';
final CollectionLens collection;
final Map<String, int> entryCountPerCountry = {}, entryCountPerPlace = {}, entryCountPerTag = {};
@ -236,6 +238,7 @@ class StatsPage extends StatelessWidget {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
settings: RouteSettings(name: CollectionPage.routeName),
builder: (context) => CollectionPage(collection.derive(filter)),
),
(route) => false,

View file

@ -107,6 +107,7 @@ class _WelcomePageState extends State<WelcomePage> {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
settings: RouteSettings(name: HomePage.routeName),
builder: (context) => HomePage(),
),
(route) => false,

View file

@ -56,7 +56,7 @@ void agreeToTerms() {
await driver.tap(find.byValueKey('continue-button'));
await driver.waitUntilNoTransientCallbacks();
expect(await driver.getText(find.byValueKey('appbar-title')), 'Aves');
expect(await driver.getText(find.byValueKey('appbar-title')), 'Collection');
});
}