shortcut to search page

This commit is contained in:
Thibault Deckers 2020-09-07 11:40:00 +09:00
parent 9da57961fc
commit af9edebf86
12 changed files with 389 additions and 68 deletions

View file

@ -87,21 +87,21 @@ public class MainActivity extends FlutterActivity {
private void setupShortcuts() { private void setupShortcuts() {
ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
Intent searchIntent = new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class); // do not use 'route' as extra key, as the Flutter framework acts on it
searchIntent.putExtra("page", "search");
ShortcutInfo search = new ShortcutInfo.Builder(this, "search") ShortcutInfo search = new ShortcutInfo.Builder(this, "search")
.setShortLabel(getString(R.string.search_shortcut_short_label)) .setShortLabel(getString(R.string.search_shortcut_short_label))
.setIcon(Icon.createWithResource(this, R.drawable.ic_outline_search)) .setIcon(Icon.createWithResource(this, R.drawable.ic_outline_search))
.setIntent(searchIntent) .setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
.putExtra("page", "/search"))
.build(); .build();
Intent videosIntent = new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class);
videosIntent.putExtra("page", "collection");
videosIntent.putExtra("filters", new String[]{"anyVideo"});
ShortcutInfo videos = new ShortcutInfo.Builder(this, "videos") ShortcutInfo videos = new ShortcutInfo.Builder(this, "videos")
.setShortLabel(getString(R.string.videos_shortcut_short_label)) .setShortLabel(getString(R.string.videos_shortcut_short_label))
.setIcon(Icon.createWithResource(this, R.drawable.ic_outline_movie)) .setIcon(Icon.createWithResource(this, R.drawable.ic_outline_movie))
.setIntent(videosIntent) .setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
.putExtra("page", "/collection")
.putExtra("filters", new String[]{"anyVideo"}))
.build(); .build();
shortcutManager.setDynamicShortcuts(Arrays.asList(videos, search)); shortcutManager.setDynamicShortcuts(Arrays.asList(videos, search));
} }

View file

