Skip to content

Commit

Permalink
Add AnimationStyle to showBottomSheet and showModalBottomSheet (f…
Browse files Browse the repository at this point in the history
…lutter#145536)

fixes [Introduce animation customizable with `AnimationStyle` to `BottomSheet`](flutter#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)
  • Loading branch information
TahaTesser authored Mar 25, 2024
1 parent 62adaff commit 23687c5
Show file tree
Hide file tree
Showing 12 changed files with 1,007 additions and 6 deletions.
101 changes: 101 additions & 0 deletions examples/api/lib/material/bottom_sheet/show_bottom_sheet.0.dart
Original file line number Diff line number Diff line change
@@ -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),
),
],
),
),
);
},
);
},
),
],
),
);
}
}
101 changes: 101 additions & 0 deletions examples/api/lib/material/bottom_sheet/show_modal_bottom_sheet.2.dart
Original file line number Diff line number Diff line change
@@ -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),
),
],
),
),
);
},
);
},
),
],
),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -57,6 +57,7 @@ class _SnackBarExampleState extends State<SnackBarExample> {
),
AnimationStyles.none => AnimationStyle.noAnimation,
};
_animationStyleSelection = styles;
});
},
segments: animationStyleSegments
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
},
),
],
),
),
);
},
);
},
),
],
),
);
}
}
Original file line number Diff line number Diff line change
@@ -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));
});
}
Loading

0 comments on commit 23687c5

Please sign in to comment.