0

I got this weird bug causing a lot of bugs on my app like :

  • not able to keep the user connected when you close and reopen the app
  • cause sometimes to have bug while retriving user info (blank user page)
  • When the bug disconnect you, it says on reconnect that user info is incorrect

I have been searching everywhere since 2 weeks and couldn't get to see where the problem is. I'm gonna share you some code like main.dart

Gif of all the manipulations leading to the bug

Main.dart code :

NotificationService notificationService = NotificationService();

Future<void> main() async {   WidgetsFlutterBinding.ensureInitialized();   await Firebase.initializeApp();   notificationService.init();   SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); runApp(AppWrapper()); }

class AppWrapper extends StatefulWidget {   @override   State<StatefulWidget> createState() {
    return _AppWrapperState();   } }

class _AppWrapperState extends State<AppWrapper> {   @override   Widget build(BuildContext context) {
    return OverlaySupport.global(
      child: MaterialApp(
          theme: ThemeData(
              textTheme: GoogleFonts.openSansTextTheme(
            Theme.of(context).textTheme,
          )),
          debugShowCheckedModeBanner: false,
          home: App()),
    );   } }

/// We are using a StatefulWidget such that we only create the [Future] once, /// no matter how many times our widget rebuild. /// If we used a [StatelessWidget], in the event where [App] is rebuilt, that /// would re-initialize FlutterFire and make our application re-enter loading state, /// which is undesired. class App extends StatefulWidget {   // Create the initialization Future outside of `build`:   @override   _AppState createState() => _AppState(); }

class _AppState extends State<App> {   /// The future is part of the state of our widget. We should not call `initializeApp`   /// directly inside [build].   final Future<FirebaseApp> _initialization = Firebase.initializeApp();   bool? _isLoggedIn;

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

  //The following variables are used to handle the navigation between the 3 main pages   List<Widget> pages = [
    SettingsPage(key: PageStorageKey('settings')),
    HomePage(key: PageStorageKey('home')),
    AccountPage(
      key: PageStorageKey('account'),
    ),   ];   int _selectedIndex = 1;

  initIsLoggedIn() async {
    try {
      // Get values from storage
      final storage = new FlutterSecureStorage();
      String isLoggedIn = await storage.read(key: "isLoggedIn") ?? "";
      if (isLoggedIn == "true") {
        //If the user seems logged in, we check it by trying to authenticate
        String mail = await storage.read(key: "mail") ?? "";
        String password = await storage.read(key: "password") ?? "";
        UserResponse userResponse = await APIUser().login(mail, password);
        User? user = userResponse.user;
        if (user != null) {
          setState(() {
            _isLoggedIn = true;
          });
        } else {
          //If we cannot connect the user with the stored mail and password, then we redirect the user to the login page
          setState(() {
            _isLoggedIn = false;
          });
          Navigator.of(context).pushAndRemoveUntil(
            MaterialPageRoute(builder: (context) => LoginPage()),
            (_) => false,
          );
        }
      } else {
        //If we cannot connect the user with the stored mail and password, then we redirect the user to the login page
        setState(() {
          _isLoggedIn = false;
        });
        Navigator.of(context).pushReplacement(
            MaterialPageRoute(builder: (context) => LoginPage()));
      }
    } catch (e) {
      //print the error and redirect the user to the login page
      print(e);
      Navigator.of(context).pushReplacement(
          MaterialPageRoute(builder: (context) => LoginPage()));
    }   }

  void _changePage(int index) {
    setState(() {
      _selectedIndex = index;
    });   }

  @override   Widget build(BuildContext context) {
    return FutureBuilder(
      // Initialize FlutterFire:
      future: _initialization,
      builder: (context, snapshot) {
        // Check for errors
        if (snapshot.hasError) {
          return somethingWentWrongWidget();
        }

        // Once complete, show your application
        if (snapshot.connectionState == ConnectionState.done &&
            _isLoggedIn != null) {
          return appContentWidget();
        }

        // Otherwise, show something whilst waiting for initialization to complete
        return LoadingPage();
      },
    );   }

  Widget somethingWentWrongWidget() {
    return Center(
      child: Text('Someting went wrong', textDirection: TextDirection.ltr),
    );   }

  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

  Widget appContentWidget() {
    double bottomNavbarHeight = 70;
    if (Platform.isIOS) {
      bottomNavbarHeight = 90;
    }
    // check if the user is logged in, if not go to login page
    bool loggedIn = _isLoggedIn ?? false;
    if (!loggedIn) {
      return LoginPage();
    } else {
      return WillPopScope(
        onWillPop: () {
          return Future.value(false);
        },
        child: Scaffold(
          key: _scaffoldKey,
          body: IndexedStack(
            index: _selectedIndex,
            children: pages,
          ),
          bottomNavigationBar: Container(
            height: bottomNavbarHeight,
            decoration: BoxDecoration(
                boxShadow: [BoxShadow(color: Colors.grey.shade200)]),
            child: Theme(
              data: ThemeData(
                brightness: Brightness.light,
                splashColor: Colors.transparent,
                highlightColor: Colors.transparent,
              ),
              child: BottomNavigationBar(
                elevation: 20,
                backgroundColor: Colors.white,
                selectedFontSize: 0,
                //As the label is obligatory, we give it a size of zero
                unselectedFontSize: 0,
                //As the label is obligatory, we give it a size of zero
                items: const <BottomNavigationBarItem>[
                  BottomNavigationBarItem(
                      icon: ImageIcon(
                        AssetImage("assets/settings.png"),
                        size: 32,
                      ),
                      label: ''),
                  BottomNavigationBarItem(
                      icon: ImageIcon(
                        AssetImage("assets/dashboard.png"),
                        size: 32,
                      ),
                      label: ''),
                  BottomNavigationBarItem(
                      icon: ImageIcon(
                        AssetImage("assets/user.png"),
                        size: 32,
                      ),
                      label: ''),
                ],
                currentIndex: _selectedIndex,
                selectedItemColor: Colors.black,
                onTap: _changePage,
              ),
            ),
          ),
        ),
      );
    }   } }

