flutter: how to customize CupertinoAlertDialog's width and height? - flutter

I'm working with flutter. I want to make a CupertinoAlertDialog(iOS style is required). The problem is UI designer require the width and height respectfully are 270 and 140. I have searched in the flutter doc but could not find related property. Is that possible? Thanks for any advice. The basic 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'),
),
],
),
);
}
);

Related

Display SnackBar on top of AlertDialog widget

I have an AlertDialog widget that will cause a SnackBar to display when you tap on its Text. The SnackBar currently displays behind the AlertDialog barrier, in the background. I want the Snackbar to display on top of the transparent AlertDialog barrier instead. Is the behavior that I'm seeking possible to achieve in Flutter? I have created a brand new Flutter app and included only the relevant code to illustrate the use-case below, as well as a screenshot.
Main.dart Gist
#override
Widget build(BuildContext context) {
WidgetsBinding.instance!.addPostFrameCallback((_) async {
showDialog(
context: context,
builder: (BuildContext dialogContext) => AlertDialog(
content: GestureDetector(
onTap: () {
ScaffoldMessenger.of(dialogContext).showSnackBar(SnackBar(
content: const Text('snack'),
duration: const Duration(seconds: 1),
action: SnackBarAction(
label: 'ACTION',
onPressed: () {},
),
));
},
child: Center(
child: Text('Show SnackBar!'),
),
),
),
);
});
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Update
Thanks to Amy, I realized that tapping on the barrier did not dismiss the dialog. Also, the code was causing to show multiple SnackBars due to the use of nested Scaffolds.
Check out the following model that fixes all issues:
showDialog
|
|
ScaffoldMessenger => "Set a scope to show SnackBars only in the inner Scaffold"
|
--- Builder => "Add a Builder widget to access the Scaffold Messenger"
|
--- Scaffold => "The inner Scaffold that is needed to show SnackBars"
|
--- GestureDetector => "Dismiss the dialog when tapped outside"
|
--- GestureDetector => "Don't dismiss it when tapped inside"
|
--- AlertDialog => "Your dialog"
Here is the implementation:
showDialog(
context: context,
builder: (context) => ScaffoldMessenger(
child: Builder(
builder: (context) => Scaffold(
backgroundColor: Colors.transparent,
body: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => Navigator.of(context).pop(),
child: GestureDetector(
onTap: () {},
child: AlertDialog(
content: GestureDetector(
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('snack'),
duration: const Duration(seconds: 1),
action: SnackBarAction(
label: 'ACTION',
onPressed: () {},
),
),
);
},
child: Center(
child: Text('Show SnackBar!'),
),
),
),
),
),
),
),
),
);
Old answer
ScaffoldMessenger shows SnackBar in the nearest descendant Scaffold. If you add another Scaffold before AlertDialog, it will use it instead of the root one which is left behind the dialog.
showDialog(
context: context,
builder: (BuildContext dialogContext) => Scaffold(
backgroundColor: Colors.transparent, // Make Scaffold's background transparent
body: AlertDialog(
content: GestureDetector(
onTap: () {
ScaffoldMessenger.of(dialogContext).showSnackBar(SnackBar(
content: const Text('snack'),
duration: const Duration(seconds: 1),
action: SnackBarAction(
label: 'ACTION',
onPressed: () {},
),
));
},
child: Center(
child: Text('Show SnackBar!'),
),
),
),
),
);
Instead of the SnackBar Use another_flushbar, It will Appear Above AlertDialog.
Flushbar(
backgroundColor: Colors.red,
message: S.of(context).choose_date,
duration: Duration(seconds: Constants.TOAST_DURATION),
).show(context);
Result:
The issue here is that showDialog uses the root navigator provided by MaterialApp. So when you show your dialog it is pushed completely over your scaffold. To solve this you need the navigator that is used to be a child of the scaffold that's showing the snackbars. So the following code adds this navigator, sets useRootNavigator to false to use this navigator, and importantly uses a BuildContext under the newly created navigator:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Navigator( //New navigator added here
initialRoute: '/',
onGenerateRoute: (setting) {
return MaterialPageRoute(
builder: (context) => Center(
child: Builder(builder: (context) {
WidgetsBinding.instance!
.addPostFrameCallback((_) async {
showDialog(
context: context,
useRootNavigator: false,//Dialog must not use root navigator
builder: (BuildContext dialogContext) =>
AlertDialog(
content: GestureDetector(
onTap: () {
ScaffoldMessenger.of(dialogContext)
.showSnackBar(SnackBar(
content: const Text('snack'),
duration: const Duration(seconds: 1),
action: SnackBarAction(
label: 'ACTION',
onPressed: () {},
),
));
},
child: Center(
child: Text('Show SnackBar!'),
),
),
),
);
});
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
]);
}),
));
}),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Result:
Note that this solution does constrain the dialog size a bit and the app bar and floating action button is above the content, which may be undesirable. This can be solved just by adding another scaffold below the newly created navigator and moving those appbar/FAB properties down as desired. Example with AppBar below the modal:
#override
Widget build(BuildContext context) {
return Scaffold(
body: Navigator(
initialRoute: '/',
onGenerateRoute: (setting) {
return MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Builder(builder: (context) {
WidgetsBinding.instance!
.addPostFrameCallback((_) async {
showDialog(
context: context,
useRootNavigator: false,
builder: (BuildContext dialogContext) =>
AlertDialog(
content: GestureDetector(
onTap: () {
ScaffoldMessenger.of(dialogContext)
.showSnackBar(SnackBar(
content: const Text('snack'),
duration: const Duration(seconds: 1),
action: SnackBarAction(
label: 'ACTION',
onPressed: () {},
),
));
},
child: Center(
child: Text('Show SnackBar!'),
),
),
),
);
});
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
]);
}),
)));
}),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Result:
hope this is what you are looking for
import 'package:flutter/material.dart';
class SnackOverDialog extends StatefulWidget {
SnackOverDialog({Key? key}) : super(key: key);
#override
_SnackOverDialogState createState() => _SnackOverDialogState();
}
class _SnackOverDialogState extends State<SnackOverDialog> {
final GlobalKey<ScaffoldState> _scaffoldkey = new GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
///* show snack
_snackbar(BuildContext context) {
_scaffoldkey.currentState!.showSnackBar(SnackBar(
content: const Text('snack'),
duration: const Duration(seconds: 1),
action: SnackBarAction(
label: 'ACTION',
onPressed: () {},
),
));
}
///* dialog
_dialog(BuildContext context) {
WidgetsBinding.instance!.addPostFrameCallback((_) async {
showDialog(
context: context,
builder: (BuildContext dialogContext) => AlertDialog(
content: Scaffold(
key: _scaffoldkey,
body: GestureDetector(
onTap: () {
_snackbar(dialogContext);
},
child: Center(
child: Text('Show SnackBar!'),
),
),
),
),
);
});
}
return Scaffold(
appBar: AppBar(
title: Text("SNackBarOVerDialog"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => _dialog(context),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

Flutter show dialog show only if 2 showDialog() is called in one function

I have a showDialog() function in flutter web, but it will only works this way (2 show dialog in one function), if I comment out the other one, the dialog will not show. I don't really understand why I need to put 2 showDialog() in order for it to show up. Here is the code:
onDeleteTap(String id) async {
print(id);
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Hapus?'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
],
),
),
actions: <Widget>[
TextButton(
child: Text('Batal'),
onPressed: () {
},
),
SizedBox(
width: 150.0,
child: ErrorButton(
text: "Hapus",
onClick: () {
},
),
),
],
);
},
);
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Hapus?'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
],
),
),
actions: <Widget>[
TextButton(
child: Text('Batal'),
onPressed: () {
},
),
SizedBox(
width: 150.0,
child: ErrorButton(
text: "Hapus",
onClick: () {
},
),
),
],
);
},
);
I think before you are calling onDeleteTap you must be using navigator.pop(context). You can check by not showing any dialog to check if you are really poping a screen (If you are having a pop your current screen will close or you will have a black screen) or you can use the debbuger to check all the lines that passes before getting to this code.

