如何设置从另一个 StreamProvider 获取参数值的 StreamProvider?
How to setup StreamProvider which take a param value from another StreamProvider?
我正在为我的应用程序使用 FirebaseAuth 和 Firestore。
'User' 是一个模型,它表示 firestore 上的文档,并使用 FirebaseUser 的 uid 作为其 DocumentId。
为了获得 'User',我必须从 FirebaseUser 传入 uid。
我的想法是设置一个FirebaseUser的StreamProvider,然后设置另一个User的StreamProvider,它以FirebaseUser为参数。
但是出现错误,我找不到解决这个问题的方法,任何建议都会对我有很大帮助。
List<SingleChildCloneableWidget> uiConsumableProviders = [
StreamProvider<FirebaseUser>(
builder: (context) => Provider.of<LoginNotifier>(context, listen: false).streamFirebaseUser,
),
StreamProvider<User>(
builder: (context) =>
Provider.of<UserNotifier>(context, listen: false).streamUser(Provider.of<FirebaseUser>(context)),
),
];
I/flutter (15813): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (15813): The following assertion was thrown building InheritedProvider<FirebaseUser>:
I/flutter (15813): inheritFromWidgetOfExactType(InheritedProvider<FirebaseUser>) or inheritFromElement() was called
I/flutter (15813): before BuilderStateDelegate<Stream<User>>.initDelegate() completed.
I/flutter (15813):
I/flutter (15813): When an inherited widget changes, for example if the value of Theme.of()
I/flutter (15813): changes, its dependent widgets are rebuilt. If the dependent widget's reference
I/flutter (15813): to the inherited widget is in a constructor or an initDelegate() method, then
I/flutter (15813): the rebuilt dependent widget will not reflect the changes in the inherited
I/flutter (15813): widget.
I/flutter (15813):
I/flutter (15813): Typically references to inherited widgets should occur in widget build()
I/flutter (15813): methods. Alternatively, initialization based on inherited widgets can be placed
I/flutter (15813): in the didChangeDependencies method, which is called after initDelegate and
I/flutter (15813): whenever the dependencies change thereafter.
I/flutter (15813):
您不能在 builder
内调用 Provider.of
。
但是您可以使用小部件将 Provider.of
转换为流,然后在 StreamProvider
的 builder
中使用该流。
这是一个可以为您完成的小部件:
class ProviderToStream<T> extends StatefulWidget
implements SingleChildCloneableWidget {
const ProviderToStream({Key key, this.builder, this.child}) : super(key: key);
final ValueWidgetBuilder<Stream<T>> builder;
final Widget child;
@override
_ProviderToStreamState<T> createState() => _ProviderToStreamState<T>();
@override
ProviderToStream<T> cloneWithChild(Widget child) {
return ProviderToStream(
key: key,
builder: builder,
child: child,
);
}
}
class _ProviderToStreamState<T> extends State<ProviderToStream> {
final StreamController<T> controller = StreamController<T>();
@override
void dispose() {
controller.close();
super.dispose();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
controller.add(Provider.of<T>(context));
}
@override
Widget build(BuildContext context) {
return widget.builder(context, controller.stream, widget.child);
}
}
那么你可以这样做:
MultiProvider(
providers: [
StreamProvider<Foo>(builder: (_) => Stream.empty()),
ProviderToStream<Foo>(
builder: (_, foo, child) => StreamProvider<String>(
builder: (_) async* {
await for (final value in foo) {
yield value.toString();
}
},
child: child,
),
),
],
child: MaterialApp(),
);
经过一些变通办法后,我通过将 rxdart
中的 Observable
与 Provider
组合解决了我的问题。像这样:
Stream<User> get userStream {
return Observable(_firebaseAuth.onAuthStateChanged).switchMap((user) {
if (user != null) {
return userCollection
.document(user.uid)
.snapshots()
.map((doc) => User.fromDocumentSnapshot(doc))
.handleError(_catchError);
} else {
return Observable<User>.just(null);
}
});}
外部 MultiProvider:
StreamProvider<User>(builder: (context) => Provider.of<FireStoreService>(context, listen: false).userStream,),
希望这对遇到同样问题的人有所帮助。
我正在为我的应用程序使用 FirebaseAuth 和 Firestore。
'User' 是一个模型,它表示 firestore 上的文档,并使用 FirebaseUser 的 uid 作为其 DocumentId。
为了获得 'User',我必须从 FirebaseUser 传入 uid。
我的想法是设置一个FirebaseUser的StreamProvider,然后设置另一个User的StreamProvider,它以FirebaseUser为参数。
但是出现错误,我找不到解决这个问题的方法,任何建议都会对我有很大帮助。
List<SingleChildCloneableWidget> uiConsumableProviders = [
StreamProvider<FirebaseUser>(
builder: (context) => Provider.of<LoginNotifier>(context, listen: false).streamFirebaseUser,
),
StreamProvider<User>(
builder: (context) =>
Provider.of<UserNotifier>(context, listen: false).streamUser(Provider.of<FirebaseUser>(context)),
),
];
I/flutter (15813): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (15813): The following assertion was thrown building InheritedProvider<FirebaseUser>:
I/flutter (15813): inheritFromWidgetOfExactType(InheritedProvider<FirebaseUser>) or inheritFromElement() was called
I/flutter (15813): before BuilderStateDelegate<Stream<User>>.initDelegate() completed.
I/flutter (15813):
I/flutter (15813): When an inherited widget changes, for example if the value of Theme.of()
I/flutter (15813): changes, its dependent widgets are rebuilt. If the dependent widget's reference
I/flutter (15813): to the inherited widget is in a constructor or an initDelegate() method, then
I/flutter (15813): the rebuilt dependent widget will not reflect the changes in the inherited
I/flutter (15813): widget.
I/flutter (15813):
I/flutter (15813): Typically references to inherited widgets should occur in widget build()
I/flutter (15813): methods. Alternatively, initialization based on inherited widgets can be placed
I/flutter (15813): in the didChangeDependencies method, which is called after initDelegate and
I/flutter (15813): whenever the dependencies change thereafter.
I/flutter (15813):
您不能在 builder
内调用 Provider.of
。
但是您可以使用小部件将 Provider.of
转换为流,然后在 StreamProvider
的 builder
中使用该流。
这是一个可以为您完成的小部件:
class ProviderToStream<T> extends StatefulWidget
implements SingleChildCloneableWidget {
const ProviderToStream({Key key, this.builder, this.child}) : super(key: key);
final ValueWidgetBuilder<Stream<T>> builder;
final Widget child;
@override
_ProviderToStreamState<T> createState() => _ProviderToStreamState<T>();
@override
ProviderToStream<T> cloneWithChild(Widget child) {
return ProviderToStream(
key: key,
builder: builder,
child: child,
);
}
}
class _ProviderToStreamState<T> extends State<ProviderToStream> {
final StreamController<T> controller = StreamController<T>();
@override
void dispose() {
controller.close();
super.dispose();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
controller.add(Provider.of<T>(context));
}
@override
Widget build(BuildContext context) {
return widget.builder(context, controller.stream, widget.child);
}
}
那么你可以这样做:
MultiProvider(
providers: [
StreamProvider<Foo>(builder: (_) => Stream.empty()),
ProviderToStream<Foo>(
builder: (_, foo, child) => StreamProvider<String>(
builder: (_) async* {
await for (final value in foo) {
yield value.toString();
}
},
child: child,
),
),
],
child: MaterialApp(),
);
经过一些变通办法后,我通过将 rxdart
中的 Observable
与 Provider
组合解决了我的问题。像这样:
Stream<User> get userStream {
return Observable(_firebaseAuth.onAuthStateChanged).switchMap((user) {
if (user != null) {
return userCollection
.document(user.uid)
.snapshots()
.map((doc) => User.fromDocumentSnapshot(doc))
.handleError(_catchError);
} else {
return Observable<User>.just(null);
}
});}
外部 MultiProvider:
StreamProvider<User>(builder: (context) => Provider.of<FireStoreService>(context, listen: false).userStream,),
希望这对遇到同样问题的人有所帮助。