Reputation: 18464
I am able to make bottomSheet to full height by using showModalBottomSheet(...)
and set the property isScrollControlled:true
.
However, the bottom sheet is over the status bar, and that is not what I expect.
Is it possible to make it below the status bar?
Upvotes: 19
Views: 14992
Reputation: 9230
As AnasSafi's answer stated, use useSafeArea: true
to solve this problem. However there are many other tricky issues to solve when creating a bottom sheet. Here is my complete working bottom sheet creator function, which will hopefully help someone.
import 'package:flutter/material.dart';
Future Function() openBottomSheet(BuildContext context, Widget child,
{String? title}) {
return () => showModalBottomSheet(
// Don't let bottom sheet extend past the top of the safe area
useSafeArea: true,
context: context,
// Round the top of the bottom sheet
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
// Add scrollbar when necessary
isScrollControlled: true,
builder: (context) => ScrollableWidget(
child: Container(
// Move bottom sheet above on-screen keyboard, if keyboard is open
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: Stack(
children: [
// Add back button at top left of bottom sheet (since it's
// not obvious that sheet can be swiped down to close it)
const BackButton(),
Padding(
// Pad the main widget (larger padding on the top to leave
// space for the back button)
padding: const EdgeInsets.fromLTRB(20.0, 35.0, 20.0, 25.0),
child: Column(
children: [
// Make content full-width, so that main widget is
// centered even if it doesn't expand
Row(
mainAxisSize: MainAxisSize.max,
children: const [SizedBox(height: 0)],
),
// Add title text to top of bottom sheet, if provided
title == null
? Container()
: Column(
children: [
Text(
title,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
],
),
// Add the main widget
child,
],
),
),
],
),
),
),
);
}
/// A scrollable widget with a scrollbar that is shown when necessary
class ScrollableWidget extends StatefulWidget {
const ScrollableWidget({super.key, required this.child});
final Widget child;
@override
State<ScrollableWidget> createState() => _ScrollableWidgetState();
}
class _ScrollableWidgetState extends State<ScrollableWidget> {
final controller = ScrollController();
@override
Widget build(BuildContext context) {
return Scrollbar(
thumbVisibility: true,
thickness: 15,
radius: const Radius.circular(8),
controller: controller,
child: SingleChildScrollView(
// Same controller must be used on Scrollbar and SingleChildScrollView
controller: controller,
child: widget.child,
),
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
Upvotes: 0
Reputation: 6294
Since 20 Jul 2022, you can set useSafeArea
parameter to true
to show modal under status bar, you can find details here.
showModalBottomSheet<void>(
useSafeArea: true, // <<<<< use this
// ....
);
Note: Now it available on
master
channel only, and later will be available onstable
channel.
Upvotes: 19
Reputation: 106
The best and the easiest way is to subtract the total height of device from status bar height. You can get statusBarHeight by MediaQuery.of(context).viewPadding.top and whole device height by MediaQuery.of(context).size.height
Upvotes: 2
Reputation: 11
You can use the constraints parameter and set the height you want
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(35),
topRight: Radius.circular(35),
),
),
constraints: BoxConstraints(maxHeight:
MediaQuery.of(context).size.height - 50),
builder: (context) {
//continue coding..
}
Upvotes: 1
Reputation: 1109
I think this solution is the most suitable for now: (use FractionallySizedBox
)
showModalBottomSheet(
context: context,
enableDrag: true,
isScrollControlled: true,
builder: (context) => FractionallySizedBox(
heightFactor: 0.9,
child: Container()
)
);
Upvotes: 2
Reputation: 2044
Just like @Roddy R's answer but a little simpler
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
builder: (ctx) => Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
child: YourCustomBottomSheet(),
),
),
Thanks Roddy!
Upvotes: 0
Reputation: 1470
No need to create a new class. Simply assign a variable to the padding on the build of your view and use it when opening the dialog.
class MyHomeScreenState extends State<MyHomeScreen> {
double? topPadding;
@override
Widget build(BuildContext context) {
topPadding = MediaQuery.of(context).padding.top;
//...
}
showModalBottomSheet(
context: context,
useRootNavigator: true,
isScrollControlled: true,
builder: (context) {
return Container(
height: MediaQuery.of(context).size.height -
topPadding!,
color: Colors.green,
);
});
If you look at SafeArea
source code this is exactly what is happening. Make sure that your build is at a 'root' level on the widget tree since descendant widgets might not have a top padding because they are not underneath the edges.
Upvotes: 8
Reputation: 1322
Another easy workaround with rounded borders
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => Container(
height: MediaQuery.of(context).size.height * 0.965,
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(16.0),
topRight: const Radius.circular(16.0),
),
),
child: Center(
child: Text("Your content goes here"),
),
),
);
Upvotes: 2
Reputation:
as an option you can modify bottom sheet 1. create new file custom_bottom_sheet.dart 2. copy-paste all code from bottom_sheet class into your custom class 3. modify buildPage() method like this
@override
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
final BottomSheetThemeData sheetTheme = theme?.bottomSheetTheme ?? Theme.of(context).bottomSheetTheme;
Widget bottomSheet = SafeArea(
child: _ModalBottomSheet<T>(
route: this,
backgroundColor: backgroundColor ?? sheetTheme?.modalBackgroundColor ?? sheetTheme?.backgroundColor,
elevation: elevation ?? sheetTheme?.modalElevation ?? sheetTheme?.elevation,
shape: shape,
clipBehavior: clipBehavior,
isScrollControlled: isScrollControlled,
enableDrag: enableDrag,
),
);
if (theme != null) bottomSheet = Theme(data: theme, child: bottomSheet);
return bottomSheet;
}
import 'package:flutter/material.dart';
import 'custom_bottom_sheet.dart' as bs;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(child: Page()),
),
);
}
}
class Page extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text('show'),
onPressed: () {
bs.showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => Container(color: Colors.red),
);
},
),
);
}
}
here is modified bottom_sheet class https://pastebin.com/5U7fsqCw
also I think you need to add property
barrierColor: Colors.white.withOpacity(0),
to prevent status bar dimming
Upvotes: 15