mirror of
https://github.com/FoggedLens/deflock-app.git
synced 2026-09-16 06:25:26 +02:00
fix a rare crash around blurring in newer versions of flutter - bit of a workaround
This commit is contained in:
@@ -594,10 +594,13 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
// Tutorial overlay - show only if tutorial should be shown and we're done checking
|
// Tutorial overlay - always mounted once the initial check
|
||||||
if (!_isCheckingTutorial && _showTutorial)
|
// completes (never conditionally inserted/removed - see
|
||||||
|
// PositioningTutorialOverlay's class doc for why), fades via
|
||||||
|
// its own `visible` flag instead.
|
||||||
|
if (!_isCheckingTutorial)
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: PositioningTutorialOverlay(),
|
child: PositioningTutorialOverlay(visible: _showTutorial),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -627,10 +627,13 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
// Tutorial overlay - show only if tutorial should be shown and we're done checking
|
// Tutorial overlay - always mounted once the initial check
|
||||||
if (!_isCheckingTutorial && _showTutorial)
|
// completes (never conditionally inserted/removed - see
|
||||||
|
// PositioningTutorialOverlay's class doc for why), fades via
|
||||||
|
// its own `visible` flag instead.
|
||||||
|
if (!_isCheckingTutorial)
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: PositioningTutorialOverlay(),
|
child: PositioningTutorialOverlay(visible: _showTutorial),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,23 +6,95 @@ import '../services/localization_service.dart';
|
|||||||
|
|
||||||
/// Overlay that appears over add/edit node sheets to guide users through
|
/// Overlay that appears over add/edit node sheets to guide users through
|
||||||
/// the positioning tutorial. Shows a blurred background with tutorial text.
|
/// the positioning tutorial. Shows a blurred background with tutorial text.
|
||||||
class PositioningTutorialOverlay extends StatelessWidget {
|
///
|
||||||
|
/// Stability note: on Android's Impeller (Vulkan) renderer, BackdropFilter
|
||||||
|
/// snapshots the render pass's "back texture" mid-frame. If that snapshot
|
||||||
|
/// races against the surface being invalidated - which happens constantly
|
||||||
|
/// right where this overlay lives, since the enclosing bottom sheet resizes
|
||||||
|
/// on open/keyboard/rotation - Impeller can hit a null texture and hard-abort
|
||||||
|
/// the whole process natively (not a catchable Dart error). Two things keep
|
||||||
|
/// that race from being triggered here:
|
||||||
|
/// - [RepaintBoundary] isolates the filter onto its own stable composited
|
||||||
|
/// layer instead of sharing one with the actively-resizing sheet.
|
||||||
|
/// - The widget is always kept mounted by the parent sheet and toggles
|
||||||
|
/// visibility via a fade (see [visible]) rather than being abruptly
|
||||||
|
/// inserted/removed from the tree, which is believed to be the other half
|
||||||
|
/// of what triggers the crash.
|
||||||
|
class PositioningTutorialOverlay extends StatefulWidget {
|
||||||
const PositioningTutorialOverlay({
|
const PositioningTutorialOverlay({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.visible,
|
||||||
this.onFadeOutComplete,
|
this.onFadeOutComplete,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Called when the fade-out animation completes (if animated)
|
/// Whether the tutorial should currently be showing. Toggling this fades
|
||||||
|
/// the overlay in/out - see class doc for why it's never removed from
|
||||||
|
/// the tree instead.
|
||||||
|
final bool visible;
|
||||||
|
|
||||||
|
/// Called when the fade-out animation completes (i.e. the overlay has
|
||||||
|
/// become fully transparent after [visible] turned false).
|
||||||
final VoidCallback? onFadeOutComplete;
|
final VoidCallback? onFadeOutComplete;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<PositioningTutorialOverlay> createState() => _PositioningTutorialOverlayState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PositioningTutorialOverlayState extends State<PositioningTutorialOverlay>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late final AnimationController _controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller = AnimationController(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
vsync: this,
|
||||||
|
value: widget.visible ? 1.0 : 0.0,
|
||||||
|
);
|
||||||
|
_controller.addStatusListener(_onStatusChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onStatusChanged(AnimationStatus status) {
|
||||||
|
if (status == AnimationStatus.dismissed) {
|
||||||
|
widget.onFadeOutComplete?.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant PositioningTutorialOverlay oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (widget.visible != oldWidget.visible) {
|
||||||
|
if (widget.visible) {
|
||||||
|
_controller.forward();
|
||||||
|
} else {
|
||||||
|
_controller.reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.removeStatusListener(_onStatusChanged);
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AnimatedBuilder(
|
// Ignore touches when faded out so the invisible-but-still-mounted
|
||||||
|
// overlay doesn't block interaction with the sheet underneath.
|
||||||
|
return IgnorePointer(
|
||||||
|
ignoring: !widget.visible,
|
||||||
|
child: FadeTransition(
|
||||||
|
opacity: _controller,
|
||||||
|
child: AnimatedBuilder(
|
||||||
animation: LocalizationService.instance,
|
animation: LocalizationService.instance,
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
final locService = LocalizationService.instance;
|
final locService = LocalizationService.instance;
|
||||||
|
|
||||||
return ClipRect(
|
return ClipRect(
|
||||||
|
child: RepaintBoundary(
|
||||||
child: BackdropFilter(
|
child: BackdropFilter(
|
||||||
filter: ImageFilter.blur(
|
filter: ImageFilter.blur(
|
||||||
sigmaX: kPositioningTutorialBlurSigma,
|
sigmaX: kPositioningTutorialBlurSigma,
|
||||||
@@ -85,8 +157,11 @@ class PositioningTutorialOverlay extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user