@ -1,7 +1,7 @@
import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart'; import 'package:aves/widgets/filter_grids/albums_page.dart';
enum HomePageSetting { collection, albums, search } enum HomePageSetting { collection, albums }
extension ExtraHomePageSetting on HomePageSetting { extension ExtraHomePageSetting on HomePageSetting {
String get name { String get name {
@ -10,8 +10,6 @@ extension ExtraHomePageSetting on HomePageSetting {
return 'Collection'; return 'Collection';
case HomePageSetting.albums: case HomePageSetting.albums:
return 'Albums'; return 'Albums';
case HomePageSetting.search:
return 'Search';
default: default:
return toString(); return toString();
} }

View file

@ -295,7 +295,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
collection.clearSelection(); collection.clearSelection();
break; break;
case CollectionAction.stats: case CollectionAction.stats:
unawaited(_goToStats()); _goToStats();
break; break;
case CollectionAction.group: case CollectionAction.group:
final value = await showDialog<EntryGroupFactor>( final value = await showDialog<EntryGroupFactor>(
@ -338,14 +338,18 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
} }
void _goToSearch() { void _goToSearch() {
showSearch( Navigator.push(
context: context, context,
delegate: ImageSearchDelegate(collection.source, collection.addFilter), SearchPageRoute(
); delegate: ImageSearchDelegate(
source: collection.source,
parentCollection: collection,
),
));
} }
Future<void> _goToStats() { void _goToStats() {
return Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
settings: RouteSettings(name: StatsPage.routeName), settings: RouteSettings(name: StatsPage.routeName),

View file

@ -6,40 +6,46 @@ import 'package:aves/model/filters/mime.dart';
import 'package:aves/model/filters/query.dart'; import 'package:aves/model/filters/query.dart';
import 'package:aves/model/filters/tag.dart'; import 'package:aves/model/filters/tag.dart';
import 'package:aves/model/mime_types.dart'; import 'package:aves/model/mime_types.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/album.dart'; import 'package:aves/model/source/album.dart';
import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/location.dart'; import 'package:aves/model/source/location.dart';
import 'package:aves/model/source/tag.dart'; import 'package:aves/model/source/tag.dart';
import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/collection/search/expandable_filter_row.dart'; import 'package:aves/widgets/collection/search/expandable_filter_row.dart';
import 'package:aves/widgets/collection/search_page.dart';
import 'package:aves/widgets/common/aves_filter_chip.dart'; import 'package:aves/widgets/common/aves_filter_chip.dart';
import 'package:aves/widgets/common/icons.dart'; import 'package:aves/widgets/common/icons.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class ImageSearchDelegate extends SearchDelegate<CollectionFilter> { class ImageSearchDelegate {
final CollectionSource source; final CollectionSource source;
final ValueNotifier<String> expandedSectionNotifier = ValueNotifier(null); final ValueNotifier<String> expandedSectionNotifier = ValueNotifier(null);
final FilterCallback onSelection; final CollectionLens parentCollection;
ImageSearchDelegate(this.source, this.onSelection); ImageSearchDelegate({@required this.source, this.parentCollection});
@override
ThemeData appBarTheme(BuildContext context) { ThemeData appBarTheme(BuildContext context) {
return Theme.of(context); return Theme.of(context);
} }
@override
Widget buildLeading(BuildContext context) { Widget buildLeading(BuildContext context) {
return IconButton( return Navigator.canPop(context)
? IconButton(
icon: AnimatedIcon( icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow, icon: AnimatedIcons.menu_arrow,
progress: transitionAnimation, progress: transitionAnimation,
), ),
onPressed: () => _select(context, null), onPressed: () => _goBack(context),
tooltip: 'Back', tooltip: MaterialLocalizations.of(context).backButtonTooltip,
)
: CloseButton(
onPressed: SystemNavigator.pop,
); );
} }
@override
List<Widget> buildActions(BuildContext context) { List<Widget> buildActions(BuildContext context) {
return [ return [
if (query.isNotEmpty) if (query.isNotEmpty)
@ -54,7 +60,6 @@ class ImageSearchDelegate extends SearchDelegate<CollectionFilter> {
]; ];
} }
@override
Widget buildSuggestions(BuildContext context) { Widget buildSuggestions(BuildContext context) {
final upQuery = query.trim().toUpperCase(); final upQuery = query.trim().toUpperCase();
bool containQuery(String s) => s.toUpperCase().contains(upQuery); bool containQuery(String s) => s.toUpperCase().contains(upQuery);
@ -137,7 +142,6 @@ class ImageSearchDelegate extends SearchDelegate<CollectionFilter> {
); );
} }
@override
Widget buildResults(BuildContext context) { Widget buildResults(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
// `buildResults` is called in the build phase, // `buildResults` is called in the build phase,
@ -154,14 +158,160 @@ class ImageSearchDelegate extends SearchDelegate<CollectionFilter> {
} }
void _select(BuildContext context, CollectionFilter filter) { void _select(BuildContext context, CollectionFilter filter) {
if (parentCollection != null) {
_applyToParentCollectionPage(context, filter);
} else {
_goToCollectionPage(context, filter);
}
}
void _applyToParentCollectionPage(BuildContext context, CollectionFilter filter) {
if (filter != null) { if (filter != null) {
onSelection(filter); parentCollection.addFilter(filter);
} }
// we post closing the search page after applying the filter selection // we post closing the search page after applying the filter selection
// so that hero animation target is ready in the `FilterBar`, // so that hero animation target is ready in the `FilterBar`,
// even when the target is a child of an `AnimatedList` // even when the target is a child of an `AnimatedList`
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
close(context, null); _goBack(context);
}); });
} }
void _goBack(BuildContext context) {
_clean();
Navigator.of(context).pop();
}
void _goToCollectionPage(BuildContext context, CollectionFilter filter) {
_clean();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
settings: RouteSettings(name: CollectionPage.routeName),
builder: (context) => CollectionPage(CollectionLens(
source: source,
filters: [filter],
groupFactor: settings.collectionGroupFactor,
sortFactor: settings.collectionSortFactor,
)),
),
settings.navRemoveRoutePredicate(CollectionPage.routeName),
);
}
void _clean() {
currentBody = null;
focusNode?.unfocus();
}
// adapted from `SearchDelegate`
void showResults(BuildContext context) {
focusNode?.unfocus();
currentBody = SearchBody.results;
}
void showSuggestions(BuildContext context) {
assert(focusNode != null, '_focusNode must be set by route before showSuggestions is called.');
focusNode.requestFocus();
currentBody = SearchBody.suggestions;
}
Animation<double> get transitionAnimation => proxyAnimation;
FocusNode focusNode;
final TextEditingController queryTextController = TextEditingController();
final ProxyAnimation proxyAnimation = ProxyAnimation(kAlwaysDismissedAnimation);
String get query => queryTextController.text;
set query(String value) {
assert(query != null);
queryTextController.text = value;
}
final ValueNotifier<SearchBody> currentBodyNotifier = ValueNotifier<SearchBody>(null);
SearchBody get currentBody => currentBodyNotifier.value;
set currentBody(SearchBody value) {
currentBodyNotifier.value = value;
}
SearchPageRoute route;
}
// adapted from `SearchDelegate`
enum SearchBody { suggestions, results }
// adapted from `SearchDelegate`
class SearchPageRoute<T> extends PageRoute<T> {
SearchPageRoute({
@required this.delegate,
}) : assert(delegate != null),
super(settings: RouteSettings(name: SearchPage.routeName)) {
assert(
delegate.route == null,
'The ${delegate.runtimeType} instance is currently used by another active '
'search. Please close that search by calling close() on the SearchDelegate '
'before openening another search with the same delegate instance.',
);
delegate.route = this;
}
final ImageSearchDelegate delegate;
@override
Color get barrierColor => null;
@override
String get barrierLabel => null;
@override
Duration get transitionDuration => const Duration(milliseconds: 300);
@override
bool get maintainState => false;
@override
Widget buildTransitions(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return FadeTransition(
opacity: animation,
child: child,
);
}
@override
Animation<double> createAnimation() {
final animation = super.createAnimation();
delegate.proxyAnimation.parent = animation;
return animation;
}
@override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return SearchPage(
delegate: delegate,
animation: animation,
);
}
@override
void didComplete(T result) {
super.didComplete(result);
assert(delegate.route == this);
delegate.route = null;
delegate.currentBody = null;
}
} }

