Why sign-up emails are awkward to test in Flutter
- Two platforms, two link systems. A verify link opens the app through Universal Links on iOS and App Links on Android, and each has its own setup that can be wrong on its own.
- Tests can’t read email. There’s no inbox in the simulator or emulator worth using, and widget tests block the network anyway.
- Two things may be listening for links. Flutter’s own deep linking and a plugin such as
app_linkscan both claim the same link. - Single-use links break if anything opens them first. Proxy Mail QA never opens or prefetches a link.
Test it by hand
- Create an address in Proxy Mail QA, with a persona and a generated password if your form needs them.
- Sign up in the app on the iOS simulator or Android emulator. Both share your computer’s clipboard.
- Get the code from the address’s row with Copy.
- Or open the link in the app:
xcrun simctl openurl booted '<link>'on iOS, oradb shell "am start -W -a android.intent.action.VIEW -d '<link>'"on Android. For amyapp://link the inbox shows both commands next to it.
Automate it with integration_test
Add the Dart helper from the Flutter guide to integration_test/. It only uses dart:io and dart:convert, so the one dev dependency is Flutter’s own integration_test:
final qa = ProxyMailQA();
final account = await qa.createAddress();
app.main();
await tester.pumpAndSettle();
final submitted = DateTime.now();
await tester.enterText(find.byKey(const Key('email')), account.address);
await tester.enterText(find.byKey(const Key('password')), account.password!);
await tester.tap(find.text('Sign up'));
await tester.pumpAndSettle();
final code = await qa.waitForCode(account.address, submitted);
await tester.enterText(find.byKey(const Key('code')), code);
Run it with flutter test integration_test --dart-define=PROXYMAIL_QA_API_KEY=$PROXYMAIL_QA_API_KEY. For links, qa.waitForLink(account.address, submitted) returns the link; add app: true for the myapp:// one when the email has both.
The same helper checks the email itself (checkEmail with your app’s link domain, and countEmails for duplicates), signs in as a saved test account, and adds each test to the pull request’s report: see the Flutter guide.
What to check in your Flutter code
- The email field leaves the address alone.
keyboardType: TextInputType.emailAddress,autocorrect: falseandautofillHints: [AutofillHints.email]. - The code field offers AutoFill.
autofillHints: [AutofillHints.oneTimeCode]. - Only one thing handles links. Use Flutter’s built-in deep linking with go_router, or a plugin such as
app_links, not both. If you use a plugin, turn Flutter’s off:FlutterDeepLinkingEnabledset toNOinInfo.plist, andflutter_deeplinking_enabledset tofalseinAndroidManifest.xml. - Cold-start links work on both platforms. Close the app, open the link, and check it lands on the right screen on iOS and on Android.
- Supabase redirects open the app. Pass
emailRedirectTo: 'com.example.myapp://login-callback/'(your own scheme), add it to Redirect URLs in Supabase, and register the scheme on both platforms. - Links don’t point at your computer. The Android emulator reaches your computer at
10.0.2.2, notlocalhost. See links that point at localhost.
Using Firebase email-link sign-in with firebase_auth? It needs Associated Domains and App Links for your Hosting domain: see Firebase email-link sign-in on mobile.