我正在构建用于注册和登录的示例应用程序, 第一页显示的是使用Firebase的移动身份验证,成功身份验证后,我从firebase中获取uid,并尝试与我的云Firestore集合(“用户”)的documenteID匹配。如果它为空,则我想显示SignupScreen(),否则,请显示HomeScreen()。
这是我的简单逻辑,但是我不知道发生这种类型的错误,
我也尝试了一些不同的代码
ChangeNotifierProvider(
create: (BuildContext context) => User(),
),
但是它仍然显示错误。
这是我的代码。
main.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:phone_auth_example/homePage.dart';
import 'package:phone_auth_example/signup_screen.dart';
import 'package:provider/provider.dart';
import './signUpPage.dart';
import './user.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
final FirebaseAuth auth = FirebaseAuth.instance;
var checkUser = false;
void isNewUser() async {
final FirebaseUser user = await auth.currentUser();
final uid = user.uid;
Provider.of<User>(context, listen: false).isUserNew(uid);
checkUser = Provider.of<User>(context, listen: false).checkUser;
}
isNewUser();
return MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: User(),
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: StreamBuilder(
// ignore: deprecated_member_use
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (ctx, userSnapshot) {
if (userSnapshot.hasData) {
if (checkUser) {
return SignupScreen();
} else {
return HomePage();
}
} else if (userSnapshot.hasError) {
return CircularProgressIndicator();
}
return LoginPage();
},
),
routes: {
SignupScreen.routeName: (ctx) => SignupScreen(),
},
),);
}
}
user.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
enum UserType {
Member,
HigherAuthority,
SecurityGuard,
}
enum UserStatus {
Owner,
Renter,
}
enum OccupancyStatus {
CurrentlyResiding,
EmptyHouse,
}
class User with ChangeNotifier {
final String id;
final UserType userType;
final String mobileNumber;
final String name;
final String email;
final String password;
final String houseNumberId;
final UserStatus userStatus;
final OccupancyStatus occupancyStatus;
User({
@required this.id,
@required this.userType,
@required this.mobileNumber,
@required this.name,
@required this.email,
@required this.password,
@required this.houseNumberId,
@required this.userStatus,
@required this.occupancyStatus,
});
bool checkUser;
Future<void> isUserNew(String userId) async {
// Get data from firestore
DocumentSnapshot user =
await Firestore.instance.collection('user').document(userId).get();
//print(variable.data == null);
// Check whether userId is in collection or not
checkUser = (user.data == null) ? true : false;
}
}
首先,它是打开的LoginPage(),它运行良好,然后,无论uid是否匹配,都直接转到HomePage()而不是signupPage(),所以这是一个问题,并且显示错误。
错误
I/Choreographer(10505): Skipped 246 frames! The application may be doing too much work on its main thread.
E/flutter (10505): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: Error: Could not find the correct Provider<User> above this MyApp Widget
E/flutter (10505):
E/flutter (10505): This likely happens because you used a `BuildContext` that does not include the provider
E/flutter (10505): of your choice. There are a few common scenarios:
E/flutter (10505):
E/flutter (10505): - The provider you are trying to read is in a different route.
E/flutter (10505):
E/flutter (10505): Providers are "scoped". So if you insert of provider inside a route, then
E/flutter (10505): other routes will not be able to access that provider.
E/flutter (10505):
E/flutter (10505): - You used a `BuildContext` that is an ancestor of the provider you are trying to read.
E/flutter (10505):
E/flutter (10505): Make sure that MyApp is under your MultiProvider/Provider<User>.
E/flutter (10505): This usually happen when you are creating a provider and trying to read it immediately.
E/flutter (10505):
E/flutter (10505): For example, instead of:
E/flutter (10505):
E/flutter (10505): ```
E/flutter (10505): Widget build(BuildContext context) {
E/flutter (10505): return Provider<Example>(
E/flutter (10505): create: (_) => Example(),
E/flutter (10505): // Will throw a ProviderNotFoundError, because `context` is associated
E/flutter (10505): // to the widget that is the parent of `Provider<Example>`
E/flutter (10505): child: Text(context.watch<Example>()),
E/flutter (10505): ),
E/flutter (10505): }
E/flutter (10505): ```
E/flutter (10505):
E/flutter (10505): consider using `builder` like so:
E/flutter (10505):
E/flutter (10505): ```
E/flutter (10505): Widget build(BuildContext context) {
E/flutter (10505): return Provider<Example>(
E/flutter (10505): create: (_) => Example(),
E/flutter (10505): // we use `builder` to obtain a new `BuildContext` that has access to the provider
E/flutter (10505): builder: (context) {
E/flutter (10505): // No longer throws
E/flutter (10505): return Text(context.watch<Example>()),
E/flutter (10505): }
E/flutter (10505): ),
E/flutter (10505): }
E/flutter (10505): ```
E/flutter (10505):
E/flutter (10505): If none of these solutions work, consider asking for help on StackOverflow:
答案 0 :(得分:0)
您的问题是,您正在尝试调用Provider.of<User>(context, listen: false).checkUser
,而上下文中尚未提供任何提供者(因为您使用MultiProvider
在树的下方)。
您应该向上移动MultiProvider。这是一个实现示例:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:phone_auth_example/homePage.dart';
import 'package:phone_auth_example/signup_screen.dart';
import 'package:provider/provider.dart';
import './signUpPage.dart';
import './user.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<User>(
create: (context) => User(),
),
],
builder: (BuildContext context, Widget child) {
final FirebaseAuth auth = FirebaseAuth.instance;
var checkUser = false;
void isNewUser() async {
final FirebaseUser user = await auth.currentUser();
final uid = user.uid;
Provider.of<User>(context, listen: false).isUserNew(uid);
checkUser = Provider.of<User>(context, listen: false).checkUser;
}
isNewUser();
return child;
},
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: StreamBuilder(
// ignore: deprecated_member_use
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (ctx, userSnapshot) {
if (userSnapshot.hasData) {
if (checkUser) {
return SignupScreen();
} else {
return HomePage();
}
} else if (userSnapshot.hasError) {
return CircularProgressIndicator();
}
return LoginPage();
},
),
routes: {
SignupScreen.routeName: (ctx) => SignupScreen(),
},
),
);
}
}
}
此外,还有2种对ChangeNotifier的滥用:
1 User类应该扩展 ChangeNotifier。
替换
class User with ChangeNotifier
使用
class User extends ChangeNotifier
如果已经创建了ChangeNotifier实例(在您的情况下为User):here is a great explaination of the use cases,则应使用2 ChangeNotifierProvider.value。既然没有,那么您应该像这样使用ChangeNotifierProvider:
ChangeNotifierProvider<User>(
create: (context) => User(),
child: ...
)