FLUTTER CLEAN ARCHITECTURE (PART 11) – cài đặt Bloc 1/2

Trần Đình Quý

Flutter Clean Architecture

Trong ứng dụng Number Trivia, chúng ta sẽ sử dụng BLoC để quản lý presentation logic. Chúng ta đã thiết lập các Sự kiện và Trạng thái của nó trong phần trước. Bây giờ là lúc bắt đầu kết hợp tất cả lại với các stream của Dart.

Cài đặt

Đúng vậy, chúng ta đã có các class Event và State có thể sử dụng từ bên trong NumberTriviaBloc, nhưng chúng ta cũng phải cân nhắc về các phụ thuộc (dependencies) mà nó sẽ có.

Vì Bloc (hoặc bất kỳ trình giữ logic trình bày nào khác) nằm ở ranh giới giữa các lớp domain và presentation, nó sẽ phụ thuộc vào hai use case mà chúng ta có cho ứng dụng của mình. Tất nhiên, nó cũng sẽ sử dụng InputConverter đã được tạo trong phần trước.

Hãy lần đầu tiên tạo constructor cùng với các field. Chúng ta sẽ thực hiện một số thủ thuật constructor với việc kiểm tra null và gán trường bên ngoài dấu ngoặc nhọn.

number_trivia_bloc.dart

class NumberTriviaBloc extends Bloc<NumberTriviaEvent, NumberTriviaState> {
  NumberTriviaBloc({
    required GetConcreteNumberTrivia concrete,
    required GetRandomNumberTrivia random,
    required this.inputConverter,
  })  : getConcreteNumberTrivia = concrete,
        getRandomNumberTrivia = random,
        super(Empty()) {
  }

  final GetConcreteNumberTrivia getConcreteNumberTrivia;
  final GetRandomNumberTrivia getRandomNumberTrivia;
  final InputConverter inputConverter;

  String _mapFailureToMessage(Failure failure) {
    switch (failure) {
      case ServerFailure _:
        return serverFailureMessage;
      case CacheFailure _:
        return cacheFailureMessage;
      default:
        return 'Unexpected error';
    }
  }
}

Khởi tạo State

init state sẽ trả về Empty

...
super(Empty()) {
...
}

validate & conversion

Điểm quan trọng nhất khi xử lý sự kiện GetTriviaForConcreteNumber được gửi đi là đảm bảo string lấy từ giao diện người dùng là một số nguyên dương hợp lệ. Nhờ sự tuyệt vời của dependency chúng ta đã có logic cần thiết để thực hiện việc xác thực và chuyển đổi này – nó nằm trong InputConverter. Do đó, chúng ta có thể tuân theo nguyên tắc single responsibility principle.

implementation.dart

...
super(Empty()) {
  on<GetTriviaForConcreteNumber>(_onGetTriviaForConcreteNumber);
}


Đây là bản dịch của văn bản bạn đã cung cấp:

Nếu việc chuyển đổi thành công, code sẽ tiếp tục lấy dữ liệu từ use case GetConcreteNumberTrivia. Tuy nhiên, trước tiên, hãy xử lý những gì xảy ra khi chuyển đổi không thành công. Trong trường hợp đó, NumberTriviaBloc có trách nhiệm thông báo cho giao diện người dùng biết điều gì đã xảy ra bằng cách phát ra trạng thái Error.

number_trivia_bloc.dart

const String SERVER_FAILURE_MESSAGE = 'Server Failure';
const String CACHE_FAILURE_MESSAGE = 'Cache Failure';
const String INVALID_INPUT_FAILURE_MESSAGE =
    'Invalid Input - The number must be a positive integer or zero.';

Trong phần triển khai tiếp theo, chúng ta sẽ thấy sức mạnh thực sự của Either. Bằng cách sử dụng phương thức fold của nó, chúng ta chỉ cần xử lý cả trường hợp thành công và thất bại, và không giống như với các exception, không có cách đơn giản nào để bỏ qua chúng.

    final Either<Failure, int> inputEither =
        inputConverter.stringToUnsignedInteger(event.numberString);

    await inputEither.fold(
      (Failure l) async => emit(Error(message: invalidInputFailureMessage)),
      (int r) async {
        throw UnimplementedError(),
      },
    );

Tiếp theo

Trong phần này chúng ta đã bắt đầu triển khai NumberTriviaBloc. Chúng ta còn thấy được lợi ích của việc sử dụng bloc. Phần tiếp theo chúng ta sẽ hoàn thiện bloc, xử lý concrete and random event.

Viết một bình luận