[HELP] go_router custom transition performance
https://programming.dev/post/28141799
programming.dev[HELP] go_router custom transition performance - programming.devI’m trying to implement the OpenContainer transition for go_router in Flutter.
It seems like no matter what I do, the end result is choppy. :/ I’m using the
native Transition widgets as well as the relatively new SnapshotWidget, but to
no avail. Should I maybe use a custom painter? Would that improve performance?
Please take a look at the code below and let me know if you see any ways that I
could improve performance. — Here’s my attempt at an OpenContainer transition
for go_router: import 'package:flutter/material.dart'; import
'package:your_project_name_xxx/ui_models/container_transition_extra.dart'; class
ContainerTransition extends StatefulWidget { final ContainerTransitionExtra
extra; final Animation<double> animation; final Widget? sourceWidget; final
Widget targetWidget; const ContainerTransition({ super.key, required this.extra,
required this.animation, required this.sourceWidget, required this.targetWidget,
}); @override State<ContainerTransition> createState() =>
_ContainerTransitionState(); } class _ContainerTransitionState extends
State<ContainerTransition> { static final _targetTween = Tween<double>(begin: 0,
end: 1); static final _sourceTween = Tween<double>(begin: 1, end: 0); late
SnapshotController _snapshotController; late CurvedAnimation _curvedAnimation;
late CurvedAnimation _sourceAnimation; late CurvedAnimation _targetAnimation;
late RelativeRect? _sourcePosition; late Animation<double>
_sourceOpacityAnimation; late Animation<double> _targetOpacityAnimation; late
Animation<BorderRadius?> _containerRadiusAnimation; late Animation<RelativeRect>
_containerPositionAnimation; @override void initState() { super.initState();
_sourcePosition = widget.extra.tween.begin; _snapshotController =
SnapshotController(allowSnapshotting: true); _curvedAnimation = CurvedAnimation(
parent: widget.animation, curve: Curves.easeInOut, );
_curvedAnimation.addStatusListener((status) { if (status.isAnimating) {
_snapshotController.allowSnapshotting = true; } else if (status.isCompleted ||
status.isDismissed) { _snapshotController.allowSnapshotting = false; } });
_sourceAnimation = CurvedAnimation( parent: _curvedAnimation, curve: Interval(0,
1 / 3), ); _targetAnimation = CurvedAnimation( parent: _curvedAnimation, curve:
Interval(1 / 3, 1), ); _sourceOpacityAnimation =
_sourceTween.animate(_sourceAnimation); _targetOpacityAnimation =
_targetTween.animate(_targetAnimation); _containerRadiusAnimation =
BorderRadiusTween( begin: BorderRadius.circular(widget.extra.containerRadius),
end: BorderRadius.zero) .animate(_curvedAnimation); _containerPositionAnimation
= _curvedAnimation.drive(widget.extra.tween); } @override void dispose() {
_snapshotController.dispose(); _sourceAnimation.dispose();
_targetAnimation.dispose(); _curvedAnimation.dispose(); super.dispose(); }
@override Widget build(BuildContext context) { return Stack( children: [
Positioned.fill( child: FadeTransition( opacity: _sourceOpacityAnimation, child:
ColoredBox(color: widget.extra.scrimColor), ), ), if (_sourcePosition != null)
Positioned.fromRelativeRect( rect: _sourcePosition!, child: FadeTransition(
opacity: _sourceOpacityAnimation, child: widget.sourceWidget, ), ),
PositionedTransition( rect: _containerPositionAnimation, child: DecoratedBox(
decoration: BoxDecoration( borderRadius: _containerRadiusAnimation.value, color:
widget.extra.containerColor, ), child: FadeTransition( opacity:
_targetOpacityAnimation, child: SnapshotWidget( controller: _snapshotController,
child: widget.targetWidget, ), ), ), ), ], ); } } Here’s how I’m building the
route: Page<T> buildContainerRoute<T>( BuildContext context, GoRouterState
state, Ref ref, Widget child, ) { final containerExtra = state.extra; if
(containerExtra is ContainerTransitionExtra) { final registryTag =
containerExtra.sourceBuilderTag; final registryBuilder = registryTag != null ?
SourceBuilderRegistry().get(registryTag) : null; if (registryBuilder != null) {
if (registryBuilder.wasUsedAlready) {
SourceBuilderRegistry().unregister(registryTag!); } else {
SourceBuilderRegistry().markAsUsed(registryTag!); } } return
CustomTransitionPage<T>( key: state.pageKey, child: child, transitionDuration:
Durations.medium2, reverseTransitionDuration: Durations.medium1,
transitionsBuilder: (context, animation, secondaryAnimation, child) { return
ContainerTransition( animation: animation, extra: containerExtra, sourceWidget:
Center(child: registryBuilder?.registryItem.call(context)), targetWidget:
ClipRect( child: OverflowBox( alignment: Alignment.topCenter, maxWidth:
containerExtra.cachedMaxWidth, maxHeight: containerExtra.cachedMaxHeight, child:
child, ), ), ); }, ); } else { if (!kIsWeb && Platform.isAndroid) { return
MaterialPage(key: state.pageKey, child: child); } else { return
CupertinoPage(key: state.pageKey, child: child); } } }