0

I am building a sample app for signup and login, the first page shows is mobile authentication using firebase, and after successful authentication, I fetch uid from firebase and try to match with documenteID of my cloud firestore collection('user'). If it gives me null then I want to show SignupScreen(), otherwise, show HomeScreen().

So it is my simple logic but I don't know this type of error occurred,

I tried some different code also which is

ChangeNotifierProvider(
      create: (BuildContext context) => User(),
    ),

but it still shows error.

Here is my code.

main.dart

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:phone_auth_example/homePage.dart';
import 'package:phone_auth_example/signup_screen.dart';
import 'package:provider/provider.dart';

import './signUpPage.dart';
import './user.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    final FirebaseAuth auth = FirebaseAuth.instance;

    var checkUser = false;
    void isNewUser() async {
      final FirebaseUser user = await auth.currentUser();
      final uid = user.uid;
      Provider.of<User>(context, listen: false).isUserNew(uid);
      checkUser = Provider.of<User>(context, listen: false).checkUser;
    }

    isNewUser();

    return MultiProvider(
        providers: [
          ChangeNotifierProvider.value(
            value: User(),
          ),
        ],
        child: MaterialApp(
          debugShowCheckedModeBanner: false,
          home: StreamBuilder(
            // ignore: deprecated_member_use
            stream: FirebaseAuth.instance.onAuthStateChanged,
            builder: (ctx, userSnapshot) {
              if (userSnapshot.hasData) {
                if (checkUser) {
                  return SignupScreen();
                } else {
                  return HomePage();
                }
              } else if (userSnapshot.hasError) {
                return CircularProgressIndicator();
              }
              return LoginPage();
            },
          ),
          routes: {
            SignupScreen.routeName: (ctx) => SignupScreen(),
          },
        ),);
  }
}

user.dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';

enum UserType {
  Member,
  HigherAuthority,
  SecurityGuard,
}

enum UserStatus {
  Owner,
  Renter,
}

enum OccupancyStatus {
  CurrentlyResiding,
  EmptyHouse,
}

class User with ChangeNotifier {
  final String id;
  final UserType userType;
  final String mobileNumber;
  final String name;
  final String email;
  final String password;
  final String houseNumberId;
  final UserStatus userStatus;
  final OccupancyStatus occupancyStatus;

  User({
    @required this.id,
    @required this.userType,
    @required this.mobileNumber,
    @required this.name,
    @required this.email,
    @required this.password,
    @required this.houseNumberId,
    @required this.userStatus,
    @required this.occupancyStatus,
  });

  bool checkUser;

  Future<void> isUserNew(String userId) async {
    // Get data from firestore
    DocumentSnapshot user =
        await Firestore.instance.collection('user').document(userId).get();

    //print(variable.data == null);

    // Check whether userId is in collection or not
    checkUser = (user.data == null) ? true : false;
  }
}

First, it's open LoginPage() that is works perfectly, and after that its direct goto HomePage() instead of signupPage(), no matter the uid match or not, so that is a problem and it shows error like...

ERROR

I/Choreographer(10505): Skipped 246 frames!  The application may be doing too much work on its main thread.
E/flutter (10505): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: Error: Could not find the correct Provider<User> above this MyApp Widget
E/flutter (10505):
E/flutter (10505): This likely happens because you used a `BuildContext` that does not include the provider
E/flutter (10505): of your choice. There are a few common scenarios:
E/flutter (10505):
E/flutter (10505): - The provider you are trying to read is in a different route.
E/flutter (10505):
E/flutter (10505):   Providers are "scoped". So if you insert of provider inside a route, then
E/flutter (10505):   other routes will not be able to access that provider.
E/flutter (10505):
E/flutter (10505): - You used a `BuildContext` that is an ancestor of the provider you are trying to read.
E/flutter (10505):
E/flutter (10505):   Make sure that MyApp is under your MultiProvider/Provider<User>.
E/flutter (10505):   This usually happen when you are creating a provider and trying to read it immediately.
E/flutter (10505):
E/flutter (10505):   For example, instead of:
E/flutter (10505):
E/flutter (10505):   ```
E/flutter (10505):   Widget build(BuildContext context) {
E/flutter (10505):     return Provider<Example>(
E/flutter (10505):       create: (_) => Example(),
E/flutter (10505):       // Will throw a ProviderNotFoundError, because `context` is associated
E/flutter (10505):       // to the widget that is the parent of `Provider<Example>`
E/flutter (10505):       child: Text(context.watch<Example>()),
E/flutter (10505):     ),
E/flutter (10505):   }
E/flutter (10505):   ```
E/flutter (10505):
E/flutter (10505):   consider using `builder` like so:
E/flutter (10505):
E/flutter (10505):   ```
E/flutter (10505):   Widget build(BuildContext context) {
E/flutter (10505):     return Provider<Example>(
E/flutter (10505):       create: (_) => Example(),
E/flutter (10505):       // we use `builder` to obtain a new `BuildContext` that has access to the provider
E/flutter (10505):       builder: (context) {
E/flutter (10505):         // No longer throws
E/flutter (10505):         return Text(context.watch<Example>()),
E/flutter (10505):       }
E/flutter (10505):     ),
E/flutter (10505):   }
E/flutter (10505):   ```
E/flutter (10505):
E/flutter (10505): If none of these solutions work, consider asking for help on StackOverflow:

