[Перевод] Эффективное использование паттернов BLoC во флаттере

Программа перевода самородков Android iOS Flutter

Друзья, давно я не писал про флаттер. После написания двух статей о шаблоне BLoC я потратил некоторое время на анализ использования этого шаблона сообществом, и, ответив на несколько вопросов о реализации шаблона BLoC, я обнаружил, что есть много опасений по поводу шаблона BLoC. Поэтому я задумал набор методов, и каждый может следовать этому набору методов, чтобы правильно реализовать паттерн BLoC, что поможет разработчикам избежать некоторых распространенных ошибок при реализации. Итак, сегодня я познакомлю вас с тем, что вы должны соблюдать при использовании паттерна BLoC.8 золотых точек.

помещение

Читатель, которого я имею в виду, должен знать, что такое шаблон BLoC, или создать приложение, использующее шаблон (по крайней мере,CTRL + Cа такжеCTRL + V). если ты слышишь это в первый разBLoCЭто слово, то следующие три статьи могут быть очень хорошими, чтобы помочь вам понять эту закономерность.

  1. Создайте проект Flutter, используя шаблон BLoCпервая частьа такжеВторая часть

  2. Когда Firebase столкнулся с шаблоном BLoC

История знакомства с BLoC

Я знаю, что шаблон BLoC сложен для понимания и реализации. Я видел много сообщений разработчиков с вопросамиГде лучший ресурс для изучения шаблона BLoC?? Прочитав различные сообщения и комментарии, я думаю, что есть несколько вещей, которые мешают людям понять эту проблему.

  1. Думайте ответственно.

  2. Попытайтесь понять, сколько файлов BLoC необходимо создать.

  3. Опасайтесь, что этот паттерн увеличит сложность кода.

  4. Не уверен, когда поток будет утилизирован.

  5. Какова полная форма шаблона BLoC? (это компонент бизнес-логики)

  6. Еще другие причины...

Но сегодня я собираюсь перечислить некоторые из наиболее важных моментов, которые помогут вам реализовать шаблон BLoC более уверенно и эффективно. Теперь давайте кратко рассмотрим некоторые важные моменты.

Каждая страница имеет свой собственный BLoC

Это самый важный момент, о котором следует помнить. Всякий раз, когда вы создаете новую страницу, такую ​​как страница входа, страница регистрации, страница профиля и т. д., которая включает обработку данных, вы должныСоздать новый BLoC. Не используйте глобальный BLoC для всех страниц вашего приложения. Вы можете подумать, что если бы у нас был глобальный BLoC, мы могли бы легко обрабатывать данные на разных страницах. Это плохо, потому что ваша библиотека должна предоставлять эти общедоступные данные в BLoC. BLoC просто берет данные и вводит их на страницу для отображения пользователю.

左图是正确的使用模式

Каждый BLoC должен иметь метод dispose()

Это относительно просто. Каждый BLoC, который вы создаете, должен иметь одинdispose()метод. В этом методе вы очищаете или закрываете все созданные вами потоки. Ниже приведенdispose()простой пример.

class MoviesBloc {
  final _repository = Repository();
  final _moviesFetcher = PublishSubject<ItemModel>();

  Observable<ItemModel> get allMovies => _moviesFetcher.stream;

  fetchAllMovies() async {
    ItemModel itemModel = await _repository.fetchAllMovies();
    _moviesFetcher.sink.add(itemModel);
  }

  dispose() {
    _moviesFetcher.close();
  }
}

Не используйте StatelessWidget в BLoC

Всякий раз, когда вы хотите создать страницу, которая передает данные или извлекает данные из BLoC,пожалуйста, используйтеStatefulWidget. использоватьStatefulWidgetпо сравнению с использованиемStatelessWidgetСамое большое преимуществоStatefulWidgetМетоды жизненного цикла в . Далее в этой статье мы обсудим два наиболее важных метода, которые необходимо охватить при использовании шаблона BLoC.StatelessWidgetОтлично подходит для создания небольших статических частей страницы, таких как отображение изображений или жестко закодированного текста. Если вы хотите увидеть, как использоватьStatelessWidgetЧтобы реализовать паттерн BLoC, см. рекомендуемую статью выше.первая часть, пока вВторая часть, я описываю, почему я хочуStatelessWidgetпереехал вStatefulWidget.

