【问题标题】:How to setup StreamProvider which take a param value from another StreamProvider?如何设置从另一个 StreamProvider 获取参数值的 StreamProvider?
【发布时间】:2019-08-28 08:41:58
【问题描述】:

我正在为我的应用程序使用 FirebaseAuth 和 Firestore。

'User' 是一个模型,它代表 Firestore 上的文档,并使用 FirebaseUser 的 uid 作为其 DocumentId。

为了获得“用户”,我必须从 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 仅被调用一次,但从另一个提供程序获得的值会随着时间而改变。
  • @RémiRousselet 是的,这正是我被卡住的原因。我的目的是我想从小部件树中的任何地方获取用户数据,所以我需要像流一样的东西。请你给我一些关于这个问题的建议吗?

标签: flutter flutter-provider


【解决方案1】:

您不能在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(),
);

【讨论】:

  • 嗨 Remi,如果我只想这样做:MultiProvider( providers: [ StreamProvider.value(value: AppAuth.currentUser), StreamProvider.value( value: AppDb. getUserData(Provider.of(context)?.uid), ],但是 UserDataModel 需要先从 AuthUser 的 uid 输入?
  • 需要包含哪些软件包才能使其工作?我收到未解决的 SingleChildCloneableWidget 引用
【解决方案2】:

经过一些变通方法后,我通过将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,),

希望这对面临同样问题的人有所帮助。

【讨论】:

    猜你喜欢
    • 2020-11-04
    • 2020-12-30
    • 2020-10-23
    • 2021-06-27
    • 2021-09-25
    • 1970-01-01
    • 2021-08-04
    • 2021-06-21
    • 1970-01-01
    相关资源
    最近更新 更多