Chris
Chris

Reputation: 155

flutter jumps back to the last route

I want to build a loading screen for my flutter app, which includes a small start animation.

The goal is that the loading screen is only left when a 4 second timer has expired and the data is loaded.

For the 4 second timer I used a future.delayed in the initState method, which sets a variable to true.

I used Riverpod Future Provider to load the data. This loads a large part of the data. When this is finished, another provider is to be initialized.

My goal is that when these two are ready, the app redirects to the home page. However, I have noticed a strange behavior. when a new user is created in the app, it happens that after the loading screen it is redirected to the home route and the app then jumps directly back to the loading screen. This happens in an endless loop.

I assume that this happens because the data from the register process is not yet finished in the db, while the currentUserProvider is already trying to retrieve it. however, I don't know how to prevent this. can anyone find the error?

Auth state provider and current user provider (that loads all the data from the db)

final authStateProvider = StreamProvider<User?>((ref) {
  final firebaseApp = ref
      .watch(firebaseProvider)
      .value;

  if (firebaseApp == null) {
    // Firebase is not initialized yet
    return const Stream.empty();
  }

  final auth = FirebaseAuth.instanceFor(app: firebaseApp);

  return auth.authStateChanges();
});

final currentUserProvider = StreamProvider<CurrentUser>((ref){
  final authState = ref.watch(authStateProvider);
  // final recipeState = ref.watch(recipesProvider);

  print("Current User Provider");

  return authState.when(
    data: (user) {
      if (user == null) {
        return const Stream.empty();
      } else {
        String uid = user.uid;
        // User is logged in, fetch user data from Firestore
        final firestore = FirebaseFirestore.instance;

        return firestore.collection('users').doc(uid).snapshots().map((snapshot) {
          //parse data
          return currentUser;
        });
      }
        },
    loading: () => const Stream.empty(),
    error: (_, __) => const Stream.empty(),
  );
});

My Loading Screen Class:

class LoadingScreen extends ConsumerStatefulWidget {
  LoadingScreen({super.key});

  static const routeName = '/load';

  @override
  ConsumerState<ConsumerStatefulWidget> createState() => _LoadingScreenState();
}

class _LoadingScreenState extends ConsumerState<LoadingScreen> {
  bool _dataLoaded = false;
  bool _timerFinished = false;
  bool _loading = false;
  final int loadingTime = 4;

  @override
  void initState() {
    super.initState();

//this initializes the timer
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _loadTimer();
    });
  }

  @override
  Widget build(BuildContext context) {
    var currentUser = ref.watch(currentUserProvider);
    if (FirebaseAuth.instance.currentUser!.emailVerified == false) {
      FirebaseAuth.instance.currentUser!.reload();
    }

    if (_dataLoaded && _timerFinished) {
      Future.delayed(Duration.zero, () {
        Navigator.pushReplacementNamed(context, Home.routeName);
      });
    }

    //this should load the data. I want this ptocess to start as soon as the current user provider is ready
    _loadData();


//heres the ui
    return Container();
  }

  Future<void> _loadTimer() async {
    await Future.delayed(Duration(seconds: loadingTime));
    setState(() {
      _timerFinished = true;
      print("Timer finished");
    });
  }

  Future<void> _loadData() async {
    print("Load Data");
    ref.watch(currentUserProvider).when(
        data: (user) async {
          print("User: $user");
          if(user == null){
            return;
          }
          //this is to make sure the datat here is only loadet once
          if (!_loading) {
            _loading = true;
              ref
                  .read(nutritionPlanProvider)
                  .init(ref, user.mealPlan ?? {}, user.nutritionPlan, context);
            setState(() {
              _dataLoaded = true;
            });
          }
        },
        loading: () {},
        error: (error, stackTrace) {
          CustomSnackBar.show(
            context,
            AppLocalizationUtil.of(context).error_fetching_user_data,
          );
          print('Error: $error');
          print('Stacktrace: $stackTrace');
        });
  }

Upvotes: 2

Views: 50

Answers (0)

Related Questions