问题描述
我在我的 Flutter 应用程序中使用带有“back4app”的“Parse Server sdk”作为后端,但我在应用程序初始启动时无法调用当前用户使用函数:“ParseUser.currentUser()”但是出于某种原因,即使在我重新启动应用程序时登录后,该函数也会返回 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,obscureText: true,labelText: 'Password'),Container(
height: 50,child: ElevatedButton(
child: const Text('Login'),onpressed: isLoggedIn ? null : () => doUserLogin(),child: ElevatedButton(
child: const Text('Sign Up'),onpressed: () => navigatetoSignUp(),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()),);
});
} 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(),snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return Center(
child: Container(
width: 100,);
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()”返回空值并将我发送到登录屏幕。如果有人知道我做错了什么,我将不胜感激。
“MyApp”类中的 FutureBuilder 也调用了 Future 两次,我不确定为什么以及这是否与它有关。
解决方法
对于主 Future 构建器的每个状态,下面的所有小部件都将重新创建,构建函数将再次被调用,并且将再次调用 Future.Builder 中的函数。
您可以使用 AsyncMemoizer 来避免它。 AsyncMemoizer 将使 Future 仅被调用一次。 你有很多方法可以解决这个问题。 例如,您可以使用 StatefullWidget 并在 initState() 上获取数据,它只会被调用一次,即使父级重建,状态也会保持不变。
最佳实践是创建其他层来获取您的数据,而您的视图只负责显示而不是获取它。
https://pub.dev/packages/async
https://pub.dev/documentation/async/latest/async/AsyncMemoizer-class.html