View file

@ -0,0 +1,127 @@
import 'package:aves/widgets/collection/search/search_delegate.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class SearchPage extends StatefulWidget {
static const routeName = '/search';
final ImageSearchDelegate delegate;
final Animation<double> animation;
const SearchPage({
this.delegate,
this.animation,
});
@override
_SearchPageState createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
FocusNode focusNode = FocusNode();
@override
void initState() {
super.initState();
widget.delegate.queryTextController.addListener(_onQueryChanged);
widget.animation.addStatusListener(_onAnimationStatusChanged);
widget.delegate.currentBodyNotifier.addListener(_onSearchBodyChanged);
focusNode.addListener(_onFocusChanged);
widget.delegate.focusNode = focusNode;
}
@override
void dispose() {
super.dispose();
widget.delegate.queryTextController.removeListener(_onQueryChanged);
widget.animation.removeStatusListener(_onAnimationStatusChanged);
widget.delegate.currentBodyNotifier.removeListener(_onSearchBodyChanged);
widget.delegate.focusNode = null;
focusNode.dispose();
}
void _onAnimationStatusChanged(AnimationStatus status) {
if (status != AnimationStatus.completed) {
return;
}
widget.animation.removeStatusListener(_onAnimationStatusChanged);
focusNode.requestFocus();
}
@override
void didUpdateWidget(SearchPage oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.delegate != oldWidget.delegate) {
oldWidget.delegate.queryTextController.removeListener(_onQueryChanged);
widget.delegate.queryTextController.addListener(_onQueryChanged);
oldWidget.delegate.currentBodyNotifier.removeListener(_onSearchBodyChanged);
widget.delegate.currentBodyNotifier.addListener(_onSearchBodyChanged);
oldWidget.delegate.focusNode = null;
widget.delegate.focusNode = focusNode;
}
}
void _onFocusChanged() {
if (focusNode.hasFocus && widget.delegate.currentBody != SearchBody.suggestions) {
widget.delegate.showSuggestions(context);
}
}
void _onQueryChanged() {
setState(() {
// rebuild ourselves because query changed.
});
}
void _onSearchBodyChanged() {
setState(() {
// rebuild ourselves because search body changed.
});
}
@override
Widget build(BuildContext context) {
final theme = widget.delegate.appBarTheme(context);
Widget body;
switch (widget.delegate.currentBody) {
case SearchBody.suggestions:
body = KeyedSubtree(
key: ValueKey<SearchBody>(SearchBody.suggestions),
child: widget.delegate.buildSuggestions(context),
);
break;
case SearchBody.results:
body = KeyedSubtree(
key: ValueKey<SearchBody>(SearchBody.results),
child: widget.delegate.buildResults(context),
);
break;
}
return Scaffold(
appBar: AppBar(
backgroundColor: theme.primaryColor,
iconTheme: theme.primaryIconTheme,
textTheme: theme.primaryTextTheme,
brightness: theme.primaryColorBrightness,
leading: widget.delegate.buildLeading(context),
title: 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: MaterialLocalizations.of(context).searchFieldLabel,
hintStyle: theme.inputDecorationTheme.hintStyle,
),
),
actions: widget.delegate.buildActions(context),
),
body: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: body,
),
);
}
}

