Skip to content

Commit

Permalink
[flutter_adaptive_scaffold] Allows for the animation duration to be a…
Browse files Browse the repository at this point in the history
…djusted using SlotLayout.from() (#6510)

Added `duration` to the `SlotLayoutConfig` constructor and `from()` method. This allows a user to specify the duration of the in/out animations which they can already supply.

The default animation of 1 second is nice for demos where a slowed down animation helps to show the behavior, but it is (arguably?) way too slow for an application where one wants a snappy animation effect.

*List which issues are fixed by this PR. You must list at least one issue.*

Fixes flutter/flutter#112938

*If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].*

No change to tests

- [test-exempt] I added new tests to check the change I am making, or this PR is [test-exempt].
  • Loading branch information
oravecz-jpmc authored Jun 24, 2024
1 parent 02e71b0 commit fdb78fc
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 16 deletions.
5 changes: 5 additions & 0 deletions packages/flutter_adaptive_scaffold/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
8 changes: 7 additions & 1 deletion packages/flutter_adaptive_scaffold/lib/src/slot_layout.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,14 @@ class SlotLayout extends StatefulWidget {
WidgetBuilder? builder,
Widget Function(Widget, Animation<double>)? inAnimation,
Widget Function(Widget, Animation<double>)? outAnimation,
Duration? duration,
required Key key,
}) =>
SlotLayoutConfig._(
builder: builder,
inAnimation: inAnimation,
outAnimation: outAnimation,
duration: duration,
key: key,
);

Expand All @@ -96,7 +98,7 @@ class _SlotLayoutState extends State<SlotLayout>
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<Widget> previousChildren) {
final Stack elements = Stack(
children: <Widget>[
Expand Down Expand Up @@ -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.
Expand All @@ -160,6 +163,9 @@ class SlotLayoutConfig extends StatelessWidget {
/// as the returned widget.
final Widget Function(Widget, Animation<double>)? 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() {
Expand Down
2 changes: 1 addition & 1 deletion packages/flutter_adaptive_scaffold/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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

Expand Down
54 changes: 40 additions & 14 deletions packages/flutter_adaptive_scaffold/test/adaptive_layout_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<SlideTransition>(slideOut('0')).position.value,
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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<SlideTransition>(slideOut('0')).position.value,
offsetMoreOrLessEquals(const Offset(-0.1, 0), epsilon: 0.05));
expect(tester.widget<SlideTransition>(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);
Expand Down Expand Up @@ -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));
Expand All @@ -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));
});
}

Expand Down Expand Up @@ -306,6 +329,7 @@ Future<MediaQuery> layout({
TextDirection directionality = TextDirection.ltr,
double? bodyRatio,
bool animations = true,
int durationMs = 1000,
}) async {
await tester.binding.setSurfaceSize(Size(width, 800));
return MediaQuery(
Expand Down Expand Up @@ -415,7 +439,7 @@ AnimatedWidget leftInOut(Widget child, Animation<double> 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(
Expand All @@ -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),
),
Expand Down

0 comments on commit fdb78fc

Please sign in to comment.