Flutter 颤振-查找已停用的小部件';的祖先与提供程序包FireStore身份验证不安全
我在使用提供程序包通过SnackBar显示消息时遇到问题。我收到的错误消息是:Flutter 颤振-查找已停用的小部件';的祖先与提供程序包FireStore身份验证不安全,flutter,dart,flutter-provider,Flutter,Dart,Flutter Provider,我在使用提供程序包通过SnackBar显示消息时遇到问题。我收到的错误消息是: VERBOSE-2:ui_dart_state.cc(157)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe. At this point the state of the widget's element tree is no longer stable. To safely refer to a widget'
VERBOSE-2:ui_dart_state.cc(157)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable.
To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
#0 Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> (package:flutter/src/widgets/framework.dart:3508:9)
#1 Element._debugCheckStateIsActiveForAncestorLookup (package:flutter/src/widgets/framework.dart:3522:6)
#2 Element.findAncestorStateOfType (package:flutter/src/widgets/framework.dart:3641:12)
#3 Scaffold.of (package:flutter/src/material/scaffold.dart:1313:42)
#4 LoginScreen.build.<anonymous closure>.<anonymous closure> (package:zvjs_app/screens/login_screen.dart:74:38)
<asynchronous suspension>
#5 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182<…>
VERBOSE-2:ui\u dart\u state.cc(157)]未处理的异常:查找停用小部件的祖先不安全。
此时,小部件元素树的状态不再稳定。
要在dispose()方法中安全地引用小部件的祖先,请通过在小部件的didChangeDependencies()方法中调用dependOnInheritedWidgetOfExactType()保存对祖先的引用。
#0元素。\u debugcheckstatesactiveforancestorlookup。(包:flatter/src/widgets/framework.dart:3508:9)
#1元素。_debugcheckstatesactiveforancestorlookup(包:flatter/src/widgets/framework.dart:3522:6)
#2 Element.findAncestorStateOfType(包:flatter/src/widgets/framework.dart:3641:12)
#3.of脚手架(包装:颤振/src/材料/脚手架。省道:1313:42)
#4登录筛选。生成。。(软件包:zvjs_app/screens/login_screen.dart:74:38)
#5 GestureRecognizer.invokeCallback(包:颤振/src/手势/recognizer.dart:182
下面是我的代码,我认为所有属于逻辑的类都是必需的。我不明白为什么未来不“可用”或者sigIn方法中的user_log_in_provider.dart中的错误意味着什么。我还尝试通过变量_errorMessage显示来自sigIn方法的errorMessage,您可以在user_log_in_provider.dart中看到该变量,然后检查该消息是否为空。以这种方式运行代码,但它显示了一条延迟的消息。例如,首次登录失败(错误的电子邮件格式)->未显示任何消息。第二次登录失败(密码错误)->显示电子邮件格式错误的消息
主飞镖
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => UserLogIn.instance()),
ChangeNotifierProvider.value(value: Accommodations()),
],
child: MaterialApp(
title: 'ZVJS',
theme: ThemeData(
primarySwatch: Colors.blue,
buttonTheme: ButtonThemeData(
buttonColor: Colors.blue[300],
padding: EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 16.0,
),
)),
home: MyHomePage(),
routes: {
RegistrationScreen.routeName: (context) => RegistrationScreen(),
MainScreen.routeName: (context) => MainScreen(),
LoginScreen.routeName: (context) => MyHomePage(),
},
),
);
}
}
class MyHomePage extends StatelessWidget {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
@override
Widget build(BuildContext context) {
return Consumer<UserLogIn>(
builder: (context, user, _) {
switch (user.status) {
case Status.Uninitialized:
// return Splash();
case Status.Unauthenticated:
case Status.Authenticating:
return LoginScreen(
emailController: _emailController,
passwordController: _passwordController);
case Status.Authenticated:
return MainScreen();
default:
return ErrorPage();
}
},
);
}
}
void main()=>runApp(MyApp());
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
回程多供应商(
供应商:[
ChangeNotifierProvider(创建:(\u)=>UserLogIn.instance()),
ChangeNotifierProvider.value(值:调节()),
],
孩子:MaterialApp(
标题:“ZVJS”,
主题:主题数据(
主样本:颜色。蓝色,
buttonTheme:buttonTheme数据(
按钮颜色:颜色。蓝色[300],
填充:EdgeInsets.symmetric(
垂直线:8.0,
水平线:16.0,
),
)),
主页:MyHomePage(),
路线:{
RegistrationScreen.routeName:(上下文)=>RegistrationScreen(),
MainScreen.routeName:(上下文)=>MainScreen(),
LoginScreen.routeName:(上下文)=>MyHomePage(),
},
),
);
}
}
类MyHomePage扩展了无状态小部件{
final _emailController=TextEditingController();
final _passwordController=TextEditingController();
@凌驾
小部件构建(构建上下文){
退货消费者(
生成器:(上下文,用户,u41;{
开关(用户状态){
案例状态。未初始化:
//返回飞溅();
案例状态。未经验证:
案例状态。验证:
返回登录屏幕(
emailController:\u emailController,
passwordController:_passwordController);
案例状态。已验证:
返回主屏幕();
违约:
返回ErrorPage();
}
},
);
}
}
登录屏幕.dart
class LoginScreen extends StatelessWidget {
static const routeName = '/loginScreen';
final _emailController;
final _passwordController;
LoginScreen(
{@required TextEditingController emailController,
@required TextEditingController passwordController})
: this._emailController = emailController,
this._passwordController = passwordController;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(Constants.logInPageTitle),
),
body: Provider.of<UserLogIn>(context).status == Status.Authenticating
? SpinnerCustom(Constants.loggingIn)
: Center(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: TextFieldCustom(
text: Constants.email,
controller: _emailController,
icon: Icon(Icons.email),
textInputType: TextInputType.emailAddress,
),
),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: TextFieldCustom(
text: Constants.password,
controller: _passwordController,
icon: Icon(Icons.lock),
textInputType: TextInputType.visiblePassword,
),
),
const SizedBox(height: 10),
Builder(
builder: (ctx) => ButtonCustom(
text: Constants.logIn,
onPressed: () async {
var provider = Provider.of<UserLogIn>(ctx, listen: false);
String message = await provider.signIn(
_emailController.text,
_passwordController.text);
if (message != null) {
Scaffold.of(ctx).showSnackBar(SnackBar(
content: Text(message),
));
}
},
),
),
],
),
),
),
);
}
}
class LoginScreen扩展了无状态小部件{
静态常量routeName='/loginScreen';
最终控制人;
最终密码控制器;
登录屏幕(
{@required TextEditingController emailController,
@必需的文本编辑控制器密码控制器})
:this.\u emailController=emailController,
这。_passwordController=passwordController;
@凌驾
小部件构建(构建上下文){
返回脚手架(
appBar:appBar(
标题:常量文本(Constants.logInPageTitle),
),
body:Provider.of(context).status==status.authentication
?喷丝头定制(常数记录)
:中心(
子:SingleChildScrollView(
子:列(
儿童:[
填充物(
填充:常量边集。对称(水平:8.0),
孩子:TextFieldCustom(
文本:Constants.email,
控制器:\u电子邮件控制器,
图标:图标(Icons.email),
textInputType:textInputType.emailAddress,
),
),
const SizedBox(高度:20),
填充物(
填充:常量边集。对称(水平:8.0),
孩子:TextFieldCustom(
text:Constants.password,
控制器:_passwordController,
图标:图标(Icons.lock),
textInputType:textInputType.visiblePassword,
),
),
const SizedBox(高度:10),
建筑商(
生成器:(ctx)=>按钮自定义(
text:Constants.logIn,
onPressed:()异步{
var provider=provider.of(ctx,listen:false);
字符串消息=等待提供程序。签名(
_emailController.text,
_passwordController.text);
如果(消息!=null){
脚手架(ctx)。显示弹簧撑杆(弹簧撑杆(
内容:文本(信息),
));
}
},
),
),
],
),
),
),
);
}
}
<
enum Status { Uninitialized, Authenticated, Authenticating, Unauthenticated }
class UserLogIn with ChangeNotifier {
FirebaseAuth _auth;
FirebaseUser _user;
Status _status = Status.Uninitialized;
String _errorMessage;
UserLogIn.instance() : _auth = FirebaseAuth.instance {
_auth.onAuthStateChanged.listen(_onAuthStateChanged);
}
Status get status => _status;
FirebaseUser get user => _user;
String get errorMessage => _errorMessage;
Future<String> signIn(String email, String password) async {
try {
_status = Status.Authenticating;
notifyListeners();
await _auth.signInWithEmailAndPassword(email: email, password: password);
return null;
} catch (e) {
_errorMessage = e.message;
print(_errorMessage);
_status = Status.Unauthenticated;
notifyListeners();
return e.message;
}
}
Future<void> _onAuthStateChanged(FirebaseUser firebaseUser) async {
if (firebaseUser == null) {
_status = Status.Unauthenticated;
} else {
_user = firebaseUser;
_status = Status.Authenticated;
}
notifyListeners();
}
}
Scaffold.of(ctx).showSnackBar(SnackBar(
content: Text(message),
));
final globalScaffoldKey = GlobalKey<ScaffoldState>();
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
...
child: MaterialApp(
builder: (context, child) {
return Scaffold(
key: globalScaffoldKey,
body: child,
);
},
...
void showSnackbar(String message) {
var currentScaffold = globalScaffoldKey.currentState;
currentScaffold.hideCurrentSnackBar(); // If there is a snackbar visible, hide it before the new one is shown.
currentScaffold.showSnackBar(SnackBar(content: Text(message)));
}
showSnackbar('My Snackbar Message')