Mateusz Pastewski
Mateusz Pastewski

Reputation: 31

Provider cannot handle Firebase authStateChanges()

I am struggeling for the long time with handling correctly reauthentication of user in conjunction with storing the data in Provider.

During the first execution of the app on the device, the user is unauthenticated. Then user can register/login and re-build of the class below occure. Unfortunately, even throu the re-build occur, also when the document in Firestore changes, the change does not reflect in the Provider object or is reflected, but only when user does full reload of the app (depending on the scenario).

Here is my code:

class LandingFlowWidget extends StatefulWidget {
  const LandingFlowWidget({Key? key}) : super(key: key);

  @override
  State<LandingFlowWidget> createState() => _LandingFlowWidgetState();
}

class _LandingFlowWidgetState extends State<LandingFlowWidget> {
  late UserData? _userData;

  @override
  void initState() {
    super.initState();
    _userData = UserData();
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
        stream: FirebaseAuth.instance.authStateChanges(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return ProgressIndicatorWidget(color: Color(0xFF3030D0));
          } else if (snapshot.hasError) {
            return ErrorScreen();
          } else if (snapshot.hasData &&
              (FirebaseAuth.instance.currentUser != null &&
                  FirebaseAuth.instance.currentUser!.isAnonymous == false))
            return VerifyEmailScreen();
          else {
            if (FirebaseAuth.instance.currentUser == null)
              return OnboardingScreen();
            return ChangeNotifierProvider<UserData?>(
                create: (context) => _userData,
                builder: (context, _) {
                  return StreamBuilder<UserData>(
                      stream: FirebaseFirestore.instance
                          .collection('users')
                          .doc(FirebaseAuth.instance.currentUser?.uid)
                          .snapshots()
                          .map((snap) => UserData.fromJson(snap.data()!)),
                      builder: (BuildContext context,
                          AsyncSnapshot<UserData> snapshot) {
                        if (snapshot.hasError) {
                          return ErrorScreen();
                        } else if (snapshot.connectionState ==
                            ConnectionState.waiting) {
                          return ProgressIndicatorWidget(
                              color: Color(0xFF3030D0));
                        } else {
                          _userData = snapshot.data;
                          _userData?.updateState();
                          return OnboardingScreen();
                        }
                      });
                });
          }
        });
  }
}

I experimented with different approaches:

I tried to look on the internet and did not find working solution of Provider + Change of Authentication. I'd appreciate some code snippets.

Upvotes: 2

Views: 62

Answers (1)

Mateusz Pastewski
Mateusz Pastewski

Reputation: 31

I found a very ugly workaround.

In main.dart, I created MultiProvider that contains StreamProvider:

MultiProvider(
    providers: [
    (...)
      StreamProvider<UserData>.value(
        value: FirebaseAuth.instance.currentUser == null
            ? Stream.empty()
            : FirebaseClient.userStream,
        initialData: UserData(),
      ),
    (...)
    ],
 (...)

The stream:

  static Stream<UserData> userStream = FirebaseFirestore.instance
  .collection('users')
  .doc(FirebaseAuth.instance.currentUser?.uid)
  .snapshots()
  .map((snap) => UserData.fromJson(snap.data()!));

As mentioned in my initial question, this code does not work when re-authentication occurs but starts working again when the user does the full reload of the app. Having said that, when re-authentication occurs, I leverage Phoenix package to reload the entire app. Then the stream builds again with the correct user uid, and everything works as expected.

I would still appreciate it if someone could suggest a more elegant solution.

Upvotes: 1

Related Questions