Skip to content

Commit fdb78fc

Browse files
authored
[flutter_adaptive_scaffold] Allows for the animation duration to be adjusted 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].
1 parent 02e71b0 commit fdb78fc

File tree

4 files changed

+53
-16
lines changed

4 files changed

+53
-16
lines changed

packages/flutter_adaptive_scaffold/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.1.11+1
2+
3+
* Allows custom animation duration for the NavigationRail and
4+
BottomNavigationBar transitions. [flutter/flutter#112938](https://github.com/flutter/flutter/issues/112938)
5+
16
## 0.1.11
27

38
* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3.

packages/flutter_adaptive_scaffold/lib/src/slot_layout.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,14 @@ class SlotLayout extends StatefulWidget {
7474
WidgetBuilder? builder,
7575
Widget Function(Widget, Animation<double>)? inAnimation,
7676
Widget Function(Widget, Animation<double>)? outAnimation,
77+
Duration? duration,
7778
required Key key,
7879
}) =>
7980
SlotLayoutConfig._(
8081
builder: builder,
8182
inAnimation: inAnimation,
8283
outAnimation: outAnimation,
84+
duration: duration,
8385
key: key,
8486
);
8587

@@ -96,7 +98,7 @@ class _SlotLayoutState extends State<SlotLayout>
9698
chosenWidget = SlotLayout.pickWidget(context, widget.config);
9799
bool hasAnimation = false;
98100
return AnimatedSwitcher(
99-
duration: const Duration(milliseconds: 1000),
101+
duration: chosenWidget?.duration ?? const Duration(milliseconds: 1000),
100102
layoutBuilder: (Widget? currentChild, List<Widget> previousChildren) {
101103
final Stack elements = Stack(
102104
children: <Widget>[
@@ -137,6 +139,7 @@ class SlotLayoutConfig extends StatelessWidget {
137139
required this.builder,
138140
this.inAnimation,
139141
this.outAnimation,
142+
this.duration,
140143
});
141144

142145
/// The child Widget that [SlotLayout] eventually returns with an animation.
@@ -160,6 +163,9 @@ class SlotLayoutConfig extends StatelessWidget {
160163
/// as the returned widget.
161164
final Widget Function(Widget, Animation<double>)? outAnimation;
162165

166+
/// The amount of time taken by the execution of the in and out animations.
167+
final Duration? duration;
168+
163169
/// An empty [SlotLayoutConfig] to be placed in a slot to indicate that the slot
164170
/// should show nothing.
165171
static SlotLayoutConfig empty() {

packages/flutter_adaptive_scaffold/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: flutter_adaptive_scaffold
22
description: Widgets to easily build adaptive layouts, including navigation elements.
3-
version: 0.1.11
3+
version: 0.1.11+1
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_adaptive_scaffold%22
55
repository: https://github.com/flutter/packages/tree/main/packages/flutter_adaptive_scaffold
66

packages/flutter_adaptive_scaffold/test/adaptive_layout_test.dart

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,13 @@ void main() {
120120
testWidgets(
121121
'slot layout properly switches between items with the appropriate animation',
122122
(WidgetTester tester) async {
123-
await tester.pumpWidget(slot(300, tester));
123+
await tester
124+
.pumpWidget(slot(300, const Duration(milliseconds: 1000), tester));
124125
expect(begin, findsOneWidget);
125126
expect(end, findsNothing);
126127

127-
await tester.pumpWidget(slot(500, tester));
128+
await tester
129+
.pumpWidget(slot(500, const Duration(milliseconds: 1000), tester));
128130
await tester.pump();
129131
await tester.pump(const Duration(milliseconds: 500));
130132
expect(tester.widget<SlideTransition>(slideOut('0')).position.value,
@@ -146,7 +148,7 @@ void main() {
146148
testWidgets('AnimatedSwitcher does not spawn duplicate keys on rapid resize',
147149
(WidgetTester tester) async {
148150
// Populate the smaller slot layout and let the animation settle.
149-
await tester.pumpWidget(slot(300, tester));
151+
await tester.pumpWidget(slot(300, const Duration(seconds: 1), tester));
150152
await tester.pumpAndSettle();
151153
expect(begin, findsOneWidget);
152154
expect(end, findsNothing);
@@ -157,12 +159,12 @@ void main() {
157159
for (int i = 0; i < 2; i++) {
158160
// Resize between the two slot layouts, but do not pump the animation
159161
// until completion.
160-
await tester.pumpWidget(slot(500, tester));
162+
await tester.pumpWidget(slot(500, const Duration(seconds: 1), tester));
161163
await tester.pump(const Duration(milliseconds: 100));
162164
expect(begin, findsOneWidget);
163165
expect(end, findsOneWidget);
164166

165-
await tester.pumpWidget(slot(300, tester));
167+
await tester.pumpWidget(slot(300, const Duration(seconds: 1), tester));
166168
await tester.pump(const Duration(milliseconds: 100));
167169
expect(begin, findsOneWidget);
168170
expect(end, findsOneWidget);
@@ -171,18 +173,18 @@ void main() {
171173

172174
testWidgets('slot layout can tolerate rapid changes in breakpoints',
173175
(WidgetTester tester) async {
174-
await tester.pumpWidget(slot(300, tester));
176+
await tester.pumpWidget(slot(300, const Duration(seconds: 1), tester));
175177
expect(begin, findsOneWidget);
176178
expect(end, findsNothing);
177179

178-
await tester.pumpWidget(slot(500, tester));
180+
await tester.pumpWidget(slot(500, const Duration(seconds: 1), tester));
179181
await tester.pump();
180182
await tester.pump(const Duration(milliseconds: 100));
181183
expect(tester.widget<SlideTransition>(slideOut('0')).position.value,
182184
offsetMoreOrLessEquals(const Offset(-0.1, 0), epsilon: 0.05));
183185
expect(tester.widget<SlideTransition>(slideIn('400')).position.value,
184186
offsetMoreOrLessEquals(const Offset(-0.9, 0), epsilon: 0.05));
185-
await tester.pumpWidget(slot(300, tester));
187+
await tester.pumpWidget(slot(300, const Duration(seconds: 1), tester));
186188
await tester.pumpAndSettle();
187189
expect(begin, findsOneWidget);
188190
expect(end, findsNothing);
@@ -243,11 +245,35 @@ void main() {
243245
tester.getBottomRight(secondaryTestBreakpoint), const Offset(390, 790));
244246
});
245247

248+
testWidgets('adaptive layout can adjust animation duration',
249+
(WidgetTester tester) async {
250+
// Populate the smaller slot layout and let the animation settle.
251+
await tester
252+
.pumpWidget(slot(300, const Duration(milliseconds: 100), tester));
253+
await tester.pumpAndSettle();
254+
expect(begin, findsOneWidget);
255+
expect(end, findsNothing);
256+
257+
// expand in 1/5 second.
258+
await tester
259+
.pumpWidget(slot(500, const Duration(milliseconds: 200), tester));
260+
261+
// after 100ms, we expect both widgets to be present.
262+
await tester.pump(const Duration(milliseconds: 50));
263+
expect(begin, findsOneWidget);
264+
expect(end, findsOneWidget);
265+
266+
// After 1/5 second, all animations should be done.
267+
await tester.pump(const Duration(milliseconds: 200));
268+
expect(begin, findsNothing);
269+
expect(end, findsOneWidget);
270+
271+
await tester.pumpAndSettle();
272+
});
273+
246274
testWidgets('adaptive layout does not animate when animations off',
247275
(WidgetTester tester) async {
248276
final Finder testBreakpoint = find.byKey(const Key('Test Breakpoint'));
249-
final Finder secondaryTestBreakpoint =
250-
find.byKey(const Key('Secondary Test Breakpoint'));
251277

252278
await tester.pumpWidget(
253279
await layout(width: 400, tester: tester, animations: false));
@@ -257,9 +283,6 @@ void main() {
257283

258284
expect(tester.getTopLeft(testBreakpoint), const Offset(10, 10));
259285
expect(tester.getBottomRight(testBreakpoint), const Offset(200, 790));
260-
expect(tester.getTopLeft(secondaryTestBreakpoint), const Offset(200, 10));
261-
expect(
262-
tester.getBottomRight(secondaryTestBreakpoint), const Offset(390, 790));
263286
});
264287
}
265288

@@ -306,6 +329,7 @@ Future<MediaQuery> layout({
306329
TextDirection directionality = TextDirection.ltr,
307330
double? bodyRatio,
308331
bool animations = true,
332+
int durationMs = 1000,
309333
}) async {
310334
await tester.binding.setSurfaceSize(Size(width, 800));
311335
return MediaQuery(
@@ -415,7 +439,7 @@ AnimatedWidget leftInOut(Widget child, Animation<double> animation) {
415439
);
416440
}
417441

418-
MediaQuery slot(double width, WidgetTester tester) {
442+
MediaQuery slot(double width, Duration duration, WidgetTester tester) {
419443
return MediaQuery(
420444
data: MediaQueryData.fromView(tester.view).copyWith(size: Size(width, 800)),
421445
child: Directionality(
@@ -425,12 +449,14 @@ MediaQuery slot(double width, WidgetTester tester) {
425449
TestBreakpoint0(): SlotLayout.from(
426450
inAnimation: leftOutIn,
427451
outAnimation: leftInOut,
452+
duration: duration,
428453
key: const Key('0'),
429454
builder: (_) => const SizedBox(width: 10, height: 10),
430455
),
431456
TestBreakpoint400(): SlotLayout.from(
432457
inAnimation: leftOutIn,
433458
outAnimation: leftInOut,
459+
duration: duration,
434460
key: const Key('400'),
435461
builder: (_) => const SizedBox(width: 10, height: 10),
436462
),

0 commit comments

Comments
 (0)