multiple runtime bugfixes for 1.5.5+65
This commit is contained in:
@@ -14,6 +14,15 @@ import 'app_progress_indicator.dart';
|
||||
/// subtree for [placeholder] for the duration of any transition; the status
|
||||
/// listener fires before that frame's layout, so the fragile subtree is gone
|
||||
/// before the new transform lays out.
|
||||
///
|
||||
/// On top of that the gate always yields one idle post-frame (`_settled`)
|
||||
/// before mounting [builder], whether or not a route animation ran. A
|
||||
/// `RenderTransform` is a `RenderProxyBox` and only takes its size *after*
|
||||
/// laying out its child, so during that first pass `size` throws — and route
|
||||
/// transitions are not the only source of a fresh transform: `PersistentTabView`
|
||||
/// wraps every tab screen in an animated `Transform.translate` and builds a tab
|
||||
/// lazily on first activation, which is how the ticker's in-place PDF pages hit
|
||||
/// the same window with no route animation in sight.
|
||||
class RouteTransitionGate extends StatefulWidget {
|
||||
const RouteTransitionGate({super.key, required this.builder, this.placeholder});
|
||||
|
||||
@@ -31,6 +40,11 @@ class _RouteTransitionGateState extends State<RouteTransitionGate> {
|
||||
Animation<double>? _animation;
|
||||
Animation<double>? _secondaryAnimation;
|
||||
|
||||
/// Becomes true one idle post-frame after the last transition ended; only
|
||||
/// then is [RouteTransitionGate.builder] safe to mount (see class doc).
|
||||
bool _settled = false;
|
||||
bool _settleScheduled = false;
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
@@ -68,15 +82,32 @@ class _RouteTransitionGateState extends State<RouteTransitionGate> {
|
||||
status == AnimationStatus.forward || status == AnimationStatus.reverse;
|
||||
|
||||
void _onAnimationStatus(AnimationStatus status) {
|
||||
if (mounted) setState(() {});
|
||||
if (!mounted) return;
|
||||
// A (re)starting transition invalidates the settled state; it has to be
|
||||
// re-earned once the transition finishes.
|
||||
setState(() {
|
||||
if (_transitioning) _settled = false;
|
||||
});
|
||||
}
|
||||
|
||||
void _scheduleSettle() {
|
||||
if (_settleScheduled) return;
|
||||
_settleScheduled = true;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_settleScheduled = false;
|
||||
if (mounted && !_transitioning && !_settled) {
|
||||
setState(() => _settled = true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_transitioning) {
|
||||
return widget.placeholder ??
|
||||
const Center(child: AppProgressIndicator.large());
|
||||
if (!_transitioning) {
|
||||
if (_settled) return widget.builder(context);
|
||||
_scheduleSettle();
|
||||
}
|
||||
return widget.builder(context);
|
||||
return widget.placeholder ??
|
||||
const Center(child: AppProgressIndicator.large());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user