0

I am trying to create a streaming app. I am integrating Firebase Dynamic links, so hosts can share a link to join their session. But I am kinda lost on how to implement it, and I would like to know your insights about this.

So let me start with the structure, this is the main pages:

  • MainApp
    • Authenticate
      • LoginPage
      • HomePage
        • Profile
        • JoinPage

This is how I setup my app, they are all wrap to Authenticate widget, this widget determines if user should be redirected to login or the home page. Pretty simple eh. :)

Then here comes the Dynamic links. My dynamic links has a query params on it to determine which session to join. It looks like this:

https://my.app.link/join-session?sessionId=\<some random keys>

And this is what I want to handle it.

If (user is not logged in ) {
    // save the sessionId to a singleton (or somewhere that persists along the app lifecycle, I use Provider here)
    // redirects user to login page
    // user logged in
    // upon going to home page, will retrieve the saved sessionId and redirect user to the session using the sessionId
} else {
    // retrieve sessionId
    // redirect user to the session using the sessionId
}

This is how my code looks:

MainApp.dart logic

....
    if (state == PageLoadState.DONE) {
      return MultiProvider(
        providers: [
          Provider<SampleAppAuthProvider>(
            create: (_) => SampleAppAuthProvider(FirebaseAuth.instance),
          ),
          Provider<SampleAppConfigProvider>(
            create: (_) => SampleAppConfigProvider(sharedPrefs)
          ),
          StreamProvider(
            initialData: null,
            create: (context) => context.read<SampleAppAuthProvider>().authState,
          ),
          Provider<DynamicLinkService>(create: (_) => DynamicLinkService(instance: FirebaseDynamicLinks.instance)),
          ChangeNotifierProvider(create: (_) => UsersApi(repo: Repository('users')), lazy: true),
          ChangeNotifierProvider(create: (_) => SessionsApi(repo: Repository('sessions')), lazy: true),
          ChangeNotifierProvider(create: (_) => MessagesApi(repo: Repository('messages')), lazy: true),
        ],
        child: MaterialApp(
          title: 'SampleApp!',
          theme: defaultTheme(),
          localizationsDelegates: context.localizationDelegates,
          supportedLocales: context.supportedLocales,
          locale: context.locale,
          onGenerateRoute: router.generateRoute,
          home: Authenticate(),
        ),
      );
    }

......

And this is my Authenticate Page:

class Authenticate extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    
    context.read<DynamicLinkService>().handleDynamicLinks();
    final firebaseUser = context.read<User>();
    
    if (firebaseUser != null) {
      return HomePage();
    }
    return LoginPage();
  }
}

I have also create a separate service for handling the dynamic links:

DynamicLinkService.dart

class DynamicLinkService {
    DynamicLinkService({@required this.instance});

    final FirebaseDynamicLinks instance;
    String _sessionIdToJoin = '';

    get sessionIdToJoin => _sessionIdToJoin;
    
    void clearSessionId () => _sessionIdToJoin = '';

    Future handleDynamicLinks() async {
    final PendingDynamicLinkData data =
        await instance.getInitialLink();

    _handleDeepLink(data);


    instance.onLink(
        onSuccess: (PendingDynamicLinkData dynamicLink) async {

      _handleDeepLink(dynamicLink);
    }, onError: (OnLinkErrorException e) async {
      print('Link Failed: ${e.message}');
    });
  }

  void _handleDeepLink(PendingDynamicLinkData data) {
    final Uri deepLink = data?.link;
    if (deepLink != null) {
      print('_handleDeepLink | deeplink: $deepLink');

      bool isJoiningSession = deepLink.pathSegments.contains('session');
      if(isJoiningSession) {
        String sessionId = deepLink.queryParameters['sessionId'];

        if (sessionId != null) {
          _sessionIdToJoin = sessionId;
        }
      }
    }
  }
}

I also have another UtilService that generates dynamic links:

Utils.dart

class Utils {
....
static Future<String> generateLink(String sessionId) async {
    final DynamicLinkParameters parameters = DynamicLinkParameters(
        uriPrefix: AppConfig.dynamicLinkBaseUrl,
        link: Uri.parse('${AppConfig.dynamicLinkBaseUrl}/join-session?sessionId=$sessionId'),
        dynamicLinkParametersOptions: DynamicLinkParametersOptions(
            shortDynamicLinkPathLength: ShortDynamicLinkPathLength.unguessable
        ),
        androidParameters: AndroidParameters(
          packageName: 'com.sample.app',
          minimumVersion: 0,
        ),
      );

      final Uri dynamicUrl = await parameters.buildUrl();
      final ShortDynamicLink shortenedLink =
          await DynamicLinkParameters.shortenUrl(
        dynamicUrl,
        DynamicLinkParametersOptions(
            shortDynamicLinkPathLength: ShortDynamicLinkPathLength.unguessable),
      );
      final Uri shortUrl = shortenedLink.shortUrl;
      return AppConfig.dynamicLinkBaseUrl + shortUrl.path;
  }
...
}

On my home page I have this method that will should be called whenever user lands in the homepage:

HomePage.dart:

class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
....

_navigateToDestination() {
    DynamicLinkService _service = context.select((DynamicLinkService service) => service);
    String sessionToJoin = _service.sessionIdToJoin;
    User user = context.read<User>();
    if(sessionToJoin != '') {
      _service.clearSessionId();
      Navigator.pushNamed(context, 
        AppRoutes.joinStream, 
        arguments: StreamLiveArguments(
          channelName: sessionToJoin,
          user: AppUtil.getName(user),
          role: ClientRole.Audience
        )
      );
    }
  }

  @override
  initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    _navigateToDestination();
    });
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(final AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      _navigateToDestination();
    }
  }
....
}

The issue on this implementation is that sometimes it works and sometimes it does not work and I am not sure why. :(

Should I just use a simple class instead of a provider? If yes, how can I persists the sessionId?

Other Scenarios that I want to handle is when user is in different page like the Profile Page. At this point, user can minimize the app and can click the dynamic link from other sites or from messenger. When the app resumes, it will open in the ProfilePage where it doesn't have handling for dynamic links (only HomePage and LoginPage has handling on it). How can I remedy this? Should I add handling of dynamic links to all my pages? Or perhaps can I create a dynamic link that can redirect to a certain page in my app? If yes, how can I do that?

I am kinda new in implementing the dynamic links (as well as Providers) and would like your opinion on this issue.Thanks in advance and I hope someone can help me or give me some insights on best practices on how to do this.

4

0 回答 0