View file

@ -1,5 +1,3 @@
import 'dart:io';
import 'package:aves/model/image_entry.dart'; import 'package:aves/model/image_entry.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/services/android_app_service.dart'; import 'package:aves/services/android_app_service.dart';
@ -12,6 +10,7 @@ import 'package:aves/widgets/common/entry_actions.dart';
import 'package:aves/widgets/common/image_providers/uri_image_provider.dart'; import 'package:aves/widgets/common/image_providers/uri_image_provider.dart';
import 'package:aves/widgets/fullscreen/debug.dart'; import 'package:aves/widgets/fullscreen/debug.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:pdf/pdf.dart'; import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pdf; import 'package:pdf/widgets.dart' as pdf;
@ -154,7 +153,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin {
} }
} else { } else {
// leave viewer // leave viewer
exit(0); unawaited(SystemNavigator.pop());
} }
} }

View file

@ -11,6 +11,7 @@ import 'package:aves/widgets/common/aves_selection_dialog.dart';
import 'package:aves/widgets/common/icons.dart'; import 'package:aves/widgets/common/icons.dart';
import 'package:aves/widgets/common/menu_row.dart'; import 'package:aves/widgets/common/menu_row.dart';
import 'package:aves/widgets/filter_grids/filter_grid_page.dart'; import 'package:aves/widgets/filter_grids/filter_grid_page.dart';
import 'package:aves/widgets/filter_grids/search_button.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
@ -36,7 +37,7 @@ class AlbumListPage extends StatelessWidget {
return FilterNavigationPage( return FilterNavigationPage(
source: source, source: source,
title: 'Albums', title: 'Albums',
actions: _buildActions(), actions: _buildActions(context),
filterEntries: getAlbumEntries(source), filterEntries: getAlbumEntries(source),
filterBuilder: (s) => AlbumFilter(s, source.getUniqueAlbumName(s)), filterBuilder: (s) => AlbumFilter(s, source.getUniqueAlbumName(s)),
emptyBuilder: () => EmptyContent( emptyBuilder: () => EmptyContent(
@ -51,10 +52,10 @@ class AlbumListPage extends StatelessWidget {
); );
} }
List<Widget> _buildActions() { List<Widget> _buildActions(BuildContext context) {
return [ return [
Builder( SearchButton(source),
builder: (context) => PopupMenuButton<ChipAction>( PopupMenuButton<ChipAction>(
key: Key('appbar-menu-button'), key: Key('appbar-menu-button'),
itemBuilder: (context) { itemBuilder: (context) {
return [ return [
@ -67,7 +68,6 @@ class AlbumListPage extends StatelessWidget {
}, },
onSelected: (action) => _onChipActionSelected(context, action), onSelected: (action) => _onChipActionSelected(context, action),
), ),
),
]; ];
} }

View file

@ -19,11 +19,12 @@ class DecoratedFilterChip extends StatelessWidget {
final FilterCallback onPressed; final FilterCallback onPressed;
const DecoratedFilterChip({ const DecoratedFilterChip({
Key key,
@required this.source, @required this.source,
@required this.filter, @required this.filter,
@required this.entry, @required this.entry,
@required this.onPressed, @required this.onPressed,
}); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View file

@ -119,6 +119,7 @@ class FilterGridPage extends StatelessWidget {
(context, i) { (context, i) {
final key = filterKeys[i]; final key = filterKeys[i];
final child = DecoratedFilterChip( final child = DecoratedFilterChip(
key: Key(key),
source: source, source: source,
filter: filterBuilder(key), filter: filterBuilder(key),
entry: filterEntries[key], entry: filterEntries[key],

View file

@ -0,0 +1,29 @@
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/widgets/collection/search/search_delegate.dart';
import 'package:aves/widgets/common/icons.dart';
import 'package:flutter/material.dart';
class SearchButton extends StatelessWidget {
final CollectionSource source;
const SearchButton(this.source);
@override
Widget build(BuildContext context) {
return IconButton(
key: Key('search-button'),
icon: Icon(AIcons.search),
onPressed: () => _goToSearch(context),
);
}
void _goToSearch(BuildContext context) {
Navigator.push(
context,
SearchPageRoute(
delegate: ImageSearchDelegate(
source: source,
),
));
}
}

View file

@ -325,11 +325,12 @@ class FullscreenBodyState extends State<FullscreenBody> with SingleTickerProvide
} }
void _onLeave() { void _onLeave() {
if (!Navigator.canPop(context)) { if (Navigator.canPop(context)) {
// exit app when trying to pop a fullscreen page that is a viewer for a single entry
exit(0);
}
_showSystemUI(); _showSystemUI();
} else {
// exit app when trying to pop a fullscreen page that is a viewer for a single entry
SystemNavigator.pop();
}
} }
// system UI // system UI

View file

@ -10,6 +10,8 @@ import 'package:aves/services/image_file_service.dart';
import 'package:aves/services/viewer_service.dart'; import 'package:aves/services/viewer_service.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/collection/search/search_delegate.dart';
import 'package:aves/widgets/collection/search_page.dart';
import 'package:aves/widgets/common/data_providers/media_store_collection_provider.dart'; import 'package:aves/widgets/common/data_providers/media_store_collection_provider.dart';
import 'package:aves/widgets/common/routes.dart'; import 'package:aves/widgets/common/routes.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart'; import 'package:aves/widgets/filter_grids/albums_page.dart';
@ -32,7 +34,7 @@ class HomePage extends StatefulWidget {
class _HomePageState extends State<HomePage> { class _HomePageState extends State<HomePage> {
MediaStoreSource _mediaStore; MediaStoreSource _mediaStore;
ImageEntry _viewerEntry; ImageEntry _viewerEntry;
HomePageSetting _shortcutPage; String _shortcutRouteName;
List<String> _shortcutFilters; List<String> _shortcutFilters;
@override @override
@ -83,8 +85,14 @@ class _HomePageState extends State<HomePage> {
debugPrint('pick mimeType=$pickMimeTypes'); debugPrint('pick mimeType=$pickMimeTypes');
break; break;
default: default:
final extraPage = intentData['page']; // do not use 'route' as extra key, as the Flutter framework acts on it
_shortcutPage = HomePageSetting.values.firstWhere((v) => v.toString().split('.')[1] == extraPage, orElse: () => null); final extraRoute = intentData['page'];
switch (extraRoute) {
case CollectionPage.routeName:
case AlbumListPage.routeName:
case SearchPage.routeName:
_shortcutRouteName = extraRoute;
}
final extraFilters = intentData['filters']; final extraFilters = intentData['filters'];
_shortcutFilters = extraFilters != null ? (extraFilters as List).cast<String>() : null; _shortcutFilters = extraFilters != null ? (extraFilters as List).cast<String>() : null;
} }
@ -117,12 +125,12 @@ class _HomePageState extends State<HomePage> {
); );
} }
HomePageSetting startPage; String routeName;
Iterable<CollectionFilter> filters; Iterable<CollectionFilter> filters;
if (AvesApp.mode == AppMode.pick) { if (AvesApp.mode == AppMode.pick) {
startPage = HomePageSetting.collection; routeName = CollectionPage.routeName;
} else { } else {
startPage = _shortcutPage ?? settings.homePage; routeName = _shortcutRouteName ?? settings.homePage.routeName;
filters = (_shortcutFilters ?? []).map((filterString) { filters = (_shortcutFilters ?? []).map((filterString) {
switch (filterString) { switch (filterString) {
case 'anyVideo': case 'anyVideo':
@ -132,14 +140,17 @@ class _HomePageState extends State<HomePage> {
return null; return null;
}); });
} }
switch (startPage) { switch (routeName) {
case HomePageSetting.albums: case AlbumListPage.routeName:
return DirectMaterialPageRoute( return DirectMaterialPageRoute(
settings: RouteSettings(name: AlbumListPage.routeName), settings: RouteSettings(name: AlbumListPage.routeName),
builder: (_) => AlbumListPage(source: _mediaStore), builder: (_) => AlbumListPage(source: _mediaStore),
); );
case HomePageSetting.search: case SearchPage.routeName:
case HomePageSetting.collection: return SearchPageRoute(
delegate: ImageSearchDelegate(source: _mediaStore),
);
case CollectionPage.routeName:
default: default:
return DirectMaterialPageRoute( return DirectMaterialPageRoute(
settings: RouteSettings(name: CollectionPage.routeName), settings: RouteSettings(name: CollectionPage.routeName),