繁体   English   中英

如何将 Stream 转换为 Flutter 中的 Listenable?

[英]How to convert a Stream to a Listenable in Flutter?

我试图弄清楚如何利用 Firebase 的onAuthStateChanges() stream 用作 go_router package 的refreshListenable参数中的Listenable以在 authState 更改时进行重定向。 此外,我正在使用flutter_riverpod进行 State 管理。

到目前为止,我的代码如下所示:

我创建了一个简单的 AuthService class (缩小到最重要的部分):

abstract class BaseAuthService {
  Stream<bool> get isLoggedIn;
  Future<bool> signInWithEmailAndPassword({ required String email, required String password });
}

class AuthService implements BaseAuthService {
  final Reader _read;

  const AuthService(this._read);

  FirebaseAuth get auth => _read(firebaseAuthProvider);

  @override
  Stream<bool> get isLoggedIn => auth.authStateChanges().map((User? user) => user != null);

  @override
  Future<bool> signInWithEmailAndPassword({ required String email, required String password }) async {
    try {
      await auth.signInWithEmailAndPassword(email: email, password: password);
      return true;
    } on FirebaseAuthException catch (e) {
      ...
    } catch (e) {
      ...
    }

    return false;
  }

接下来我创建了这些提供程序:

final firebaseAuthProvider = Provider.autoDispose<FirebaseAuth>((ref) => FirebaseAuth.instance);

final authServiceProvider = Provider.autoDispose<AuthService>((ref) => AuthService(ref.read));

如前所述,我想以某种方式监听这些 authChanges 并将它们传递给路由器:

final router = GoRouter(
    refreshListenable: ???
    redirect: (GoRouterState state) {
        bool isLoggedIn = ???
        
        if (!isLoggedIn && !onAuthRoute) redirect to /signin;
    }
)

根据 go_router 文档,您可以简单地使用以下方法: https://gorouter.dev/redirection#refreshing-with-a-stream

GoRouterRefreshStream(_fooBloc.stream)

更新:2022 年 9 月 20 日

这个 class 从 go_router 5.0 中移除,并且 go_router 中没有提供替代 class。 原因是这个 class 与路由无关,不应包含在 go_router 中。 要迁移使用此 class 的现有应用程序,可以将 class 复制到其代码并直接拥有实现。

import 'dart:async';

import 'package:flutter/foundation.dart';

class GoRouterRefreshStream extends ChangeNotifier {
  GoRouterRefreshStream(Stream<dynamic> stream) {
    notifyListeners();
    _subscription = stream.asBroadcastStream().listen(
          (dynamic _) => notifyListeners(),
        );
  }

  late final StreamSubscription<dynamic> _subscription;

  @override
  void dispose() {
    _subscription.cancel();
    super.dispose();
  }
}

我真的不知道如何使用 riverpod 来做到这一点,但我认为您不需要使用 riverpod 的上下文。 使用 Provider 我会做这样的事情:

  // Somewhere in main.dart I register my dependencies with Provider:

      Provider(
        create: (context) =>  AuthService(//pass whatever),
     // ...

  // Somewhere in my *stateful* App Widget' State:
  // ...
  late ValueListenable<bool> isLoggedInListenable;

  @override
  void initState(){
    // locate my authService Instance
    final authService = context.read<AuthService>();
    // as with anything that uses a stream, you need some kind of initial value
    // "convert" the stream to a value listenable
    isLoggedInListenable = authService.isLoggedIn.toValueListenable(false);
    super.initState();
  }

  @override
  Widget build(BuildContext context){
    final router = GoRouter(
      refreshListenable: isLoggedInListenable,
      redirect: (GoRouterState state) {
        bool isLoggedIn = isLoggedInListenable.value;
      
        if (!isLoggedIn && !onAuthRoute) //redirect to /signin;
        // ...
      }
    );
    return MaterialApp.router(
      // ...
    );
  }

这是将流“转换”为 ValueListenable 的扩展

extension StreamExtensions<T> on Stream<T> {
  ValueListenable<T> toValueNotifier(
    T initialValue, {
    bool Function(T previous, T current)? notifyWhen,
  }) {
    final notifier = ValueNotifier<T>(initialValue);
    listen((value) {
      if (notifyWhen == null || notifyWhen(notifier.value, value)) {
        notifier.value = value;
      }
    });
    return notifier;
  }

  Listenable toListenable() {
    final notifier = ChangeNotifier();
    listen((_) {
      // ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member
      notifier.notifyListeners();
    });
    return notifier;
  }
}

它有效是因为 ValueListenable 是一个 Listenable! 与 ChangeNotifier 相同(它也只保存数据)。

在您的情况下,如果您可以在声明路由器之前获得 authService 的实例,则可以将流转换为可监听的然后使用它。 确保它是小部件状态的一部分,否则您可能会收集通知程序垃圾。 另外,我添加了一个notifyWhen方法,以防您想按条件过滤通知。 在这种情况下不需要并且 ValueNotifier 只会在值实际更改时发出通知。

再补充一点,对于使用flutter_blocflutter_bloc这个扩展也有效:

extension BlocExtensions<T> on BlocBase<T> {
  Listenable asListenable() {
    final notifier = ChangeNotifier();
    stream.listen((_) {
      // ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member
      notifier.notifyListeners();
    });
    return notifier;
  }

  ValueListenable<T> asValueListenable({
    BlocBuilderCondition? notifyWhen,
  }) {
    final notifier = ValueNotifier<T>(state);
    stream.listen((value) {
      if (notifyWhen == null || notifyWhen(notifier.value, value)) {
        notifier.value = value;
      }
    });
    return notifier;
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM