diff --git a/packages/flutter/lib/src/material/ink_sparkle.dart b/packages/flutter/lib/src/material/ink_sparkle.dart index f2c6ccbbad..2fe42dc8c4 100644 --- a/packages/flutter/lib/src/material/ink_sparkle.dart +++ b/packages/flutter/lib/src/material/ink_sparkle.dart @@ -135,7 +135,9 @@ class InkSparkle extends InteractiveInkFeature { _animationController = AnimationController( duration: _animationDuration, vsync: controller.vsync, - )..addListener(controller.markNeedsPaint)..forward(); + )..addListener(controller.markNeedsPaint) + ..addStatusListener(_handleStatusChanged) + ..forward(); _radiusScale = TweenSequence( >[ @@ -209,6 +211,11 @@ class InkSparkle extends InteractiveInkFeature { _turbulenceSeed = turbulenceSeed ?? math.Random().nextDouble() * 1000.0; } + void _handleStatusChanged(AnimationStatus status) { + if (status == AnimationStatus.completed) + dispose(); + } + static const Duration _animationDuration = Duration(milliseconds: 617); static const double _targetRadiusMultiplier = 2.3; static const double _rotateRight = math.pi * 0.0078125; @@ -259,6 +266,8 @@ class InkSparkle extends InteractiveInkFeature { @override void paintFeature(Canvas canvas, Matrix4 transform) { + assert(_animationController.isAnimating); + // InkSparkle can only paint if its shader has been compiled. if (_InkSparkleFactory._shaderManager == null) { // Skipping paintFeature because the shader it relies on is not ready to diff --git a/packages/flutter/lib/src/material/material.dart b/packages/flutter/lib/src/material/material.dart index 1542228e30..e3a59d04bc 100644 --- a/packages/flutter/lib/src/material/material.dart +++ b/packages/flutter/lib/src/material/material.dart @@ -557,6 +557,12 @@ class _RenderInkFeatures extends RenderProxyBox implements MaterialInkController bool absorbHitTest; + @visibleForTesting + List? get debugInkFeatures { + if (kDebugMode) + return _inkFeatures; + return null; + } List? _inkFeatures; @override diff --git a/packages/flutter/test/material/ink_sparkle_test.dart b/packages/flutter/test/material/ink_sparkle_test.dart index ff7c88018e..5d71e9b42f 100644 --- a/packages/flutter/test/material/ink_sparkle_test.dart +++ b/packages/flutter/test/material/ink_sparkle_test.dart @@ -48,16 +48,23 @@ void main() { final Finder buttonFinder = find.text('Sparkle!'); await tester.tap(buttonFinder); await tester.pump(); - await tester.pumpAndSettle(); + await tester.pump(const Duration(milliseconds: 200)); final MaterialInkController material = Material.of(tester.element(buttonFinder))!; - await tester.pump(const Duration(milliseconds: 200)); expect(material, paintsExactlyCountTimes(#drawRect, 1)); + + // ignore: avoid_dynamic_calls + expect((material as dynamic).debugInkFeatures, hasLength(1)); + + await tester.pumpAndSettle(); + // ink feature is disposed. + // ignore: avoid_dynamic_calls + expect((material as dynamic).debugInkFeatures, isEmpty); }, skip: kIsWeb, // [intended] SPIR-V shaders are not yet supported for web. ); - testWidgets('InkSparkle default splashFactory paints with drawPaint when unbounded', (WidgetTester tester) async { + testWidgets('InkSparkle default splashFactory paints with drawPaint when unbounded', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( home: Scaffold( body: Center( @@ -72,10 +79,9 @@ void main() { final Finder buttonFinder = find.text('Sparkle!'); await tester.tap(buttonFinder); await tester.pump(); - await tester.pumpAndSettle(); + await tester.pump(const Duration(milliseconds: 200)); final MaterialInkController material = Material.of(tester.element(buttonFinder))!; - await tester.pump(const Duration(milliseconds: 200)); expect(material, paintsExactlyCountTimes(#drawPaint, 1)); }, skip: kIsWeb, // [intended] SPIR-V shaders are not yet supported for web.