Sometimes Provider doesn't trigger after anonymous sign-up in flutter

Viewed 59

Hello fellow flutter developers,

I have a bug that's been making my life pretty complicated while developing my own app using Flutter. It goes like this:

  1. User opens app
  2. if they're not signed in, they're redirected to USP page.
  3. If they click next, they're redirected to the sign up page.
  4. Sign-up is provided by Firebase and it's anonymous
  5. If sign-up is successful, a Provider should be triggered and a new page is loaded

The bug is that sometimes the user is sent back to the USP page (meaning their user_id is null) despite no exception between thrown during sign-up. If I force the navigation to the signed-in page, then the user doesn't have an user_id and that's an issue for me.

Any one experienced and fixed the same issue? Below you can see how I built my code, maybe this can help?

This is my main file:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await SignIn.initializeFirebase();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

  getSignInUser() {
    return SignIn().user;
  }

  Widget getMaterialApp() {
    return MaterialApp(
      title: 'app_title',
      home: HomePagePicker(),
      onGenerateRoute: RouteGenerator.generateRoute,
      debugShowCheckedModeBanner: false,
    );
  }

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        StreamProvider<MyUser?>.value(value: SignIn().user, initialData: null),
      ],
      child: getMaterialApp(),
    );
  }

}


class HomePagePicker extends StatefulWidget {

  @override
  _HomePagePickerState createState() => _HomePagePickerState();
}

class _HomePagePickerState extends State<HomePagePicker> {

  @override
  Widget build(BuildContext context) {
    MyUser? myUser = Provider.of<MyUser?>(context);

    if (myUser == null) return IntroScreen(); // this shows the USPs
    else  {
     // this takes you to the signed-in part of the app
      return AnotherScreen();
    }
  }
}

The IntroScreen is a very simple screen with a few USPs and a button to open the registration page. It goes something like

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'login.dart';

class IntroScreen extends StatelessWidget {
  static const routeName = '/introScreen';

  @override
  Widget build(BuildContext context) {
    analytics.setScreenName("introScreen");
    return Scaffold(
        body: AnnotatedRegion<SystemUiOverlayStyle>(
            value: SystemUiOverlayStyle(statusBarColor: ThemeConfig.darkPrimary),
            child: Column(...), // show the USPs
        floatingActionButton: getFloatingButton(context)
    );

  }

  Widget getFloatingButton(BuildContext buildContext) {
    return FloatingActionButton(
      backgroundColor: ThemeConfig.primary,
      foregroundColor: Colors.white,
      child: Icon(Icons.arrow_forward),
      onPressed: () {
        navigateToScreen(MyLogin.routeName, buildContext, null);
      },
    );
  }

  // this is in another file normally but putting it here for completeness
  navigateToScreen(String routeName, BuildContext context, Object? arguments) {
    Navigator.pushNamed(
        context,
        routeName,
        arguments: arguments
    );

}

The important bit in the registration page is this

Future<void> finalizeRegistration(String userName, String userToken) async {
    await usersCollection.add({'userName': userName, "userToken": userToken});
  }

Future<void> registerUser(String userName) {
    return SignIn()
        .anonymousSignIn(userName)
        .timeout(Duration(seconds: 2))
        .then((userToken) {
          finalizeRegistration(userName, userToken)
              .then((value) => Navigator.pop(context));
        })
        .catchError((error) {
          registrationErrorDialog();
        });
  }

The SignIn class is the following

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:house_party/models/MyUser.dart';

class SignIn {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  static Future<FirebaseApp> initializeFirebase() async {
    FirebaseApp firebaseApp = await Firebase.initializeApp();
    return firebaseApp;
  }

  Stream<MyUser?> get user {
    return _auth
        .authStateChanges()
        .asyncMap(getUser);
  }

  Future<String> anonymousSignIn(String userName) async {
    var authResult = await _auth.signInAnonymously();
    return authResult.user!.uid;
  }


  Future<MyUser?> getUser(User? user) async {
    if (user == null) {
      return Future.value(null);
    }
    return FirebaseFirestore.instance
        .collection('users')
        .where('userToken', isEqualTo: user.uid)
        .get()
        .then((res) {
          if (res.docs.isNotEmpty) {
            return MyUser.fromFireStore(res.docs.first.data());
          } else {
            return null;
          }
    });
  }

}

Finally, I'm using these versions of firebase

  firebase_core: ^1.0.0
  cloud_firestore: ^1.0.0
  firebase_dynamic_links: ^2.0.0
  firebase_auth: 1.1.2
  firebase_analytics: ^8.1.1

I hope the problem statement is clear enough!

Thanks in advance!

1 Answers

I fixed this problem (or at least I'm not able to reproduce it anymore) by upgrading the firebase libraries as follows

  firebase_core: ^1.4.0
  cloud_firestore: ^2.4.0
  firebase_dynamic_links: ^2.0.7
  firebase_auth: 3.0.1
  firebase_analytics: ^8.2.0
Related