mirror of
https://github.com/flutter/flutter
synced 2024-10-05 15:59:49 +00:00
Add AnimationStyle
to showBottomSheet
and showModalBottomSheet
(#145536)
fixes [Introduce animation customizable with `AnimationStyle` to `BottomSheet`](https://github.com/flutter/flutter/issues/145532) ### Default bottom sheet animation ![00-ezgif com-video-to-gif-converter](https://github.com/flutter/flutter/assets/48603081/a295b002-b310-4dea-8bc4-23b1d299748c) ### Custom bottom sheet animation ![01-ezgif com-video-to-gif-converter](https://github.com/flutter/flutter/assets/48603081/8c5c3d5f-e67d-4ed5-880d-f17d262087e1) ### No bottom sheet animation ![02-ezgif com-video-to-gif-converter](https://github.com/flutter/flutter/assets/48603081/872409d8-8a8d-4db9-b95b-7f96a62cdffc)
This commit is contained in:
parent
62adaff870
commit
23687c5260
101
examples/api/lib/material/bottom_sheet/show_bottom_sheet.0.dart
Normal file
101
examples/api/lib/material/bottom_sheet/show_bottom_sheet.0.dart
Normal file
|
@ -0,0 +1,101 @@
|
|||
// 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';
|
||||
|
||||
/// Flutter code sample for [showBottomSheet].
|
||||
|
||||
void main() => runApp(const BottomSheetExampleApp());
|
||||
|
||||
class BottomSheetExampleApp extends StatelessWidget {
|
||||
const BottomSheetExampleApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
appBar: AppBar(title: const Text('Bottom Sheet Sample')),
|
||||
body: const BottomSheetExample(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum AnimationStyles { defaultStyle, custom, none }
|
||||
const List<(AnimationStyles, String)> animationStyleSegments = <(AnimationStyles, String)>[
|
||||
(AnimationStyles.defaultStyle, 'Default'),
|
||||
(AnimationStyles.custom, 'Custom'),
|
||||
(AnimationStyles.none, 'None'),
|
||||
];
|
||||
|
||||
class BottomSheetExample extends StatefulWidget {
|
||||
const BottomSheetExample({super.key});
|
||||
|
||||
@override
|
||||
State<BottomSheetExample> createState() => _BottomSheetExampleState();
|
||||
}
|
||||
|
||||
class _BottomSheetExampleState extends State<BottomSheetExample> {
|
||||
Set<AnimationStyles> _animationStyleSelection = <AnimationStyles>{AnimationStyles.defaultStyle};
|
||||
AnimationStyle? _animationStyle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
SegmentedButton<AnimationStyles>(
|
||||
selected: _animationStyleSelection,
|
||||
onSelectionChanged: (Set<AnimationStyles> styles) {
|
||||
setState(() {
|
||||
_animationStyle = switch (styles.first) {
|
||||
AnimationStyles.defaultStyle => null,
|
||||
AnimationStyles.custom => AnimationStyle(
|
||||
duration: const Duration(seconds: 3),
|
||||
reverseDuration: const Duration(seconds: 1),
|
||||
),
|
||||
AnimationStyles.none => AnimationStyle.noAnimation,
|
||||
};
|
||||
_animationStyleSelection = styles;
|
||||
});
|
||||
},
|
||||
segments: animationStyleSegments
|
||||
.map<ButtonSegment<AnimationStyles>>(((AnimationStyles, String) shirt) {
|
||||
return ButtonSegment<AnimationStyles>(value: shirt.$1, label: Text(shirt.$2));
|
||||
})
|
||||
.toList(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
ElevatedButton(
|
||||
child: const Text('showBottomSheet'),
|
||||
onPressed: () {
|
||||
showBottomSheet(
|
||||
context: context,
|
||||
sheetAnimationStyle: _animationStyle,
|
||||
builder: (BuildContext context) {
|
||||
return SizedBox.expand(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const Text('Bottom sheet'),
|
||||
ElevatedButton(
|
||||
child: const Text('Close'),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
// 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';
|
||||
|
||||
/// Flutter code sample for [showModalBottomSheet].
|
||||
|
||||
void main() => runApp(const ModalBottomSheetApp());
|
||||
|
||||
class ModalBottomSheetApp extends StatelessWidget {
|
||||
const ModalBottomSheetApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
appBar: AppBar(title: const Text('Modal Bottom Sheet Sample')),
|
||||
body: const ModalBottomSheetExample(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum AnimationStyles { defaultStyle, custom, none }
|
||||
const List<(AnimationStyles, String)> animationStyleSegments = <(AnimationStyles, String)>[
|
||||
(AnimationStyles.defaultStyle, 'Default'),
|
||||
(AnimationStyles.custom, 'Custom'),
|
||||
(AnimationStyles.none, 'None'),
|
||||
];
|
||||
|
||||
class ModalBottomSheetExample extends StatefulWidget {
|
||||
const ModalBottomSheetExample({super.key});
|
||||
|
||||
@override
|
||||
State<ModalBottomSheetExample> createState() => _ModalBottomSheetExampleState();
|
||||
}
|
||||
|
||||
class _ModalBottomSheetExampleState extends State<ModalBottomSheetExample> {
|
||||
Set<AnimationStyles> _animationStyleSelection = <AnimationStyles>{AnimationStyles.defaultStyle};
|
||||
AnimationStyle? _animationStyle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
SegmentedButton<AnimationStyles>(
|
||||
selected: _animationStyleSelection,
|
||||
onSelectionChanged: (Set<AnimationStyles> styles) {
|
||||
setState(() {
|
||||
_animationStyle = switch (styles.first) {
|
||||
AnimationStyles.defaultStyle => null,
|
||||
AnimationStyles.custom => AnimationStyle(
|
||||
duration: const Duration(seconds: 3),
|
||||
reverseDuration: const Duration(seconds: 1),
|
||||
),
|
||||
AnimationStyles.none => AnimationStyle.noAnimation,
|
||||
};
|
||||
_animationStyleSelection = styles;
|
||||
});
|
||||
},
|
||||
segments: animationStyleSegments
|
||||
.map<ButtonSegment<AnimationStyles>>(((AnimationStyles, String) shirt) {
|
||||
return ButtonSegment<AnimationStyles>(value: shirt.$1, label: Text(shirt.$2));
|
||||
})
|
||||
.toList(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
ElevatedButton(
|
||||
child: const Text('showModalBottomSheet'),
|
||||
onPressed: () {
|
||||
showModalBottomSheet<void>(
|
||||
context: context,
|
||||
sheetAnimationStyle: _animationStyle,
|
||||
builder: (BuildContext context) {
|
||||
return SizedBox.expand(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const Text('Modal bottom sheet'),
|
||||
ElevatedButton(
|
||||
child: const Text('Close'),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ class SnackBarExample extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _SnackBarExampleState extends State<SnackBarExample> {
|
||||
final Set<AnimationStyles> _animationStyleSelection = <AnimationStyles>{AnimationStyles.defaultStyle};
|
||||
Set<AnimationStyles> _animationStyleSelection = <AnimationStyles>{AnimationStyles.defaultStyle};
|
||||
AnimationStyle? _animationStyle;
|
||||
|
||||
@override
|
||||
|
@ -57,6 +57,7 @@ class _SnackBarExampleState extends State<SnackBarExample> {
|
|||
),
|
||||
AnimationStyles.none => AnimationStyle.noAnimation,
|
||||
};
|
||||
_animationStyleSelection = styles;
|
||||
});
|
||||
},
|
||||
segments: animationStyleSegments
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
// 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';
|
||||
|
||||
/// Flutter code sample for [ScaffoldState.showBottomSheet].
|
||||
|
||||
void main() => runApp(const ShowBottomSheetExampleApp());
|
||||
|
||||
class ShowBottomSheetExampleApp extends StatelessWidget {
|
||||
const ShowBottomSheetExampleApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
appBar: AppBar(title: const Text('ScaffoldState BottomSheet Sample')),
|
||||
body: const ShowBottomSheetExample(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum AnimationStyles { defaultStyle, custom, none }
|
||||
const List<(AnimationStyles, String)> animationStyleSegments = <(AnimationStyles, String)>[
|
||||
(AnimationStyles.defaultStyle, 'Default'),
|
||||
(AnimationStyles.custom, 'Custom'),
|
||||
(AnimationStyles.none, 'None'),
|
||||
];
|
||||
|
||||
class ShowBottomSheetExample extends StatefulWidget {
|
||||
const ShowBottomSheetExample({super.key});
|
||||
|
||||
@override
|
||||
State<ShowBottomSheetExample> createState() => _ShowBottomSheetExampleState();
|
||||
}
|
||||
|
||||
class _ShowBottomSheetExampleState extends State<ShowBottomSheetExample> {
|
||||
Set<AnimationStyles> _animationStyleSelection = <AnimationStyles>{AnimationStyles.defaultStyle};
|
||||
AnimationStyle? _animationStyle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
SegmentedButton<AnimationStyles>(
|
||||
selected: _animationStyleSelection,
|
||||
onSelectionChanged: (Set<AnimationStyles> styles) {
|
||||
setState(() {
|
||||
_animationStyle = switch (styles.first) {
|
||||
AnimationStyles.defaultStyle => null,
|
||||
AnimationStyles.custom => AnimationStyle(
|
||||
duration: const Duration(seconds: 3),
|
||||
reverseDuration: const Duration(seconds: 1),
|
||||
),
|
||||
AnimationStyles.none => AnimationStyle.noAnimation,
|
||||
};
|
||||
_animationStyleSelection = styles;
|
||||
});
|
||||
},
|
||||
segments: animationStyleSegments
|
||||
.map<ButtonSegment<AnimationStyles>>(((AnimationStyles, String) shirt) {
|
||||
return ButtonSegment<AnimationStyles>(value: shirt.$1, label: Text(shirt.$2));
|
||||
})
|
||||
.toList(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
ElevatedButton(
|
||||
child: const Text('showBottomSheet'),
|
||||
onPressed: () {
|
||||
Scaffold.of(context).showBottomSheet(
|
||||
sheetAnimationStyle: _animationStyle,
|
||||
(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 200,
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const Text('BottomSheet'),
|
||||
ElevatedButton(
|
||||
child: const Text('Close'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
// 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_api_samples/material/bottom_sheet/show_bottom_sheet.0.dart' as example;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Bottom sheet animation can be customized using AnimationStyle', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.BottomSheetExampleApp(),
|
||||
);
|
||||
|
||||
// Show the bottom sheet with default animation style.
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'showBottomSheet'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the default forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 125));
|
||||
|
||||
// The bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, closeTo(178.0, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the default forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 125));
|
||||
|
||||
// The bottom sheet is fully visible.
|
||||
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, equals(56.0));
|
||||
|
||||
// Dismiss the bottom sheet.
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Close'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Select custom animation style.
|
||||
await tester.tap(find.text('Custom'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Show the bottom sheet with custom animation style.
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'showBottomSheet'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the custom forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 1500));
|
||||
|
||||
// The bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, closeTo(178.0, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the custom forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 1500));
|
||||
|
||||
// The bottom sheet is fully visible.
|
||||
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, equals(56.0));
|
||||
|
||||
// Dismiss the bottom sheet.
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Close'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Select no animation style.
|
||||
await tester.tap(find.text('None'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Show the bottom sheet with no animation style.
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'showBottomSheet'));
|
||||
await tester.pump();
|
||||
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, equals(56.0));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
// 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_api_samples/material/bottom_sheet/show_modal_bottom_sheet.2.dart' as example;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Modal bottom sheet animation can be customized using AnimationStyle', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.ModalBottomSheetApp(),
|
||||
);
|
||||
|
||||
// Show the bottom sheet with default animation style.
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'showModalBottomSheet'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the default forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 125));
|
||||
|
||||
// The bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, closeTo(316.7, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the default forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 125));
|
||||
|
||||
// The bottom sheet is fully visible.
|
||||
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, equals(262.5));
|
||||
|
||||
// Dismiss the bottom sheet.
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Close'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Select custom animation style.
|
||||
await tester.tap(find.text('Custom'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Show the bottom sheet with custom animation style.
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'showModalBottomSheet'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the custom forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 1500));
|
||||
|
||||
// The bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, closeTo(316.7, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the custom forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 1500));
|
||||
|
||||
// The bottom sheet is fully visible.
|
||||
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, equals(262.5));
|
||||
|
||||
// Dismiss the bottom sheet.
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Close'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Select no animation style.
|
||||
await tester.tap(find.text('None'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Show the bottom sheet with no animation style.
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'showModalBottomSheet'));
|
||||
await tester.pump();
|
||||
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, equals(262.5));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
// 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_api_samples/material/scaffold/scaffold_state.show_bottom_sheet.1.dart' as example;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Scaffold showBottomSheet animation can be customized using AnimationStyle', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.ShowBottomSheetExampleApp(),
|
||||
);
|
||||
|
||||
// Show the bottom sheet with default animation style.
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'showBottomSheet'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the default forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 125));
|
||||
|
||||
// The bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, closeTo(444.8, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the default forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 125));
|
||||
|
||||
// The bottom sheet is fully visible.
|
||||
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, equals(400.0));
|
||||
|
||||
// Dismiss the bottom sheet.
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Close'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Select custom animation style.
|
||||
await tester.tap(find.text('Custom'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Show the bottom sheet with custom animation style.
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'showBottomSheet'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the custom forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 1500));
|
||||
|
||||
// The bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, closeTo(444.8, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the custom forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 1500));
|
||||
|
||||
// The bottom sheet is fully visible.
|
||||
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, equals(400.0));
|
||||
|
||||
// Dismiss the bottom sheet.
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Close'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Select no animation style.
|
||||
await tester.tap(find.text('None'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Show the bottom sheet with no animation style.
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'showBottomSheet'));
|
||||
await tester.pump();
|
||||
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, equals(400.0));
|
||||
});
|
||||
}
|
|
@ -13,6 +13,9 @@ import 'tween.dart';
|
|||
/// - [ExpansionTile]
|
||||
/// - [MaterialApp]
|
||||
/// - [PopupMenuButton]
|
||||
/// - [ScaffoldMessengerState.showSnackBar]
|
||||
/// - [showBottomSheet]
|
||||
/// - [showModalBottomSheet]
|
||||
///
|
||||
/// If [duration] and [reverseDuration] are set to [Duration.zero], the
|
||||
/// corresponding animation will be disabled.
|
||||
|
|
|
@ -237,10 +237,13 @@ class BottomSheet extends StatefulWidget {
|
|||
/// This API available as a convenience for a Material compliant bottom sheet
|
||||
/// animation. If alternative animation durations are required, a different
|
||||
/// animation controller could be provided.
|
||||
static AnimationController createAnimationController(TickerProvider vsync) {
|
||||
static AnimationController createAnimationController(
|
||||
TickerProvider vsync,
|
||||
{ AnimationStyle? sheetAnimationStyle }
|
||||
) {
|
||||
return AnimationController(
|
||||
duration: _bottomSheetEnterDuration,
|
||||
reverseDuration: _bottomSheetExitDuration,
|
||||
duration: sheetAnimationStyle?.duration ?? _bottomSheetEnterDuration,
|
||||
reverseDuration: sheetAnimationStyle?.reverseDuration ?? _bottomSheetExitDuration,
|
||||
debugLabel: 'BottomSheet',
|
||||
vsync: vsync,
|
||||
);
|
||||
|
@ -846,6 +849,7 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> {
|
|||
this.transitionAnimationController,
|
||||
this.anchorPoint,
|
||||
this.useSafeArea = false,
|
||||
this.sheetAnimationStyle,
|
||||
});
|
||||
|
||||
/// A builder for the contents of the sheet.
|
||||
|
@ -992,6 +996,20 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> {
|
|||
/// The default is false.
|
||||
final bool useSafeArea;
|
||||
|
||||
/// Used to override the modal bottom sheet animation duration and reverse
|
||||
/// animation duration.
|
||||
///
|
||||
/// If [AnimationStyle.duration] is provided, it will be used to override
|
||||
/// the modal bottom sheet animation duration in the underlying
|
||||
/// [BottomSheet.createAnimationController].
|
||||
///
|
||||
/// If [AnimationStyle.reverseDuration] is provided, it will be used to
|
||||
/// override the modal bottom sheet reverse animation duration in the
|
||||
/// underlying [BottomSheet.createAnimationController].
|
||||
///
|
||||
/// To disable the modal bottom sheet animation, use [AnimationStyle.noAnimation].
|
||||
final AnimationStyle? sheetAnimationStyle;
|
||||
|
||||
/// {@template flutter.material.ModalBottomSheetRoute.barrierOnTapHint}
|
||||
/// The semantic hint text that informs users what will happen if they
|
||||
/// tap on the widget. Announced in the format of 'Double tap to ...'.
|
||||
|
@ -1051,7 +1069,10 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> {
|
|||
_animationController = transitionAnimationController;
|
||||
willDisposeAnimationController = false;
|
||||
} else {
|
||||
_animationController = BottomSheet.createAnimationController(navigator!);
|
||||
_animationController = BottomSheet.createAnimationController(
|
||||
navigator!,
|
||||
sheetAnimationStyle: sheetAnimationStyle,
|
||||
);
|
||||
}
|
||||
return _animationController!;
|
||||
}
|
||||
|
@ -1159,6 +1180,26 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> {
|
|||
/// ** See code in examples/api/lib/material/bottom_sheet/show_modal_bottom_sheet.1.dart **
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// The [sheetAnimationStyle] parameter is used to override the modal bottom sheet
|
||||
/// animation duration and reverse animation duration.
|
||||
///
|
||||
/// If [AnimationStyle.duration] is provided, it will be used to override
|
||||
/// the modal bottom sheet animation duration in the underlying
|
||||
/// [BottomSheet.createAnimationController].
|
||||
///
|
||||
/// If [AnimationStyle.reverseDuration] is provided, it will be used to
|
||||
/// override the modal bottom sheet reverse animation duration in the
|
||||
/// underlying [BottomSheet.createAnimationController].
|
||||
///
|
||||
/// To disable the bottom sheet animation, use [AnimationStyle.noAnimation].
|
||||
///
|
||||
/// {@tool dartpad}
|
||||
/// This sample showcases how to override the [showModalBottomSheet] animation
|
||||
/// duration and reverse animation duration using [AnimationStyle].
|
||||
///
|
||||
/// ** See code in examples/api/lib/material/bottom_sheet/show_modal_bottom_sheet.2.dart **
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [BottomSheet], which becomes the parent of the widget returned by the
|
||||
|
@ -1171,6 +1212,8 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> {
|
|||
/// [DisplayFeature]s can split the screen into sub-screens.
|
||||
/// * The Material 2 spec at <https://m2.material.io/components/sheets-bottom>.
|
||||
/// * The Material 3 spec at <https://m3.material.io/components/bottom-sheets/overview>.
|
||||
/// * [AnimationStyle], which is used to override the modal bottom sheet
|
||||
/// animation duration and reverse animation duration.
|
||||
Future<T?> showModalBottomSheet<T>({
|
||||
required BuildContext context,
|
||||
required WidgetBuilder builder,
|
||||
|
@ -1191,6 +1234,7 @@ Future<T?> showModalBottomSheet<T>({
|
|||
RouteSettings? routeSettings,
|
||||
AnimationController? transitionAnimationController,
|
||||
Offset? anchorPoint,
|
||||
AnimationStyle? sheetAnimationStyle,
|
||||
}) {
|
||||
assert(debugCheckHasMediaQuery(context));
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
|
@ -1217,6 +1261,7 @@ Future<T?> showModalBottomSheet<T>({
|
|||
transitionAnimationController: transitionAnimationController,
|
||||
anchorPoint: anchorPoint,
|
||||
useSafeArea: useSafeArea,
|
||||
sheetAnimationStyle: sheetAnimationStyle,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -1235,6 +1280,26 @@ Future<T?> showModalBottomSheet<T>({
|
|||
/// The [enableDrag] parameter specifies whether the bottom sheet can be
|
||||
/// dragged up and down and dismissed by swiping downwards.
|
||||
///
|
||||
/// The [sheetAnimationStyle] parameter is used to override the bottom sheet
|
||||
/// animation duration and reverse animation duration.
|
||||
///
|
||||
/// If [AnimationStyle.duration] is provided, it will be used to override
|
||||
/// the bottom sheet animation duration in the underlying
|
||||
/// [BottomSheet.createAnimationController].
|
||||
///
|
||||
/// If [AnimationStyle.reverseDuration] is provided, it will be used to
|
||||
/// override the bottom sheet reverse animation duration in the underlying
|
||||
/// [BottomSheet.createAnimationController].
|
||||
///
|
||||
/// To disable the bottom sheet animation, use [AnimationStyle.noAnimation].
|
||||
///
|
||||
/// {@tool dartpad}
|
||||
/// This sample showcases how to override the [showBottomSheet] animation
|
||||
/// duration and reverse animation duration using [AnimationStyle].
|
||||
///
|
||||
/// ** See code in examples/api/lib/material/bottom_sheet/show_bottom_sheet.0.dart **
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// To rebuild the bottom sheet (e.g. if it is stateful), call
|
||||
/// [PersistentBottomSheetController.setState] on the controller returned by
|
||||
/// this method.
|
||||
|
@ -1265,6 +1330,8 @@ Future<T?> showModalBottomSheet<T>({
|
|||
/// * [Scaffold.of], for information about how to obtain the [BuildContext].
|
||||
/// * The Material 2 spec at <https://m2.material.io/components/sheets-bottom>.
|
||||
/// * The Material 3 spec at <https://m3.material.io/components/bottom-sheets/overview>.
|
||||
/// * [AnimationStyle], which is used to override the bottom sheet animation
|
||||
/// duration and reverse animation duration.
|
||||
PersistentBottomSheetController showBottomSheet({
|
||||
required BuildContext context,
|
||||
required WidgetBuilder builder,
|
||||
|
@ -1276,6 +1343,7 @@ PersistentBottomSheetController showBottomSheet({
|
|||
bool? enableDrag,
|
||||
bool? showDragHandle,
|
||||
AnimationController? transitionAnimationController,
|
||||
AnimationStyle? sheetAnimationStyle,
|
||||
}) {
|
||||
assert(debugCheckHasScaffold(context));
|
||||
|
||||
|
@ -1289,6 +1357,7 @@ PersistentBottomSheetController showBottomSheet({
|
|||
enableDrag: enableDrag,
|
||||
showDragHandle: showDragHandle,
|
||||
transitionAnimationController: transitionAnimationController,
|
||||
sheetAnimationStyle: sheetAnimationStyle,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -2506,6 +2506,26 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
|
|||
///
|
||||
/// ** See code in examples/api/lib/material/scaffold/scaffold_state.show_bottom_sheet.0.dart **
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// The [sheetAnimationStyle] parameter is used to override the bottom sheet
|
||||
/// animation duration and reverse animation duration.
|
||||
///
|
||||
/// If [AnimationStyle.duration] is provided, it will be used to override
|
||||
/// the bottom sheet animation duration in the underlying
|
||||
/// [BottomSheet.createAnimationController].
|
||||
///
|
||||
/// If [AnimationStyle.reverseDuration] is provided, it will be used to
|
||||
/// override the bottom sheet reverse animation duration in the underlying
|
||||
/// [BottomSheet.createAnimationController].
|
||||
///
|
||||
/// To disable the bottom sheet animation, use [AnimationStyle.noAnimation].
|
||||
///
|
||||
/// {@tool dartpad}
|
||||
/// This sample showcases how to override the [showBottomSheet] animation
|
||||
/// duration and reverse animation duration using [AnimationStyle].
|
||||
///
|
||||
/// ** See code in examples/api/lib/material/scaffold/scaffold_state.show_bottom_sheet.1.dart **
|
||||
/// {@end-tool}
|
||||
/// See also:
|
||||
///
|
||||
/// * [BottomSheet], which becomes the parent of the widget returned by the
|
||||
|
@ -2516,6 +2536,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
|
|||
/// * [Scaffold.of], for information about how to obtain the [ScaffoldState].
|
||||
/// * The Material 2 spec at <https://m2.material.io/components/sheets-bottom>.
|
||||
/// * The Material 3 spec at <https://m3.material.io/components/bottom-sheets/overview>.
|
||||
/// * [AnimationStyle], which is used to override the modal bottom sheet
|
||||
/// animation duration and reverse animation duration.
|
||||
PersistentBottomSheetController showBottomSheet(
|
||||
WidgetBuilder builder, {
|
||||
Color? backgroundColor,
|
||||
|
@ -2526,6 +2548,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
|
|||
bool? enableDrag,
|
||||
bool? showDragHandle,
|
||||
AnimationController? transitionAnimationController,
|
||||
AnimationStyle? sheetAnimationStyle,
|
||||
}) {
|
||||
assert(() {
|
||||
if (widget.bottomSheet != null) {
|
||||
|
@ -2540,7 +2563,9 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
|
|||
assert(debugCheckHasMediaQuery(context));
|
||||
|
||||
_closeCurrentBottomSheet();
|
||||
final AnimationController controller = (transitionAnimationController ?? BottomSheet.createAnimationController(this))..forward();
|
||||
final AnimationController controller = (transitionAnimationController
|
||||
?? BottomSheet.createAnimationController(this, sheetAnimationStyle: sheetAnimationStyle))
|
||||
..forward();
|
||||
setState(() {
|
||||
_currentBottomSheet = _buildBottomSheet(
|
||||
builder,
|
||||
|
|
|
@ -2290,6 +2290,252 @@ void main() {
|
|||
expect(modalBarrier.semanticsLabel, MaterialLocalizations.of(scaffoldKey.currentContext!).scrimLabel);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('Bottom sheet animation can be customized', (WidgetTester tester) async {
|
||||
final Key sheetKey = UniqueKey();
|
||||
|
||||
Widget buildWidget({ AnimationStyle? sheetAnimationStyle }) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
showBottomSheet(
|
||||
context: context,
|
||||
sheetAnimationStyle: sheetAnimationStyle,
|
||||
builder: (BuildContext context) {
|
||||
return SizedBox.expand(
|
||||
child: ColoredBox(
|
||||
key: sheetKey,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
child: FilledButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Close'),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const Text('X'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test custom animation style.
|
||||
await tester.pumpWidget(buildWidget(
|
||||
sheetAnimationStyle: AnimationStyle(
|
||||
duration: const Duration(milliseconds: 800),
|
||||
reverseDuration: const Duration(milliseconds: 400),
|
||||
),
|
||||
));
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the custom forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
|
||||
// The bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, closeTo(134.6, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the custom forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
|
||||
// The bottom sheet is fully visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, equals(0.0));
|
||||
|
||||
// Dismiss the bottom sheet.
|
||||
await tester.tap(find.widgetWithText(FilledButton, 'Close'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the custom reverse duration.
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
|
||||
// The bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, closeTo(134.6, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the custom reverse duration.
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
|
||||
// The bottom sheet is dismissed.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, equals(600.0));
|
||||
|
||||
// Test no animation style.
|
||||
await tester.pumpWidget(buildWidget(sheetAnimationStyle: AnimationStyle.noAnimation));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pump();
|
||||
|
||||
// The bottom sheet is fully visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, equals(0.0));
|
||||
|
||||
// Dismiss the bottom sheet.
|
||||
await tester.tap(find.widgetWithText(FilledButton, 'Close'));
|
||||
await tester.pump();
|
||||
|
||||
// The bottom sheet is dismissed.
|
||||
expect(find.byKey(sheetKey), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('Modal bottom sheet default animation', (WidgetTester tester) async {
|
||||
final Key sheetKey = UniqueKey();
|
||||
|
||||
// Test default modal bottom sheet animation.
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
showModalBottomSheet<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return SizedBox.expand(
|
||||
child: ColoredBox(
|
||||
key: sheetKey,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
child: FilledButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Close'),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const Text('X'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Tap the 'X' to show the bottom sheet.
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the default forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 125));
|
||||
|
||||
// The modal bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, closeTo(316.7, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the default forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 125));
|
||||
|
||||
// The modal bottom sheet is fully visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, equals(262.5));
|
||||
|
||||
// Dismiss the bottom sheet.
|
||||
await tester.tap(find.widgetWithText(FilledButton, 'Close'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the default reverse duration.
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
// The modal bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, closeTo(316.7, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the default reverse duration.
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
// The modal bottom sheet is dismissed.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, equals(600.0));
|
||||
});
|
||||
|
||||
testWidgets('Modal bottom sheet animation can be customized', (WidgetTester tester) async {
|
||||
final Key sheetKey = UniqueKey();
|
||||
|
||||
Widget buildWidget({ AnimationStyle? sheetAnimationStyle }) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
showModalBottomSheet<void>(
|
||||
context: context,
|
||||
sheetAnimationStyle: sheetAnimationStyle,
|
||||
builder: (BuildContext context) {
|
||||
return SizedBox.expand(
|
||||
child: ColoredBox(
|
||||
key: sheetKey,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
child: FilledButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Close'),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const Text('X'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test custom animation style.
|
||||
await tester.pumpWidget(buildWidget(
|
||||
sheetAnimationStyle: AnimationStyle(
|
||||
duration: const Duration(milliseconds: 800),
|
||||
reverseDuration: const Duration(milliseconds: 400),
|
||||
),
|
||||
));
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the custom forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
|
||||
// The bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, closeTo(316.7, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the custom forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
|
||||
// The bottom sheet is fully visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, equals(262.5));
|
||||
|
||||
// Dismiss the bottom sheet.
|
||||
await tester.tap(find.widgetWithText(FilledButton, 'Close'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the custom reverse duration.
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
|
||||
// The bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, closeTo(316.7, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the custom reverse duration.
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
|
||||
// The bottom sheet is dismissed.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, equals(600.0));
|
||||
|
||||
// Test no animation style.
|
||||
await tester.pumpWidget(buildWidget(sheetAnimationStyle: AnimationStyle.noAnimation));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pump();
|
||||
|
||||
// The bottom sheet is fully visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, equals(262.5));
|
||||
|
||||
// Dismiss the bottom sheet.
|
||||
await tester.tap(find.widgetWithText(FilledButton, 'Close'));
|
||||
await tester.pump();
|
||||
|
||||
// The bottom sheet is dismissed.
|
||||
expect(find.byKey(sheetKey), findsNothing);
|
||||
});
|
||||
}
|
||||
|
||||
class _TestPage extends StatelessWidget {
|
||||
|
|
|
@ -3102,6 +3102,160 @@ void main() {
|
|||
// The SnackBar is dismissed.
|
||||
expect(tester.getTopLeft(find.text('I am a snack bar.')).dy, closeTo(614, 0.1));
|
||||
});
|
||||
|
||||
testWidgets('Scaffold showBottomSheet default animation', (WidgetTester tester) async {
|
||||
final Key sheetKey = UniqueKey();
|
||||
|
||||
// Test default bottom sheet animation.
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Scaffold.of(context).showBottomSheet(
|
||||
(BuildContext context) {
|
||||
return SizedBox.expand(
|
||||
child: ColoredBox(
|
||||
key: sheetKey,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
child: FilledButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Close'),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const Text('X'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Tap the 'X' to show the bottom sheet.
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the default forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 125));
|
||||
|
||||
// The bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, closeTo(134.6, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the default forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 125));
|
||||
|
||||
// The bottom sheet is fully visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, equals(0.0));
|
||||
|
||||
// Dismiss the bottom sheet.
|
||||
await tester.tap(find.widgetWithText(FilledButton, 'Close'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the default reverse duration.
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
// The bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, closeTo(134.6, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the default reverse duration.
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
// The bottom sheet is dismissed.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, equals(600.0));
|
||||
});
|
||||
|
||||
testWidgets('Scaffold showBottomSheet animation can be customized', (WidgetTester tester) async {
|
||||
final Key sheetKey = UniqueKey();
|
||||
|
||||
Widget buildWidget({ AnimationStyle? sheetAnimationStyle }) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Scaffold.of(context).showBottomSheet(
|
||||
sheetAnimationStyle: sheetAnimationStyle,
|
||||
(BuildContext context) {
|
||||
return SizedBox.expand(
|
||||
child: ColoredBox(
|
||||
key: sheetKey,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
child: FilledButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Close'),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const Text('X'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test custom animation style.
|
||||
await tester.pumpWidget(buildWidget(
|
||||
sheetAnimationStyle: AnimationStyle(
|
||||
duration: const Duration(milliseconds: 800),
|
||||
reverseDuration: const Duration(milliseconds: 400),
|
||||
),
|
||||
));
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the custom forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
|
||||
// The bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, closeTo(134.6, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the custom forward duration.
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
|
||||
// The bottom sheet is fully visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, equals(0.0));
|
||||
|
||||
// Dismiss the bottom sheet.
|
||||
await tester.tap(find.widgetWithText(FilledButton, 'Close'));
|
||||
await tester.pump();
|
||||
// Advance the animation by 1/2 of the custom reverse duration.
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
|
||||
// The bottom sheet is partially visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, closeTo(134.6, 0.1));
|
||||
|
||||
// Advance the animation by 1/2 of the custom reverse duration.
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
|
||||
// The bottom sheet is dismissed.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, equals(600.0));
|
||||
|
||||
// Test no animation style.
|
||||
await tester.pumpWidget(buildWidget(sheetAnimationStyle: AnimationStyle.noAnimation));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pump();
|
||||
|
||||
// The bottom sheet is fully visible.
|
||||
expect(tester.getTopLeft(find.byKey(sheetKey)).dy, equals(0.0));
|
||||
|
||||
// Dismiss the bottom sheet.
|
||||
await tester.tap(find.widgetWithText(FilledButton, 'Close'));
|
||||
await tester.pump();
|
||||
|
||||
// The bottom sheet is dismissed.
|
||||
expect(find.byKey(sheetKey), findsNothing);
|
||||
});
|
||||
}
|
||||
|
||||
class _GeometryListener extends StatefulWidget {
|
||||
|
|
Loading…
Reference in a new issue