Flutter重定向到登录页面,使null上调用了'ancestorWidgetOfExactType'

时间:2019-02-12 19:52:55

标签: dart flutter

这是我的main.dart

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    return ScopedModel < UserModel > (
      model: UserModel(),
      child: ScopedModelDescendant < UserModel > (
        builder: (context, child, model) {
          return MaterialApp(
            
            title: 'S.O.S. CURUMIM',
            theme: new ThemeData(
              primarySwatch: Colors.blue,
              primaryColor: Color.fromARGB(255, 4, 125, 141)
            ),
            debugShowCheckedModeBanner: false,
            home: RootPage(),
          );
        }),
    );
  }

这是我的根页面

class RootPage extends StatefulWidget {

  @override
  State < StatefulWidget > createState() => _RootPageState();
}

enum AuthStatus {
  notDetermined,
  notSignedIn,
  signedIn,
}

class _RootPageState extends State < RootPage > {
  AuthStatus authStatus = AuthStatus.notDetermined;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    var auth = UserModel.of(context).isLoggedIn();

    setState(() {
      authStatus =
        auth == false ? AuthStatus.notSignedIn : AuthStatus.signedIn;
      print("authStatus: $authStatus");
    });

  }

  void _signedIn() {
    setState(() {
      authStatus = AuthStatus.signedIn;
    });
  }

  void _signedOut() {
    setState(() {
      authStatus = AuthStatus.notSignedIn;
      print("signedout");
    });
  }

  @override
  Widget build(BuildContext context) {
    switch (authStatus) {
      case AuthStatus.notDetermined:
        return _buildWaitingScreen();
      case AuthStatus.notSignedIn:
        return LoginScreen(onSignedIn: _signedIn);
      case AuthStatus.signedIn:
         return new HomeScreen(onSignedOut: _signedOut, );

    }
    return null;
  }

  Widget _buildWaitingScreen() {
    return Scaffold(
      body: Container(
        alignment: Alignment.center,
        child: CircularProgressIndicator(),
      ),
    );
  }
}

这是我的主屏幕

class HomeScreen extends StatelessWidget {
  TutHomeScreen({
    this.onSignedOut
  });
  final VoidCallback onSignedOut;
  @override
  Widget build(BuildContext context) {
    final _pageController = PageController();
    
    void _signOut(BuildContext context) async {
      try {
        print("singout called");
        await UserModel.of(context).signOut(context);
       onSignedOut();          
      } catch (e) {
        print(e);
      }
    }

    return PageView(
      controller: _pageController,
      physics: NeverScrollableScrollPhysics(),
      children: < Widget > [
        Scaffold(
          appBar: AppBar(
            title: Text("Curumins",
              style: TextStyle(
                color: Colors.white,
                fontSize: 22.0,
                fontWeight: FontWeight.bold,
              ),
            ),
            centerTitle: true,
            iconTheme: new IconThemeData(color: Colors.white),
            backgroundColor: Colors.deepOrange,
            actions: auth ? < Widget > [
              FlatButton(
                child: Text('Logout',
                  style: TextStyle(fontSize: 17.0, color: Colors.white)),
                onPressed: () {
                  _signOut(context);
                 

                })
            ] : null,

          ),
          body: CuruminsTab(),
          drawer: CustomDrawer(_pageController),
        ),

       ...

      ]);



  }
}

登录工作正常,但注销会使应用程序崩溃,并显示以下消息:

I / flutter(3857): The following NoSuchMethodError was thrown building Builder(dirty):
  I / flutter(3857): The method 'ancestorWidgetOfExactType'
was called on null.
I / flutter(3857): Receiver: null
I / flutter(3857): Tried calling: ancestorWidgetOfExactType(_InheritedModel < UserModel > )

我可能需要一些如何正确注销的帮助,在此先感谢您

编辑:添加了UserModel代码:

class UserModel extends Model {
  final FirebaseMessaging _messaging = FirebaseMessaging();
  FirebaseAuth _auth = FirebaseAuth.instance;

  FirebaseUser firebaseUser;
  Map < String, dynamic > userData = Map();

