diff --git a/packages/flutter_adaptive_scaffold/CHANGELOG.md b/packages/flutter_adaptive_scaffold/CHANGELOG.md index 43cdac63da7..dd4c337ee6b 100644 --- a/packages/flutter_adaptive_scaffold/CHANGELOG.md +++ b/packages/flutter_adaptive_scaffold/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.1.11+1 + +* Allows custom animation duration for the NavigationRail and + BottomNavigationBar transitions. [flutter/flutter#112938](https://github.com/flutter/flutter/issues/112938) + ## 0.1.11 * Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. diff --git a/packages/flutter_adaptive_scaffold/lib/src/slot_layout.dart b/packages/flutter_adaptive_scaffold/lib/src/slot_layout.dart index ce3f5a374c4..38789fb8660 100644 --- a/packages/flutter_adaptive_scaffold/lib/src/slot_layout.dart +++ b/packages/flutter_adaptive_scaffold/lib/src/slot_layout.dart @@ -74,12 +74,14 @@ class SlotLayout extends StatefulWidget { WidgetBuilder? builder, Widget Function(Widget, Animation)? inAnimation, Widget Function(Widget, Animation)? outAnimation, + Duration? duration, required Key key, }) => SlotLayoutConfig._( builder: builder, inAnimation: inAnimation, outAnimation: outAnimation, + duration: duration, key: key, ); @@ -96,7 +98,7 @@ class _SlotLayoutState extends State chosenWidget = SlotLayout.pickWidget(context, widget.config); bool hasAnimation = false; return AnimatedSwitcher( - duration: const Duration(milliseconds: 1000), + duration: chosenWidget?.duration ?? const Duration(milliseconds: 1000), layoutBuilder: (Widget? currentChild, List previousChildren) { final Stack elements = Stack( children: [ @@ -137,6 +139,7 @@ class SlotLayoutConfig extends StatelessWidget { required this.builder, this.inAnimation, this.outAnimation, + this.duration, }); /// The child Widget that [SlotLayout] eventually returns with an animation. @@ -160,6 +163,9 @@ class SlotLayoutConfig extends StatelessWidget { /// as the returned widget. final Widget Function(Widget, Animation)? outAnimation; + /// The amount of time taken by the execution of the in and out animations. + final Duration? duration; + /// An empty [SlotLayoutConfig] to be placed in a slot to indicate that the slot /// should show nothing. static SlotLayoutConfig empty() { diff --git a/packages/flutter_adaptive_scaffold/pubspec.yaml b/packages/flutter_adaptive_scaffold/pubspec.yaml index ed61991cf35..c3815b4abf3 100644 --- a/packages/flutter_adaptive_scaffold/pubspec.yaml +++ b/packages/flutter_adaptive_scaffold/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_adaptive_scaffold description: Widgets to easily build adaptive layouts, including navigation elements. -version: 0.1.11 +version: 0.1.11+1 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_adaptive_scaffold%22 repository: https://github.com/flutter/packages/tree/main/packages/flutter_adaptive_scaffold diff --git a/packages/flutter_adaptive_scaffold/test/adaptive_layout_test.dart b/packages/flutter_adaptive_scaffold/test/adaptive_layout_test.dart index 5924f0a8a74..3166e5b382d 100644 --- a/packages/flutter_adaptive_scaffold/test/adaptive_layout_test.dart +++ b/packages/flutter_adaptive_scaffold/test/adaptive_layout_test.dart @@ -120,11 +120,13 @@ void main() { testWidgets( 'slot layout properly switches between items with the appropriate animation', (WidgetTester tester) async { - await tester.pumpWidget(slot(300, tester)); + await tester + .pumpWidget(slot(300, const Duration(milliseconds: 1000), tester)); expect(begin, findsOneWidget); expect(end, findsNothing); - await tester.pumpWidget(slot(500, tester)); + await tester + .pumpWidget(slot(500, const Duration(milliseconds: 1000), tester)); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); expect(tester.widget(slideOut('0')).position.value, @@ -146,7 +148,7 @@ void main() { testWidgets('AnimatedSwitcher does not spawn duplicate keys on rapid resize', (WidgetTester tester) async { // Populate the smaller slot layout and let the animation settle. - await tester.pumpWidget(slot(300, tester)); + await tester.pumpWidget(slot(300, const Duration(seconds: 1), tester)); await tester.pumpAndSettle(); expect(begin, findsOneWidget); expect(end, findsNothing); @@ -157,12 +159,12 @@ void main() { for (int i = 0; i < 2; i++) { // Resize between the two slot layouts, but do not pump the animation // until completion. - await tester.pumpWidget(slot(500, tester)); + await tester.pumpWidget(slot(500, const Duration(seconds: 1), tester)); await tester.pump(const Duration(milliseconds: 100)); expect(begin, findsOneWidget); expect(end, findsOneWidget); - await tester.pumpWidget(slot(300, tester)); + await tester.pumpWidget(slot(300, const Duration(seconds: 1), tester)); await tester.pump(const Duration(milliseconds: 100)); expect(begin, findsOneWidget); expect(end, findsOneWidget); @@ -171,18 +173,18 @@ void main() { testWidgets('slot layout can tolerate rapid changes in breakpoints', (WidgetTester tester) async { - await tester.pumpWidget(slot(300, tester)); + await tester.pumpWidget(slot(300, const Duration(seconds: 1), tester)); expect(begin, findsOneWidget); expect(end, findsNothing); - await tester.pumpWidget(slot(500, tester)); + await tester.pumpWidget(slot(500, const Duration(seconds: 1), tester)); await tester.pump(); await tester.pump(const Duration(milliseconds: 100)); expect(tester.widget(slideOut('0')).position.value, offsetMoreOrLessEquals(const Offset(-0.1, 0), epsilon: 0.05)); expect(tester.widget(slideIn('400')).position.value, offsetMoreOrLessEquals(const Offset(-0.9, 0), epsilon: 0.05)); - await tester.pumpWidget(slot(300, tester)); + await tester.pumpWidget(slot(300, const Duration(seconds: 1), tester)); await tester.pumpAndSettle(); expect(begin, findsOneWidget); expect(end, findsNothing); @@ -243,11 +245,35 @@ void main() { tester.getBottomRight(secondaryTestBreakpoint), const Offset(390, 790)); }); + testWidgets('adaptive layout can adjust animation duration', + (WidgetTester tester) async { + // Populate the smaller slot layout and let the animation settle. + await tester + .pumpWidget(slot(300, const Duration(milliseconds: 100), tester)); + await tester.pumpAndSettle(); + expect(begin, findsOneWidget); + expect(end, findsNothing); + + // expand in 1/5 second. + await tester + .pumpWidget(slot(500, const Duration(milliseconds: 200), tester)); + + // after 100ms, we expect both widgets to be present. + await tester.pump(const Duration(milliseconds: 50)); + expect(begin, findsOneWidget); + expect(end, findsOneWidget); + + // After 1/5 second, all animations should be done. + await tester.pump(const Duration(milliseconds: 200)); + expect(begin, findsNothing); + expect(end, findsOneWidget); + + await tester.pumpAndSettle(); + }); + testWidgets('adaptive layout does not animate when animations off', (WidgetTester tester) async { final Finder testBreakpoint = find.byKey(const Key('Test Breakpoint')); - final Finder secondaryTestBreakpoint = - find.byKey(const Key('Secondary Test Breakpoint')); await tester.pumpWidget( await layout(width: 400, tester: tester, animations: false)); @@ -257,9 +283,6 @@ void main() { expect(tester.getTopLeft(testBreakpoint), const Offset(10, 10)); expect(tester.getBottomRight(testBreakpoint), const Offset(200, 790)); - expect(tester.getTopLeft(secondaryTestBreakpoint), const Offset(200, 10)); - expect( - tester.getBottomRight(secondaryTestBreakpoint), const Offset(390, 790)); }); } @@ -306,6 +329,7 @@ Future layout({ TextDirection directionality = TextDirection.ltr, double? bodyRatio, bool animations = true, + int durationMs = 1000, }) async { await tester.binding.setSurfaceSize(Size(width, 800)); return MediaQuery( @@ -415,7 +439,7 @@ AnimatedWidget leftInOut(Widget child, Animation animation) { ); } -MediaQuery slot(double width, WidgetTester tester) { +MediaQuery slot(double width, Duration duration, WidgetTester tester) { return MediaQuery( data: MediaQueryData.fromView(tester.view).copyWith(size: Size(width, 800)), child: Directionality( @@ -425,12 +449,14 @@ MediaQuery slot(double width, WidgetTester tester) { TestBreakpoint0(): SlotLayout.from( inAnimation: leftOutIn, outAnimation: leftInOut, + duration: duration, key: const Key('0'), builder: (_) => const SizedBox(width: 10, height: 10), ), TestBreakpoint400(): SlotLayout.from( inAnimation: leftOutIn, outAnimation: leftInOut, + duration: duration, key: const Key('400'), builder: (_) => const SizedBox(width: 10, height: 10), ),