amplify-flutter icon indicating copy to clipboard operation
amplify-flutter copied to clipboard

iOS deep links are not working when app comes from a terminated state using the `Authenticator` pre-built widget.

Open ramon-san opened this issue 1 year ago • 4 comments

Description

I just finished setting up my app's deep link structure as per the Flutter documentation and it all works well except for the iOS launch of deep links when using the Authenticator pre-built widget.

The Android version of my code works just fine and even when app is terminated it opens the deep link as expected, the issue only occurs in iOS when the app comes from a terminated state. I attach two videos of what I'm talking about:

https://github.com/user-attachments/assets/c16dca92-a222-4c78-bbd1-09571cae1b49

https://github.com/user-attachments/assets/d6e2838b-da37-4956-a9e3-eea389bfadc8

The first video uses the Authenticator pre-built widget as outlined in the code that appears next to the simulator, here you can see that deep link doesn't work when app comes from terminated state, when app is already launched it works just fine. The second video uses the bare-bone MaterialApp widget as seen in the code next to the simulator and in this case the app coming from a terminated state properly handles the deep link.

In the video that doesn't use Authenticator the first screen is different (no stores are displayed) becuase my API requires an authenticated user, not because anything else changed in the code.

In the first video it is clearly seen that the terminated app doesn't handle deep links correctly, the first time you open this it just stays in the home page (the "/" route) without navigating to the complete path. In the second video it is clear that the deep link works as intended, taking the user to the complete path (the "/help" route).

I was wondering if this is a known issue or if there is some clear problem with my implementation. The complete structure of my main.dart file is the following:

// Dart libraries
import 'dart:convert';
// External libraries
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:amplify_api/amplify_api.dart';
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_analytics_pinpoint/amplify_analytics_pinpoint.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:go_router/go_router.dart';
import 'package:amplify_authenticator/amplify_authenticator.dart';
// Utilities
import 'package:pey/utils/rest_api_manager.dart';
import 'package:pey/routes.dart';
// Components
import 'package:pey/components/main/splash_screen.dart';
import 'package:pey/components/main/initialization_error.dart';
// Models
import 'package:pey/models/cart.dart';
import 'package:pey/models/store.dart';
// Configs
import 'package:pey/configs/amplify.dart';

Future<void> configureAmplify() async {
  try {
    if (Amplify.isConfigured) {
      safePrint("Amplify is already configured 🎉");
      return;
    }

    await Amplify.addPlugins([
      AmplifyAnalyticsPinpoint(),
      AmplifyAuthCognito(),
      AmplifyAPI(),
    ]);

    await Amplify.configure(jsonEncode(amplifyConfig));
    safePrint("Successfully configured Amplify 🎉");
  } on Exception catch (e) {
    safePrint('An error occurred configuring Amplify: $e');
    rethrow;
  }
}

Future<void> configureStripe() async {
  String merchantId = "merchant.com.pey";
  try {
    final publishableKey = await RestApi.fetchStripePublishableKey();
    Stripe.publishableKey = publishableKey;
    Stripe.merchantIdentifier = merchantId;
    await Stripe.instance.applySettings();
    safePrint("Successfully configured Stripe 🎉");
  } catch (e) {
    safePrint('An error occurred configuring Stripe: $e');
    rethrow;
  }
}

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const InitializationWrapper());
}

class InitializationWrapper extends StatefulWidget {
  const InitializationWrapper({super.key});

  @override
  State<InitializationWrapper> createState() => _InitializationWrapperState();
}

class _InitializationWrapperState extends State<InitializationWrapper> {
  late Future<void> _initializationFuture;
  final GoRouter _router = router;

  @override
  void initState() {
    super.initState();
    _initializationFuture = _initialize();
  }

  Future<void> _initialize() async {
    await configureAmplify();
    await configureStripe();
  }

  void _retryInitialization() {
    setState(() {
      _initializationFuture = _initialize();
    });
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _initializationFuture,
      builder: (context, snapshot) {
        if (snapshot.hasError) {
          return MaterialApp(
            home: InitializationError(
              error: snapshot.error?.toString(),
              onRetry: _retryInitialization,
            ),
          );
        }

        if (snapshot.connectionState == ConnectionState.done) {
          return MultiProvider(
            providers: [
              ChangeNotifierProvider(create: (context) => CartModel()),
              ChangeNotifierProvider(create: (context) => StoreModel())
            ],
            child: Authenticator(
              child: MaterialApp.router(
                routerConfig: _router,
                theme: ThemeData(
                  colorScheme: ColorScheme.fromSeed(
                    seedColor: const Color(0xFF0067F1),
                    primary: const Color(0xFF0067F1),
                  ),
                  useMaterial3: true,
                ),
                builder: Authenticator.builder(),
              ),
            ),
          );
        }

        return const MaterialApp(
          home: SplashScreen(),
        );
      },
    );
  }
}

I was trying to narrow down the causes of failure given this GitHub issue which stated that conditional statements when launching the MaterialApp caused issues; this turned out to be true and it seems the culprit right now is Authenticator. I first want to fix the Authenticator which clearly causes the issue and then see if my other conditions (SplashScreen and InitializationError) also affect the deep link behavior. In the video examples I showed above the Widget build(BuildContext context) { ... } used the simplified versions of the code that appear in the videos.

This issue in the Flutter repo goes deep into the functionality of deep links in iOS when app comes from a terminated state.

Categories

  • [ ] Analytics
  • [ ] API (REST)
  • [ ] API (GraphQL)
  • [X] Auth
  • [X] Authenticator
  • [ ] DataStore
  • [ ] Notifications (Push)
  • [ ] Storage

Steps to Reproduce

Already explained in the description, try to configure flutter deep links with an app that uses Amplify's pre-built Authenticator widget.

Screenshots

No response

Platforms

  • [X] iOS
  • [ ] Android
  • [ ] Web
  • [ ] macOS
  • [ ] Windows
  • [ ] Linux

Flutter Version

3.24.3

Amplify Flutter Version

^2.0.0

Deployment Method

AWS CDK

Schema

No response

ramon-san avatar Nov 27 '24 19:11 ramon-san

Hi @ramon-san, thank you for taking the time to raise this issue as well as the extraordinary detail in describing the problem. We will investigate this issue and get back to you when we have an update.

ekjotmultani avatar Nov 27 '24 22:11 ekjotmultani

Hi @ramon-san, I have been able to reproduce this issue. It will be tracked as a bug and I will report back to this issue when we have a fix.

ekjotmultani avatar Dec 04 '24 00:12 ekjotmultani

Hello, was wondering if there is any ETA on this bug fix. Accessing specific areas of the app through a QR code is core functionality of what I'm building so a fix to this bug would be huge. If the time to fix this will be very long (more than a month) please let me know so that I start building a personalized Auth solution that doesn't cause this issue.

ramon-san avatar Dec 11 '24 18:12 ramon-san

Hi @ramon-san, we currently do not have a ETA on the fix for this. We will post any updates we have here as we can.

Equartey avatar Dec 12 '24 16:12 Equartey