Flutter/dart: Parse Server sdk: ParseUser.currentUser() function returning null on reset

496

For each state of the main Future builder, all the widgets bellow will be recreating, the build function will be called again, and will call again the function inside Future.Builder.

you can use AsyncMemoizer to avoid it. AsyncMemoizer will make a Future be called only once. You have many ways to solve this. You can use a StatefullWidget for example and fetch the data on initState() it will be called only once, and the state will remain even if the parent rebuild.

And the best practice would create other layers to fetch your data, and your view is just responsible to show, not to fetch it.

https://pub.dev/packages/async

https://pub.dev/documentation/async/latest/async/AsyncMemoizer-class.html

Share:
496
RyanBuss01
Author by

RyanBuss01

Updated on December 28, 2022

Comments

  • RyanBuss01
    RyanBuss01 over 1 year

    I am using "Parse Server sdk" with "back4app" as a backend in my flutter application and I am having trouble calling the current user on initial start up of the app using the function: "ParseUser.currentUser()" but for some reason even after logging in when I restart the application the function returns null

    pubspec.yaml

    dependencies:
      parse_server_sdk: ^2.1.0
    

    Main.dart

    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
    
      final keyApplicationId = 'xxxxxxxxx';
      final keyParseServerUrl = 'https://parseapi.back4app.com';
      final keyClientkey = 'xxxxxxxxxx';
    
      await Parse().initialize(
          keyApplicationId,
          keyParseServerUrl,
        clientKey: keyClientkey,
        debug: true
      );
    
      print('connected to Parse Server');
    
      runApp(MyApp());
    }
    
    
    
    class MyApp extends StatelessWidget {
      Future<bool> hasUserLogged() async {
        print('future called');
        ParseUser currentUser = await ParseUser.currentUser() as ParseUser;
        if (currentUser == null) {
          return false;
        }
        //Validates that the user's session token is valid
        final ParseResponse parseResponse =
        await ParseUser.getCurrentUserFromServer(
            currentUser.get<String>('sessionToken'));
    
        if (!parseResponse.success) {
          print('call failed');
          //Invalid session. Logout
          await currentUser.logout();
          return false;
        } else {
          print('user found');
          return true;
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter - Parse Server',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: FutureBuilder<bool>(
              future: hasUserLogged(),
              builder: (context, snapshot) {
                switch (snapshot.connectionState) {
                  case ConnectionState.none:
                  case ConnectionState.waiting:
                    return Scaffold(
                      body: Center(
                        child: Container(
                            width: 100,
                            height: 100,
                            child: CircularProgressIndicator()),
                      ),
                    );
                  default:
                    if (snapshot.hasData && snapshot.data) {
                      print('to User');
                      return UserPage();
                    } else {
                      print('to Login');
                      return LoginPage();
                    }
                }
              }),
        );
      }
    }
    

    Log In Page:

    class LoginPage extends StatefulWidget {
      @override
      _LoginPageState createState() => _LoginPageState();
    }
    
    class _LoginPageState extends State<LoginPage> {
      final controllerUsername = TextEditingController();
      final controllerPassword = TextEditingController();
      bool isLoggedIn = false;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
              title: const Text('Flutter - Parse Server'),
            ),
            body: Center(
              child: SingleChildScrollView(
                padding: const EdgeInsets.all(8),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.stretch,
                  children: [
                    Container(
                      height: 200,
                      child: Image.network(
                          'https://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
                    ),
                    Center(
                      child: const Text('Flutter on Back4App',
                          style:
                          TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                    ),
                    SizedBox(
                      height: 16,
                    ),
                    TextField(
                      controller: controllerUsername,
                      enabled: !isLoggedIn,
                      keyboardType: TextInputType.text,
                      textCapitalization: TextCapitalization.none,
                      autocorrect: false,
                      decoration: InputDecoration(
                          border: OutlineInputBorder(
                              borderSide: BorderSide(color: Colors.black)),
                          labelText: 'Username'),
                    ),
                    SizedBox(
                      height: 8,
                    ),
                    TextField(
                      controller: controllerPassword,
                      enabled: !isLoggedIn,
                      obscureText: true,
                      keyboardType: TextInputType.text,
                      textCapitalization: TextCapitalization.none,
                      autocorrect: false,
                      decoration: InputDecoration(
                          border: OutlineInputBorder(
                              borderSide: BorderSide(color: Colors.black)),
                          labelText: 'Password'),
                    ),
                    SizedBox(
                      height: 16,
                    ),
                    Container(
                      height: 50,
                      child: ElevatedButton(
                        child: const Text('Login'),
                        onPressed: isLoggedIn ? null : () => doUserLogin(),
                      ),
                    ),
                    SizedBox(
                      height: 16,
                    ),
                    Container(
                      height: 50,
                      child: ElevatedButton(
                        child: const Text('Sign Up'),
                        onPressed: () => navigateToSignUp(),
                      ),
                    ),
                    SizedBox(
                      height: 16,
                    ),
                    Container(
                      height: 50,
                      child: ElevatedButton(
                        child: const Text('Reset Password'),
                        onPressed: () => navigateToResetPassword(),
                      ),
                    )
                  ],
                ),
              ),
            ));
      }
    
      void doUserLogin() async {
        final username = controllerUsername.text.trim();
        final password = controllerPassword.text.trim();
    
        final user = ParseUser(username, password, null);
    
        var response = await user.login();
    
        if (response.success) {
          navigateToUser();
        } else {
          Message.showError(context: context, message: response.error.message);
        }
      }
    
      void navigateToUser() {
        Navigator.pushAndRemoveUntil(
          context,
          MaterialPageRoute(builder: (context) => UserPage()),
              (Route<dynamic> route) => false,
        );
      }
    
      void navigateToSignUp() {
        Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => SignUpPage()),
        );
      }
    
      void navigateToResetPassword() {
        Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => ResetPasswordPage()),
        );
      }
    }
    

    Home Page:

    class UserPage extends StatelessWidget {
      ParseUser currentUser;
    
      Future<ParseUser> getUser() async {
        currentUser = await ParseUser.currentUser() as ParseUser;
        return currentUser;
      }
    
      @override
      Widget build(BuildContext context) {
        void doUserLogout() async {
          var response = await currentUser.logout();
          if (response.success) {
            Message.showSuccess(
                context: context,
                message: 'User was successfully logout!',
                onPressed: () {
                  Navigator.pushAndRemoveUntil(
                    context,
                    MaterialPageRoute(builder: (context) => LoginPage()),
                        (Route<dynamic> route) => false,
                  );
                });
          } else {
            Message.showError(context: context, message: response.error.message);
          }
        }
    
        return Scaffold(
            appBar: AppBar(
              title: Text('User logged in - Current User'),
            ),
            body: FutureBuilder<ParseUser>(
                future: getUser(),
                builder: (context, snapshot) {
                  switch (snapshot.connectionState) {
                    case ConnectionState.none:
                    case ConnectionState.waiting:
                      return Center(
                        child: Container(
                            width: 100,
                            height: 100,
                            child: CircularProgressIndicator()),
                      );
                      break;
                    default:
                      return Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.stretch,
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            Center(child: Text('Hello, ${snapshot.data.username}')),
                            SizedBox(
                              height: 16,
                            ),
                            Container(
                              height: 50,
                              child: ElevatedButton(
                                child: const Text('Logout'),
                                onPressed: () => doUserLogout(),
                              ),
                            ),
                          ],
                        ),
                      );
                  }
                }));
      }
    }
    

    when using the app Logging in works fine but when I reload the app it just "ParseServer.currentUser()" in "MyApp" class returns null and sends me to the log in screen. I would appreciate the help if anyone knows what I did wrong.

    also the FutureBuilder in "MyApp" class calls the Future twice I'm not sure why and if that may have something to do with it.