Переопределите didChangeDependencies() для инициализации BLoC.

Если вам нуженcontextдля инициализации объекта BLoC, то этот метод находится вStatefulWidgetСамый важный метод, который необходимо переопределить в . Вы можете думать об этом как о методе инициализации (предпочтительно только для инициализации BLoC). Можно сказать, у насinitState()метод, так почему же мы используемdidChangeDependencies()метод. В документации четко указано, что изdidChangeDependencies()передачаBuildContext.inheritFromWidgetOfExactTypeбезопасно. Вот простой пример использования этого метода:

@override
  void didChangeDependencies() {
    bloc = MovieDetailBlocProvider.of(context);
    bloc.fetchTrailersById(movieId);
    super.didChangeDependencies();
  }

Переопределите метод dispose(), чтобы уничтожить BLoC.

Так же, как есть метод инициализации, у нас также есть метод удаления соединения, которое мы создали в BLoC.dispose()Метод заключается в вызове соответствующего BLoC, подключенного к странице.dispose()Лучшее место для метода. Всякий раз, когда вы покидаете страницу, вам нужно вызывать этот метод (на самом делеStatefulWidgetпри утилизации). Вот небольшой пример метода:

@override
  void dispose() {
    bloc.dispose();
    super.dispose();
  }

Используйте RxDart только тогда, когда вам нужно обрабатывать сложную логику

Если вы использовали паттерн BLoC раньше, вы, должно быть, слышали о нем.[RxDart](https://github.com/ReactiveX/rxdart)библиотека. Эта библиотека является библиотекой реактивного функционального программирования Google Dart, это просто оболочка вокруг того, что предоставляет Dart.StreamAPI. Я рекомендую вам использовать эту библиотеку только в том случае, если вам нужно обрабатывать сложную логику, например объединение нескольких сетевых запросов в цепочку. Для некоторых простых реализаций используйте предоставленный язык DartStreamAPI достаточно, потому что этот API очень зрелый. Ниже я добавил BLoC, который используетStreamAPI вместоRxDartбиблиотека, это сделает операцию очень простой, нам не нужны дополнительные библиотеки для достижения того же самого:

import 'dart:async';

class Bloc {

  //Our pizza house
  final order = StreamController<String>();

  //Our order office
  Stream<String> get orderOffice => order.stream.transform(validateOrder);

  //Pizza house menu and quantity
  static final _pizzaList = {
    "Sushi": 2,
    "Neapolitan": 3,
    "California-style": 4,
    "Marinara": 2
  };

  //Different pizza images
  static final _pizzaImages = {
    "Sushi": "http://pngimg.com/uploads/pizza/pizza_PNG44077.png",
    "Neapolitan": "http://pngimg.com/uploads/pizza/pizza_PNG44078.png",
    "California-style": "http://pngimg.com/uploads/pizza/pizza_PNG44081.png",
    "Marinara": "http://pngimg.com/uploads/pizza/pizza_PNG44084.png"
  };


  //Validate if pizza can be baked or not. This is John
  final validateOrder =
      StreamTransformer<String, String>.fromHandlers(handleData: (order, sink) {
    if (_pizzaList[order] != null) {
      //pizza is available
      if (_pizzaList[order] != 0) {
        //pizza can be delivered
        sink.add(_pizzaImages[order]);
        final quantity = _pizzaList[order];
        _pizzaList[order] = quantity-1;
      } else {
        //out of stock
        sink.addError("Out of stock");
      }
    } else {
      //pizza is not in the menu
      sink.addError("Pizza not found");
    }
  });

  //This is Mia
  void orderItem(String pizza) {
    order.sink.add(pizza);
  }
}

Используйте PublishSubject вместо BehaviorSubject

Для тех, кто использует в проектах FlutterRxDartДля библиотечных людей это будет более явным.BehaviorSubjectэто особыйStreamController, который захватывает последний элемент, добавленный в контроллер, и запускает его как первое событие нового прослушивателя. даже если тыBehaviorSubjectзвонитьclose()илиdrain(), он по-прежнему сохраняет последний элемент и срабатывает, когда этот прослушиватель подписан. Если разработчики не понимают эту функцию, это может превратиться в кошмар. а такжеPublishSubjectПоследний элемент не сохраняется, что больше подходит для большинства случаев. в этотпроект, вы можете просмотретьBehaviorSubjectфункция. Запустите приложение и перейдите на страницу «Добавить цель», введите данные в форму и вернитесь назад. Теперь снова посетите страницу «Добавить цель», и вы обнаружите, что форма предварительно заполнена данными, которые вы ввели ранее. Если вы такие же ленивые, как и я, то можете посмотреть видео, которое я прикрепил ниже:

Goals App Demo

Надлежащее использование поставщиков BLoC

Прежде чем я скажу это, взгляните на приведенный ниже фрагмент кода (строки 9 и 10).

import 'package:flutter/material.dart';
import 'ui/login.dart';
import 'blocs/goals_bloc_provider.dart';
import 'blocs/login_bloc_provider.dart';

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return LoginBlocProvider(
      child: GoalsBlocProvider(
        child: MaterialApp(
          theme: ThemeData(
            accentColor: Colors.black,
            primaryColor: Colors.amber,
          ),
          home: Scaffold(
            appBar: AppBar(
              title: Text(
                "Goals",
                style: TextStyle(color: Colors.black),
              ),
              backgroundColor: Colors.amber,
              elevation: 0.0,
            ),
            body: LoginScreen(),
          ),
        ),
      ),
    );
  }
}

