PopScope
in Flutter is a widget that allows you to control the behavior of the system back button (Android) or the back swipe gesture (iOS). It’s useful when you want to override or prevent navigation pops under certain conditions.
Usages of PopScope
-
Prevent Accidental Exits:
- Show a confirmation dialog before exiting an app.
- Example: Asking the user if they really want to exit the app when pressing back.
-
Handle Unsaved Changes:
- Prevent navigation when a form has unsaved changes.
- Example: Alert the user before discarding unsaved input.
-
Custom Navigation Logic:
- Execute custom logic before popping.
- Example: Closing an in-app tutorial modal instead of navigating away.
-
Blocking Back Navigation Temporarily:
- Prevent exiting a specific screen under conditions.
- Example: A loading screen where back navigation should be disabled.
-
Multi-Step Navigation Handling:
- Customize how users navigate between steps in a form or tutorial.
- Example: Instead of exiting, move to the previous step.
Edge Cases
-
Back Button on Android vs. Swipe Back on iOS:
PopScope
handles both but needs testing for different gestures across platforms.
-
Nested
PopScope
Widgets:- If multiple
PopScope
widgets exist, they may interfere with each other’s behavior.
- If multiple
-
State Restoration & Back Navigation:
- Preventing pop could conflict with state restoration, especially in deep navigation stacks.
-
Overriding Default Behavior Unintentionally:
- If not properly managed, users might get stuck on a screen without an exit option.
Advantages
✅ Fine-grained Control Over Navigation – Can override default back behavior dynamically.
✅ Prevents Unintended Exits – Useful for confirmation dialogs or handling unsaved data.
✅ Custom Logic Execution Before Popping – Allows executing functions before the screen pops.
✅ Cross-Platform Handling – Works across Android & iOS without extra configuration.
Disadvantages
❌ Might Confuse Users if Overused – Blocking back navigation without clear feedback can be frustrating.
❌ Can Conflict with Navigation Stacks – If not handled properly, may lead to unexpected behavior in nested navigation.
❌ Increased Complexity – Requires additional logic to manage conditions for allowing or preventing pops.
Example: Preventing Accidental Exits with a Confirmation Dialog
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: ConfirmExitScreen(),
);
}
}
class ConfirmExitScreen extends StatefulWidget {
@override
_ConfirmExitScreenState createState() => _ConfirmExitScreenState();
}
class _ConfirmExitScreenState extends State<ConfirmExitScreen> {
@override
Widget build(BuildContext context) {
return PopScope(
canPop: false, // Prevents immediate back navigation
onPopInvoked: (didPop) async {
if (didPop) return; // If already popped, do nothing
bool? shouldExit = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text("Exit App?"),
content: Text("Are you sure you want to exit?"),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text("No"),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text("Yes"),
),
],
),
);
if (shouldExit == true) {
if (mounted) Navigator.of(context).pop(); // Manually pop if confirmed
}
},
child: Scaffold(
appBar: AppBar(title: Text("PopScope Example")),
body: Center(child: Text("Press back to see the confirmation dialog.")),
),
);
}
}
How It Works
canPop: false
ensures that pressing the back button does not immediately pop the screen.onPopInvoked
intercepts the back action.- A confirmation dialog appears asking if the user really wants to exit.
- If the user selects “Yes,” the screen pops. If “No,” the app remains open.
Other Variations
-
Prevent Exiting During a Task
If a process is running, you can block back navigation:PopScope( canPop: !isLoading, // Allow pop only if not loading child: Scaffold(body: Center(child: CircularProgressIndicator())), );
-
Saving Unsaved Changes Before Exit
PopScope( canPop: hasSavedChanges, // Only allow pop if changes are saved onPopInvoked: (didPop) async { if (!hasSavedChanges) { saveChanges(); // Auto-save before exiting } }, child: Scaffold(body: Text("Edit your settings here")), );
For more Flutter stuff – check out https://programtom.com/dev/category/software-development/flutter/