问题描述
我正在使用 firebase auth 在 Flutter 中开发用户身份验证流程。
我有应用程序的基础,它允许用户登录、主页、密码重置,但是我在为用户开发个人资料屏幕时被卡住了。我的目的是允许用户修改预先填充的表单以更新他们的详细信息。
为了使架构尽可能干净,我有一个视图和一个视图模型,然后我可以在其中连接到服务(身份验证和 Firestore)。
同时使用 Get_it 和 Provider 来促进状态管理。 Get_it 以便我可以轻松访问类/视图模型和提供程序,以便在数据发生更改时更新视图。
我遇到的问题是 Profileviewmodel 的 user 属性在我最初打开配置文件视图时为空,尽管它是在登录时从身份验证服务填充的,这反过来在我尝试设置初始值时导致错误表单中的文本框。如果我从错误中返回并重试,那么一切都会按预期加载。
从我的角度来看,当用户登录时,身份验证模型应该包含一个有效的 User 对象。在配置文件 initState 视图上调用 model.loadData() 应该返回包含当前用户对象的视图模型。
用户登录工作正常,但是它使用提供者而不是服务定位器 (Get_it) 来获取用户对象。为了完整起见,我已经包含了下面的大部分代码。
提前致谢
我的服务定位器
GetIt serviceLocator = GetIt.instance;
void setupServiceLocator() {
// services
serviceLocator.registerLazySingleton<AuthenticationService>(
() => AuthenticationService());
serviceLocator
.registerLazySingleton<FirestoreDatabase>(() => FirestoreDatabase());
// view models
serviceLocator.registerFactory<Profileviewmodel>(() => Profileviewmodel());
serviceLocator
.registerFactory<PasswordResetviewmodel>(() => PasswordResetviewmodel());
}
个人资料视图
class ProfileScreen extends StatefulWidget {
const ProfileScreen({Key key}) : super(key: key);
@override
_ProfileScreenState createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
final _formKey = GlobalKey<FormState>();
bool checkBoxValue = false;
double _height;
double _width;
double _pixelRatio;
bool _large;
bool _medium;
String displayName;
String email;
String phone;
String url;
Profileviewmodel model = serviceLocator<Profileviewmodel>();
@override
void initState() {
super.initState();
model.loadData();
}
@override
Widget build(BuildContext context) {
_height = MediaQuery.of(context).size.height;
_width = MediaQuery.of(context).size.width;
_pixelRatio = MediaQuery.of(context).devicePixelRatio;
_large = ResponsiveWidget.isScreenLarge(_width,_pixelRatio);
_medium = ResponsiveWidget.isScreenMedium(_width,_pixelRatio);
return Material(
child: Scaffold(
body: Container(
height: _height,width: _width,margin: EdgeInsets.only(bottom: 5),child: SingleChildScrollView(
child: Column(
children: <Widget>[
form(),button(),],),);
}
Widget form() {
return Container(
margin: EdgeInsets.only(
left: _width / 12.0,right: _width / 12.0,top: _height / 20.0),child: Form(
key: _formKey,child: Column(
children: <Widget>[
displayNameTextFormField()
],);
}
Widget displayNameTextFormField() {
return CustomTextField(
keyboardType: TextInputType.text,icon: Icons.person,hint: "display Name",initalValue: model.user.displayName,onSave: (value) {
setState(() {
displayName = value;
});
},);
}
Widget button() {
return ElevatedButton(
onpressed: () {
if (_formKey.currentState.validate()) {
//submit the form
_formKey.currentState.save();
model.saveProfile(email,displayName,url,phone);
Navigator.of(context).pop();
}
},child: Container(
alignment: Alignment.center,//height: _height / 20,width: _large ? _width / 4 : (_medium ? _width / 3.75 : _width / 3.5),decoration: Boxdecoration(
borderRadius: BorderRadius.all(Radius.circular(20.0)),gradient: LinearGradient(
colors: <Color>[Colors.orange[200],Colors.pinkAccent],padding: const EdgeInsets.all(12.0),child: Text(
'SIGN UP',style: TextStyle(fontSize: _large ? 14 : (_medium ? 12 : 10)),);
}
}
个人资料视图模型
class Profileviewmodel extends ChangeNotifier {
// final firestoreService = serviceLocator<FirestoreDatabase>();
final authenticationService = serviceLocator<AuthenticationService>();
User _user;
User get user => _user;
void loadData() {
_user = authenticationService.currentUser;
notifyListeners();
}
Future<void> saveProfile(email,phoneNumber) async {
_user = await authenticationService.updateUserDetails(
email,phoneNumber);
notifyListeners();
}
}
认证服务
import 'package:Flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
enum Status {
Uninitialized,Authenticated,Authenticating,Unauthenticated,Registering
}
class AuthenticationService extends ChangeNotifier {
//Firebase Auth object
FirebaseAuth _auth;
//Default status
Status _status = Status.Uninitialized;
Status get status => _status;
User _currentUser;
User get currentUser => _currentUser;
Stream<User> get user => _auth.authStateChanges().map(_userFromFirebase);
AuthenticationService() {
//initialise object
_auth = FirebaseAuth.instance;
//listener for authentication changes such as user sign in and sign out
_auth.authStateChanges().listen(onAuthStateChanged);
}
//Create user object based on the given FirebaseUser
User _userFromFirebase(User user) {
if (user == null) {
return null;
}
_currentUser = user;
return user;
}
//Method to detect live auth changes such as user sign in and sign out
Future<void> onAuthStateChanged(User firebaseUser) async {
if (firebaseUser == null) {
_status = Status.Unauthenticated;
} else {
_userFromFirebase(firebaseUser);
_status = Status.Authenticated;
}
notifyListeners();
}
//Method for new user registration using email and password
Future<User> registerWithEmailAndPassword(
String email,String password,String displayName) async {
try {
_status = Status.Registering;
notifyListeners();
final UserCredential result = await _auth.createuserWithEmailAndPassword(
email: email,password: password);
var userFromFirebase = _userFromFirebase(result.user);
await userFromFirebase.updateProfile(
displayName: displayName,photoURL: null);
return userFromFirebase;
} catch (e) {
print("Error on the new user registration = " + e.toString());
_status = Status.Unauthenticated;
notifyListeners();
return null;
}
}
//Method to handle user sign in using email and password
Future<bool> signInWithEmailAndPassword(String email,String password) async {
try {
_status = Status.Authenticating;
notifyListeners();
await _auth.signInWithEmailAndPassword(email: email,password: password);
_currentUser = FirebaseAuth.instance.currentUser;
return true;
} catch (e) {
print("Error on the sign in = " + e.toString());
_status = Status.Unauthenticated;
notifyListeners();
return false;
}
}
//Method to handle password reset email
Future<void> sendPasswordResetEmail(String email) async {
await _auth.sendPasswordResetEmail(email: email);
}
//update the user details
Future<User> updateUserDetails(
String email,String displayName,String url,String phoneNumber) async {
await currentUser.updateProfile(displayName: displayName,photoURL: url);
if (currentUser.email != email) {
await currentUser.updateEmail(email);
}
if (currentUser.phoneNumber != phoneNumber) {}
_currentUser = FirebaseAuth.instance.currentUser;
return currentUser;
}
//Method to handle user signing out
Future signOut() async {
_auth.signOut();
_status = Status.Unauthenticated;
notifyListeners();
return Future.delayed(Duration.zero);
}
}
主要动作
Future<void> main() async {
setupServiceLocator();
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MainWidget());
}
class MainWidget extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<AuthenticationService>(
create: (context) => AuthenticationService(),child: AuthenticationWrapper(
databaseBuilder: (_,user) => FirestoreDatabase(user: user),);
}
}
身份验证包装
class AuthenticationWrapper extends StatelessWidget {
const AuthenticationWrapper({Key key,this.databaseBuilder})
: super(key: key);
// Expose builders for 3rd party services at the root of the widget tree
// This is useful when mocking services while testing
final FirestoreDatabase Function(BuildContext context,User user)
databaseBuilder;
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return AuthWidgetBuilder(
databaseBuilder: databaseBuilder,builder: (BuildContext context,AsyncSnapshot<User> usersnapshot) {
return MaterialApp(
debugShowCheckedModeBanner: false,title: 'My Docs App',routes: {
// When navigating to the "/second" route,build the SecondScreen widget.
'/signup': (context) => SignUpScreen(),'/profile': (context) => ProfileScreen(),'/passwordreset': (context) => PasswordResetScreen(),},home: SafeArea(
child: Consumer<AuthenticationService>(
builder: (_,authProviderRef,__) {
if (usersnapshot.connectionState == ConnectionState.active) {
if (usersnapshot.hasData) {
// if (!usersnapshot.data.emailVerified) {
// return VerifyEmailPage();
// }
return HomeScreen();
} else {
return SignInScreen();
}
}
return Material(
child: CircularProgressIndicator(),);
},);
},);
}
}
身份验证构建器
class AuthWidgetBuilder extends StatelessWidget {
const AuthWidgetBuilder(
{Key key,@required this.builder,@required this.databaseBuilder})
: super(key: key);
final Widget Function(BuildContext,AsyncSnapshot<User>) builder;
final FirestoreDatabase Function(BuildContext context,User user)
databaseBuilder;
@override
Widget build(BuildContext context) {
final authService =
Provider.of<AuthenticationService>(context,listen: false);
return StreamBuilder<User>(
stream: authService.user,AsyncSnapshot<User> snapshot) {
final User user = snapshot.data;
if (user != null) {
/*
* For any other Provider services that rely on user data can be
* added to the following MultiProvider list.
* Once a user has been detected,a re-build will be initiated.
*/
return MultiProvider(
providers: [
Provider<User>.value(value: user),Provider<FirestoreDatabase>(
create: (context) => databaseBuilder(context,user),child: builder(context,snapshot),);
}
return builder(context,snapshot);
},);
}
}
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)