MultiProvider 和摘要 class
MultiProvider and abstract class
美好的一天!
如何使用 MaterialApp 的 Provider?我有一个 MultiProvider 和抽象 class。需要将 auth 传递给 LandingPage
这是我想获得的:
Widget build(BuildContext context) {
return Provider<AuthBase>(
create: (context) => Auth(),
child: MaterialApp(
title: "Bloc Test",
theme: ThemeData(primarySwatch: Colors.indigo),
home: LandingPage(),
),
);
}
这是我的工作代码:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<ToDoProvider>(
create: (ctx) => ToDoProvider(),
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Ultimative ToDo',
theme: ThemeData(
scaffoldBackgroundColor: myListMainColor,
textTheme:
GoogleFonts.sourceSansProTextTheme(Theme.of(context).textTheme),
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: '/',
routes: {
'/': (context) => LandingPage(auth: Auth()),
OpenedToDo.routeName: (context) => OpenedToDo(),
},
),
我有一个抽象的 class AuthBase,但我不能将它与 ChangeNotifier 混合使用。所以这就是为什么我不能将新的代码字符串放入 MultiProvider 的原因。
abstract class AuthBase {
User get currentUser;
Future<User> signInAnonymously();
Stream<User> authStateChanges();
Future<void> singOut(BuildContext context);
Future<User> singInWithGoogle();
Future<User> createUserWithEmailAndPassword(String email, String password);
Future<User> signInWithEmailAndPassword(String email, String password);
}
class Auth implements AuthBase {
final _firebaseAuth = FirebaseAuth.instance;
@override
Stream<User> authStateChanges() => _firebaseAuth.authStateChanges();
@override
User get currentUser => _firebaseAuth.currentUser;
@override
Future<User> signInAnonymously() async {
}
@override
Future<User> signInWithEmailAndPassword(String email, String password) async {
}
@override
Future<User> createUserWithEmailAndPassword(
}
@override
Future<User> singInWithGoogle() async {
}
@override
Future<void> singOut(BuildContext context) async {
}
}
错误是:
Error: Could not find the correct Provider above this
StreamBuilder Widget
This likely happens because you used a BuildContext
that does not
include the provider of your choice. There are a few common scenarios:
The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route,
then other routes will not be able to access that provider.
You used a BuildContext
that is an ancestor of the provider you are trying to read.
Make sure that StreamBuilder is under your
MultiProvider/Provider. This usually happen when you are
creating a provider and trying to read it immediately.
For example, instead of:
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>()),
), } ```
consider using `builder` like so:
``` Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context) {
// No longer throws
return Text(context.watch<Example>()),
}
), } ```
着陆页:
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: auth.authStateChanges(),
builder: (context, snapshot) {
//* если подключился к данным
if (snapshot.connectionState == ConnectionState.active) {
//* получаем данные о пользователе
final User user = snapshot.data;
print('~ uid is ${user?.uid}');
if (user == null) {
//Navigator.of(context).pushNamed(SignInPage.routeName, arguments: auth);
return SignInPage.create(context);
} else {
// Navigator.of(context).pushNamed(HomeScreen.routeName);
return HomeScreen();
}
}
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
},
);
}
SignInPage 有这部分代码:
static Widget create(BuildContext context) {
final auth = Provider.of<AuthBase>(context, listen: false);
return Provider<SignInBloc>(
create: (_) => SignInBloc(auth: auth),
//* обязательно должен быть dispose
dispose: (_, bloc) => bloc.dispose(),
//* consumer помогает прокинуть данные в конструктор
child: Consumer<SignInBloc>(
child: SignInPage(),
builder: (_, bloc, __) => SignInPage(bloc: bloc),
),
);
}
在转到问题的抽象主题之前,我建议在主文件夹上启动 MultiProvider。
像这样
import 'package:provider/provider.dart' as provider;
void main() {
runApp(provider.MultiProvider(
providers: [
provider.ChangeNotifierProvider.value(value: Prov1()),
provider.ChangeNotifierProvider.value(value: Prov2()),
provider.ChangeNotifierProvider.value(value: Prov3()),
provider.ChangeNotifierProvider.value(value: Prov4()),
],
child: MyApp(),
));
}
从现在开始,您可以在您的应用程序的任何位置进行访问。
也许在实现抽象 class 的 classes 上使用您想要的提供程序,而不是直接在其上更改数据。希望我能帮上忙,把事情弄清楚。
好的,我找到了解决方法。这很容易。
因此,在 multiProvider 的代码部分的 main.dart 中,我应该只添加一个字符串:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<ToDoProvider>(
create: (ctx) => ToDoProvider(),
),
THIS CODE ==> Provider<AuthBase>.value(value: Auth()),
],
child: MaterialApp(
...
LandingPage.dart:
class LandingPage extends StatelessWidget {
static const routeName = '/landingPage';
@override
Widget build(BuildContext context) {
final auth = Provider.of<AuthBase>(context, listen: false);
return StreamBuilder(
stream: auth.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
final User user = snapshot.data;
print('~ uid is ${user?.uid}');
if (user == null) {
return SignInPage.create(context);
} else {
// Navigator.of(context).pushNamed(HomeScreen.routeName);
return HomeScreen();
}
}
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
},
);
}
}
在SignInPage.dart中:
static Widget create(BuildContext context) {
final auth = Provider.of<AuthBase>(context, listen: false);
return Provider<SignInBloc>(
create: (_) => SignInBloc(auth: auth),
dispose: (_, bloc) => bloc.dispose(),
child: Consumer<SignInBloc>(
child: SignInPage(),
builder: (_, bloc, __) => SignInPage(bloc: bloc),
),
);
}
所以因为 AuthBase 是抽象的 class 我不能将它与 ChangeNotifier 混合,但我不需要那样,因为 class 是不可变的。这就是为什么我使用 Provider.value(value: value).
美好的一天!
如何使用 MaterialApp 的 Provider?我有一个 MultiProvider 和抽象 class。需要将 auth 传递给 LandingPage
这是我想获得的:
Widget build(BuildContext context) {
return Provider<AuthBase>(
create: (context) => Auth(),
child: MaterialApp(
title: "Bloc Test",
theme: ThemeData(primarySwatch: Colors.indigo),
home: LandingPage(),
),
);
}
这是我的工作代码:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<ToDoProvider>(
create: (ctx) => ToDoProvider(),
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Ultimative ToDo',
theme: ThemeData(
scaffoldBackgroundColor: myListMainColor,
textTheme:
GoogleFonts.sourceSansProTextTheme(Theme.of(context).textTheme),
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: '/',
routes: {
'/': (context) => LandingPage(auth: Auth()),
OpenedToDo.routeName: (context) => OpenedToDo(),
},
),
我有一个抽象的 class AuthBase,但我不能将它与 ChangeNotifier 混合使用。所以这就是为什么我不能将新的代码字符串放入 MultiProvider 的原因。
abstract class AuthBase {
User get currentUser;
Future<User> signInAnonymously();
Stream<User> authStateChanges();
Future<void> singOut(BuildContext context);
Future<User> singInWithGoogle();
Future<User> createUserWithEmailAndPassword(String email, String password);
Future<User> signInWithEmailAndPassword(String email, String password);
}
class Auth implements AuthBase {
final _firebaseAuth = FirebaseAuth.instance;
@override
Stream<User> authStateChanges() => _firebaseAuth.authStateChanges();
@override
User get currentUser => _firebaseAuth.currentUser;
@override
Future<User> signInAnonymously() async {
}
@override
Future<User> signInWithEmailAndPassword(String email, String password) async {
}
@override
Future<User> createUserWithEmailAndPassword(
}
@override
Future<User> singInWithGoogle() async {
}
@override
Future<void> singOut(BuildContext context) async {
}
}
错误是:
Error: Could not find the correct Provider above this StreamBuilder Widget
This likely happens because you used a
BuildContext
that does not include the provider of your choice. There are a few common scenarios:
The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then other routes will not be able to access that provider.
You used a
BuildContext
that is an ancestor of the provider you are trying to read.Make sure that StreamBuilder is under your MultiProvider/Provider. This usually happen when you are creating a provider and trying to read it immediately.
For example, instead of:
return Provider<Example>( create: (_) => Example(), // Will throw a ProviderNotFoundError, because `context` is associated // to the widget that is the parent of `Provider<Example>` child: Text(context.watch<Example>()), ), } ``` consider using `builder` like so: ``` Widget build(BuildContext context) { return Provider<Example>( create: (_) => Example(), // we use `builder` to obtain a new `BuildContext` that has access to the provider builder: (context) { // No longer throws return Text(context.watch<Example>()), } ), } ```
着陆页:
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: auth.authStateChanges(),
builder: (context, snapshot) {
//* если подключился к данным
if (snapshot.connectionState == ConnectionState.active) {
//* получаем данные о пользователе
final User user = snapshot.data;
print('~ uid is ${user?.uid}');
if (user == null) {
//Navigator.of(context).pushNamed(SignInPage.routeName, arguments: auth);
return SignInPage.create(context);
} else {
// Navigator.of(context).pushNamed(HomeScreen.routeName);
return HomeScreen();
}
}
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
},
);
}
SignInPage 有这部分代码:
static Widget create(BuildContext context) {
final auth = Provider.of<AuthBase>(context, listen: false);
return Provider<SignInBloc>(
create: (_) => SignInBloc(auth: auth),
//* обязательно должен быть dispose
dispose: (_, bloc) => bloc.dispose(),
//* consumer помогает прокинуть данные в конструктор
child: Consumer<SignInBloc>(
child: SignInPage(),
builder: (_, bloc, __) => SignInPage(bloc: bloc),
),
);
}
在转到问题的抽象主题之前,我建议在主文件夹上启动 MultiProvider。 像这样
import 'package:provider/provider.dart' as provider;
void main() {
runApp(provider.MultiProvider(
providers: [
provider.ChangeNotifierProvider.value(value: Prov1()),
provider.ChangeNotifierProvider.value(value: Prov2()),
provider.ChangeNotifierProvider.value(value: Prov3()),
provider.ChangeNotifierProvider.value(value: Prov4()),
],
child: MyApp(),
));
}
从现在开始,您可以在您的应用程序的任何位置进行访问。 也许在实现抽象 class 的 classes 上使用您想要的提供程序,而不是直接在其上更改数据。希望我能帮上忙,把事情弄清楚。
好的,我找到了解决方法。这很容易。 因此,在 multiProvider 的代码部分的 main.dart 中,我应该只添加一个字符串:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<ToDoProvider>(
create: (ctx) => ToDoProvider(),
),
THIS CODE ==> Provider<AuthBase>.value(value: Auth()),
],
child: MaterialApp(
...
LandingPage.dart:
class LandingPage extends StatelessWidget {
static const routeName = '/landingPage';
@override
Widget build(BuildContext context) {
final auth = Provider.of<AuthBase>(context, listen: false);
return StreamBuilder(
stream: auth.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
final User user = snapshot.data;
print('~ uid is ${user?.uid}');
if (user == null) {
return SignInPage.create(context);
} else {
// Navigator.of(context).pushNamed(HomeScreen.routeName);
return HomeScreen();
}
}
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
},
);
}
}
在SignInPage.dart中:
static Widget create(BuildContext context) {
final auth = Provider.of<AuthBase>(context, listen: false);
return Provider<SignInBloc>(
create: (_) => SignInBloc(auth: auth),
dispose: (_, bloc) => bloc.dispose(),
child: Consumer<SignInBloc>(
child: SignInPage(),
builder: (_, bloc, __) => SignInPage(bloc: bloc),
),
);
}
所以因为 AuthBase 是抽象的 class 我不能将它与 ChangeNotifier 混合,但我不需要那样,因为 class 是不可变的。这就是为什么我使用 Provider.value(value: value).