持久化会话的Bloc模式



我使用BloC模式创建一个应用程序。有一个数据提供程序层负责从远程服务器请求会话代码。

abstract class AppApi{
/// Logins a user to server and return a [Session].
///
/// Throw [HttpException] if response status code is not 200.Throw
/// [FetchSessionException] if response message is not "OK" or response data is null.
Future<Session> fetchSession(String username, String password);
}

有一个存储库层负责向应用层公开带有Stream<Session> get session的会话。

abstract class LoginRepository {
factory LoginRepository(AppApi appApi) =>
LoginRepositoryImpl(appApi);
Stream<Session> get session;
Future<void> getSession(String username, String password);
}

应用层中有一个cubit订阅存储库会话流,使用hybricCubit保存会话。

class SessionCubit extends HydratedCubit<PersistenceSession> {
final LoginRepository _loginRepository;
late StreamSubscription<Session> _loginStreamSubscription;
static final defaultSessionState = PersistenceSession.empty;
SessionCubit(this._loginRepository) : super(defaultSessionState) {
_loginStreamSubscription =
_loginRepository.session.asBroadcastStream().listen((session) {
if (session == Session.empty) {
emit(defaultSessionState);
} else {
emit(
state.copyWith(session: session.sessionCode),
);
}
});
}
}

每当其他腕表想要请求服务器时,它们都会使用context.read<PersisSessionCubit>.state.session.code请求会话并将代码作为参数传递给存储库和数据层。

但是我想在存储库层或数据层中持久化会话,然后应用程序层中的其他腕表使用这个保存的会话和StreamSubscription

我可以在哪一层保存会话以防止紧耦合?

您可以将StreamSubscription<Session>保留在LoginRepository类中。LoginRepository的实例将被传递到任何需要它的Bloc/cube的构造函数中,并且它们都将侦听相同的流。

话虽如此,我认为你现在如何设置它没有任何问题,你只是在需要的时候从SessionCubit抓取会话代码。您已经有了松耦合设置,因为您没有其他腕尺直接依赖于SessionCubit。这使得测试更容易,而不必在每个blocTest实例中存根其他腕表。

这两种方法都符合Bloc模式,并且保持了Bloc类之间的松耦合。

步骤1:定义SessionState类

class SessionState {
final bool isAuthenticated;
final String? username;
SessionState({required this.isAuthenticated, this.username});
SessionState copyWith({bool? isAuthenticated, String? username}) {
return SessionState(
isAuthenticated: isAuthenticated ?? this.isAuthenticated,
username: username ?? this.username,
);
}
}

步骤2:定义s# # Heading ##essionEvent类

abstract class SessionEvent {}
class LoginEvent extends SessionEvent {
final String username;
final String password;
LoginEvent({required this.username, required this.password});
}
class LogoutEvent extends SessionEvent {}

步骤3:定义SessionBloc类

class SessionBloc extends Bloc<SessionEvent, SessionState> {
SessionBloc() : super(SessionState(isAuthenticated: false));

步骤4:从持久存储

加载初始会话状态
@override
Future<void> onLoad() async {
super.onLoad();
SharedPreferences prefs = await SharedPreferences.getInstance();
bool isAuthenticated = prefs.getBool('isAuthenticated') ?? false;
String? username = prefs.getString('username');
SessionState initialState =
SessionState(isAuthenticated: isAuthenticated, username: username);
emit(initialState);
}
@override
Stream<SessionState> mapEventToState(SessionEvent event) async* {
if (event is LoginEvent) {
// Step 5: Handle the LoginEvent by updating the session state and saving it to persistent storage
SharedPreferences prefs = await SharedPreferences.getInstance();
bool isAuthenticated = true;
String username = event.username;
await prefs.setBool('isAuthenticated', isAuthenticated);
await prefs.setString('username', username);
yield state.copyWith(isAuthenticated: isAuthenticated, username: username);
} else if (event is LogoutEvent) {

步骤5:通过更新会话状态并将其保存到持久存储来处理LogoutEvent

SharedPreferences prefs = await SharedPreferences.getInstance();
bool isAuthenticated = false;
String? username = null;
await prefs.setBool('isAuthenticated', isAuthenticated);
await prefs.remove('username');
yield state.copyWith(isAuthenticated: isAuthenticated, username: username);
}}}

步骤6:在你的应用程序中使用SessionBloc

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocProvider(
create: (BuildContext context) => SessionBloc(),
child: BlocBuilder<SessionBloc, SessionState>(
builder: (context, state) {
if (!state.isAuthenticated) {
return LoginPage();
} else {
return HomePage(username: state.username!);
}
},
),
),
);
}
}

我根据BLoC架构指南管理架构,分离数据层、领域层和应用层。

  • 应用层:
    • 肘/集团
    • 颤振部件
  • 库层:<<ul>
  • 登录库/gh>
  • 数据层:远程服务器
    • 使用hive持久化存储
  • 然后在存储库层创建一个算法来检查持久化数据层中的会话是否不存在或已过期,然后调用logout方法(由于API不支持刷新令牌端点,我调用logout方法,该方法清除存储并将用户重定向到登录屏幕)然后,当用户请求登录时,我们得到一个带有会话的200响应状态码,我们将会话存储在持久化层中。

    解决这个问题的关键是在存储库层和持久化策略上管理对远程服务器的调用,或者在持久化数据层使用会话。

    最新更新