这是我的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编辑器停止了抱怨,
答案 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;
}