  bool isLoading = false;

  static UserModel of (BuildContext context) =>
    ScopedModel.of < UserModel > (context);

  @override
  void addListener(VoidCallback listener) {
    super.addListener(listener);

    _loadCurrentUser();
  }

  void signUp() {
    ...
  }

  void signIn({
    @required String email,
    @required String pass,
    @required VoidCallback onSuccess,
    @required VoidCallback onFail
  }) async {
    isLoading = true;
    notifyListeners();
    _auth.signInWithEmailAndPassword(email: email, password: pass).then(
      (user) async {
        firebaseUser = user;
        await _loadCurrentUser();
        onSuccess();
        isLoading = false;
        notifyListeners();
      }).catchError((e) {
      onFail();
      print("err: ${e.toString()}");
      isLoading = false;
      notifyListeners();
    });
  }

  bool isLoggedIn() {
    return firebaseUser != null;
  }

  Future < void > signOut(UserModel) async {
      await _auth.signOut();
      userData = Map();
      firebaseUser = null;
      notifyListeners();
    }
    ...
}

添加了随机文本,因此SO编辑器停止抱怨,添加了随机文本,因此SO编辑器停止抱怨,添加了随机文本,因此SO编辑器停止了抱怨,

3 个答案:

答案 0 :(得分:0)

您的UserModel类的样子如何?它的名称暗示它是一个模型类,但是您在其上调用了诸如signOut之类的方法,这表明它是一种身份验证服务。

您的ScopedModelDescendant<UserModel>已经在构建器中为您提供了模型,因此您可以将其直接传递给RootPage

在如何将事物连接起来上似乎有些混乱。

答案 1 :(得分:0)

