flutter: how to customize cuperinoAlertDialog style? - flutter

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

Related

How to stack two bottom sheet in flutter?

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.

Error: The getter 'context' isn't defined for the class 'MyApp'

I want to make an app that contain 4 buttons and it will go to other page if I click the button. But when I use the navigator it shows an error at context.
import 'package:flutter/material.dart';
import 'package:mytestapp/LectureVideo.dart';
import 'package:path/path.dart';
void main() {
runApp( MaterialApp(
debugShowCheckedModeBanner: false, //hide debug sash at top right
title: 'HomePage',
home: MyApp(),
)
);
}
class MyApp extends StatelessWidget{
#override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width; //getting screen width
return Scaffold(
appBar: AppBar(title: Text('Welcome to EMTeach'),
centerTitle: true,
backgroundColor: Colors.lime[200],
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(margin: EdgeInsets.only(bottom: 20.0)),
FirstButton(width),
Container(margin: EdgeInsets.only(bottom: 20.0)),
SecondButton(width),
Container(margin: EdgeInsets.only(bottom: 20.0)),
ThirdButton(width),
Container(margin: EdgeInsets.only(bottom: 20.0)),
FourthButton(width),
Container(margin: EdgeInsets.only(bottom: 20.0)),
],
),
),
);
}
Widget FirstButton(double width) {
return Expanded(
child: SizedBox(
width: width - 20,
child: RaisedButton(
onPressed: () {
Navigator.push(
context, //error
MaterialPageRoute(builder: (context) => LectureVideo()));
},
color: Colors.red,
child: Text(
"Lecture Video",
textScaleFactor: 2.0,
),
),
//fit: BoxFit.cover,
),
);
}
Widget SecondButton(double width) {
return Expanded(
child: SizedBox(
width: width - 20,
child: RaisedButton(
onPressed: () {},
color: Colors.lightGreen,
child: Text(
"Application Video",
textScaleFactor: 2.0,
),
),
//fit: BoxFit.cover,
),
);
}
Widget ThirdButton(double width) {
return Expanded(
child: SizedBox(
width: width - 20,
child: RaisedButton(
onPressed: () {},
color: Colors.lightBlue,
child: Text(
"List of Equation",
textScaleFactor: 2.0,
),
),
//fit: BoxFit.cover,
),
);
}
Widget FourthButton(double width) {
return Expanded(
child: SizedBox(
width: width - 20,
child: RaisedButton(
onPressed: () {},
color: Colors.orange,
child: Text(
"List of Question",
textScaleFactor: 2.0,
),
),
//fit: BoxFit.cover,
),
);
}
}
the problem is you are not passing the context which is the parameter of the build function over to your buttons try this:
Widget FirstButton(double width,BuildContext context ) {
return Expanded(
child: SizedBox(
width: width - 20,
child: RaisedButton(
onPressed: () {
Navigator.push(
context, //error
MaterialPageRoute(builder: (context) => LectureVideo()));
},
color: Colors.red,
child: Text(
"Lecture Video",
textScaleFactor: 2.0,
),
),
//fit: BoxFit.cover,
),
);
}

How to overlap button on BottomSheet in Flutter?

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),
)),
]);
},
)

How to show custom dialog at the top center of the screen in flutter?

I am going to show a custom dialog at the top center of the screen.
I've attached a screenshot .
How can I accomplish this? Default position of Dialog is center of the screen.
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
You can use Align and Material widget. (Alignment.topCenter) i.e
void showCustomDialog(BuildContext context, String message) {
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext cxt) {
return Align(
alignment: Alignment.topCenter,
child: Padding(
padding: EdgeInsets.all(16),
child: Material(
color: Colors.green,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15)),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
InkWell(
onTap: () {
Navigator.of(context).pop();
},
child: Image.asset("assets/close.png")),
SizedBox(width: 16),
Expanded(
child: Text(
message,
style: TextStyle(
color: Colors.white,
),
),
),
],
),
],
),
),
),
),
);
},
);
}

How to add a FAB using stack when floatingActionButtonLocation is defined?

In my application I have defined a FAB with the property floatingActionButtonLocation set to FloatingActionButtonLocation.centerDocked. I have defined a bottom app bar that holds the centerDocked FAB. I also want to use a Stack widget to position two additional FABs on the top right of the screen. It should look something similar to this - expected result
But when I tried to use a Stack, the FAB which is present in the bottom app bar is displayed but the two FABs under the stack widget are invisible.
docked FAB
FABs under stack widget
code where I have defined the Stack
return new Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: Stack(
children: <Widget>[
Positioned(
// the below values for stack are experimental at the moment
left: MediaQuery.of(context).copyWith().size.width * 0.60,
right: MediaQuery.of(context).copyWith().size.width * 0.01,
// top: MediaQuery.of(context).copyWith().size.height * 0.20,
// bottom: MediaQuery.of(context).copyWith().size.height * 0.7,
child: Row(
children: <Widget>[
// invisible FABs
FloatingActionButton(
onPressed: (){
_animate2Pagenegative();
// print(widget.date);
},
child: Icon(Icons.fast_rewind),
heroTag: UniqueKey(),
),
FloatingActionButton(
onPressed: (){
_animate2Pageforward();
// print(widget.date);
},
heroTag: UniqueKey(),
child: Icon(Icons.fast_forward),
),
],
),
),
// FAB which is displayed correctly
FloatingActionButton(
backgroundColor: Colors.red,
onPressed: () async{
String result1 = await Navigator.push( // string which stores the user entered value
context,
MaterialPageRoute(
builder: (context) => InputScreen(), //screen which has TextField
));
setState(() {
addItem(result1, false, "");
});
},
tooltip: 'Add a task',
child: Icon(Icons.add),
elevation: 0.0,
),
],
),
)
body of Scaffold:
body: new Column(
children: <Widget>[
new Expanded(
child: new DaysPageView(
// onDaysChanged: getDatestring,
physics: new NeverScrollableScrollPhysics(),
dayoverride: getDate(widget.date),
scrollDirection: _scrollDirection,
pageSnapping: _pageSnapping,
reverse: _reverse,
controller: _daysPageController,
pageBuilder: _daysPageBuilder,
),
),
],
),
You don't need other FloatingActionsButtons to do that design. What I understand is that you need the upper part not to scroll with the content hence you thought of FABs. There are multiple ways to achieve that. Here is an example, I'm using colors to show you the different parts of your screen. And of course these are random sizes to show the case.
return new Scaffold(
body: Column(
children: <Widget>[
Container(
height: 150,
color: Colors.red,
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 20, left: 20),
child: Text(
"- 2 TASKS MORE",
style: TextStyle(
fontSize: 35
),
),
),
GestureDetector(
onTap: () {
print('left arrow pressed');
},
child: Container(
margin: EdgeInsets.only(top: 20, left: 20),
child: Icon(Icons.arrow_left)
),
),
GestureDetector(
onTap: () {
print('right arrow pressed');
},
child: Container(
margin: EdgeInsets.only(top: 20, left: 20),
child: Icon(Icons.arrow_right)
),
)
],
),
SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Column(
children: <Widget>[
Container(
height: 600,
color: Colors.deepPurpleAccent,
),
],
),
)
],
),
bottomNavigationBar: BottomAppBar(
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
],
),
shape: CircularNotchedRectangle(),
color: Colors.red,
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.red,
onPressed: ()
async {
print('FAB pressed');
},
tooltip: 'Add a task',
child: Icon(Icons.add),
elevation: 0.0,
),
);
And here is the result