How to get Flutter AlertDialog actions to watch for keyboard event (enter key)? - flutter

Is there a way to get the "enter" key keyboard event in flutter to default to one of the action buttons on my AlertDialog? Note I'm referring to use of flutter for web where there is a physical keyboard. For example I have this dialog:
Future<void> _confirmDelete(int index) async {
var fp = SessionInfo.of(context).myFloorplans.entries.toList()[index].value;
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Confirm Delete?'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0))),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text("Please confirm deleting ${fp.name}"),
],
),
),
actions: <Widget>[
FlatButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text('Delete'),
onPressed: () {
_deleteFloorplan(index);
Navigator.of(context).pop();
},
),
],
);
},
);
}
Is there a way to default the "enter" key on the keyboard to act like they hit delete? Maybe not the safest UI experience yet, but mostly just asking to understand the foundation here.
Thanks,
Justin

The only thing you need to do is wrap your AlertDialog in a RawKeyboardListener.
check this example :
RawKeyboardListener(
// its better to initialize and dispose of the focus node only for this alert dialog
focusNode: FocusNode(),
autofocus: true,
onKey: (v) {
if (v.logicalKey == LogicalKeyboardKey.enter) {
_deleteFloorplan(index);
Navigator.pop(context);
}
},
child: your alert dialog ...

I don't see anything that will cause the keyboard to show in this dialog.
Anyway if you have a TextFormField that will show the keyboard you can do something like this
onFieldSubmitted: (value) {
_deleteFloorplan(index);
Navigator.of(context).pop();
}
onFieldSubmitted gets called whenever the user hits the text Input action button (the "enter" key you mentioned)

Related

How to change state to previous state in bloc?

I have sigIn function that get data from Api and move to another screen if request is successful and if request is not successful then show alertDialog
I change state and before fetching data I show CircularProgressIndicator to make user know that data is fetching.
But when alertDialog window pops up and I close it then CircularProgressIndicator doesn't disappear. How to remove WaitingSignInAuth and show me Scaffold with inputs
When error comes then I emit ErrorSignInAuth but why there is WaitingSignInAuth to.. Why ErrorSignInAuth doesn't replace WaitingSignInAuth or it should work differently?
This is the code:
#override
Widget build(BuildContext context) {
return BlocConsumer<AuthCubit, AuthState>(
listener: (context, state) {
if (state is WaitingSignInAuth) {
showDialog(
context: context,
builder: (context) => Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.black.withOpacity(0.6),
child: const Center(
child: CircularProgressIndicator(
strokeWidth: 1,
color: Colors.black,
backgroundColor: Colors.white,
),
),
));
}
if (state is ErrorSignInAuth) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Bad request"),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text("Error")
],
),
),
actions: <Widget>[
TextButton(
child: const Text("Close"),
onPressed: () {
Navigator.pop(context); // If i press it then alertDialog diappears but not the loading circle
},
)
],
);
}
);
}
},
);
}
This is how it looks like:
The issue is, that you showDialog() twice. First is shown when WaitingSignInAuth state is fired, and the second one when you receive ErrorSignInAuth.
So Navigator.pop(context); in the "error" alert dialog closes only the showDialog that you've triggered in if (state is ErrorSignInAuth), the second dialog, which contains progress indicator is still visible.
To easy fix it, you can change your code to:
TextButton(
child: const Text("Close"),
onPressed: () {
Navigator.pop(context);
Navigator.pop(context);
},
)
But in my opinion thats not the best fix to your issue. IMO you don't need to show ProgressIndicator for WaitingSignInAuth in the dialog. Adjust your UI in the way, that the container with progress indicator will be displayed over it, but this doesn't need to be a dialog.

GetX not showing the updated value

