A Flutter plugin providing double-tap-to-exit functionality for Android with native Toast support and safe iOS handling.
- 🔙 Intercepts back button presses — shows a message on the first press, exits on the second.
- ⏱️ Configurable time window for the double-tap detection.
- 💬 SnackBar message by default — fully customisable style.
- 📱 Optional native Android Toast via a simple boolean flag.
- 🎨 Toast customisation — control duration and screen position.
- 🍎 Safe no-op on iOS — no app rejection risk.
- 🎯 Exit callback for cleanup before the app closes.
- 📦 Zero external dependencies.
Important: For Android 13+ (API 33+) predictive back support, you must add the following attribute to your
<application>tag inandroid/app/src/main/AndroidManifest.xml:
<application
android:enableOnBackInvokedCallback="true"
...>Without this, the OS will close your activity immediately on back press — bypassing Tap2Exit entirely.
This enables the native OnBackInvokedCallback that tap2exit registers to intercept back events at the OS level, before the activity is destroyed.
Add tap2exit to your pubspec.yaml:
dependencies:
tap2exit: ^1.3.0Then run:
flutter pub getWrap your top-level page widget with Tap2Exit:
import 'package:tap2exit/tap2exit.dart';
@override
Widget build(BuildContext context) {
return Tap2Exit(
child: Scaffold(
appBar: AppBar(title: const Text('My App')),
body: const Center(child: Text('Hello!')),
),
);
}| Parameter | Type | Default | Description |
|---|---|---|---|
child |
Widget |
required | The widget to wrap. |
message |
String |
'Press back again to exit' |
Message shown on the first back press. |
duration |
Duration |
Duration(seconds: 2) |
Time window for the second press to trigger exit. |
useToast |
bool |
false |
Use native Android Toast instead of SnackBar. |
toastDuration |
ToastDuration |
ToastDuration.short |
Native toast display length (short ≈ 2 s, long ≈ 3.5 s). |
toastGravity |
ToastGravity? |
null |
Native toast position (bottom, center, top). |
onExit |
VoidCallback? |
null |
Callback invoked just before the app exits. |
onFirstBackPress |
VoidCallback? |
null |
Callback invoked on the first back press. |
onBackFirstPress |
void Function(BuildContext)? |
null |
Replaces Toast/SnackBar entirely — show your own custom Dart UI. |
snackBarStyle |
Tap2ExitSnackBarStyle? |
null |
Customise SnackBar appearance. |
customMessageWidget |
Widget Function(BuildContext, String)? |
null |
Custom overlay widget for the first-press message. |
Tap2Exit(
snackBarStyle: Tap2ExitSnackBarStyle(
backgroundColor: Colors.black87,
textStyle: const TextStyle(color: Colors.white, fontSize: 16),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
margin: const EdgeInsets.all(16),
),
child: MyHomePage(),
)Tap2Exit(
useToast: true,
child: MyHomePage(),
)Tap2Exit(
useToast: true,
toastDuration: ToastDuration.long,
toastGravity: ToastGravity.center,
child: MyHomePage(),
)Use onBackFirstPress to replace the default Toast/SnackBar with any Dart UI:
Tap2Exit(
useToast: true,
onBackFirstPress: (context) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Tap back again to exit')),
);
},
child: MyHomePage(),
)Tap2Exit(
onExit: () async {
await saveUserData();
debugPrint('Goodbye!');
},
child: MyHomePage(),
)| Platform | exitApp |
showToast |
|---|---|---|
| Android | Activity.finishAffinity() |
Native Toast.makeText() |
| iOS | No-op (not allowed by Apple) | No-op (falls back to SnackBar in Flutter) |
On Android 13+ (API 33+), tap2exit registers a native OnBackInvokedCallback to intercept back events at the OS level. PopScope sets canPop: true so it stays out of the way — only the native callback fires.
On pre-13 / non-Android platforms, no native callback is registered. PopScope sets canPop: false and intercepts back presses via onPopInvokedWithResult.
This two-layer strategy prevents the back handler from double-firing on any API level.
A fully working example app is included in the example/ directory. Run it with:
cd example
flutter runMIT — see LICENSE for details.