I am trying to implement a function to enable AlertDialog scrollable when the keyboard is displayed. In order to achieve it, I used CustomScrollView as follow. However, I could not close the popup window by clicking outside of it,
await showDialog<void>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return CustomScrollView(slivers: <Widget>[
SliverFillRemaining(
hasScrollBody: true,
child: AlertDialog(
title: Text('hello'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
)))
]);
});
Before adding CustomScrollView, the barrierDismissible works find as follow:
await showDialog<void>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return AlertDialog(
title: Text('hello'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
));
});
Can anyone please help how to enable the whole dialog scrollable when keyboard is shown? Thank you
Related
I'am trying to show webView in a showModalBottomSheet, using inAppWebView plugin for webView. So when the webView state changes bottomSheet should adjust its height. Currently I just made a hotFix by giving scroll to bottomSheet.
return return showModalBottomSheet(
enableDrag: false,
isDismissible: false,
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(20.r),
topLeft: Radius.circular(20.r),
),
),
context: context,
builder: (context) => BottomSheet(),
);
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height - (kToolbarHeight * 2),
decoration: BoxDecoration(
color: lmWhite,
borderRadius: BorderRadius.circular(20.r),
),
clipBehavior: Clip.hardEdge,
child: Stack(
children: [
InAppWebView(),
Opacity(),
],
),
);
}
Default height for bottomSheet is half the screenSize
If you want your bottomSheet to Expand according to your content use below code
showModalBottomSheet<dynamic>(
isScrollControlled: true,
context: context,
builder: (BuildContext bc) {
return Wrap(
children: <Widget>[...]
)
}
)
This will automatically expand the bottomSheet according to content inside.
I used to make a text ('Loading') to be centered in the show dialog but what happened is the text is appearing in a bad way as when we make a screen without scaffold, it is appearing here the same, I used to put scaffold above the center widget but it didn't work also.
showDialog(
context: context,
barrierColor: Colors.blueGrey.shade50.withOpacity(0.5),
barrierDismissible: true,
builder: (ctx) {
return WillPopScope(
onWillPop: () async => false,
child: Center(
child: Container(
child: const Text('Loading'),
),
),
);
});
It is missing Material, You can wrap with any material widget.
barrierDismissible: true,
builder: (ctx) {
return Material( //here
child: WillPopScope(
onWillPop: () async => false,
I am trying to call setState inside an AlertDialog, but surprisingly setState is not changing the state variable. The variable (tasbeehChantCanAdd) i want to set from the dialog box is inside the body of the main page(outside the dialog box) This is the code for the alertDialog:
Future<void> _alertRemoveEntry(BuildContext context, String textToRemove) {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return WillPopScope(
onWillPop: (){},
child: StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(15.0),
),
),
title: const Text('Enter your info',style: TextStyle(fontWeight: FontWeight.normal,fontSize: 14),),
content: Container(
height: 150,
child: const Text("Sure to remove this?")
),
actions: <Widget>[
Container(
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
//side: BorderSide(color: Colors.red)
),
primary: Colors.purple,
),
child: Text("CANCEL"),
onPressed: () {
Navigator.of(context).pop();
},
),
Container(
width: 20,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
//side: BorderSide(color: Colors.red)
),
primary: Colors.purple,
),
child: Text("REMOVE"),
onPressed: (){
setState((){
tasbeehChantCanAdd = "state changed";
});
ClassHub().myListSharePreference("sptasbeehAddedList", "set", tasbeehChantCanAdd, "");
Navigator.of(context).pop();
},
),
],),),
],
);
},
),
);
});
}
Please what am i doing wrong? Any help would be appreciated. Thanks
The main state class also have setState and while you are using StatefulBuilder it also has setState, being scope priority setState is coming from StatefulBuilder. You can rename it and use
child: StatefulBuilder(
builder: (context, setStateSB) {
....
setState((){ /// for state class IU update
tasbeehChantCanAdd = "state changed";
});
setStateSB((){ // update Ui inside dialog
tasbeehChantCanAdd = "state changed";
});
Try to add your AlertDialog widget inside StatefulBuilder hope its helpful to you.
Refer StatefulBuilder here
yourDropdown(BuildContext context) {
return showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, StateSetter setState) {
return AlertDialog(
);
},
);
},
);
}
Refer my answer here
Are you me from another dimension? Seriously, I just solved the same problem. I don't know if it is the recommended way of setting states on dialog, since the documentation on showDialog mentions something about states, but I solved it this way:
var choice = await showDialog(
context: context,
builder: (context) {
var list = [];
return StatefulBuilder(builder: (BuildContext context, setState) {
return AlertDialog(
[...]
);
});
});
Just put the variables you need to update through setState just before the StatefulBuilder
I have a dialog that appears after user click on send button . When dialog appears , I want to show a text after 5 seconds . I use Future.delayed but the text doesn't appear at all . It appears only when I close the dialog and open it again . I want to show the text after 5 seconds from opening the dialog .
Here is my function
void _initialize() {
Future<void>.delayed(const Duration(seconds: 3), () {
if (mounted) {
setState(() {
visibility = true;
});
}
});
}
}
And here is my dialog code in the onTab of the button
_initialize()
showDialog(context: context,
barrierDismissible: false,
builder: (BuildContext contextd){
return WillPopScope(
onWillPop: () {return Future.value(false);},
child: Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)
),
child: Padding(
padding: EdgeInsets.fromLTRB(20.w, 20.h, 20.w, 20.h),
child: Column(
children: [
//here are some widgets
Visibility(
visible:visibility?true:false,
child: Text("Resend?",style: TextStyle(decoration:
TextDecoration.underline,),)),
],),
),
),
);
});
Try using StatefulBuilder like this:
showDialog(
context: context,
builder: (BuildContext context) => StatefulBuilder(
builder: (context, setState) {
return //somthing to return;
},
));
This happens because your setState that update visibility has a different context than builder dialog. Visibility is actually update but only appear in dialog when close and open again because this is how build dialog is updated. If you want to work with the context dialog need to use that widget:
StatefulBuilder(
builder: (context, setState) {
Into your dialog and then call that setState.
Try using a StatefulBuilder. Using the bool visible and the setDialogState argument, you can update the state of the contents of the dialog.
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return StatefulBuilder(
builder(context, setDialogState) {
// this variable is used for the conditional display
bool visible = false;
// this future will update the visible variable in 3 seconds, after the dialog is displayed already
Future.delayed(Duration(seconds: 3)).then(() => setDialogState(() { visible = true;}));
return WillPopScope(
onWillPop: () { return Future.value(false); },
child: Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)
),
child: Padding(
padding: EdgeInsets.fromLTRB(20.w, 20.h, 20.w, 20.h),
child: Column(
children: [
//here are some widgets
Visibility(
visible:visible,
child: Text(
"Resend?",
style: TextStyle(
decoration: TextDecoration.underline,
),
),
),
],
),
),
),
);
},
);
};
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!