viewer: fixed reappearing swiped snack bar
This commit is contained in:
parent
7e11a397c6
commit
234c8aa21e
3 changed files with 146 additions and 9 deletions
|
@ -7,6 +7,7 @@ import 'package:aves/model/settings/settings.dart';
|
|||
import 'package:aves/services/accessibility_service.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/common/action_mixins/overlay_snack_bar.dart';
|
||||
import 'package:aves/widgets/common/basic/circle.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/viewer/entry_viewer_page.dart';
|
||||
|
@ -58,24 +59,22 @@ mixin FeedbackMixin {
|
|||
(context) => SafeArea(
|
||||
child: Padding(
|
||||
padding: margin,
|
||||
child: SnackBar(
|
||||
child: OverlaySnackBar(
|
||||
content: snackBarContent,
|
||||
animation: const AlwaysStoppedAnimation<double>(1),
|
||||
action: action != null
|
||||
? SnackBarAction(
|
||||
label: action.label,
|
||||
? TextButton(
|
||||
style: ButtonStyle(
|
||||
foregroundColor: MaterialStateProperty.all(Theme.of(context).snackBarTheme.actionTextColor),
|
||||
),
|
||||
onPressed: () {
|
||||
// the regular snack bar dismiss behavior is confused
|
||||
// because it expects a `Scaffold` in context,
|
||||
// so we manually dimiss the overlay entry
|
||||
// TODO TLAD [bug] after dismissing the overlay, tapping on the snack bar area makes the overlay visible again
|
||||
notificationOverlayEntry?.dismiss();
|
||||
action.onPressed();
|
||||
},
|
||||
child: Text(action.label),
|
||||
)
|
||||
: null,
|
||||
duration: duration,
|
||||
dismissDirection: DismissDirection.horizontal,
|
||||
onDismiss: () => notificationOverlayEntry?.dismiss(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
135
lib/widgets/common/action_mixins/overlay_snack_bar.dart
Normal file
135
lib/widgets/common/action_mixins/overlay_snack_bar.dart
Normal file
|
@ -0,0 +1,135 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
// adapted from Flutter `SnackBar` in `/material/snack_bar.dart`
|
||||
|
||||
// As of Flutter v3.0.1, `SnackBar` is not customizable enough to add margin
|
||||
// and ignore pointers in that area, so we use an overlay entry instead.
|
||||
// This overlay entry is not below a `Scaffold` (which is expected by `SnackBar`
|
||||
// and `SnackBarAction`), and is not dismissed the same way.
|
||||
// This adaptation assumes the `SnackBarBehavior.floating` behavior and no animation.
|
||||
class OverlaySnackBar extends StatelessWidget {
|
||||
final Widget content;
|
||||
final Widget? action;
|
||||
final DismissDirection dismissDirection;
|
||||
final VoidCallback onDismiss;
|
||||
|
||||
const OverlaySnackBar({
|
||||
super.key,
|
||||
required this.content,
|
||||
required this.action,
|
||||
required this.dismissDirection,
|
||||
required this.onDismiss,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final colorScheme = theme.colorScheme;
|
||||
final snackBarTheme = theme.snackBarTheme;
|
||||
final isThemeDark = theme.brightness == Brightness.dark;
|
||||
final buttonColor = isThemeDark ? colorScheme.primary : colorScheme.secondary;
|
||||
|
||||
final brightness = isThemeDark ? Brightness.light : Brightness.dark;
|
||||
final themeBackgroundColor = isThemeDark ? colorScheme.onSurface : Color.alphaBlend(colorScheme.onSurface.withOpacity(0.80), colorScheme.surface);
|
||||
final inverseTheme = theme.copyWith(
|
||||
colorScheme: ColorScheme(
|
||||
primary: colorScheme.onPrimary,
|
||||
secondary: buttonColor,
|
||||
surface: colorScheme.onSurface,
|
||||
background: themeBackgroundColor,
|
||||
error: colorScheme.onError,
|
||||
onPrimary: colorScheme.primary,
|
||||
onSecondary: colorScheme.secondary,
|
||||
onSurface: colorScheme.surface,
|
||||
onBackground: colorScheme.background,
|
||||
onError: colorScheme.error,
|
||||
brightness: brightness,
|
||||
),
|
||||
);
|
||||
|
||||
final contentTextStyle = snackBarTheme.contentTextStyle ?? ThemeData(brightness: brightness).textTheme.subtitle1;
|
||||
|
||||
const horizontalPadding = 16.0;
|
||||
final padding = EdgeInsetsDirectional.only(start: horizontalPadding, end: action != null ? 0 : horizontalPadding);
|
||||
const actionHorizontalMargin = horizontalPadding / 2;
|
||||
const singleLineVerticalPadding = 14.0;
|
||||
|
||||
Widget snackBar = Padding(
|
||||
padding: padding,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: singleLineVerticalPadding),
|
||||
child: DefaultTextStyle(
|
||||
style: contentTextStyle!,
|
||||
child: content,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (action != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: actionHorizontalMargin),
|
||||
child: TextButtonTheme(
|
||||
data: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
primary: buttonColor,
|
||||
padding: const EdgeInsets.symmetric(horizontal: horizontalPadding),
|
||||
),
|
||||
),
|
||||
child: action!,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
final elevation = snackBarTheme.elevation ?? 6.0;
|
||||
final backgroundColor = snackBarTheme.backgroundColor ?? inverseTheme.colorScheme.background;
|
||||
final shape = snackBarTheme.shape ?? const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0)));
|
||||
|
||||
snackBar = Material(
|
||||
shape: shape,
|
||||
elevation: elevation,
|
||||
color: backgroundColor,
|
||||
child: Theme(
|
||||
data: inverseTheme,
|
||||
child: snackBar,
|
||||
),
|
||||
);
|
||||
|
||||
const topMargin = 5.0;
|
||||
const bottomMargin = 10.0;
|
||||
const horizontalMargin = 15.0;
|
||||
snackBar = Padding(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
horizontalMargin,
|
||||
topMargin,
|
||||
horizontalMargin,
|
||||
bottomMargin,
|
||||
),
|
||||
child: snackBar,
|
||||
);
|
||||
|
||||
snackBar = SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: snackBar,
|
||||
);
|
||||
|
||||
snackBar = Semantics(
|
||||
container: true,
|
||||
liveRegion: true,
|
||||
onDismiss: onDismiss,
|
||||
child: Dismissible(
|
||||
key: const Key('dismissible'),
|
||||
direction: dismissDirection,
|
||||
resizeDuration: null,
|
||||
onDismissed: (direction) => onDismiss(),
|
||||
child: snackBar,
|
||||
),
|
||||
);
|
||||
|
||||
return snackBar;
|
||||
}
|
||||
}
|
|
@ -146,6 +146,9 @@ flutter:
|
|||
# `OutputBuffer` in `/services/common/output_buffer.dart`
|
||||
# adapts from Flutter `_OutputBuffer` in `/foundation/consolidate_response.dart`
|
||||
#
|
||||
# `OverlaySnackBar` in `/widgets/common/action_mixins/overlay_snack_bar.dart`
|
||||
# adapts from Flutter `SnackBar` in `/material/snack_bar.dart`
|
||||
#
|
||||
# `EagerScaleGestureRecognizer` in `/widgets/common/behaviour/eager_scale_gesture_recognizer.dart`
|
||||
# adapts from Flutter `ScaleGestureRecognizer` in `/gestures/scale.dart`
|
||||
#
|
||||
|
|
Loading…
Reference in a new issue