Шаблон блока для сохранения сеанса

Я использовал шаблон 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);
}

На уровне приложения есть кубит, который подписывается на поток сеанса репозитория и с помощью 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.

На каком уровне я могу сохранить сеанс, чтобы предотвратить тесную связь?

Взгляните на это. bloclibrary.dev/#/architecture?id=bloc-to-bloc-коммуникация

Monasha 10.04.2023 11:44
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
72
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете просто оставить StreamSubscription<Session> в классе LoginRepository. Этот экземпляр LoginRepository будет передан в конструктор любого блока/кубита, которому он нужен, и все они будут слушать один и тот же поток.

При этом я не вижу ничего плохого в том, как вы настроили его сейчас, когда вы просто берете код сеанса из SessionCubit, когда он вам нужен. У вас уже есть настройка слабой связи, потому что у вас нет других локтей, напрямую зависящих от SessionCubit. Это упрощает тестирование без необходимости заглушать другие Cubits в каждом экземпляре blocTest.

Любой из этих подходов соответствует шаблону Bloc и поддерживает слабую связь между классами Bloc.

Ответ принят как подходящий

Я управлял архитектурой на основе руководства по архитектуре BLoC, чтобы разделить уровень данных, уровень предметной области и уровень приложения.

  • Прикладной уровень:
    • локти/ блок
    • Флаттер-виджеты
  • Слой репозитория:
    • Репозиторий входа
  • Слой данных:
    • Удаленный сервер
    • Постоянное хранение с ульем

Затем на уровне репозитория создайте алгоритм, чтобы проверить, не существует ли сеанс на уровне сохраняемых данных или срок его действия истек, затем вызовите метод выхода из системы (поскольку API не поддерживает конечную точку токена обновления, я вызвал метод выхода из системы, который очищает хранилище и перенаправляет пользователя на экран входа) Затем, когда пользователь запрашивает логин и мы получаем код состояния ответа 200 с сеансом, мы сохраняем сеанс на уровне сохранения.

Ключ к решению этой проблемы находится на уровне репозитория и политике сохранения для управления вызовами на удаленный сервер или использования сеанса на уровне сохраняемых данных.

Шаг 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!);
            }
          },
        ),
      ),
    );
  }
}

Другие вопросы по теме