#24 viewer: added option to show/hide info on overlay

This commit is contained in:
Thibault Deckers 2021-01-12 12:54:53 +09:00
parent 80d7de43ed
commit 6ea9923a92
4 changed files with 113 additions and 75 deletions

View file

@ -47,6 +47,7 @@ class Settings extends ChangeNotifier {
// viewer // viewer
static const showOverlayMinimapKey = 'show_overlay_minimap'; static const showOverlayMinimapKey = 'show_overlay_minimap';
static const showOverlayInfoKey = 'show_overlay_info';
static const showOverlayShootingDetailsKey = 'show_overlay_shooting_details'; static const showOverlayShootingDetailsKey = 'show_overlay_shooting_details';
// info // info
@ -166,6 +167,10 @@ class Settings extends ChangeNotifier {
set showOverlayMinimap(bool newValue) => setAndNotify(showOverlayMinimapKey, newValue); set showOverlayMinimap(bool newValue) => setAndNotify(showOverlayMinimapKey, newValue);
bool get showOverlayInfo => getBoolOrDefault(showOverlayInfoKey, true);
set showOverlayInfo(bool newValue) => setAndNotify(showOverlayInfoKey, newValue);
bool get showOverlayShootingDetails => getBoolOrDefault(showOverlayShootingDetailsKey, true); bool get showOverlayShootingDetails => getBoolOrDefault(showOverlayShootingDetailsKey, true);
set showOverlayShootingDetails(bool newValue) => setAndNotify(showOverlayShootingDetailsKey, newValue); set showOverlayShootingDetails(bool newValue) => setAndNotify(showOverlayShootingDetailsKey, newValue);

View file

@ -5,18 +5,25 @@ import 'package:flutter/material.dart';
final _filter = ImageFilter.blur(sigmaX: 4, sigmaY: 4); final _filter = ImageFilter.blur(sigmaX: 4, sigmaY: 4);
class BlurredRect extends StatelessWidget { class BlurredRect extends StatelessWidget {
final bool enabled;
final Widget child; final Widget child;
const BlurredRect({Key key, this.child}) : super(key: key); const BlurredRect({
Key key,
this.enabled = true,
this.child,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ClipRect( return enabled
child: BackdropFilter( ? ClipRect(
filter: _filter, child: BackdropFilter(
child: child, filter: _filter,
), child: child,
); ),
)
: child;
} }
} }

View file

@ -25,33 +25,42 @@ class _SettingsPageState extends State<SettingsPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context);
return MediaQueryDataProvider( return MediaQueryDataProvider(
child: Scaffold( child: Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text('Settings'), title: Text('Settings'),
), ),
body: SafeArea( body: Theme(
child: Consumer<Settings>( data: theme.copyWith(
builder: (context, settings, child) => AnimationLimiter( textTheme: theme.textTheme.copyWith(
child: ListView( // dense style font for tile subtitles, without modifying title font
padding: EdgeInsets.all(8), bodyText2: TextStyle(fontSize: 12),
children: AnimationConfiguration.toStaggeredList( ),
duration: Durations.staggeredAnimation, ),
delay: Durations.staggeredAnimationDelay, child: SafeArea(
childAnimationBuilder: (child) => SlideAnimation( child: Consumer<Settings>(
verticalOffset: 50.0, builder: (context, settings, child) => AnimationLimiter(
child: FadeInAnimation( child: ListView(
child: child, padding: EdgeInsets.all(8),
children: AnimationConfiguration.toStaggeredList(
duration: Durations.staggeredAnimation,
delay: Durations.staggeredAnimationDelay,
childAnimationBuilder: (child) => SlideAnimation(
verticalOffset: 50.0,
child: FadeInAnimation(
child: child,
),
), ),
children: [
_buildNavigationSection(context),
_buildDisplaySection(context),
_buildThumbnailsSection(context),
_buildViewerSection(context),
_buildSearchSection(context),
_buildPrivacySection(context),
],
), ),
children: [
_buildNavigationSection(context),
_buildDisplaySection(context),
_buildThumbnailsSection(context),
_buildViewerSection(context),
_buildSearchSection(context),
_buildPrivacySection(context),
],
), ),
), ),
), ),
@ -188,9 +197,15 @@ class _SettingsPageState extends State<SettingsPage> {
onChanged: (v) => settings.showOverlayMinimap = v, onChanged: (v) => settings.showOverlayMinimap = v,
title: Text('Show minimap'), title: Text('Show minimap'),
), ),
SwitchListTile(
value: settings.showOverlayInfo,
onChanged: (v) => settings.showOverlayInfo = v,
title: Text('Show information'),
subtitle: Text('Show title, date, location, etc.'),
),
SwitchListTile( SwitchListTile(
value: settings.showOverlayShootingDetails, value: settings.showOverlayShootingDetails,
onChanged: (v) => settings.showOverlayShootingDetails = v, onChanged: settings.showOverlayInfo ? (v) => settings.showOverlayShootingDetails = v : null,
title: Text('Show shooting details'), title: Text('Show shooting details'),
), ),
], ],

View file

@ -51,6 +51,8 @@ class _ViewerBottomOverlayState extends State<ViewerBottomOverlay> {
return index < entries.length ? entries[index] : null; return index < entries.length ? entries[index] : null;
} }
MultiPageController get multiPageController => widget.multiPageController;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -71,7 +73,9 @@ class _ViewerBottomOverlayState extends State<ViewerBottomOverlay> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final hasEdgeContent = settings.showOverlayInfo || multiPageController != null;
return BlurredRect( return BlurredRect(
enabled: hasEdgeContent,
child: Selector<MediaQueryData, Tuple3<double, EdgeInsets, EdgeInsets>>( child: Selector<MediaQueryData, Tuple3<double, EdgeInsets, EdgeInsets>>(
selector: (c, mq) => Tuple3(mq.size.width, mq.viewInsets, mq.viewPadding), selector: (c, mq) => Tuple3(mq.size.width, mq.viewInsets, mq.viewPadding),
builder: (c, mq, child) { builder: (c, mq, child) {
@ -84,7 +88,7 @@ class _ViewerBottomOverlayState extends State<ViewerBottomOverlay> {
final availableWidth = mqWidth - viewPadding.horizontal; final availableWidth = mqWidth - viewPadding.horizontal;
return Container( return Container(
color: kOverlayBackgroundColor, color: hasEdgeContent ? kOverlayBackgroundColor: Colors.transparent,
padding: viewInsets + viewPadding.copyWith(top: 0), padding: viewInsets + viewPadding.copyWith(top: 0),
child: FutureBuilder<OverlayMetadata>( child: FutureBuilder<OverlayMetadata>(
future: _detailLoader, future: _detailLoader,
@ -100,7 +104,7 @@ class _ViewerBottomOverlayState extends State<ViewerBottomOverlay> {
details: _lastDetails, details: _lastDetails,
position: widget.showPosition ? '${widget.index + 1}/${widget.entries.length}' : null, position: widget.showPosition ? '${widget.index + 1}/${widget.entries.length}' : null,
availableWidth: availableWidth, availableWidth: availableWidth,
multiPageController: widget.multiPageController, multiPageController: multiPageController,
); );
}, },
), ),
@ -136,8 +140,6 @@ class _BottomOverlayContent extends AnimatedWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final infoMaxWidth = availableWidth - infoPadding.horizontal;
return DefaultTextStyle( return DefaultTextStyle(
style: Theme.of(context).textTheme.bodyText2.copyWith( style: Theme.of(context).textTheme.bodyText2.copyWith(
shadows: [Constants.embossShadow], shadows: [Constants.embossShadow],
@ -150,48 +152,11 @@ class _BottomOverlayContent extends AnimatedWidget {
child: Selector<MediaQueryData, Orientation>( child: Selector<MediaQueryData, Orientation>(
selector: (c, mq) => mq.orientation, selector: (c, mq) => mq.orientation,
builder: (c, orientation, child) { builder: (c, orientation, child) {
final twoColumns = orientation == Orientation.landscape && infoMaxWidth / 2 > _subRowMinWidth; Widget infoColumn;
final subRowWidth = twoColumns ? min(_subRowMinWidth, infoMaxWidth / 2) : infoMaxWidth;
final positionTitle = _PositionTitleRow(entry: entry, collectionPosition: position, multiPageController: multiPageController);
final hasShootingDetails = details != null && !details.isEmpty && settings.showOverlayShootingDetails;
Widget infoColumn = Padding( if (settings.showOverlayInfo) {
padding: infoPadding, infoColumn = _buildInfoColumn(orientation);
child: Column( }
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (positionTitle.isNotEmpty) positionTitle,
_buildSoloLocationRow(),
if (twoColumns)
Padding(
padding: EdgeInsets.only(top: _interRowPadding),
child: Row(
children: [
Container(
width: subRowWidth,
child: _DateRow(
entry: entry,
multiPageController: multiPageController,
)),
_buildDuoShootingRow(subRowWidth, hasShootingDetails),
],
),
)
else ...[
Container(
padding: EdgeInsets.only(top: _interRowPadding),
width: subRowWidth,
child: _DateRow(
entry: entry,
multiPageController: multiPageController,
),
),
_buildSoloShootingRow(subRowWidth, hasShootingDetails),
],
],
),
);
if (multiPageController != null) { if (multiPageController != null) {
infoColumn = Column( infoColumn = Column(
@ -203,18 +168,64 @@ class _BottomOverlayContent extends AnimatedWidget {
controller: multiPageController, controller: multiPageController,
availableWidth: availableWidth, availableWidth: availableWidth,
), ),
infoColumn, if (infoColumn != null) infoColumn,
], ],
); );
} }
return infoColumn; return infoColumn ?? SizedBox();
}, },
), ),
), ),
); );
} }
Widget _buildInfoColumn(Orientation orientation) {
final infoMaxWidth = availableWidth - infoPadding.horizontal;
final twoColumns = orientation == Orientation.landscape && infoMaxWidth / 2 > _subRowMinWidth;
final subRowWidth = twoColumns ? min(_subRowMinWidth, infoMaxWidth / 2) : infoMaxWidth;
final positionTitle = _PositionTitleRow(entry: entry, collectionPosition: position, multiPageController: multiPageController);
final hasShootingDetails = details != null && !details.isEmpty && settings.showOverlayShootingDetails;
return Padding(
padding: infoPadding,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (positionTitle.isNotEmpty) positionTitle,
_buildSoloLocationRow(),
if (twoColumns)
Padding(
padding: EdgeInsets.only(top: _interRowPadding),
child: Row(
children: [
Container(
width: subRowWidth,
child: _DateRow(
entry: entry,
multiPageController: multiPageController,
)),
_buildDuoShootingRow(subRowWidth, hasShootingDetails),
],
),
)
else ...[
Container(
padding: EdgeInsets.only(top: _interRowPadding),
width: subRowWidth,
child: _DateRow(
entry: entry,
multiPageController: multiPageController,
),
),
_buildSoloShootingRow(subRowWidth, hasShootingDetails),
],
],
),
);
}
Widget _buildSoloLocationRow() => AnimatedSwitcher( Widget _buildSoloLocationRow() => AnimatedSwitcher(
duration: Durations.viewerOverlayChangeAnimation, duration: Durations.viewerOverlayChangeAnimation,
switchInCurve: Curves.easeInOutCubic, switchInCurve: Curves.easeInOutCubic,