mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-22 08:22:16 +03:00
Prevent underlying page from stealing focus from ResponsiveDialog.
This commit is contained in:
parent
8337d5090e
commit
fdff8ba1ff
@ -32,27 +32,20 @@ Future<T?> showBlurDialog<T>({
|
||||
required BuildContext context,
|
||||
required Widget Function(BuildContext) builder,
|
||||
RouteSettings? routeSettings,
|
||||
}) async {
|
||||
const transitionDelay = Duration(milliseconds: 150);
|
||||
final result = await showGeneralDialog<T>(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
|
||||
pageBuilder: (ctx, anim1, anim2) => builder(ctx),
|
||||
transitionDuration: transitionDelay,
|
||||
transitionBuilder: (ctx, anim1, anim2, child) => BackdropFilter(
|
||||
filter:
|
||||
ImageFilter.blur(sigmaX: 20 * anim1.value, sigmaY: 20 * anim1.value),
|
||||
child: FadeTransition(
|
||||
opacity: anim1,
|
||||
child: child,
|
||||
}) async =>
|
||||
await showGeneralDialog<T>(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
|
||||
pageBuilder: (ctx, anim1, anim2) => builder(ctx),
|
||||
transitionDuration: const Duration(milliseconds: 150),
|
||||
transitionBuilder: (ctx, anim1, anim2, child) => BackdropFilter(
|
||||
filter: ImageFilter.blur(
|
||||
sigmaX: 20 * anim1.value, sigmaY: 20 * anim1.value),
|
||||
child: FadeTransition(
|
||||
opacity: anim1,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
routeSettings: routeSettings,
|
||||
);
|
||||
// Make sure we wait for the dialog to fade out before returning the result.
|
||||
// This is needed for subsequent dialogs with autofocus.
|
||||
await Future.delayed(transitionDelay);
|
||||
|
||||
return result;
|
||||
}
|
||||
routeSettings: routeSettings,
|
||||
);
|
||||
|
@ -39,56 +39,75 @@ class ResponsiveDialog extends StatefulWidget {
|
||||
|
||||
class _ResponsiveDialogState extends State<ResponsiveDialog> {
|
||||
final Key _childKey = GlobalKey();
|
||||
final _focus = FocusScopeNode();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_focus.dispose();
|
||||
}
|
||||
|
||||
Widget _buildFullscreen(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: widget.title,
|
||||
actions: widget.actions,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: widget.allowCancel
|
||||
? () {
|
||||
widget.onCancel?.call();
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
: null),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child:
|
||||
SafeArea(child: Container(key: _childKey, child: widget.child)),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildDialog(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final cancelText = widget.onCancel == null && widget.actions.isEmpty
|
||||
? l10n.s_close
|
||||
: l10n.s_cancel;
|
||||
return AlertDialog(
|
||||
title: widget.title,
|
||||
titlePadding: const EdgeInsets.only(top: 24, left: 18, right: 18),
|
||||
scrollable: true,
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 8),
|
||||
content: SizedBox(
|
||||
width: 380,
|
||||
child: Container(key: _childKey, child: widget.child),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(cancelText),
|
||||
onPressed: () {
|
||||
widget.onCancel?.call();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
...widget.actions
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) =>
|
||||
LayoutBuilder(builder: ((context, constraints) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
if (constraints.maxWidth < 540) {
|
||||
// Fullscreen
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: widget.title,
|
||||
actions: widget.actions,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: widget.allowCancel
|
||||
? () {
|
||||
widget.onCancel?.call();
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
: null),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: SafeArea(
|
||||
child: Container(key: _childKey, child: widget.child)),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// Dialog
|
||||
final cancelText = widget.onCancel == null && widget.actions.isEmpty
|
||||
? l10n.s_close
|
||||
: l10n.s_cancel;
|
||||
return AlertDialog(
|
||||
title: widget.title,
|
||||
titlePadding: const EdgeInsets.only(top: 24, left: 18, right: 18),
|
||||
scrollable: true,
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 8),
|
||||
content: SizedBox(
|
||||
width: 380,
|
||||
child: Container(key: _childKey, child: widget.child),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(cancelText),
|
||||
onPressed: () {
|
||||
widget.onCancel?.call();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
...widget.actions
|
||||
],
|
||||
);
|
||||
}
|
||||
// This keeps the focus in the dialog, even if the underlying page changes.
|
||||
return FocusScope(
|
||||
node: _focus,
|
||||
autofocus: true,
|
||||
onFocusChange: (focused) {
|
||||
if (!focused) {
|
||||
_focus.requestFocus();
|
||||
}
|
||||
},
|
||||
child: constraints.maxWidth < 540
|
||||
? _buildFullscreen(context)
|
||||
: _buildDialog(context),
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user