flutter: Can I adjust the width or height of the CupertinoAlertDialog(iOS style)?

I'm new in flutter. I want a iOS style app so I use CupertinoAlertDialog. But I want to customize its height and width. Is that possible? Here is what I have done.
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home: Scaffold(
body: RaisedButton(
child: Text("Pick Me !!!"),
onPressed: () {
showDialog(
context: context,
builder: (_) => Center(
child: CupertinoAlertDialog (
title: new Text("drop out"),
content: new Text("quit the window"),
actions: <Widget>[
FlatButton(
child: Text('Cancle!'),
onPressed: () {
Navigator.pop(_);
},
),
FlatButton(
child: Text('OK'),
onPressed: () {
Navigator.pop(_);
},
)
],
),
)
);
},
)
),
);
}

How to display a card on tapping a FAB?

So this is what I got so far. Basically, I want to display the card once the user taps on the FAB. Now, when I tap on the FAB, there's no response.
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
actions: <Widget>[
IconButton(
icon: Icon(Icons.exit_to_app),
onPressed: () => logoutUser().then((value) =>
Navigator.of(context).pushReplacementNamed('/SignIn')),
)
],
title: Text('TODO'),
),
body: Container(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add), onPressed: () => displayCard()),
);
}
Widget displayCard() {
return Center(
child: Card(
color: Colors.blue,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: Icon(Icons.album),
title: Text('The Enchanted Nightingale'),
subtitle: Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
),
],
),
),
);
}
Right now, you're returning a Widget to your onPressed function, which is a VoidCallBack. It won't do anything with the Widget it receives back from displayCard().
Consider using a Dialog popup. Replace your widget displayCard() with something like the following.
void displayCard(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("The Enchanted Nightingale"),
content: Text("Music by Julie Gable. Lyrics by Sidney Stein."),
actions: <Widget>[
FlatButton(
child: Text("Dismiss"),
onPressed: () {
//remove the dialog popup
Navigator.of(context).pop();
}
)
]
);
}
);
}
Then, update your floatActionButton code to pass context as a parameter
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add), onPressed: () => displayCard(context)),

