#222 optional bottom nav bar

This commit is contained in:
Thibault Deckers 2022-05-06 18:02:38 +09:00
parent 37fc57f694
commit 39aebf49e2
21 changed files with 547 additions and 206 deletions

View file

@ -599,6 +599,7 @@
"settingsSectionNavigation": "Navigation",
"settingsHome": "Home",
"settingsShowBottomNavigationBar": "Show bottom navigation bar",
"settingsKeepScreenOnTile": "Keep screen on",
"settingsKeepScreenOnTitle": "Keep Screen On",
"settingsDoubleBackExit": "Tap “back” twice to exit",

View file

@ -27,6 +27,7 @@ class SettingsDefaults {
static const mustBackTwiceToExit = true;
static const keepScreenOn = KeepScreenOn.viewerOnly;
static const homePage = HomePageSetting.collection;
static const showBottomNavigationBar = false;
static const confirmDeleteForever = true;
static const confirmMoveToBin = true;
static const confirmMoveUndatedItems = true;

View file

@ -56,6 +56,7 @@ class Settings extends ChangeNotifier {
static const mustBackTwiceToExitKey = 'must_back_twice_to_exit';
static const keepScreenOnKey = 'keep_screen_on';
static const homePageKey = 'home_page';
static const showBottomNavigationBarKey = 'show_bottom_navigation_bar';
static const confirmDeleteForeverKey = 'confirm_delete_forever';
static const confirmMoveToBinKey = 'confirm_move_to_bin';
static const confirmMoveUndatedItemsKey = 'confirm_move_undated_items';
@ -294,6 +295,10 @@ class Settings extends ChangeNotifier {
set homePage(HomePageSetting newValue) => setAndNotify(homePageKey, newValue.toString());
bool get showBottomNavigationBar => getBoolOrDefault(showBottomNavigationBarKey, SettingsDefaults.showBottomNavigationBar);
set showBottomNavigationBar(bool newValue) => setAndNotify(showBottomNavigationBarKey, newValue);
bool get confirmDeleteForever => getBoolOrDefault(confirmDeleteForeverKey, SettingsDefaults.confirmDeleteForever);
set confirmDeleteForever(bool newValue) => setAndNotify(confirmDeleteForeverKey, newValue);
@ -682,6 +687,7 @@ class Settings extends ChangeNotifier {
break;
case isInstalledAppAccessAllowedKey:
case isErrorReportingAllowedKey:
case showBottomNavigationBarKey:
case mustBackTwiceToExitKey:
case confirmDeleteForeverKey:
case confirmMoveToBinKey:

View file

@ -139,7 +139,6 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
return AvesColorsProvider(
child: child!,
);
// return child!;
},
onGenerateTitle: (context) => context.l10n.appName,
theme: Themes.lightTheme,

View file

@ -16,7 +16,8 @@ import 'package:aves/widgets/common/behaviour/double_back_pop.dart';
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
import 'package:aves/widgets/common/providers/query_provider.dart';
import 'package:aves/widgets/common/providers/selection_provider.dart';
import 'package:aves/widgets/drawer/app_drawer.dart';
import 'package:aves/widgets/navigation/drawer/app_drawer.dart';
import 'package:aves/widgets/navigation/nav_bar/nav_bar.dart';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -74,30 +75,35 @@ class _CollectionPageState extends State<CollectionPage> {
Widget build(BuildContext context) {
final liveFilter = _collection.filters.firstWhereOrNull((v) => v is QueryFilter && v.live) as QueryFilter?;
return MediaQueryDataProvider(
child: Scaffold(
body: SelectionProvider<AvesEntry>(
child: QueryProvider(
initialQuery: liveFilter?.query,
child: Builder(
builder: (context) => WillPopScope(
onWillPop: () {
final selection = context.read<Selection<AvesEntry>>();
if (selection.isSelecting) {
selection.browse();
return SynchronousFuture(false);
}
return SynchronousFuture(true);
},
child: DoubleBackPopScope(
child: GestureAreaProtectorStack(
child: SafeArea(
bottom: false,
child: ChangeNotifierProvider<CollectionLens>.value(
value: _collection,
child: const CollectionGrid(
// key is expected by test driver
key: Key('collection-grid'),
settingsRouteKey: CollectionPage.routeName,
child: Selector<Settings, bool>(
selector: (context, s) => s.showBottomNavigationBar,
builder: (context, showBottomNavigationBar, child) {
return Scaffold(
body: SelectionProvider<AvesEntry>(
child: QueryProvider(
initialQuery: liveFilter?.query,
child: Builder(
builder: (context) => WillPopScope(
onWillPop: () {
final selection = context.read<Selection<AvesEntry>>();
if (selection.isSelecting) {
selection.browse();
return SynchronousFuture(false);
}
return SynchronousFuture(true);
},
child: DoubleBackPopScope(
child: GestureAreaProtectorStack(
child: SafeArea(
bottom: false,
child: ChangeNotifierProvider<CollectionLens>.value(
value: _collection,
child: const CollectionGrid(
// key is expected by test driver
key: Key('collection-grid'),
settingsRouteKey: CollectionPage.routeName,
),
),
),
),
),
@ -105,10 +111,12 @@ class _CollectionPageState extends State<CollectionPage> {
),
),
),
),
),
drawer: AppDrawer(currentCollection: _collection),
resizeToAvoidBottomInset: false,
drawer: AppDrawer(currentCollection: _collection),
bottomNavigationBar: showBottomNavigationBar ? AppBottomNavBar(currentCollection: _collection) : null,
resizeToAvoidBottomInset: false,
extendBody: true,
);
},
),
);
}

View file

@ -1,118 +0,0 @@
import 'package:aves/model/filters/favourite.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/mime.dart';
import 'package:aves/model/filters/type.dart';
import 'package:aves/theme/colors.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/debug/app_debug_page.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/countries_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:flutter/material.dart';
class DrawerFilterIcon extends StatelessWidget {
final CollectionFilter? filter;
const DrawerFilterIcon({
Key? key,
required this.filter,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final textScaleFactor = MediaQuery.textScaleFactorOf(context);
final iconSize = 24 * textScaleFactor;
final _filter = filter;
if (_filter == null) return Icon(AIcons.allCollection, size: iconSize);
return _filter.iconBuilder(context, iconSize) ?? const SizedBox();
}
}
class DrawerFilterTitle extends StatelessWidget {
final CollectionFilter? filter;
const DrawerFilterTitle({
Key? key,
required this.filter,
}) : super(key: key);
@override
Widget build(BuildContext context) {
String _getString(CollectionFilter? filter) {
final l10n = context.l10n;
if (filter == null) return l10n.drawerCollectionAll;
if (filter == FavouriteFilter.instance) return l10n.drawerCollectionFavourites;
if (filter == MimeFilter.image) return l10n.drawerCollectionImages;
if (filter == MimeFilter.video) return l10n.drawerCollectionVideos;
if (filter == TypeFilter.animated) return l10n.drawerCollectionAnimated;
if (filter == TypeFilter.motionPhoto) return l10n.drawerCollectionMotionPhotos;
if (filter == TypeFilter.panorama) return l10n.drawerCollectionPanoramas;
if (filter == TypeFilter.raw) return l10n.drawerCollectionRaws;
if (filter == TypeFilter.sphericalVideo) return l10n.drawerCollectionSphericalVideos;
return filter.getLabel(context);
}
return Text(_getString(filter));
}
}
class DrawerPageIcon extends StatelessWidget {
final String route;
const DrawerPageIcon({
Key? key,
required this.route,
}) : super(key: key);
@override
Widget build(BuildContext context) {
switch (route) {
case AlbumListPage.routeName:
return const Icon(AIcons.album);
case CountryListPage.routeName:
return const Icon(AIcons.location);
case TagListPage.routeName:
return const Icon(AIcons.tag);
case AppDebugPage.routeName:
return ShaderMask(
shaderCallback: AvesColorsData.debugGradient.createShader,
blendMode: BlendMode.srcIn,
child: const Icon(AIcons.debug),
);
default:
return const SizedBox();
}
}
}
class DrawerPageTitle extends StatelessWidget {
final String route;
const DrawerPageTitle({
Key? key,
required this.route,
}) : super(key: key);
@override
Widget build(BuildContext context) {
String _getString() {
final l10n = context.l10n;
switch (route) {
case AlbumListPage.routeName:
return l10n.albumPageTitle;
case CountryListPage.routeName:
return l10n.countryPageTitle;
case TagListPage.routeName:
return l10n.tagPageTitle;
case AppDebugPage.routeName:
return 'Debug';
default:
return route;
}
}
return Text(_getString());
}
}

View file

@ -21,13 +21,14 @@ import 'package:aves/widgets/common/identity/scroll_thumb.dart';
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
import 'package:aves/widgets/common/providers/tile_extent_controller_provider.dart';
import 'package:aves/widgets/common/tile_extent_controller.dart';
import 'package:aves/widgets/drawer/app_drawer.dart';
import 'package:aves/widgets/filter_grids/common/covered_filter_chip.dart';
import 'package:aves/widgets/filter_grids/common/draggable_thumb_label.dart';
import 'package:aves/widgets/filter_grids/common/filter_tile.dart';
import 'package:aves/widgets/filter_grids/common/list_details_theme.dart';
import 'package:aves/widgets/filter_grids/common/section_keys.dart';
import 'package:aves/widgets/filter_grids/common/section_layout.dart';
import 'package:aves/widgets/navigation/drawer/app_drawer.dart';
import 'package:aves/widgets/navigation/nav_bar/nav_bar.dart';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -69,42 +70,49 @@ class FilterGridPage<T extends CollectionFilter> extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MediaQueryDataProvider(
child: Scaffold(
body: WillPopScope(
onWillPop: () {
final selection = context.read<Selection<FilterGridItem<T>>>();
if (selection.isSelecting) {
selection.browse();
return SynchronousFuture(false);
}
return SynchronousFuture(true);
},
child: DoubleBackPopScope(
child: GestureAreaProtectorStack(
child: SafeArea(
bottom: false,
child: FilterGrid<T>(
// key is expected by test driver
key: const Key('filter-grid'),
settingsRouteKey: settingsRouteKey,
appBar: appBar,
appBarHeight: appBarHeight,
sections: sections,
newFilters: newFilters,
sortFactor: sortFactor,
showHeaders: showHeaders,
selectable: selectable,
queryNotifier: queryNotifier,
applyQuery: applyQuery,
emptyBuilder: emptyBuilder,
heroType: heroType,
child: Selector<Settings, bool>(
selector: (context, s) => s.showBottomNavigationBar,
builder: (context, showBottomNavigationBar, child) {
return Scaffold(
body: WillPopScope(
onWillPop: () {
final selection = context.read<Selection<FilterGridItem<T>>>();
if (selection.isSelecting) {
selection.browse();
return SynchronousFuture(false);
}
return SynchronousFuture(true);
},
child: DoubleBackPopScope(
child: GestureAreaProtectorStack(
child: SafeArea(
bottom: false,
child: FilterGrid<T>(
// key is expected by test driver
key: const Key('filter-grid'),
settingsRouteKey: settingsRouteKey,
appBar: appBar,
appBarHeight: appBarHeight,
sections: sections,
newFilters: newFilters,
sortFactor: sortFactor,
showHeaders: showHeaders,
selectable: selectable,
queryNotifier: queryNotifier,
applyQuery: applyQuery,
emptyBuilder: emptyBuilder,
heroType: heroType,
),
),
),
),
),
),
),
drawer: const AppDrawer(),
resizeToAvoidBottomInset: false,
drawer: const AppDrawer(),
bottomNavigationBar: showBottomNavigationBar ? const AppBottomNavBar() : null,
resizeToAvoidBottomInset: false,
extendBody: true,
);
},
),
);
}

View file

@ -17,19 +17,19 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/extensions/media_query.dart';
import 'package:aves/widgets/common/identity/aves_logo.dart';
import 'package:aves/widgets/debug/app_debug_page.dart';
import 'package:aves/widgets/drawer/collection_nav_tile.dart';
import 'package:aves/widgets/drawer/page_nav_tile.dart';
import 'package:aves/widgets/drawer/tile.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/countries_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:aves/widgets/navigation/drawer/collection_nav_tile.dart';
import 'package:aves/widgets/navigation/drawer/page_nav_tile.dart';
import 'package:aves/widgets/navigation/drawer/tile.dart';
import 'package:aves/widgets/settings/settings_page.dart';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class AppDrawer extends StatelessWidget {
class AppDrawer extends StatefulWidget {
// collection loaded in the `CollectionPage`, if any
final CollectionLens? currentCollection;
@ -38,6 +38,9 @@ class AppDrawer extends StatelessWidget {
this.currentCollection,
}) : super(key: key);
@override
State<AppDrawer> createState() => _AppDrawerState();
static List<String> getDefaultAlbums(BuildContext context) {
final source = context.read<CollectionSource>();
final specialAlbums = source.rawAlbums.where((album) {
@ -47,6 +50,14 @@ class AppDrawer extends StatelessWidget {
..sort(source.compareAlbumsByName);
return specialAlbums;
}
}
class _AppDrawerState extends State<AppDrawer> {
// using the default controller conflicts
// with bottom nav bar primary scroll monitoring
final ScrollController _scrollController = ScrollController();
CollectionLens? get currentCollection => widget.currentCollection;
@override
Widget build(BuildContext context) {
@ -73,6 +84,7 @@ class AppDrawer extends StatelessWidget {
builder: (context, mqPaddingBottom, child) {
final iconTheme = IconTheme.of(context);
return SingleChildScrollView(
controller: _scrollController,
// key is expected by test driver
key: const Key('drawer-scrollview'),
padding: EdgeInsets.only(bottom: mqPaddingBottom),

View file

@ -5,7 +5,7 @@ import 'package:aves/theme/icons.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/drawer/tile.dart';
import 'package:aves/widgets/navigation/drawer/tile.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

View file

@ -1,5 +1,5 @@
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/drawer/tile.dart';
import 'package:aves/widgets/navigation/drawer/tile.dart';
import 'package:flutter/material.dart';
class PageNavTile extends StatelessWidget {

View file

@ -0,0 +1,81 @@
import 'package:aves/model/filters/filters.dart';
import 'package:aves/theme/colors.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/debug/app_debug_page.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/countries_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:aves/widgets/navigation/nav_display.dart';
import 'package:flutter/material.dart';
class DrawerFilterIcon extends StatelessWidget {
final CollectionFilter? filter;
const DrawerFilterIcon({
Key? key,
required this.filter,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final textScaleFactor = MediaQuery.textScaleFactorOf(context);
final iconSize = 24 * textScaleFactor;
final _filter = filter;
if (_filter == null) return Icon(AIcons.allCollection, size: iconSize);
return _filter.iconBuilder(context, iconSize) ?? const SizedBox();
}
}
class DrawerFilterTitle extends StatelessWidget {
final CollectionFilter? filter;
const DrawerFilterTitle({
Key? key,
required this.filter,
}) : super(key: key);
@override
Widget build(BuildContext context) => Text(NavigationDisplay.getFilterTitle(context, filter));
}
class DrawerPageIcon extends StatelessWidget {
final String route;
const DrawerPageIcon({
Key? key,
required this.route,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final icon = NavigationDisplay.getPageIcon(route);
if (icon != null) {
switch (route) {
case AlbumListPage.routeName:
case CountryListPage.routeName:
case TagListPage.routeName:
return Icon(icon);
case AppDebugPage.routeName:
return ShaderMask(
shaderCallback: AvesColorsData.debugGradient.createShader,
blendMode: BlendMode.srcIn,
child: Icon(icon),
);
}
}
return const SizedBox();
}
}
class DrawerPageTitle extends StatelessWidget {
final String route;
const DrawerPageTitle({
Key? key,
required this.route,
}) : super(key: key);
@override
Widget build(BuildContext context) => Text(NavigationDisplay.getPageTitle(context, route));
}

View file

@ -0,0 +1,100 @@
import 'package:flutter/material.dart';
class FloatingNavBar extends StatefulWidget {
final ScrollController? scrollController;
final Widget child;
const FloatingNavBar({
Key? key,
required this.scrollController,
required this.child,
}) : super(key: key);
@override
_FloatingNavBarState createState() => _FloatingNavBarState();
}
class _FloatingNavBarState extends State<FloatingNavBar> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Offset> _offsetAnimation;
double? _lastOffset;
double _delta = 0;
static const double _deltaThreshold = 50;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 200),
vsync: this,
);
_offsetAnimation = Tween<Offset>(
begin: const Offset(0, 0),
end: const Offset(0, 1),
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.linear,
))
..addListener(() {
if (mounted) {
setState(() {});
}
});
_registerWidget(widget);
}
@override
void didUpdateWidget(covariant FloatingNavBar oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.scrollController != widget.scrollController) {
_unregisterWidget(oldWidget);
_registerWidget(widget);
}
}
@override
void dispose() {
_unregisterWidget(widget);
super.dispose();
}
void _registerWidget(FloatingNavBar widget) {
_lastOffset = null;
_delta = 0;
widget.scrollController?.addListener(_onScrollChange);
}
void _unregisterWidget(FloatingNavBar widget) {
widget.scrollController?.removeListener(_onScrollChange);
}
@override
Widget build(BuildContext context) {
return SlideTransition(
position: _offsetAnimation,
child: widget.child,
);
}
void _onScrollChange() {
final scrollController = widget.scrollController;
if (scrollController == null) return;
final offset = scrollController.offset;
_delta += offset - (_lastOffset ?? offset);
_lastOffset = offset;
if (_delta.abs() > _deltaThreshold) {
if (_delta > 0) {
// hide
_controller.forward();
} else {
// show
_controller.reverse();
}
_delta = 0;
}
}
}

View file

@ -0,0 +1,117 @@
import 'package:aves/model/filters/favourite.dart';
import 'package:aves/model/filters/mime.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/widgets/collection/collection_page.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/fx/blurred.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/navigation/nav_bar/floating.dart';
import 'package:aves/widgets/navigation/nav_bar/nav_item.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class AppBottomNavBar extends StatelessWidget {
// collection loaded in the `CollectionPage`, if any
final CollectionLens? currentCollection;
const AppBottomNavBar({
Key? key,
this.currentCollection,
}) : super(key: key);
@override
Widget build(BuildContext context) {
const borderRadius = BorderRadius.all(Radius.circular(8));
final blurred = context.select<Settings, bool>((s) => s.enableOverlayBlurEffect);
final showVideo = context.select<Settings, bool>((s) => !s.hiddenFilters.contains(MimeFilter.video));
final items = [
const AvesBottomNavItem(route: CollectionPage.routeName),
if (showVideo) AvesBottomNavItem(route: CollectionPage.routeName, filter: MimeFilter.video),
const AvesBottomNavItem(route: CollectionPage.routeName, filter: FavouriteFilter.instance),
const AvesBottomNavItem(route: AlbumListPage.routeName),
];
Widget child = Padding(
padding: const EdgeInsets.all(8),
child: BlurredRRect(
enabled: blurred,
borderRadius: borderRadius,
child: BottomNavigationBar(
items: items
.map((item) => BottomNavigationBarItem(
icon: item.icon(context),
label: item.label(context),
))
.toList(),
onTap: (index) => _goTo(context, items, index),
currentIndex: _getCurrentIndex(context, items),
type: BottomNavigationBarType.fixed,
backgroundColor: Theme.of(context).canvasColor.withOpacity(.85),
showSelectedLabels: false,
showUnselectedLabels: false,
),
),
);
return Hero(
tag: 'nav-bar',
flightShuttleBuilder: (flight, animation, direction, fromHero, toHero) {
return MediaQuery.removeViewInsets(
context: context,
removeBottom: true,
child: toHero.widget,
);
},
child: FloatingNavBar(
scrollController: PrimaryScrollController.of(context),
child: SafeArea(
child: child,
),
),
);
}
int _getCurrentIndex(BuildContext context, List<AvesBottomNavItem> items) {
final currentRoute = context.currentRouteName;
final currentItem = items.firstWhereOrNull((item) {
if (currentRoute != item.route) return false;
if (item.route != CollectionPage.routeName) return true;
final currentFilters = currentCollection?.filters;
if (currentFilters == null || currentFilters.length > 1) return false;
return currentFilters.firstOrNull == item.filter;
});
final currentIndex = currentItem != null ? items.indexOf(currentItem) : 0;
return currentIndex;
}
void _goTo(BuildContext context, List<AvesBottomNavItem> items, int index) {
final item = items[index];
final routeName = item.route;
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
settings: RouteSettings(name: routeName),
builder: (context) {
switch (routeName) {
case AlbumListPage.routeName:
return const AlbumListPage();
case CollectionPage.routeName:
default:
return CollectionPage(
source: context.read<CollectionSource>(),
filters: {item.filter},
);
}
},
),
(route) => false,
);
}
}

View file

@ -0,0 +1,36 @@
import 'package:aves/model/filters/filters.dart';
import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/navigation/drawer/tile.dart';
import 'package:aves/widgets/navigation/nav_display.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
class AvesBottomNavItem extends Equatable {
final String route;
final CollectionFilter? filter;
@override
List<Object?> get props => [route, filter];
const AvesBottomNavItem({
required this.route,
this.filter,
});
Widget icon(BuildContext context) {
if (route == CollectionPage.routeName) {
return DrawerFilterIcon(filter: filter);
}
final textScaleFactor = MediaQuery.textScaleFactorOf(context);
final iconSize = 24 * textScaleFactor;
return Icon(NavigationDisplay.getPageIcon(route), size: iconSize);
}
String label(BuildContext context) {
if (route == CollectionPage.routeName) {
return NavigationDisplay.getFilterTitle(context, filter);
}
return NavigationDisplay.getPageTitle(context, route);
}
}

View file

@ -0,0 +1,59 @@
import 'package:aves/model/filters/favourite.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/mime.dart';
import 'package:aves/model/filters/type.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/debug/app_debug_page.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/countries_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class NavigationDisplay {
static String getFilterTitle(BuildContext context, CollectionFilter? filter) {
final l10n = context.l10n;
if (filter == null) return l10n.drawerCollectionAll;
if (filter == FavouriteFilter.instance) return l10n.drawerCollectionFavourites;
if (filter == MimeFilter.image) return l10n.drawerCollectionImages;
if (filter == MimeFilter.video) return l10n.drawerCollectionVideos;
if (filter == TypeFilter.animated) return l10n.drawerCollectionAnimated;
if (filter == TypeFilter.motionPhoto) return l10n.drawerCollectionMotionPhotos;
if (filter == TypeFilter.panorama) return l10n.drawerCollectionPanoramas;
if (filter == TypeFilter.raw) return l10n.drawerCollectionRaws;
if (filter == TypeFilter.sphericalVideo) return l10n.drawerCollectionSphericalVideos;
return filter.getLabel(context);
}
static String getPageTitle(BuildContext context, route) {
final l10n = context.l10n;
switch (route) {
case AlbumListPage.routeName:
return l10n.albumPageTitle;
case CountryListPage.routeName:
return l10n.countryPageTitle;
case TagListPage.routeName:
return l10n.tagPageTitle;
case AppDebugPage.routeName:
return 'Debug';
default:
return route;
}
}
static IconData? getPageIcon(String route) {
switch (route) {
case AlbumListPage.routeName:
return AIcons.album;
case CountryListPage.routeName:
return AIcons.location;
case TagListPage.routeName:
return AIcons.tag;
case AppDebugPage.routeName:
return AIcons.debug;
default:
return null;
}
}
}

View file

@ -31,6 +31,7 @@ class DisplaySection extends SettingsSection {
SettingsTileDisplayThemeBrightness(),
SettingsTileDisplayThemeColorMode(),
SettingsTileDisplayDisplayRefreshRateMode(),
SettingsTileDisplayEnableBlurEffect(),
];
}
@ -75,3 +76,15 @@ class SettingsTileDisplayDisplayRefreshRateMode extends SettingsTile {
dialogTitle: context.l10n.settingsDisplayRefreshRateModeTitle,
);
}
class SettingsTileDisplayEnableBlurEffect extends SettingsTile {
@override
String title(BuildContext context) => context.l10n.settingsViewerEnableOverlayBlurEffect;
@override
Widget build(BuildContext context) => SettingsSwitchListTile(
selector: (context, s) => s.enableOverlayBlurEffect,
onChanged: (v) => settings.enableOverlayBlurEffect = v,
title: title(context),
);
}

View file

@ -1,11 +1,11 @@
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/drawer/app_drawer.dart';
import 'package:aves/widgets/drawer/tile.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/countries_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:aves/widgets/navigation/drawer/app_drawer.dart';
import 'package:aves/widgets/navigation/drawer/tile.dart';
import 'package:aves/widgets/search/search_delegate.dart';
import 'package:aves/widgets/settings/navigation/drawer_tab_albums.dart';
import 'package:aves/widgets/settings/navigation/drawer_tab_fixed.dart';

View file

@ -3,8 +3,8 @@ import 'package:aves/model/source/collection_source.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/buttons.dart';
import 'package:aves/widgets/drawer/tile.dart';
import 'package:aves/widgets/filter_grids/album_pick.dart';
import 'package:aves/widgets/navigation/drawer/tile.dart';
import 'package:aves/widgets/settings/navigation/drawer_editor_banner.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

View file

@ -31,6 +31,7 @@ class NavigationSection extends SettingsSection {
@override
FutureOr<List<SettingsTile>> tiles(BuildContext context) => [
SettingsTileNavigationHomePage(),
SettingsTileShowBottomNavigationBar(),
SettingsTileNavigationDrawer(),
SettingsTileNavigationConfirmationDialog(),
SettingsTileNavigationKeepScreenOn(),
@ -53,6 +54,18 @@ class SettingsTileNavigationHomePage extends SettingsTile {
);
}
class SettingsTileShowBottomNavigationBar extends SettingsTile {
@override
String title(BuildContext context) => context.l10n.settingsShowBottomNavigationBar;
@override
Widget build(BuildContext context) => SettingsSwitchListTile(
selector: (context, s) => s.showBottomNavigationBar,
onChanged: (v) => settings.showBottomNavigationBar = v,
title: title(context),
);
}
class SettingsTileNavigationDrawer extends SettingsTile {
@override
String title(BuildContext context) => context.l10n.settingsNavigationDrawerTile;

View file

@ -52,11 +52,6 @@ class ViewerOverlayPage extends StatelessWidget {
onChanged: (v) => settings.showOverlayThumbnailPreview = v,
title: context.l10n.settingsViewerShowOverlayThumbnails,
),
SettingsSwitchListTile(
selector: (context, s) => s.enableOverlayBlurEffect,
onChanged: (v) => settings.enableOverlayBlurEffect = v,
title: context.l10n.settingsViewerEnableOverlayBlurEffect,
),
],
),
),

View file

@ -1,7 +1,8 @@
{
"de": [
"settingsSearchFieldLabel",
"settingsSearchEmpty"
"settingsSearchEmpty",
"settingsShowBottomNavigationBar"
],
"es": [
@ -16,22 +17,26 @@
"appPickDialogTitle",
"appPickDialogNone",
"settingsSearchFieldLabel",
"settingsSearchEmpty"
"settingsSearchEmpty",
"settingsShowBottomNavigationBar"
],
"fr": [
"settingsSearchFieldLabel",
"settingsSearchEmpty"
"settingsSearchEmpty",
"settingsShowBottomNavigationBar"
],
"id": [
"settingsSearchFieldLabel",
"settingsSearchEmpty"
"settingsSearchEmpty",
"settingsShowBottomNavigationBar"
],
"it": [
"settingsSearchFieldLabel",
"settingsSearchEmpty"
"settingsSearchEmpty",
"settingsShowBottomNavigationBar"
],
"ja": [
@ -46,26 +51,31 @@
"appPickDialogTitle",
"appPickDialogNone",
"settingsSearchFieldLabel",
"settingsSearchEmpty"
"settingsSearchEmpty",
"settingsShowBottomNavigationBar"
],
"ko": [
"settingsSearchFieldLabel",
"settingsSearchEmpty"
"settingsSearchEmpty",
"settingsShowBottomNavigationBar"
],
"pt": [
"settingsSearchFieldLabel",
"settingsSearchEmpty"
"settingsSearchEmpty",
"settingsShowBottomNavigationBar"
],
"ru": [
"settingsSearchFieldLabel",
"settingsSearchEmpty"
"settingsSearchEmpty",
"settingsShowBottomNavigationBar"
],
"zh": [
"settingsSearchFieldLabel",
"settingsSearchEmpty"
"settingsSearchEmpty",
"settingsShowBottomNavigationBar"
]
}