// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; class MockOnEndFunction { int called = 0; void handler() { called++; } } const Duration animationDuration = Duration(milliseconds:1000); const Duration additionalDelay = Duration(milliseconds:1); void main() { late MockOnEndFunction mockOnEndFunction; const Key switchKey = Key('switchKey'); setUp(() { mockOnEndFunction = MockOnEndFunction(); }); testWidgets('BoxConstraintsTween control test', (WidgetTester tester) async { final BoxConstraintsTween tween = BoxConstraintsTween( begin: BoxConstraints.tight(const Size(20.0, 50.0)), end: BoxConstraints.tight(const Size(10.0, 30.0)), ); final BoxConstraints result = tween.lerp(0.25); expect(result.minWidth, 17.5); expect(result.maxWidth, 17.5); expect(result.minHeight, 45.0); expect(result.maxHeight, 45.0); }); testWidgets('DecorationTween control test', (WidgetTester tester) async { final DecorationTween tween = DecorationTween( begin: const BoxDecoration(color: Color(0xFF00FF00)), end: const BoxDecoration(color: Color(0xFFFFFF00)), ); final BoxDecoration result = tween.lerp(0.25) as BoxDecoration; expect(result.color, const Color(0xFF3FFF00)); }); testWidgets('EdgeInsetsTween control test', (WidgetTester tester) async { final EdgeInsetsTween tween = EdgeInsetsTween( begin: const EdgeInsets.symmetric(vertical: 50.0), end: const EdgeInsets.only(top: 10.0, bottom: 30.0), ); final EdgeInsets result = tween.lerp(0.25); expect(result.left, 0.0); expect(result.right, 0.0); expect(result.top, 40.0); expect(result.bottom, 45.0); }); testWidgets('Matrix4Tween control test', (WidgetTester tester) async { final Matrix4Tween tween = Matrix4Tween( begin: Matrix4.translationValues(10.0, 20.0, 30.0), end: Matrix4.translationValues(14.0, 24.0, 34.0), ); final Matrix4 result = tween.lerp(0.25); expect(result, equals(Matrix4.translationValues(11.0, 21.0, 31.0))); }); testWidgets('AnimatedContainer onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( callback: mockOnEndFunction.handler, switchKey: switchKey, state: _TestAnimatedContainerWidgetState(), ), )); final Finder widgetFinder = find.byKey(switchKey); await tester.tap(widgetFinder); await tester.pump(); expect(mockOnEndFunction.called, 0); await tester.pump(animationDuration); expect(mockOnEndFunction.called, 0); await tester.pump(additionalDelay); expect(mockOnEndFunction.called, 1); await tapTest2and3(tester, widgetFinder, mockOnEndFunction); }); testWidgets('AnimatedPadding onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( callback: mockOnEndFunction.handler, switchKey: switchKey, state: _TestAnimatedPaddingWidgetState(), ), )); final Finder widgetFinder = find.byKey(switchKey); await tester.tap(widgetFinder); await tester.pump(); expect(mockOnEndFunction.called, 0); await tester.pump(animationDuration); expect(mockOnEndFunction.called, 0); await tester.pump(additionalDelay); expect(mockOnEndFunction.called, 1); await tapTest2and3(tester, widgetFinder, mockOnEndFunction); }); testWidgets('AnimatedAlign onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( callback: mockOnEndFunction.handler, switchKey: switchKey, state: _TestAnimatedAlignWidgetState(), ), )); final Finder widgetFinder = find.byKey(switchKey); await tester.tap(widgetFinder); await tester.pump(); expect(mockOnEndFunction.called, 0); await tester.pump(animationDuration); expect(mockOnEndFunction.called, 0); await tester.pump(additionalDelay); expect(mockOnEndFunction.called, 1); await tapTest2and3(tester, widgetFinder, mockOnEndFunction); }); testWidgets('AnimatedPositioned onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( callback: mockOnEndFunction.handler, switchKey: switchKey, state: _TestAnimatedPositionedWidgetState(), ), )); final Finder widgetFinder = find.byKey(switchKey); await tester.tap(widgetFinder); await tester.pump(); expect(mockOnEndFunction.called, 0); await tester.pump(animationDuration); expect(mockOnEndFunction.called, 0); await tester.pump(additionalDelay); expect(mockOnEndFunction.called, 1); await tapTest2and3(tester, widgetFinder, mockOnEndFunction); }); testWidgets('AnimatedPositionedDirectional onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( callback: mockOnEndFunction.handler, switchKey: switchKey, state: _TestAnimatedPositionedDirectionalWidgetState(), ), )); final Finder widgetFinder = find.byKey(switchKey); await tester.tap(widgetFinder); await tester.pump(); expect(mockOnEndFunction.called, 0); await tester.pump(animationDuration); expect(mockOnEndFunction.called, 0); await tester.pump(additionalDelay); expect(mockOnEndFunction.called, 1); await tapTest2and3(tester, widgetFinder, mockOnEndFunction); }); testWidgets('AnimatedSlide onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( callback: mockOnEndFunction.handler, switchKey: switchKey, state: _TestAnimatedSlideWidgetState(), ), )); final Finder widgetFinder = find.byKey(switchKey); await tester.tap(widgetFinder); await tester.pump(); expect(mockOnEndFunction.called, 0); await tester.pump(animationDuration); expect(mockOnEndFunction.called, 0); await tester.pump(additionalDelay); expect(mockOnEndFunction.called, 1); await tapTest2and3(tester, widgetFinder, mockOnEndFunction); }); testWidgets('AnimatedSlide transition test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( switchKey: switchKey, state: _TestAnimatedSlideWidgetState(), ), )); final RebuildCountingState<StatefulWidget> state = tester.widget<TestAnimatedWidget>( find.byType(TestAnimatedWidget) ).rebuildState!; final Finder switchFinder = find.byKey(switchKey); final SlideTransition slideWidget = tester.widget<SlideTransition>( find.ancestor( of: find.byType(Placeholder), matching: find.byType(SlideTransition), ).first, ); expect(state.builds, equals(1)); await tester.tap(switchFinder); expect(state.builds, equals(1)); await tester.pump(); expect(slideWidget.position.value, equals(Offset.zero)); expect(state.builds, equals(2)); await tester.pump(const Duration(milliseconds: 500)); expect(slideWidget.position.value, equals(const Offset(0.5,0.5))); expect(state.builds, equals(2)); await tester.pump(const Duration(milliseconds: 250)); expect(slideWidget.position.value, equals(const Offset(0.75,0.75))); expect(state.builds, equals(2)); await tester.pump(const Duration(milliseconds: 250)); expect(slideWidget.position.value, equals(const Offset(1,1))); expect(state.builds, equals(2)); }); testWidgets('AnimatedScale onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( callback: mockOnEndFunction.handler, switchKey: switchKey, state: _TestAnimatedScaleWidgetState(), ), )); final Finder widgetFinder = find.byKey(switchKey); await tester.tap(widgetFinder); await tester.pump(); expect(mockOnEndFunction.called, 0); await tester.pump(animationDuration); expect(mockOnEndFunction.called, 0); await tester.pump(additionalDelay); expect(mockOnEndFunction.called, 1); await tapTest2and3(tester, widgetFinder, mockOnEndFunction); }); testWidgets('AnimatedScale transition test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( switchKey: switchKey, state: _TestAnimatedScaleWidgetState(), ), )); final RebuildCountingState<StatefulWidget> state = tester.widget<TestAnimatedWidget>( find.byType(TestAnimatedWidget) ).rebuildState!; final Finder switchFinder = find.byKey(switchKey); final ScaleTransition scaleWidget = tester.widget<ScaleTransition>( find.ancestor( of: find.byType(Placeholder), matching: find.byType(ScaleTransition), ).first, ); expect(state.builds, equals(1)); await tester.tap(switchFinder); expect(state.builds, equals(1)); await tester.pump(); expect(scaleWidget.scale.value, equals(1.0)); expect(state.builds, equals(2)); await tester.pump(const Duration(milliseconds: 500)); expect(scaleWidget.scale.value, equals(1.5)); expect(state.builds, equals(2)); await tester.pump(const Duration(milliseconds: 250)); expect(scaleWidget.scale.value, equals(1.75)); expect(state.builds, equals(2)); await tester.pump(const Duration(milliseconds: 250)); expect(scaleWidget.scale.value, equals(2.0)); expect(state.builds, equals(2)); }); testWidgets('AnimatedRotation onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( callback: mockOnEndFunction.handler, switchKey: switchKey, state: _TestAnimatedRotationWidgetState(), ), )); final Finder widgetFinder = find.byKey(switchKey); await tester.tap(widgetFinder); await tester.pump(); expect(mockOnEndFunction.called, 0); await tester.pump(animationDuration); expect(mockOnEndFunction.called, 0); await tester.pump(additionalDelay); expect(mockOnEndFunction.called, 1); await tapTest2and3(tester, widgetFinder, mockOnEndFunction); }); testWidgets('AnimatedRotation transition test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( switchKey: switchKey, state: _TestAnimatedRotationWidgetState(), ), )); final RebuildCountingState<StatefulWidget> state = tester.widget<TestAnimatedWidget>( find.byType(TestAnimatedWidget) ).rebuildState!; final Finder switchFinder = find.byKey(switchKey); final RotationTransition rotationWidget = tester.widget<RotationTransition>( find.ancestor( of: find.byType(Placeholder), matching: find.byType(RotationTransition), ).first, ); expect(state.builds, equals(1)); await tester.tap(switchFinder); expect(state.builds, equals(1)); await tester.pump(); expect(rotationWidget.turns.value, equals(0.0)); expect(state.builds, equals(2)); await tester.pump(const Duration(milliseconds: 500)); expect(rotationWidget.turns.value, equals(0.75)); expect(state.builds, equals(2)); await tester.pump(const Duration(milliseconds: 250)); expect(rotationWidget.turns.value, equals(1.125)); expect(state.builds, equals(2)); await tester.pump(const Duration(milliseconds: 250)); expect(rotationWidget.turns.value, equals(1.5)); expect(state.builds, equals(2)); }); testWidgets('AnimatedOpacity onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( callback: mockOnEndFunction.handler, switchKey: switchKey, state: _TestAnimatedOpacityWidgetState(), ), )); final Finder widgetFinder = find.byKey(switchKey); await tester.tap(widgetFinder); await tester.pump(); expect(mockOnEndFunction.called, 0); await tester.pump(animationDuration); expect(mockOnEndFunction.called, 0); await tester.pump(additionalDelay); expect(mockOnEndFunction.called, 1); await tapTest2and3(tester, widgetFinder, mockOnEndFunction); }); testWidgets('AnimatedOpacity transition test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( switchKey: switchKey, state: _TestAnimatedOpacityWidgetState(), ), )); final RebuildCountingState<StatefulWidget> state = tester.widget<TestAnimatedWidget>( find.byType(TestAnimatedWidget) ).rebuildState!; final Finder switchFinder = find.byKey(switchKey); final FadeTransition opacityWidget = tester.widget<FadeTransition>( find.ancestor( of: find.byType(Placeholder), matching: find.byType(FadeTransition), ).first, ); expect(state.builds, equals(1)); await tester.tap(switchFinder); expect(state.builds, equals(1)); await tester.pump(); expect(opacityWidget.opacity.value, equals(0.0)); expect(state.builds, equals(2)); await tester.pump(const Duration(milliseconds: 500)); expect(opacityWidget.opacity.value, equals(0.5)); expect(state.builds, equals(2)); await tester.pump(const Duration(milliseconds: 250)); expect(opacityWidget.opacity.value, equals(0.75)); expect(state.builds, equals(2)); await tester.pump(const Duration(milliseconds: 250)); expect(opacityWidget.opacity.value, equals(1.0)); expect(state.builds, equals(2)); }); testWidgets('AnimatedFractionallySizedBox onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( callback: mockOnEndFunction.handler, switchKey: switchKey, state: _TestAnimatedFractionallySizedBoxWidgetState(), ), )); final Finder widgetFinder = find.byKey(switchKey); await tester.tap(widgetFinder); await tester.pump(); expect(mockOnEndFunction.called, 0); await tester.pump(animationDuration); expect(mockOnEndFunction.called, 0); await tester.pump(additionalDelay); expect(mockOnEndFunction.called, 1); await tapTest2and3(tester, widgetFinder, mockOnEndFunction); }); testWidgets('SliverAnimatedOpacity onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(TestAnimatedWidget( callback: mockOnEndFunction.handler, switchKey: switchKey, state: _TestSliverAnimatedOpacityWidgetState(), )); final Finder widgetFinder = find.byKey(switchKey); await tester.tap(widgetFinder); await tester.pump(); expect(mockOnEndFunction.called, 0); await tester.pump(animationDuration); expect(mockOnEndFunction.called, 0); await tester.pump(additionalDelay); expect(mockOnEndFunction.called, 1); await tapTest2and3(tester, widgetFinder, mockOnEndFunction); }); testWidgets('SliverAnimatedOpacity transition test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( switchKey: switchKey, state: _TestSliverAnimatedOpacityWidgetState(), ), )); final RebuildCountingState<StatefulWidget> state = tester.widget<TestAnimatedWidget>( find.byType(TestAnimatedWidget) ).rebuildState!; final Finder switchFinder = find.byKey(switchKey); final SliverFadeTransition opacityWidget = tester.widget<SliverFadeTransition>( find.ancestor( of: find.byType(Placeholder), matching: find.byType(SliverFadeTransition), ).first, ); expect(state.builds, equals(1)); await tester.tap(switchFinder); expect(state.builds, equals(1)); await tester.pump(); expect(opacityWidget.opacity.value, equals(0.0)); expect(state.builds, equals(2)); await tester.pump(const Duration(milliseconds: 500)); expect(opacityWidget.opacity.value, equals(0.5)); expect(state.builds, equals(2)); await tester.pump(const Duration(milliseconds: 250)); expect(opacityWidget.opacity.value, equals(0.75)); expect(state.builds, equals(2)); await tester.pump(const Duration(milliseconds: 250)); expect(opacityWidget.opacity.value, equals(1.0)); expect(state.builds, equals(2)); }); testWidgets('AnimatedDefaultTextStyle onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( callback: mockOnEndFunction.handler, switchKey: switchKey, state: _TestAnimatedDefaultTextStyleWidgetState(), ), )); final Finder widgetFinder = find.byKey(switchKey); await tester.tap(widgetFinder); await tester.pump(); expect(mockOnEndFunction.called, 0); await tester.pump(animationDuration); expect(mockOnEndFunction.called, 0); await tester.pump(additionalDelay); expect(mockOnEndFunction.called, 1); await tapTest2and3(tester, widgetFinder, mockOnEndFunction); }); testWidgets('AnimatedPhysicalModel onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( callback: mockOnEndFunction.handler, switchKey: switchKey, state: _TestAnimatedPhysicalModelWidgetState(), ), )); final Finder widgetFinder = find.byKey(switchKey); await tester.tap(widgetFinder); await tester.pump(); expect(mockOnEndFunction.called, 0); await tester.pump(animationDuration); expect(mockOnEndFunction.called, 0); await tester.pump(additionalDelay); expect(mockOnEndFunction.called, 1); await tapTest2and3(tester, widgetFinder, mockOnEndFunction); }); testWidgets('TweenAnimationBuilder onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( callback: mockOnEndFunction.handler, switchKey: switchKey, state: _TestTweenAnimationBuilderWidgetState(), ), )); final Finder widgetFinder = find.byKey(switchKey); await tester.tap(widgetFinder); await tester.pump(); expect(mockOnEndFunction.called, 0); await tester.pump(animationDuration); expect(mockOnEndFunction.called, 0); await tester.pump(additionalDelay); expect(mockOnEndFunction.called, 1); await tapTest2and3(tester, widgetFinder, mockOnEndFunction); }); testWidgets('AnimatedTheme onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget( callback: mockOnEndFunction.handler, switchKey: switchKey, state: _TestAnimatedThemeWidgetState(), ), )); final Finder widgetFinder = find.byKey(switchKey); await tester.tap(widgetFinder); await tester.pump(); expect(mockOnEndFunction.called, 0); await tester.pump(animationDuration); expect(mockOnEndFunction.called, 0); await tester.pump(additionalDelay); expect(mockOnEndFunction.called, 1); await tapTest2and3(tester, widgetFinder, mockOnEndFunction); }); testWidgets('Ensure CurvedAnimations are disposed on widget change', (WidgetTester tester) async { final GlobalKey<ImplicitlyAnimatedWidgetState<AnimatedOpacity>> key = GlobalKey<ImplicitlyAnimatedWidgetState<AnimatedOpacity>>(); final ValueNotifier<Curve> curve = ValueNotifier<Curve>(const Interval(0.0, 0.5)); addTearDown(curve.dispose); await tester.pumpWidget(wrap( child: ValueListenableBuilder<Curve>( valueListenable: curve, builder: (_, Curve c, __) => AnimatedOpacity( key: key, opacity: 1.0, duration: const Duration(seconds: 1), curve: c, child: Container(color: Colors.green)), ), )); final ImplicitlyAnimatedWidgetState<AnimatedOpacity>? firstState = key.currentState; final Animation<double>? firstAnimation = firstState?.animation; if (firstAnimation == null) { fail('animation was null!'); } final CurvedAnimation firstCurvedAnimation = firstAnimation as CurvedAnimation; expect(firstCurvedAnimation.isDisposed, isFalse); curve.value = const Interval(0.0, 0.6); await tester.pumpAndSettle(); final ImplicitlyAnimatedWidgetState<AnimatedOpacity>? secondState = key.currentState; final Animation<double>? secondAnimation = secondState?.animation; if (secondAnimation == null) { fail('animation was null!'); } final CurvedAnimation secondCurvedAnimation = secondAnimation as CurvedAnimation; expect(firstState, equals(secondState)); expect(firstAnimation, isNot(equals(secondAnimation))); expect(firstCurvedAnimation.isDisposed, isTrue); expect(secondCurvedAnimation.isDisposed, isFalse); await tester.pumpWidget( wrap( child: const Offstage(), ), ); await tester.pumpAndSettle(); expect(secondCurvedAnimation.isDisposed, isTrue); }); } Future<void> tapTest2and3(WidgetTester tester, Finder widgetFinder, MockOnEndFunction mockOnEndFunction) async { await tester.tap(widgetFinder); await tester.pump(); await tester.pump(animationDuration + additionalDelay); expect(mockOnEndFunction.called, 2); await tester.tap(widgetFinder); await tester.pump(); await tester.pump(animationDuration + additionalDelay); expect(mockOnEndFunction.called, 3); } Widget wrap({required Widget child}) { return Directionality( textDirection: TextDirection.ltr, child: Material( child: Center(child: child), ), ); } abstract class RebuildCountingState<T extends StatefulWidget> extends State<T> { int builds = 0; } class TestAnimatedWidget extends StatefulWidget { const TestAnimatedWidget({ super.key, this.callback, required this.switchKey, required this.state, }); final VoidCallback? callback; final Key switchKey; final State<StatefulWidget> state; RebuildCountingState<StatefulWidget>? get rebuildState => state is RebuildCountingState<StatefulWidget> ? state as RebuildCountingState<StatefulWidget> : null; @override State<StatefulWidget> createState() => state; // ignore: no_logic_in_create_state, this test predates the lint } abstract class _TestAnimatedWidgetState extends RebuildCountingState<TestAnimatedWidget> { bool toggle = false; final Widget child = const Placeholder(); final Duration duration = animationDuration; void onChanged(bool v) { setState(() { toggle = v; }); } Widget getAnimatedWidget(); @override Widget build(BuildContext context) { builds++; final Widget animatedWidget = getAnimatedWidget(); return Stack( children: <Widget>[ animatedWidget, Switch(key: widget.switchKey, value: toggle, onChanged: onChanged), ], ); } } class _TestAnimatedContainerWidgetState extends _TestAnimatedWidgetState { @override Widget getAnimatedWidget() { return AnimatedContainer( duration: duration, onEnd: widget.callback, width: toggle ? 10 : 20, foregroundDecoration: toggle ? const BoxDecoration() : null, child: child, ); } } class _TestAnimatedPaddingWidgetState extends _TestAnimatedWidgetState { @override Widget getAnimatedWidget() { return AnimatedPadding( duration: duration, onEnd: widget.callback, padding: toggle ? const EdgeInsets.all(8.0) : const EdgeInsets.all(16.0), child: child, ); } } class _TestAnimatedAlignWidgetState extends _TestAnimatedWidgetState { @override Widget getAnimatedWidget() { return AnimatedAlign( duration: duration, onEnd: widget.callback, alignment: toggle ? Alignment.topLeft : Alignment.bottomRight, child: child, ); } } class _TestAnimatedPositionedWidgetState extends _TestAnimatedWidgetState { @override Widget getAnimatedWidget() { return AnimatedPositioned( duration: duration, onEnd: widget.callback, left: toggle ? 10 : 20, child: child, ); } } class _TestAnimatedPositionedDirectionalWidgetState extends _TestAnimatedWidgetState { @override Widget getAnimatedWidget() { return AnimatedPositionedDirectional( duration: duration, onEnd: widget.callback, start: toggle ? 10 : 20, child: child, ); } } class _TestAnimatedSlideWidgetState extends _TestAnimatedWidgetState { @override Widget getAnimatedWidget() { return AnimatedSlide( duration: duration, onEnd: widget.callback, offset: toggle ? const Offset(1,1) : Offset.zero, child: child, ); } } class _TestAnimatedScaleWidgetState extends _TestAnimatedWidgetState { @override Widget getAnimatedWidget() { return AnimatedScale( duration: duration, onEnd: widget.callback, scale: toggle ? 2.0 : 1.0, child: child, ); } } class _TestAnimatedRotationWidgetState extends _TestAnimatedWidgetState { @override Widget getAnimatedWidget() { return AnimatedRotation( duration: duration, onEnd: widget.callback, turns: toggle ? 1.5 : 0.0, child: child, ); } } class _TestAnimatedOpacityWidgetState extends _TestAnimatedWidgetState { @override Widget getAnimatedWidget() { return AnimatedOpacity( duration: duration, onEnd: widget.callback, opacity: toggle ? 1.0 : 0.0, child: child, ); } } class _TestAnimatedFractionallySizedBoxWidgetState extends _TestAnimatedWidgetState { @override Widget getAnimatedWidget() { return AnimatedFractionallySizedBox( duration: duration, onEnd: widget.callback, heightFactor: toggle ? 0.25 : 0.75, widthFactor: toggle ? 0.25 : 0.75, child: child, ); } } class _TestSliverAnimatedOpacityWidgetState extends _TestAnimatedWidgetState { @override Widget getAnimatedWidget() { return SliverAnimatedOpacity( sliver: SliverToBoxAdapter(child: child), duration: duration, onEnd: widget.callback, opacity: toggle ? 1.0 : 0.0, ); } @override Widget build(BuildContext context) { builds++; final Widget animatedWidget = getAnimatedWidget(); return Material( child: Directionality( textDirection: TextDirection.ltr, child: CustomScrollView( slivers: <Widget>[ animatedWidget, SliverToBoxAdapter( child: Switch( key: widget.switchKey, value: toggle, onChanged: onChanged, ), ), ], ), ), ); } } class _TestAnimatedDefaultTextStyleWidgetState extends _TestAnimatedWidgetState { @override Widget getAnimatedWidget() { return AnimatedDefaultTextStyle( duration: duration, onEnd: widget.callback, style: toggle ? const TextStyle(fontStyle: FontStyle.italic) : const TextStyle(fontStyle: FontStyle.normal), child: child, ); } } class _TestAnimatedPhysicalModelWidgetState extends _TestAnimatedWidgetState { @override Widget getAnimatedWidget() { return AnimatedPhysicalModel( duration: duration, onEnd: widget.callback, color: toggle ? Colors.red : Colors.green, elevation: 0, shadowColor: Colors.blue, shape: BoxShape.rectangle, child: child, ); } } class _TestTweenAnimationBuilderWidgetState extends _TestAnimatedWidgetState { @override Widget getAnimatedWidget() { return TweenAnimationBuilder<double>( tween: toggle ? Tween<double>(begin: 1, end: 2) : Tween<double>(begin: 2, end: 1), duration: duration, onEnd: widget.callback, child: child, builder: (BuildContext context, double? size, Widget? child) { return SizedBox( width: size, height: size, child: child, ); }, ); } } class _TestAnimatedThemeWidgetState extends _TestAnimatedWidgetState { @override Widget getAnimatedWidget() { return AnimatedTheme( data: toggle ? ThemeData.dark() : ThemeData.light(), duration: duration, onEnd: widget.callback, child: child, ); } }