Вы можете ясно видеть, что несколько поставщиков BLoC вложены друг в друга. На этом этапе вы должны быть обеспокоены тем, что если вы продолжите добавлять больше BLoC в ту же цепочку, это приведет к кошмару, и вы можете прийти к выводу, что модель BLoC не может масштабироваться. Но, позвольте мне сказать вам, может быть особый случай, когда вам нужно получить доступ к нескольким BLoC в дереве виджетов (BLoC содержат только конфигурацию пользовательского интерфейса, требуемую приложением), поэтому для этого случая приведенная выше вложенность прекрасно подходит. Но я рекомендую в большинстве случаев избегать такого вложения и предоставлять BLoC только там, где это действительно необходимо. Так, например, когда вам нужно перейти на новую страницу, вы можете использовать BLoC Provider следующим образом:

openDetailPage(ItemModel data, int index) {
    final page = MovieDetailBlocProvider(
      child: MovieDetail(
        title: data.results[index].title,
        posterUrl: data.results[index].backdrop_path,
        description: data.results[index].overview,
        releaseDate: data.results[index].release_date,
        voteAverage: data.results[index].vote_average.toString(),
        movieId: data.results[index].id,
      ),
    );
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) {
        return page;
      }),
    );
  }

так,MovieDetailBlocProviderне для всего дерева компонентов, а дляMovieDetailСтраница предоставляет BLoC. Как видите, я будуMovieDetailScreenхранится в новомfinal variable, чтобы каждый раз избегатьMovieDetailScreenКогда клавиатура открыта или закрыта, она будет воссозданаMovieDetailScreenЭта проблема.

это еще не закончено

Хотя это конец этой статьи, это не конец этой темы. Я также продолжу обогащать этот том новыми идеями по оптимизации паттернов BLoC. Я надеюсь, что эти идеи помогут вам лучше реализовать шаблон BLoC. Продолжайте учиться и продолжайте кодировать :). Если вам понравился этот пост, вы можете показать свою любовь, поставив лайк.

Если у вас есть какие-либо вопросы, пожалуйстаLinkedInСвяжитесь со мной, илиTwitterСледуй за мной. Я сделаю все возможное, чтобы решить вашу проблему.

Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.


Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из ИнтернетаНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,товар,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.