import 'package:flutter/material.dart'; import 'app_progress_indicator.dart'; /// Delays building [builder] until the enclosing route's enter animation has /// finished. Some widgets (notably `SfPdfViewer`) call `localToGlobal` during /// their first layout and assert with `RenderBox was not laid out` when an /// ancestor page-transition `RenderTransform` still has no size mid-push. /// Gating the mount behind the settled animation avoids that race. class RouteTransitionGate extends StatefulWidget { const RouteTransitionGate({super.key, required this.builder, this.placeholder}); final WidgetBuilder builder; /// Shown while the route is still animating in. Defaults to a centered /// large progress indicator. final Widget? placeholder; @override State createState() => _RouteTransitionGateState(); } class _RouteTransitionGateState extends State { bool _ready = false; Animation? _routeAnimation; @override void didChangeDependencies() { super.didChangeDependencies(); if (_ready || _routeAnimation != null) return; final animation = ModalRoute.of(context)?.animation; if (animation == null || animation.isCompleted) { _ready = true; return; } _routeAnimation = animation..addStatusListener(_onAnimationStatus); } void _onAnimationStatus(AnimationStatus status) { if (status == AnimationStatus.completed && mounted) { setState(() => _ready = true); } } @override void dispose() { _routeAnimation?.removeStatusListener(_onAnimationStatus); super.dispose(); } @override Widget build(BuildContext context) { if (!_ready) { return widget.placeholder ?? const Center(child: AppProgressIndicator.large()); } return widget.builder(context); } }