Flutter/dart:解析服务器 sdk:ParseUser.currentUser() 函数在重置时返回 null
Flutter/dart: Parse Server sdk: ParseUser.currentUser() function returning null on reset
我在我的 flutter 应用程序中使用带有“back4app”的“Parse Server sdk”作为后端,我在使用以下函数首次启动应用程序时无法调用当前用户:“ParseUser.currentUser ()" 但出于某种原因,即使在我重新启动应用程序时登录后,函数 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();
}
}
}),
);
}
}
登录页面:
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()),
);
}
}
主页:
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(),
),
),
],
),
);
}
}));
}
}
使用该应用程序时,登录工作正常,但当我重新加载该应用程序时,它只是“MyApp”中的“ParseServer.currentUser()” class returns null 并将我发送到登录屏幕。如果有人知道我做错了什么,我将不胜感激。
“MyApp”中的 FutureBuilder class 调用 Future 两次,我不确定为什么以及是否可能与此有关。
对于主要 Future 构建器的每个状态,将重新创建下面的所有小部件,将再次调用构建函数,并将再次调用 Future.Builder 中的函数。
您可以使用 AsyncMemoizer 来避免它。 AsyncMemoizer 将使 Future 仅被调用一次。
你有很多方法可以解决这个问题。
例如,您可以使用 StatefullWidget 并在 initState() 上获取数据,它只会被调用一次,即使父级重建,状态也会保持不变。
最好的做法是创建其他层来获取您的数据,而您的视图只负责显示,而不是获取数据。
https://pub.dev/packages/async
https://pub.dev/documentation/async/latest/async/AsyncMemoizer-class.html
我在我的 flutter 应用程序中使用带有“back4app”的“Parse Server sdk”作为后端,我在使用以下函数首次启动应用程序时无法调用当前用户:“ParseUser.currentUser ()" 但出于某种原因,即使在我重新启动应用程序时登录后,函数 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();
}
}
}),
);
}
}
登录页面:
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()),
);
}
}
主页:
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(),
),
),
],
),
);
}
}));
}
}
使用该应用程序时,登录工作正常,但当我重新加载该应用程序时,它只是“MyApp”中的“ParseServer.currentUser()” class returns null 并将我发送到登录屏幕。如果有人知道我做错了什么,我将不胜感激。
“MyApp”中的 FutureBuilder class 调用 Future 两次,我不确定为什么以及是否可能与此有关。
对于主要 Future 构建器的每个状态,将重新创建下面的所有小部件,将再次调用构建函数,并将再次调用 Future.Builder 中的函数。
您可以使用 AsyncMemoizer 来避免它。 AsyncMemoizer 将使 Future 仅被调用一次。 你有很多方法可以解决这个问题。 例如,您可以使用 StatefullWidget 并在 initState() 上获取数据,它只会被调用一次,即使父级重建,状态也会保持不变。
最好的做法是创建其他层来获取您的数据,而您的视图只负责显示,而不是获取数据。
https://pub.dev/packages/async
https://pub.dev/documentation/async/latest/async/AsyncMemoizer-class.html