diff --git a/packages/flutter/lib/src/material/banner.dart b/packages/flutter/lib/src/material/banner.dart index 67d15b8b75..6a92208d1d 100644 --- a/packages/flutter/lib/src/material/banner.dart +++ b/packages/flutter/lib/src/material/banner.dart @@ -267,11 +267,15 @@ class MaterialBanner extends StatefulWidget { class _MaterialBannerState extends State { bool _wasVisible = false; + CurvedAnimation? _heightAnimation; + CurvedAnimation? _slideOutCurvedAnimation; @override void initState() { super.initState(); widget.animation?.addStatusListener(_onAnimationStatusChanged); + _setCurvedAnimations(); + } @override @@ -280,12 +284,31 @@ class _MaterialBannerState extends State { if (widget.animation != oldWidget.animation) { oldWidget.animation?.removeStatusListener(_onAnimationStatusChanged); widget.animation?.addStatusListener(_onAnimationStatusChanged); + _setCurvedAnimations(); } } + void _setCurvedAnimations() { + _heightAnimation?.dispose(); + _slideOutCurvedAnimation?.dispose(); + if (widget.animation != null) { + _heightAnimation = CurvedAnimation(parent: widget.animation!, curve: _materialBannerHeightCurve); + _slideOutCurvedAnimation = CurvedAnimation( + parent: widget.animation!, + curve: const Threshold(0.0), + ); + } else { + _heightAnimation = null; + _slideOutCurvedAnimation = null; + } + + } + @override void dispose() { widget.animation?.removeStatusListener(_onAnimationStatusChanged); + _heightAnimation?.dispose(); + _slideOutCurvedAnimation?.dispose(); super.dispose(); } @@ -408,14 +431,10 @@ class _MaterialBannerState extends State { child: materialBanner, ); - final CurvedAnimation heightAnimation = CurvedAnimation(parent: widget.animation!, curve: _materialBannerHeightCurve); final Animation slideOutAnimation = Tween( begin: const Offset(0.0, -1.0), end: Offset.zero, - ).animate(CurvedAnimation( - parent: widget.animation!, - curve: const Threshold(0.0), - )); + ).animate(_slideOutCurvedAnimation!); materialBanner = Semantics( container: true, @@ -436,11 +455,11 @@ class _MaterialBannerState extends State { materialBannerTransition = materialBanner; } else { materialBannerTransition = AnimatedBuilder( - animation: heightAnimation, + animation: _heightAnimation!, builder: (BuildContext context, Widget? child) { return Align( alignment: AlignmentDirectional.bottomStart, - heightFactor: heightAnimation.value, + heightFactor: _heightAnimation!.value, child: child, ); }, diff --git a/packages/flutter/test/material/banner_theme_test.dart b/packages/flutter/test/material/banner_theme_test.dart index d5a99531c6..05fbcf1215 100644 --- a/packages/flutter/test/material/banner_theme_test.dart +++ b/packages/flutter/test/material/banner_theme_test.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { test('MaterialBannerThemeData copyWith, ==, hashCode basics', () { @@ -324,7 +325,10 @@ void main() { expect(find.byType(Divider), findsNothing); }); - testWidgets('MaterialBanner widget properties take priority over theme when presented by ScaffoldMessenger', (WidgetTester tester) async { + testWidgets('MaterialBanner widget properties take priority over theme when presented by ScaffoldMessenger', + // TODO(polina-c): remove when fixed https://github.com/flutter/flutter/issues/145600 [leak-tracking-opt-in] + experimentalLeakTesting: LeakTesting.settings.withTracked(classes: const ['CurvedAnimation']), + (WidgetTester tester) async { const Color backgroundColor = Colors.purple; const double elevation = 6.0; const TextStyle textStyle = TextStyle(color: Colors.green);