使用 provider 和 get_it 在视图模型中 Flutter Null 属性

问题描述

我正在使用 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 (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...