I have a sliding up with two buttons and I want to freeze the widget (sliding up) and unlock after the user taps on a button.
My sliding up code is here.
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: _body(context),
);
}
Widget _body(BuildContext context) {
return Column(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: w(30)),
child: Column(
children: [
SizedBox(
height: w(15),
),
Lottie.asset('assets/lottie/privacy_policy.json', width: w(150)),
SizedBox(
height: w(20),
),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
AboutDetailsPage.show(
context, FlutterI18n.translate(context, "about.privacyPolicy"), PrivacyDetailsBody(), true);
},
child: Align(
alignment: Alignment.centerLeft,
child: Text(
FlutterI18n.translate(context, "about.privacyPolicyUpdateDescription"),
style: textStyle17().copyWith(color: TecnoColors.theme().grey1, height: 1.5),
),
),
),
],
),
),
SizedBox(
height: w(35),
),
Container(
padding: EdgeInsets.symmetric(horizontal: w(15)),
child: Column(
children: [
Container(
child: TecnoButton(FlutterI18n.translate(context, "about.privacyPolicyUpdateAllow"),
backgroundColor: TecnoColors.theme().blue2),
),
SizedBox(
height: w(15),
),
Container(
child: GestureDetector(
onTap: () {
getUserBLoC(context).logout();
Navigator.pushReplacement(
context,
new CupertinoPageRoute<Null>(
settings: RouteSettings(name: 'LoginPage'),
builder: (BuildContext context) => new BoxLoginPage(loginPageMode.LOGIN),
));
},
child: Text(
FlutterI18n.translate(context, "about.privacyPolicyUpdateDeny"),
style: textStyle16().copyWith(color: TecnoColors.theme().blue2),
),
),
)
],
),
)
],
);
}
Basically if the user open this widget, the user can not close it, just after he taps on a button.
After the sliding up appears, the sliding up can not close, even if the user click out of. The sliding up can close just if user taps on one of widgets button
You can wrap you sliding up will WillPopScope and on will pop you can check if user has pressed the button the make
Navigator.pop(context);
else, show some message
Related
I want to stack two bottom sheet each other in flutter as show in photo. The upper one is shown when in error state. In photo, it build with alert dialog. I want is with bottom sheet. How can I get it?
Edit:
Here is my code that I want to do. Lower bottom sheet is with pin field, autoComplete. autoComplete trigger StreamController, and then streamBuilder watch Error state and show dialog.
confirmPasswordModalBottomSheet(
BiometricAuthRegisterBloc biometricAuthRegBloc) {
showMaterialModalBottomSheet(
context: context,
builder: (BuildContext context) {
return StreamBuilder(
stream: biometricAuthRegBloc.biometricAuthRegisterStream,
builder: (context,AsyncSnapshot<ResponseObject>biometricAuthRegSnapShot) {
if (biometricAuthRegSnapShot.hasData) {
if (biometricAuthRegSnapShot.data!.messageState ==
MessageState.requestError) {
showModalBottomSheet(context: context, builder:
(BuildContext context){
return Container(
width: 200,height: 200,
child: Center(child: Text('Helllllllllo'),),);
});
}
}
return SizedBox(
width: 100,
height: 300,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: margin30,
),
Text(CURRENT_PIN_TITLE),
SizedBox(
height: margin30,
),
Padding(
padding: const EdgeInsets.only(
left: margin60, right: margin60),
child: PinCodeField(
pinLength: 6,
onChange: () {},
onComplete: (value) {
biometricAuthRegBloc.biometricAuthRegister(
biometricType:_biometricAuthTypeForApi,
password: value);
},
),
),
SizedBox(
height: margin30,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal:
margin80),
child: AppButton(
onClick: () {},
label: CANCEL_BTN_LABEL,
),
),
Container(
padding: const EdgeInsets.all(8.0),
margin:
EdgeInsets.symmetric(vertical: 8.0,
horizontal: 30),
decoration: BoxDecoration(
color: Colors.grey,
border: Border.all(color: Colors.black),
),
child: const Text(
FINGER_PRINT_DIALOG,
textAlign: TextAlign.center,
),
)
],
),
);
});
},
);
}
When I do like that above, I get setState() or markNeedsBuild() called during build. Error and why? Sorry for my previous incomplete question.
I am bit confused with your question but stacking two bottomsheet is just easy. You just need to call the showModalBottomSheet whenever you want it shown to user. You can check out the following implementation:
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ElevatedButton(
onPressed: () {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return Container(
height: 500,
color: Colors.amber,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Modal BottomSheet 1'),
ElevatedButton(
child: const Text('Show second modal 2'),
onPressed: () {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return Container(
height: 200,
color: Colors.redAccent,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Modal BottomSheet 2'),
ElevatedButton(
child: const Text('Close BottomSheet'),
onPressed: () => Navigator.pop(context),
),
],
),
),
);
},
);
},
),
],
),
),
);
},
);
},
child: Text('Show bottom sheet 1'),
),
);
}
}
I have solution. All I need to do is, need to add WidgetBinding.insatance.addPostFrameCallback((timeStamp){showModalBottomSheet()}); in the StreamBuilder return.
In my design, there is a close button in the BottomSheet. I have tried Stack. But it didn't work. Does anyone know how to achieve the result? Thanks :)
modalBottomSheet(context) {
return showModalBottomSheet(
context: context,
builder: (context) {
return Stack(
children: [
Container(
child: Text('Sample Text'),
),
Container(
height: 50,
width: 50,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
),
],
);
}
);
}
So I've been trying around for a bit, and this seems to work as you explained.
modalBottomSheet(context) {
return showModalBottomSheet(
context: context,
builder: (context) {
// using a scaffold helps to more easily position the FAB
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: double.maxFinite,
),
Padding(
padding: EdgeInsets.all(30.0),
child: Text("Text in the sheet"),
),
],
),
// translate the FAB up by 30
floatingActionButton: Container(
transform: Matrix4.translationValues(0.0, -30, 0.0), // translate up by 30
child: FloatingActionButton(
onPressed: () {
// do stuff
print('doing stuff');
},
child: Icon(Icons.add),
),
),
// dock it to the center top (from which it is translated)
floatingActionButtonLocation: FloatingActionButtonLocation.centerTop,
);
});
}
The meat of the solution here is to transform the Container which contains the FAB. I got this idea from this older SO answer to a somewhat related question.
The result looks like this:
You'll probably have to make some more edits to achieve the exact result you desire, but I hope this sets you on the right path.
Edit
When, in the above solution, you want to press the FAB, and you tap the top half, the onPressed handler fires, but the modal also closes. You should probably use a WillPopScope that only pops when the actual button is pressed (and not the area around/above it). If you think it's fine pressing anywhere above it as well, you can just leave it as-is.
showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
builder: (BuildContext context) {
return Stack(clipBehavior: Clip.none, children: [
Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.vertical(top: Radius.circular(20))),
height: 220.h,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0, vertical: 16),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
ElevatedButton(
color: Colors.green,
onPressed: () {},
child: const Center(child: Text('Remove')))
],
),
),
),
),
Positioned(
top: -30.h,
right: 12.w,
child: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.close),
)),
]);
},
)
I'm working with flutter. I want to make a CupertinoAlertDialog(iOS style is required). My problem is UI designers require the background color of the alert dialog should be #F0F0F0. But I can only adjust its theme into dark or light(e.g. following picture). The code I completed is placed below.
showCupertinoDialog(
context: context,
builder: (BuildContext context){
return Theme(
data: ThemeData.dark(),
child: CupertinoAlertDialog(
title: Text('Title'),
content: Text('Some message here'),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Cancle'),
),
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('OK'),
),
],
),
);
}
);
Is that possible? Thanks for any advice.
If I recall correctly, the background color for CupertinoAlertDialog is hardcoded. However, you can create a custom dialog that can change the background color of it as well as the functions of the buttons.
You need to create a type Dialog for the showDialog function instead of showCupertinoDialog:
Dialog customDialog = Dialog(
backgroundColor: Color(0xfff0f0f0), // your color
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)), // change 40 to your desired radius
child: CustomAlertDialog(),
);
I also created a stateless widget called CustomAlertDialog, but if you don't want to, you can replace the CustomAlertDialog() with its content.
class CustomAlertDialog extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 150,
child: Column(
children: [
Expanded(
flex: 2,
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: Colors.grey, width: 1),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
child: Center(
child: Text(
"Title",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 20),
),
),
),
Container(
child: Center(
child: Text("Some message here"),
),
),
],
),
),
),
Expanded(
flex: 1,
child: Row(
children: [
Expanded(
flex: 1,
child: GestureDetector(
child: Container(
decoration: BoxDecoration(
border: Border(
right: BorderSide(color: Colors.grey, width: 1),
),
),
child: Center(
child: Text("Cancel"),
),
),
onTap: () {
Navigator.of(context).pop(); // replace with your own functions
},
),
),
Expanded(
flex: 1,
child: GestureDetector(
child: Container(
child: Center(
child: Text("OK"),
),
),
onTap: () {
Navigator.of(context).pop(); // replace with your own functions
},
),
),
],
),
),
],
),
);
}
}
Lastly, replace your whole showCupertinoDialog with this showDialog function:
showDialog(
barrierDismissible: true, // set false if you dont want the dialog to be dismissed when user taps anywhere [![enter image description here][1]][1]outside of the alert
context: context,
builder: (context) {
return customDialog;
},
);
Result: https://i.stack.imgur.com/cV13A.png
First of all, in my app when the user draws something and and then press save button, the user user should see the same drawing on his/her phone but on a shorter scale, because he/she can draw more drawings and save them, I wanted to show all of them in a singleChildScrollView. I have tried, but I am getting man errors.
I want to show a grid of different screen on one screen so that whenever the user tap on one of the screens then the screen will expand fully on to the screen.
Builder(
builder: (context) => Column(
children: [
Expanded(
child: Container(
padding: EdgeInsets.all(0),
height: height,
color: Colors.white,
child: SingleChildScrollView(
child: Form(
key: formKey,
child: Column(
children: <Widget>[
/////////////////////////////////////////////////////////////////////////////////////////////////////
//This below is the code that I tried.
widget.drawModel != null
? Transform.scale(
scale: 0.5,
child: Scaffold(
body: Container(
constraints: BoxConstraints(
maxHeight:
MediaQuery.of(context).size.height /
2,
maxWidth:
MediaQuery.of(context).size.width / 2,
),
child: CustomPaint(
painter: Draw(
points: widget
.drawModel[
widget.drawModel.length - 1]
.points),
),
),
),
)
: SizedBox(height: 10),
///////////////////////////////////////////////////////////////////////////////////////////////////
images.length != 0
// List view for images
? Column(
children: <Widget>[
for (int i = 0; i < images.length; i++)
Padding(
padding: EdgeInsets.symmetric(
horizontal: ImageColumnPad * width),
child: Dismissible(
key: ObjectKey(images[i]),
onDismissed: (direction) {
var item = images.elementAt(i);
deleteItem(i);
Scaffold.of(context).showSnackBar(
SnackBar(
shape: RoundedRectangleBorder(),
content: Text("Item deleted",
style:
TextStyle(fontSize: 15)),
action: SnackBarAction(
label: "UNDO",
onPressed: () {
undoDeletion(i, item);
},
),
),
);
},
child: GestureDetector(
onTap: () => {
//TODO: Implement delete function here
},
child: Center(
child: Image.file(
images[i],
fit: BoxFit.contain,
),
),
),
),
),
],
)
: SizedBox(height: 2),
...
...
...
You can wrap the body with Column.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First Route'),
),
body: Scaffold(
body: Center(
child: RaisedButton(
child: Text('Open route'),
onPressed: () {},
),
),
),
);
}
edit:
I made "I AM" bright neon green, but its mixing with the button colors to a dark green.
I want to display text over two buttons that cover the whole screen, 1 top half and 1 bottom half. I want text to display in the middle of the screen, kind of covering part of each button.
This is my code for the buttons:
Widget build(BuildContext context) {
return Scaffold(
body: Align(
alignment: Alignment.center,
child: Column(
children: <Widget>[
Container(
height: (MediaQuery.of(context).size.height) / 2,
width: MediaQuery.of(context).size.width,
child: RaisedButton(
color: Colors.black87,
child: poorText,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
),
),
Container(
height: (MediaQuery.of(context).size.height) / 2,
width: MediaQuery.of(context).size.width,
child: RaisedButton(
color: Colors.black87,
child: richText,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ThirdRoute()),
);
},
),
),
],
),
),
);}
I have tried to use stack, but it says too many positional arguments are being used. How would I keep my column layout and layer text over the buttons? If there is another way without column that still allows makes the buttons work that's fine too. Thanks!
You can use a Stack widget to achieve that:
Note. Because the two buttons are aligned to the center, you have to align the text widget also to the center so it can be placed directly ob=ver the two button.
Check the code below:
It works perfectly:
Widget build(BuildContext context) {
return Scaffold(
// use a stack widget
body: Stack(
children: [
// text to display over buttons
Align(
// set alignment to center to place it over the buttons
alignment: Alignment.center,
child: Text(
'Text you want to display over the buttons',
// give it your style
style: TextStyle(
),
),
),
Align(
alignment: Alignment.center,
child: Column(
children: <Widget>[
Container(
height: (MediaQuery.of(context).size.height) / 2,
width: MediaQuery.of(context).size.width,
child: RaisedButton(
color: Colors.black87,
child: poorText,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
),
),
Container(
height: (MediaQuery.of(context).size.height) / 2,
width: MediaQuery.of(context).size.width,
child: RaisedButton(
color: Colors.black87,
child: richText,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ThirdRoute()),
);
},
),
),
],
),
),
],
),
);}
I hope this helps.
Use 2 Columns widget within a Stack widget. The code below achieves exactly what you want if I understood you well.
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Column(
children: <Widget>[
Container(
height: (MediaQuery.of(context).size.height) / 2,
width: MediaQuery.of(context).size.width,
child: RaisedButton(
color: Colors.blue,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
),
),
Container(
height: (MediaQuery.of(context).size.height) / 2,
width: MediaQuery.of(context).size.width,
child: RaisedButton(
color: Colors.red,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ThirdRoute()),
);
},
),
),
],
),
Column(
children: <Widget>[
Container(
// set alignment to center to place it over the buttons
height: (MediaQuery.of(context).size.height) / 2,
width: MediaQuery.of(context).size.width,
child: Center(child: Text('Text 1')),
),
Container(
// set alignment to center to place it over the buttons
height: (MediaQuery.of(context).size.height) / 2,
width: MediaQuery.of(context).size.width,
child: Center(child: Text('Text 2')),
),
],
)
],
);
}