My login_page.dart :

class LoginPage extends StatefulWidget {
  @override
  LoginPageState createState() => LoginPageState();
}

class LoginPageState extends State<LoginPage> {
  final _loginFormKey = GlobalKey<FormState>();
  TextEditingController mailController = TextEditingController();
  TextEditingController passwordController = TextEditingController();
  bool displayIncorrectLoginErrorMessage = false;
  bool displayNotVerifiedEmailErrorMessage = false;
  TextEditingController mailVerificationCodeController =
      TextEditingController();
  TextEditingController mailToResetPasswordController = TextEditingController();
  bool obscurePassword = true;
  bool passwordForgottenWWrongEmail = false;

  @override
  Widget build(BuildContext context) {
    double screenHeight = MediaQuery.of(context).size.height;
    double spaceBeforeEndPage = 20;
    if (screenHeight > 680) {
      spaceBeforeEndPage = screenHeight - 680;
    }
    return Scaffold(
      backgroundColor: Colors.white,
      body: SingleChildScrollView(
        child: Column(
          children: [
            Container(
              //This one is for setting the background image
              decoration: BoxDecoration(
                  image: DecorationImage(
                      alignment: Alignment.topCenter,
                      fit: BoxFit.fitWidth,
                      image: AssetImage('assets/home_background.png'))),
              child: SingleChildScrollView(
                child: Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 25.0),
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.start,
                    children: [
                      SizedBox(
                        height: 120,
                      ),
                      Image(
                        //Logo Ellipse
                        image: AssetImage('assets/.png'),
                        width: 150,
                      ),
                      SizedBox(
                        height: 80,
                      ),
                      Align(
                        alignment: Alignment.centerLeft,
                        child: Text(
                          'Connectez-vous.',
                          style: TextStyle(
                              fontSize: 25, fontWeight: FontWeight.bold),
                        ),
                      ),
                      SizedBox(
                        height: 50,
                      ),
                      Form(
                          key: _loginFormKey,
                          child: Column(
                            children: [
                              VariableDecorationHeightTextFormField(
                                  controller: mailController,
                                  labelText: "Adresse mail"),
                              VariableDecorationHeightAndObscureIconTextFormField(
                                controller: passwordController,
                                labelText: "Mot de passe",
                              ),
                            ],
                          )),
                      Align(
                        alignment: Alignment.topRight,
                        child: TextButton(
                          onPressed: () async {
                            //Open an alert dialog that ask the email address then send the request to the api
                            showDialog(
                                context: context,
                                barrierDismissible: false,
                                builder: (BuildContext context) {
                                  return resetPasswordAlertDialog(context);
                                });
                          },
                          child: Text(
                            " Mot de passe oublié ?",
                            style: TextStyle(color: Colors.grey, fontSize: 12),
                          ),
                        ),
                      ),
                      incorrectLoginErrorMessageWidget(),
                      notVerifiedEmailErrorMessageWidget(),
                      SizedBox(
                        height: 20,
                      ),
                      ElevatedButton(
                        onPressed: () async {
                          String mail = mailController.text;
                          String? password = passwordController.text;
                          mail = await Utility().removeSpacesAtTheEnd(mail);
                          UserResponse userResponse =
                              await APIUser().login(mail, password);
                          if (!userResponse.userConfirmed) {
                            //The user is not confirmed
                            setState(() {
                              displayNotVerifiedEmailErrorMessage = true;
                            });
                          } else if (userResponse.user != null) {
                            User? user = userResponse.user;
                            if (user != null) {
                              print("User " +
                                  user.username +
                                  " connected without problem");
                              await login(mail, password);
                              //Update user's notification token
                              await NotificationService()
                                  .tryConnectUserAndUpdateNotificationTokenIfNeeded();
                              Navigator.of(context).push(MaterialPageRoute(
                                  builder: (context) => App()));
                            } else {
                              print('User not connected');
                              setState(() {
                                displayIncorrectLoginErrorMessage = true;
                              });
                            }
                          } else {
                            setState(() {
                              displayIncorrectLoginErrorMessage = true;
                            });
                          }
                        },
                        child: Text(
                          "Connexion",
                          style: TextStyle(color: Colors.white),
                        ),
                        style: ElevatedButton.styleFrom(primary: Colors.black),
                      ),
                    ],
                  ),
                ),
              ),
            ),
            SizedBox(
              height: spaceBeforeEndPage,
            ),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 20.0),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text(
                    "Pas encore de compte ?",
                    style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                  ),
                  CenteredShadowButton(
                    function: () {
                      Navigator.of(context).pushReplacement(MaterialPageRoute(
                          builder: (context) => RegisterPage()));
                    },
                    child: Text(
                      "S'inscrire",
                      style: TextStyle(color: Colors.black),
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  login(String mail, String password) async {
    // Create storage
    final storage = new FlutterSecureStorage();
    // Write values
    await storage.write(key: "mail", value: mail);
    await storage.write(key: "password", value: password);
    await storage.write(key: "isLoggedIn", value: "true");
    //Refresh main
    Navigator.of(context).push(MaterialPageRoute(builder: (context) => App()));
  }

...

Sorry for the flood of code line, if you need anything just ask me, I will try my best to provide it to you.

weaked
  • 5
  • 1
  • Check this link I hope it helps you - https://stackoverflow.com/questions/51901002/is-there-a-way-to-load-async-data-on-initstate-method – Vishal Zaveri May 30 '22 at 15:22

0 Answers0