Files
Client/lib/widget/route_transition_gate.dart
T

60 lines
1.8 KiB
Dart

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<RouteTransitionGate> createState() => _RouteTransitionGateState();
}
class _RouteTransitionGateState extends State<RouteTransitionGate> {
bool _ready = false;
Animation<double>? _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);
}
}