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.
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