Safearea() does not wrap the showModalBottomSheet properly. I need to show the modal under the status bar.
class ModalBottomSheet {
static void renderModalBottomSheet(BuildContext context, Widget widget) {
showModalBottomSheet(
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(border2),
),
),
context: context,
builder: (BuildContext context) {
return SafeArea(
child: Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: widget,
),
);
},
);
}
}
I have tried the following solutions but it still doesn't work properly
MediaQuery.of(context).padding.top
MediaQueryData.fromWindow(WidgetsBinding.instance.window).padding.top
Update:
I managed to solve it this way.
add this to the bottomsheet
backgroundColor: Colors.transparent,
and padding top
top: MediaQuery.of(context).padding.top,
full code:
class ModalBottomSheet {
static void renderModalBottomSheet(BuildContext context, Widget widget) {
showModalBottomSheet(
isScrollControlled: true,
backgroundColor: Colors.transparent,
context: context,
builder: (_) {
return SafeArea(
child: Padding(
padding: EdgeInsets.only(
top: MediaQuery.of(context).padding.top,
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: widget,
),
);
},
);
}
}
your child widget can have the border radius and colors instead.
open keyboard
closed keyboard
Try this. If it doesn't work, refer this page
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[...
you should get MediaQuery.of(context).viewPadding.top before showModalBottomSheet and pass it to sheet inside, then use EdgeInsets.top inside the sheet.
Related
Problem:
I'm using MediaQuery and viewInsets to add Padding, when the user
triggers the keyboard in a modalBottomSheet.
It looks OK, but I get a message about overflow
When I draw down the modalBottomSheet manually, I can see the overflow happening behind the sheet.
Code first, then screenshots:
This is the GestureDetector opening the modal sheet:
GestureDetector(
onTap: () {
showModalBottomSheet(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(23.r),
),
),
isScrollControlled: true,
context: context,
builder: (bctx) => StatefulBuilder(builder:
(BuildContext context, StateSetter setModalState) {
return ModalAddFavorite();
}));
},
This is the Widegt that I use as modal sheeet:
class ModalAddFavorite extends StatefulWidget {
const ModalAddFavorite({Key? key}) : super(key: key);
#override
_ModalAddFavoriteState createState() => _ModalAddFavoriteState();
}
class _ModalAddFavoriteState extends State<ModalAddFavorite> {
#override
Widget build(BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setModalState) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom * 0.98.h),
//
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 20.h,
),
Container(
width: 80.w,
height: 6.w,
decoration: BoxDecoration(
color: Provider.of<CustomColors>(context, listen: false)
.customColorScheme['Grey 2'],
borderRadius: BorderRadius.circular(6.r),
),
),
SizedBox(
height: 25.h,
),
//
Text(
'ADD A FAVORITE',
style: Provider.of<CustomTextStyle>(context)
.customTextStyle('ModalHeader'),
),
SizedBox(
height: 25.5.h,
),
//
//
InputFieldAddFavorite(),
SizedBox(
height: 40.h,
)
],
),
),
);
});
}
}
Screenshots:
Modal Sheet open / keyboard inactive / no overflow
Modal sheet open / keyboard active / overflow warning in Flutter
Modal shett pulled back manually // overflow visible behind the sheet:
Try to add physics: NeverScrollableScrollPhysics() under your SingleChildScrollView().
Issue solved: Instead of wrapping the modal sheet in a SingleChildScrollView, I needed to wrap the Column that contains the page itself.
I am still new with Flutter. I try to make my dialog to be able to dismiss when click outside of the dialog. However if i use Scaffold, the barrierDismissible:true is not working. I tried to use Wrap but an error : No Material widget found will be displayed. Is there any idea on how to dismiss the dialog?
This is my code:
showGeneralDialog(
barrierDismissible: true,
pageBuilder: (context, anim1, anim2) {
context1 = context;
return StatefulBuilder(
builder: (context, setState) {
return Scaffold(
backgroundColor: Colors.black .withOpacity(0.0),
body: Align(
alignment: Alignment.bottomCenter,
child: Container(
child: InkWell()
)
)
}
}
)
Scaffold is not required to display showGeneralDialog. The Material widget was required in your code because the InkWell widget needs a Material ancestor. You could use any widget that provides material such as Card or Material widget itself. Also barrierLabel cannot be null.
Please see the working code below or you can directly run the code on Dartpad https://dartpad.dev/6c047a6cabec9bbd00a048c972098671
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text("showGeneralDialog Demo"),
),
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierLabel:
MaterialLocalizations.of(context).modalBarrierDismissLabel,
barrierColor: Colors.black54,
pageBuilder: (context, anim1, anim2) {
return Center(
child: Container(
width: 200,
height: 100,
child: StatefulBuilder(
builder: (context, snapshot) {
return const Card(
color: Colors.white,
child: Center(
child: InkWell(
child: Text(
"Press outside to close",
style: TextStyle(color: Colors.black),
),
),
),
);
},
),
),
);
},
);
},
child: const Text("Show Dialog"));
}
}
For anyone who needs to use a Scaffold in their AlertDialogs (perhaps to use ScaffoldMessenger), here is the simple work around:
Wrap the Scaffold with an IgnorePointer. The "barrierDismissible" value will now work.
#override
Widget build(BuildContext context) {
return IgnorePointer(
child: Scaffold(
backgroundColor: Colors.transparent,
body: AlertDialog(
title: title,
content: SizedBox(
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
child: ListBody(
children: content
),
),
),
actions: actions,
insetPadding: const EdgeInsets.all(24.0),
shape: Theme.of(context).dialogTheme.shape,
backgroundColor: Theme.of(context).dialogTheme.backgroundColor,
)
),
);
}
Add this in showGeneralDialog
barrierLabel: ""
Code will look like this
showGeneralDialog(
barrierDismissible: true,
barrierLabel: "",
pageBuilder: (context, anim1, anim2) {
context1 = context;
return StatefulBuilder(
builder: (context, setState) {
return Scaffold(
backgroundColor: Colors.black .withOpacity(0.0),
body: Align(
alignment: Alignment.bottomCenter,
child: Container(
child: InkWell()
)
)
}
}
)
I was encountering this issue when using showGeneralDialog on top of a map view. The root cause of my issue was that I had wrapped the dialog in a PointerInterceptor.
To fix it, I had to set intercepting to false.
PointerInterceptor(
intercepting: onMap, // false when not an map
child: Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
)
)
Just noticed that the top SafeArea is being ignored when showModalBottomSheet is set to isScrollControlled: true.(I want my modal to fit the screen) Anyone found a way to fix it?
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => MyModal(),
)
My test modal
class MyModal extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Column(
children: <Widget>[
Text('Modal'),
Expanded(
child: Container(
color: Colors.red,
))
],
),
);
}
}
OUTPUT
I kinda temporary fixed it by setting height of modal content
SafeArea(
child: Container(
height: MediaQuery.of(context).size.height - 80,
child: Column(
children: ...
OUTPUT
If you're using GetX
Try it
final padding = MediaQuery.of(Get.context!).viewPadding;
Else you can pass parent context as param
final padding = MediaQuery.of(parentContext).viewPadding;
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?
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;
}
use your class
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
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.
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 on stable channel.
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"),
),
),
);
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()
)
);
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..
}
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
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!
I want to display some option in the bottom of Flutter AlertDialog. As you can see
If I understand correctly you can use the showDialog component which lays behind the AlertDialog with a nested Column. For a greyed out background ou can use Opacity with a Container:
showDialog(
context: context,
builder: (context) {
return Opacity(
opacity: 0.8, // some opacity
child: Container(
// container to set color
color: Colors.grey,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: [
RaisedButton(
onPressed: null,
), // replace with your buttons
RaisedButton(
onPressed: null,
),
]
)
)
);
}
);
Simple way: you can use padding
showDialog<String>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top:300.0),
child: //AlertDiloag / CupertinoAlertDialog
);
},
);