1 Answers1

1

Your issue is that you are trying to call Provider.of<User>(context, listen: false).checkUser while no provider has been given in the context yet (since you use MultiProvider lower down your tree).

You should move up the MultiProvider. Here is an example of an implementation:

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:phone_auth_example/homePage.dart';
import 'package:phone_auth_example/signup_screen.dart';
import 'package:provider/provider.dart';

import './signUpPage.dart';
import './user.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    @override
    Widget build(BuildContext context) {
      return MultiProvider(
        providers: [
          ChangeNotifierProvider<User>(
            create: (context) => User(),
          ),
        ],
        builder: (BuildContext context, Widget child) {
          final FirebaseAuth auth = FirebaseAuth.instance;

          var checkUser = false;
          void isNewUser() async {
            final FirebaseUser user = await auth.currentUser();
            final uid = user.uid;
            Provider.of<User>(context, listen: false).isUserNew(uid);
            checkUser = Provider.of<User>(context, listen: false).checkUser;
          }

          isNewUser();

          return child;
        },
        child: MaterialApp(
          debugShowCheckedModeBanner: false,
          home: StreamBuilder(
            // ignore: deprecated_member_use
            stream: FirebaseAuth.instance.onAuthStateChanged,
            builder: (ctx, userSnapshot) {
              if (userSnapshot.hasData) {
                if (checkUser) {
                  return SignupScreen();
                } else {
                  return HomePage();
                }
              } else if (userSnapshot.hasError) {
                return CircularProgressIndicator();
              }
              return LoginPage();
            },
          ),
          routes: {
            SignupScreen.routeName: (ctx) => SignupScreen(),
          },
        ),
      );
    }
  }
}

Furthermore there are 2 misuse of ChangeNotifier:

1 The User class should extend ChangeNotifier.

Replace

class User with ChangeNotifier

With

class User extends ChangeNotifier

2 ChangeNotifierProvider.value should be used if you already created an instance of the ChangeNotifier (User in your case): here is a great explaination of the use cases. Since you did not, your should use ChangeNotifierProvider like this:

ChangeNotifierProvider<User>(
      create: (context) => User(),
      child: ...
)
Lulupointu
  • 3,364
  • 1
  • 12
  • 28
  • I tried whatever you mentioned in the answer but still, it is showing the same error. Can you please check once again? – RAVI KACHHADIYA Oct 25 '20 at 07:40
  • Can you try this? – Lulupointu Oct 25 '20 at 07:57
  • Now it's showing like this... Local module descriptor class for providerinstaller not found. I/DynamiteModule(26592): Considering local module providerinstaller:0 and remote module providerinstaller:0 W/ProviderInstaller(26592): Failed to load providerinstaller module: No acceptable module found. Local version is 0 and remote version is 0. I/Choreographer(26592): Skipped 38 frames! The application may be doing too much work on its main thread. W/ResourceType(26592): No package identifier when getting name for resource number 0x00000000 V/NativeCrypto(26592): Registering – RAVI KACHHADIYA Oct 25 '20 at 10:21
  • Try "flutter clean" ? – Lulupointu Oct 25 '20 at 10:25