您的document.addEventListener('click', function (event) { const target = event.target; // root is the element which was used in ReactDOM.render(<App />, root) // replace document.getElementById('root') with whatever works in your case const root = document.getElementById('root') const targetInReact = root.contains(target); }); 类实际上不是模型,更像是身份验证服务。

因此,我认为UserModel不是此处使用的正确抽象。

如果您想从小部件中访问身份验证服务,建议您使用提供程序包。这是一个很好的: https://pub.dartlang.org/packages/provider

这是基于ScopedModel的,因此您可以编写如下代码来访问您的服务:

InheritedWidget

答案 2 :(得分:0)

好吧,对于使用范围模型动态加载首页有任何问题的人,我最终基于https://medium.com/@anilcan/how-to-use-dynamic-home-page-in-flutter-83080da07012实现了以下例程。它要简单得多,并且可以与我的设置完美配合。

这是main.dart:

Widget _defaultHome = new LoginScreen();
void main() async {
  Future < String > getUserRole() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    String role = prefs.getString("role");
    print("GetUserRole: $role");
    return role;
  }
  getUserRole().then((role) {
    if (role != null) {
      if (role == "kid") _defaultHome = new KidHomeScreen();
      if (role == "tut") _defaultHome = new TutHomeScreen();
    }
  });
  runApp(new MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ScopedModel < UserModel > (
      model: UserModel(),
      child: ScopedModelDescendant < UserModel > (
        builder: (context, child, model) {
          return MaterialApp(
            title: 'My App',
            home: _defaultHome,
            routes: < String, WidgetBuilder > {
              // Set routes for using the Navigator.
              '/kidhome': (BuildContext context) => new KidHomeScreen(),
              '/tuthome': (BuildContext context) => new TutHomeScreen(),
              '/login': (BuildContext context) => new LoginScreen()
            },
          );
        }),
    );
  }

如果用户未登录(role =“ none”),这将显示LoginScreen。每当用户注销时,角色在SharedPrefs中都设置为“ none”。登录后,他被带到相关的主屏幕。这是LoginScreen中的登录例程:

class LoginPageState extends State<LoginPage>{
  
  final _formKey = GlobalKey<FormState>();
  final _scaffoldKey = GlobalKey<ScaffoldState>();
  final _emailController = TextEditingController();
  final _passController = TextEditingController();
    @override
    Widget build(BuildContext context) {
      key: _scaffoldKey,
      body: ScopedModelDescendant < UserModel > (
        builder: (context, child, model) {
          if (model.isLoading)
            return Center(child: CircularProgressIndicator(), );
          return new ListView(
            padding: const EdgeInsets.all(0.0),
              children: < Widget > [
                new Stack(
                  alignment: AlignmentDirectional.bottomCenter,
                  children: < Widget > [
                    new Column(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: < Widget > [
                        new Form(
                          key: _formKey,
                          child: Padding(
                            padding: EdgeInsets.all(16.0),
                            child: Column(
                              children: < Widget > [
                                extFormField(style: TextStyle(color: Colors.white),
                                  controller: _emailController,
                                  decoration: InputDecoration(
                                    icon: Icon(Icons.email, color: Colors.white),
                                    labelText: 'E-Mail',
                                    labelStyle: TextStyle(color: Colors.white),
                                    border: new UnderlineInputBorder(
                                      borderSide: new BorderSide(
                                        color: Colors.grey
                                      )
                                    )
                                  ),
                                  keyboardType: TextInputType.emailAddress,
                                  validator: (text) {
                                    if (text.isEmpty || !text.contains("@"))
                                      return "Inavlid E-mail!";
                                  },
                                ),
                                SizedBox(height: 16.0, ),
                                TextFormField(style: TextStyle(color: Colors.white),
                                  controller: _passController,
                                  decoration: InputDecoration(
                                    icon: Icon(Icons.lock, color: Colors.white),
                                    labelText: 'Password',
                                    labelStyle: TextStyle(color: Colors.white),
                                    border: new UnderlineInputBorder(
                                      borderSide: new BorderSide(
                                        color: Colors.grey
                                      )
                                    )
                                  ),
                                  obscureText: true,
                                  validator: (text) {
                                    if (text.isEmpty || text.length < 6)
                                      return "Invalid Password!";
                                  },
                                ),
                                Align(
                                  alignment: Alignment.centerRight,
                                  child: FlatButton(
                                    onPressed: () {
                                      if (_emailController.text.isEmpty)
                                        _scaffoldKey.currentState.showSnackBar(
                                          SnackBar(content: Text("Please provide your e-mail 
                                            for password recovery!", style: TextStyle(color: Colors.white),),
                                            backgroundColor: Colors.redAccent,
                                            duration: Duration(seconds: 2),
                                          ));
                                          else {
                                            UserModel.of(context).recoverPass(_emailController.text);
                                            _scaffoldKey.currentState.showSnackBar(
                                              SnackBar(
                                                content: Text("Check your e-mail!"),
                                                backgroundColor: Theme
                                                .of(context)
                                                .primaryColor,
                                                duration: Duration(seconds: 2),
                                              )
                                            );
                                          }
                                        },
                                        child: Text("Forgot my password", style: TextStyle(color: Colors.white),
                                          textAlign: TextAlign.right,
                                        ),
                                        padding: EdgeInsets.zero,
                                    ),
                                  ),


                                ], )
                            ),
                          ),
                          new SignUp()
                        ],
                      ),
                      Padding(
                        padding: const EdgeInsets.only(bottom: 50.0),
                          child: new InkWell(
                            onTap: () {
                              if (_formKey.currentState.validate()) {
                                model.signIn(
                                  email: _emailController.text.trim(),
                                  pass: _passController.text.trim(),
                                  onSuccess: _onSuccess,
                                  onFail: _onFail
                                );
                              }

                            },
                            child: Text("Sign In")),
                      )

                    ],
                  )

                ]);
          });

      }
    }

    void _onSuccess() {
      getUserRole().then((role) {
        if (role == "kid")
          Navigator.of(context).pushReplacementNamed('/kidhome');
        if (role == "tut")
          Navigator.of(context).pushReplacementNamed('/tuthome');
      });
    }

    Future < String > getUserRole() async {
      final SharedPreferences prefs = await SharedPreferences.getInstance();
      String role = prefs.getString("role");
      return role;
    }