How to pass a variable reference between different routes?

Im writing a simple gamebook game in flutter with menu, game and options route. In option route there is button that on pressed should delete all saved games.
Right at this moment Im loading saved games on application launch from SharedPreferences. Right after loading them I set up boolean _savedGame that im using in 'Continue' button in menu route and 'Delete saved games' button in options route to activate or deactivate them. The whole problem is - i dont know how to change variables in menu route from option route. When im creating option route I give it _savedGame so that it knows if it should render active or deactivated button.
PS. Yes, I know that right now im sending option route a copy of _savedGame variable.
Menu route option page button.
RaisedButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OptionsPage(_savedGame),
),
),
Option page
class OptionsPageState extends State<OptionsPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Options",
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.blueGrey[900],
),
body: Container(
alignment: Alignment.center,
color: Colors.cyan,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child:
Text('Delete saved games', style: TextStyle(fontSize: 40)),
onPressed:
widget.isGameSaved ? () => _showWarning(context) : null,
),
const SizedBox(height: 30),
RaisedButton(
child: Text('Back', style: TextStyle(fontSize: 40)),
onPressed: () {
Navigator.pop(context);
},
),
])),
),
);
}
Future<void> _showWarning(BuildContext context) {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Are you sure you want to delete saved game?'),
actions: <Widget>[
FlatButton(
child: Text('No'),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text('Yes'),
onPressed: () {
saveGame('empty');
Navigator.of(context).pop();
setState(() {
widget.isGameSaved = false;
});
},
),
],
);
},
);
}
}
How do I "setState" for variables in different routes?
What you could do is have an InheritedWidget (call it say GameStateWidget) above your Navigator (or MaterialApp if you're using its navigator). In the InheritedWidget have a ValueNotifier, say savedGame that has the value you want to share.
Then in the route where you need to set the value
GameStateWidget.of(context).savedGame.value = ...
And in the route where you need the value
ValueListenableBuilder(
valueListenable: GameStateWidget.of(context).savedGame,
builder: (context, savedGameValue, child) => ...
)