mirror of
https://github.com/flutter/packages.git
synced 2025-07-03 09:08:54 +08:00
[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 https://github.com/flutter/flutter/issues/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].
This commit is contained in:
@ -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.
|
||||
|
@ -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,
|
||||
);
|
||||
|
||||
@ -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>[
|
||||
@ -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<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() {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
@ -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<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);
|
||||
@ -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<MediaQuery> 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<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(
|
||||
@ -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),
|
||||
),
|
||||
|
Reference in New Issue
Block a user