I am trying a simple pop-up that should increment everytime I press the 'Use' button. Using the print command, it is showing the correct current value everytime I press. But for the Text itself, it is just staying a constant value. Any suggestion to fix this? Thanks!
showUsePopup() async {
AwesomeDialog(
context: globalScaffoldKey.currentContext!,
dialogType: DialogType.NO_HEADER,
dismissOnTouchOutside: false,
headerAnimationLoop: false,
animType: AnimType.SCALE,
body: Column(
children: [
Row(
children: [
Text('${controller.numOfItemObs.value}'),
ElevatedButton(
onPressed: () {
controller.numOfItemObs.value++;
print('Current value: ${controller.numOfItemObs.value}');
},
child: const Text('Use'),
),
],
),
],
),
showCloseIcon: false,
btnOkOnPress: () async {},
).show();
}
You need to wrap up the Text widget with Obx. It will update the Text value whenever the changes happen. Here is the example that you can follow.
Obx(
() => Text('${controller.numOfItemObs.value}')')
),

Is it possible to make alert dialog dissapear after page navigation?

The "OK" button on my alert dialog navigates to a new page. But when I go back to the previous page, the alert is still there. Is there any way for the alert to disappear after I navigate to the new page?
// Alert Dialog
Future<void> _handlePhoto(BuildContext context) {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: Text('Please Position Crosshair'),
content: const Text(
'Before detecting cancer, ensure your focus area is centered.'),
actions: <Widget>[
FlatButton(
child: Text('OK'),
onPressed: () {
Navigator.of(context).pushNamed(
'/camerapage',
arguments: cameras,
);
},
),
],
);
},
);
}
Before pushing the new page, you need to pop the dialog like this. So when you come back it is not there.
onPressed: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed(
'/camerapage',
arguments: cameras,
);
}

How to dismiss a dialog and a bottom sheet consecutively?

I have a function that displays a bottom sheet. If one item of this bottom sheet is selected it is dismissed using Navigator.of(context).pop() and a dialog is shown. If the user taps on the "Cancel" button I want to dismiss the dialog, too, so I tried using Navigator.of(context).pop() again. But nothing happens and I get the error message Looking up a deactivated widget's ancestor is unsafe. I tried deleting the first one and now the dialog can be dismissed by pressing "Cancel" button but the bottom sheet is still there. Ideally was a way to make the app in the current form work but dismissing both at the same time would also be fine.
I would really appreciate any help.
Edit: Added code
List<Widget> exerciseSelectionItems(BuildContext context, String schoolClassId) {
List<Widget> items = new List<Widget>();
for (var i = 0; i < selectableExercises.length; i++) {
Exercise currentExercise = selectableExercises[i];
items.add(
ListTile(
leading: Icon(currentExercise.icon),
title: Text(currentExercise.name),
onTap: () {
Navigator.of(context).pop();
showDialog(
context: context,
child: AlertDialog(
title: Text(
AppLocalizations.of(context).translate('create_homework'),
),
content: Text(
AppLocalizations.of(context)
.translate('create_homework_message'),
),
actions: <Widget>[
FlatButton(
child: Text('OK'),
onPressed: () {
Database().addHomework(
schoolClassId,
Homework(i, DateTime.now().millisecondsSinceEpoch),
);
Navigator.of(context).pop();
},
),
FlatButton(
child: Text(
AppLocalizations.of(context).translate('cancel'),
),
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
);
},
),
);
}
return items;}

Using a textfield as a button

I'm trying to use a textfield as a button so when someone taps it, it takes him to a different page.
I've tried disabling the textfield but then I cannot use the onTap property. But if the textfield is not disabled, when I click it, it gets editing focus and this is something I don't want.
Can anyone help what's the easiest way this could be done?
Thanks
TextField(
readOnly: true,
onTap: () {
// Do something
},
);
You can use a Flatbutton instead. Just put this in your code:
FlatButton(
onPressed: () {
/*...*/
},
child: Text(
"Clickable text!",
),
)
Or else use a GestureDetector:
GestureDetector(
OnTap: () {...},
child: TextField(
// Text...
),
);
you can wrap it with an InkWell, see below:
InkWell(
onTap: () {
},
child: Container(
padding: EdgeInsets.all(4.0),
child: Text('Click me'),
),
);
you can try the following.
GestureDetector(
onTap: () {
// redirect // nav to different screen
},
child: TextField(
enabled: false
),
)
References
TextField
GestureDetector