#218 viewer: snack bars avoid interactive widgets at the bottom
This commit is contained in:
parent
2a0b9984d8
commit
eb4af53f5c
7 changed files with 61 additions and 11 deletions
|
@ -15,6 +15,8 @@ All notable changes to this project will be documented in this file.
|
|||
### Changed
|
||||
|
||||
- upgraded Flutter to stable v2.10.4
|
||||
- snack bars are dismissible with an horizontal swipe instead of a down swipe
|
||||
- Viewer: snack bars avoid quick actions and thumbnails at the bottom
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
@ -8,9 +8,11 @@ import 'package:aves/services/accessibility_service.dart';
|
|||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/viewer/entry_viewer_page.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:overlay_support/overlay_support.dart';
|
||||
import 'package:percent_indicator/circular_percent_indicator.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
@ -34,16 +36,45 @@ mixin FeedbackMixin {
|
|||
// provide the messenger if feedback happens as the widget is disposed
|
||||
void showFeedbackWithMessenger(BuildContext context, ScaffoldMessengerState messenger, String message, [SnackBarAction? action]) {
|
||||
_getSnackBarDuration(action != null).then((duration) {
|
||||
final progressColor = Theme.of(context).colorScheme.secondary;
|
||||
messenger.showSnackBar(SnackBar(
|
||||
content: _FeedbackMessage(
|
||||
message: message,
|
||||
progressColor: progressColor,
|
||||
duration: action != null ? duration : null,
|
||||
),
|
||||
action: action,
|
||||
duration: duration,
|
||||
));
|
||||
final snackBarContent = _FeedbackMessage(
|
||||
message: message,
|
||||
progressColor: Theme.of(context).colorScheme.secondary,
|
||||
duration: action != null ? duration : null,
|
||||
);
|
||||
|
||||
if (context.currentRouteName == EntryViewerPage.routeName) {
|
||||
// avoid interactive widgets at the bottom of the page
|
||||
final margin = EntryViewerPage.snackBarMargin(context);
|
||||
|
||||
// as of Flutter v2.10.4, `SnackBar` can only be positioned at the bottom,
|
||||
// and space under the snack bar `margin` does not receive gestures
|
||||
// (because it is used by the `Dismissible` wrapping the snack bar)
|
||||
// so we use `showOverlayNotification` instead
|
||||
showOverlayNotification(
|
||||
(context) => SafeArea(
|
||||
child: Padding(
|
||||
padding: margin,
|
||||
child: SnackBar(
|
||||
content: snackBarContent,
|
||||
animation: const AlwaysStoppedAnimation<double>(1),
|
||||
action: action,
|
||||
duration: duration,
|
||||
dismissDirection: DismissDirection.horizontal,
|
||||
),
|
||||
),
|
||||
),
|
||||
duration: duration,
|
||||
position: NotificationPosition.bottom,
|
||||
context: context,
|
||||
);
|
||||
} else {
|
||||
messenger.showSnackBar(SnackBar(
|
||||
content: snackBarContent,
|
||||
action: action,
|
||||
duration: duration,
|
||||
dismissDirection: DismissDirection.horizontal,
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ class ThumbnailScroller extends StatefulWidget {
|
|||
|
||||
@override
|
||||
State<ThumbnailScroller> createState() => _ThumbnailScrollerState();
|
||||
|
||||
static double get preferredHeight => _ThumbnailScrollerState.thumbnailExtent;
|
||||
}
|
||||
|
||||
class _ThumbnailScrollerState extends State<ThumbnailScroller> {
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:aves/model/source/collection_lens.dart';
|
|||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/viewer/entry_viewer_stack.dart';
|
||||
import 'package:aves/widgets/viewer/multipage/conductor.dart';
|
||||
import 'package:aves/widgets/viewer/overlay/bottom.dart';
|
||||
import 'package:aves/widgets/viewer/video/conductor.dart';
|
||||
import 'package:aves/widgets/viewer/visual/conductor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -44,6 +45,10 @@ class EntryViewerPage extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
static EdgeInsets snackBarMargin(BuildContext context) {
|
||||
return EdgeInsets.only(bottom: ViewerBottomOverlay.actionSafeHeight(context));
|
||||
}
|
||||
}
|
||||
|
||||
class ViewStateConductorProvider extends StatelessWidget {
|
||||
|
|
|
@ -33,6 +33,10 @@ class ViewerBottomOverlay extends StatefulWidget {
|
|||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _ViewerBottomOverlayState();
|
||||
|
||||
static double actionSafeHeight(BuildContext context) {
|
||||
return ViewerButtonRow.preferredHeight(context) + (settings.showOverlayThumbnailPreview ? ViewerThumbnailPreview.preferredHeight : 0);
|
||||
}
|
||||
}
|
||||
|
||||
class _ViewerBottomOverlayState extends State<ViewerBottomOverlay> {
|
||||
|
|
|
@ -19,6 +19,8 @@ class ViewerThumbnailPreview extends StatefulWidget {
|
|||
|
||||
@override
|
||||
State<ViewerThumbnailPreview> createState() => _ViewerThumbnailPreviewState();
|
||||
|
||||
static double get preferredHeight => ThumbnailScroller.preferredHeight;
|
||||
}
|
||||
|
||||
class _ViewerThumbnailPreviewState extends State<ViewerThumbnailPreview> {
|
||||
|
|
|
@ -31,6 +31,10 @@ class ViewerButtonRow extends StatelessWidget {
|
|||
static const double outerPadding = 8;
|
||||
static const double innerPadding = 8;
|
||||
|
||||
static double preferredHeight(BuildContext context) => _buttonSize(context) + ViewerButtonRowContent.padding;
|
||||
|
||||
static double _buttonSize(BuildContext context) => OverlayButton.getSize(context);
|
||||
|
||||
const ViewerButtonRow({
|
||||
Key? key,
|
||||
required this.mainEntry,
|
||||
|
@ -107,7 +111,7 @@ class ViewerButtonRow extends StatelessWidget {
|
|||
bottom: false,
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final buttonWidth = OverlayButton.getSize(context);
|
||||
final buttonWidth = _buttonSize(context);
|
||||
final availableCount = ((constraints.maxWidth - outerPadding * 2) / (buttonWidth + innerPadding)).floor();
|
||||
return Selector<Settings, bool>(
|
||||
selector: (context, s) => s.isRotationLocked,
|
||||
|
|
Loading…
Reference in a new issue