Flutter
Dialog
UI Development
Mobile App
User Interaction

Prevent dialog from closing on outside touch in Flutter

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Some dialogs should close on outside tap, but critical dialogs should not dismiss until the user chooses an explicit action. Flutter supports this behavior directly, but production-ready dialog flows also need back-button handling and async safety. A robust implementation prevents accidental dismissals and duplicate actions.

Disable Outside-Tap Dismissal

Set barrierDismissible to false in showDialog.

dart
1import 'package:flutter/material.dart';
2
3Future<bool?> showDeleteDialog(BuildContext context) {
4  return showDialog<bool>(
5    context: context,
6    barrierDismissible: false,
7    builder: (dialogContext) {
8      return AlertDialog(
9        title: const Text('Delete project'),
10        content: const Text('This action cannot be undone.'),
11        actions: [
12          TextButton(
13            onPressed: () => Navigator.of(dialogContext).pop(false),
14            child: const Text('Cancel'),
15          ),
16          ElevatedButton(
17            onPressed: () => Navigator.of(dialogContext).pop(true),
18            child: const Text('Delete'),
19          ),
20        ],
21      );
22    },
23  );
24}

Use dialogContext from the builder when popping the dialog route.

Handle System Back Navigation

Outside-touch dismissal and back-button dismissal are separate. If back should also be blocked, wrap dialog content in PopScope.

dart
1Future<void> showMandatoryDialog(BuildContext context) {
2  return showDialog<void>(
3    context: context,
4    barrierDismissible: false,
5    builder: (dialogContext) {
6      return PopScope(
7        canPop: false,
8        child: AlertDialog(
9          title: const Text('Finish setup'),
10          content: const Text('Complete this step before leaving.'),
11          actions: [
12            ElevatedButton(
13              onPressed: () => Navigator.of(dialogContext).pop(),
14              child: const Text('Done'),
15            ),
16          ],
17        ),
18      );
19    },
20  );
21}

Use strict back blocking only for truly mandatory flows.

Protect Async Confirm Actions

Dialogs often trigger network requests. Without guards, users can tap confirm repeatedly.

dart
1import 'package:flutter/material.dart';
2
3class ConfirmDialog extends StatefulWidget {
4  const ConfirmDialog({super.key});
5
6  
7  State<ConfirmDialog> createState() => _ConfirmDialogState();
8}
9
10class _ConfirmDialogState extends State<ConfirmDialog> {
11  bool _loading = false;
12
13  Future<void> _confirm() async {
14    if (_loading) return;
15    setState(() => _loading = true);
16
17    try {
18      await Future<void>.delayed(const Duration(seconds: 2));
19      if (!mounted) return;
20      Navigator.of(context).pop(true);
21    } catch (_) {
22      if (!mounted) return;
23      setState(() => _loading = false);
24    }
25  }
26
27  
28  Widget build(BuildContext context) {
29    return AlertDialog(
30      title: const Text('Confirm payment'),
31      content: const Text('Proceed now?'),
32      actions: [
33        TextButton(
34          onPressed: _loading ? null : () => Navigator.of(context).pop(false),
35          child: const Text('Cancel'),
36        ),
37        ElevatedButton(
38          onPressed: _loading ? null : _confirm,
39          child: _loading
40              ? const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2))
41              : const Text('Confirm'),
42        ),
43      ],
44    );
45  }
46}

Disable buttons during submission to prevent duplicate API calls.

UX Recommendations

Non-dismissible dialogs should still provide clear escape paths unless workflow is legally or technically mandatory.

Good practices:

  • clear title and consequence message
  • explicit cancel and confirm actions
  • loading state for async operations
  • accessible focus order for keyboard and screen reader users

This keeps strict dialog behavior understandable instead of frustrating.

Test Dialog Behavior

Widget tests should verify outside taps and back behavior.

dart
1import 'package:flutter/material.dart';
2import 'package:flutter_test/flutter_test.dart';
3
4void main() {
5  testWidgets('outside tap does not dismiss dialog', (tester) async {
6    await tester.pumpWidget(const MaterialApp(home: Scaffold(body: SizedBox.expand())));
7
8    final context = tester.element(find.byType(Scaffold));
9    // ignore: discarded_futures
10    showDeleteDialog(context);
11    await tester.pumpAndSettle();
12
13    await tester.tapAt(const Offset(4, 4));
14    await tester.pumpAndSettle();
15
16    expect(find.byType(AlertDialog), findsOneWidget);
17  });
18}

Automated checks protect behavior through framework upgrades.

Nested Navigator Considerations

If your app uses nested navigators, pass the correct context to showDialog and Navigator.pop so only the dialog route closes. Mismatched navigator contexts can dismiss unexpected routes.

Common Pitfalls

  • Setting barrierDismissible correctly but forgetting back-button behavior.
  • Using wrong context for Navigator.pop and closing parent route.
  • Allowing repeated confirm taps during async requests.
  • Creating mandatory dialogs without clear user actions.
  • Skipping widget tests for dismissal rules.

Summary

  • Use barrierDismissible: false to block outside-tap dismissal.
  • Decide back-button behavior separately with PopScope.
  • Guard async confirm flows with loading states.
  • Keep strict dialogs clear and accessible.
  • Add widget tests to ensure dismissal behavior stays correct.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.