[英]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_bloc
人flutter_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.