Flutter Navigate to home route from provider

Viewed 93

I am new to flutter and I have been trying to create an app that requires authentication. After getting the user ID and basic information, I check for the role of the user and once that is done, I send the user to the home page. I tried to send the user to home using SchedulerBinding.instance!.addPostFrameCallback((_) { Navigator.of(context).pushReplacementNamed('/');}); but I get:

Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe. At this point the state of the widget's element tree is no longer stable. To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.

Here is my code (In my provider class):

Future<void> getUserRole(BuildContext context, String? userID) async {
try {
  if (userID != null) {
    print('initialized');
    initialized = true;
    print('Getting role of user wirh ID: $userID');
    var doc = await databaseReference.collection('Users').doc(userID).get();
    if (doc.exists) {
      Map<String, dynamic> data = doc.data()!;

      campusID = data['campusID'];
      firstName = data['firstName'];
      lastName = data['lastName'];
      exams = data['auth'];

      if (data['role'] == 'Admin') {
        print('Found admin');
        isAdmin = true;
      } else if (data['role'] == 'Faculty') {
        print('Found faculty');
        isTeacher = true;
      } else {
        print('Found student');
      }

      login = true;
      print('login!');
    } else {
      //Create new user
      uuid = userID;
      print('Adding new user...');
      SchedulerBinding.instance!.addPostFrameCallback((_) {
        Navigator.of(context).pushReplacementNamed('/register');
      });
    }
  }
} catch (err) {
  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
      shape: const RoundedRectangleBorder(
          borderRadius: BorderRadius.all(Radius.circular(15))),
      backgroundColor: Colors.red,
      behavior: SnackBarBehavior.floating,
      content: Text('Error: $err')));

  print('Could not get user roles: $err');
} finally {
  print('Noti');
  notifyListeners();
}}

Here is my home page:

class ScanPage extends StatelessWidget {
  ScanPage({Key? key}) : super(key: key);
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  @override
  Widget build(BuildContext context) {
    UserProvider userProvider = Provider.of<UserProvider>(context);

    if (userProvider.user == null) {
      SchedulerBinding.instance!.addPostFrameCallback((_) {
        userProvider.getUser(context);
      });
      print('Getting user');
    } else if (!userProvider.initialized) {
      SchedulerBinding.instance!.addPostFrameCallback((_) {
        userProvider.getUserRole(context, userProvider.user!.uid);
      });
      print('Getting user role');
    }

    if (userProvider.user?.displayName == null || !userProvider.initialized) {
      return Scaffold(
        backgroundColor: Colors.grey[300],
        key: _scaffoldKey,
        body: const Center(child: CircularProgressIndicator()),
      );
    }

    return const ScanBody();
  }}

I think context is not supposed to be use in provider but I am not sure what to do. Please help.

1 Answers

I think this may happen as a result of looking up info from a parent widget that has been destroyed.

I suspect changing this:

else {
      //Create new user
      uuid = userID;
      print('Adding new user...');
      SchedulerBinding.instance!.addPostFrameCallback((_) {
        Navigator.of(context).pushReplacementNamed('/register');
      });

To this, to keep the previous screen in memory:

else {
      //Create new user
      uuid = userID;
      print('Adding new user...');
      SchedulerBinding.instance!.addPostFrameCallback((_) {
        Navigator.of(context).pushNamed('/register');       //change this.
      });
Related