Изучение потоков во Flutter | Кодементор

Привет народ! Я вернулся с совершенно новой статьей. На этот раз мы поговорим о потоках во Flutter. Что такое Stream и как их использовать во Flutter? Мы ответим на этот вопрос в сегодняшней статье. Так что пристегните ремни безопасности, так как мы проведем поток дискуссий, чтобы понять мир Streams во Flutter.

План действий

Поскольку потоки немного сложно объяснить в одной или двух строках. Итак, я расскажу вам очень интересную историю из реальной жизни, чтобы вы поняли, что такое потоки. Как только история будет завершена (теоретическая часть), мы будем создавать приложение для той же (практической части). Чтобы мы лучше поняли, как на самом деле работают потоки.

Предпосылка

Чтобы понять эту тему, я ожидаю, что целевая аудитория должна иметь базовое практическое понимание того, как создать приложение с использованием платформы Flutter, и должна иметь базовое представление о БЛОК шаблон. Если вы не знаете об этом шаблоне, вы все равно можете насладиться статьей. Для шаблона BLoC вы можете проверить мои предыдущие статьи (ЧАСТЬ 1 а также Часть 2), где я подробно объясняю этот шаблон.

Что такое поток?

Согласно документам Flutter, «потоки предоставляют асинхронную последовательность данных». Хм! с этим лайнером не все так ясно. Итак, позвольте мне дать вам лучшее объяснение, рассказав вам историю из реального мира. Поверьте, это будет вкусно 😜.

История пиццерии


Прекрасная пицца

Давным-давно жил человек Джон кто очень хорошо умел делать пицца. Он приготовил много разных видов пиццы, таких как неаполитанская пицца, пицца в калифорнийском стиле, суши-пицца, пицца Маринара и т. д. Пицца, приготовленная Джоном, была настолько вкусной, что люди со всего мира приезжали к нему, чтобы попробовать волшебство, которое он записал в пицце, которую он приготовил. . У Джона был небольшой дом, где он принимал заказы, пек пиццу и доставлял ее покупателям. У него была одна молодая красивая дочь Миа (10% клиентов пришли в дом, чтобы увидеть ее 😛. Шучу), которая помогала отцу в его бизнесе. Джон следовал очень простому, но эффективному процессу, чтобы сделать хороший бизнес:

  1. Мия принимала заказы от клиентов и передавала их своему Джону.
  2. Джон посмотрит на заказ и проверит, можно ли испечь пиццу (без ингредиентов). Если у него есть ингредиенты, он испечет пиццу и передаст заказ в офис.
  3. Клиенты придут забрать офис и забрать свой заказ. Если Джон не может испечь пиццу, он скажет оставить записку для клиента, в которой говорится, что «Пицца, которую вы заказали, недоступна».

Вывод из истории

После прохождения этой интересной истории. Есть несколько интересных вещей, на которые я хотел бы указать, которые помогут нам понять полную концепцию Streams.

  1. Дом, где пекут пиццу. Этот дом является постоянным местом, иначе он не будет создаваться каждый раз, когда появится новый клиент. Дом создается только один раз и будет принимать заказы и обрабатывать их.
  2. Единственная обязанность Мии — принять заказ и передать его Джону. Она не будет решать, можно ли испечь эту пиццу или нет. Таким образом, Мия передаст заказ только Джону для его обработки.
  3. Джон принимает решения. Он обработает заказ. Он решит, можно ли испечь пиццу или нет. Как только он закончит обработку. Он будет передавать выход(пицца или «Нет в наличии»), чтобы забрать офис.
  4. Клиенты придут и заберут свой заказ (пицца или «Нет в наличии») в офисе сбора.

Итак, в итоге будет последовательный поток данных в поток, и эти данные в потоке будут обработаны и отправлены в выходной поток. Давайте теперь преобразуем приведенную выше историю в рабочий код. В процессе я объясню вам полную картину концепции Stream.

Потоки в Дарте

Когда мы говорим о Потоки специально для дартс. Мы будем использовать асинхронный библиотека предоставлена ​​дартс. Эта библиотека поддерживает асинхронное программирование и содержит все классы и методы для создания потоков. Итак, без дальнейших обсуждений, давайте начнем программировать нашу пиццерию. Минуточку, прежде чем что-либо внедрять, позвольте мне сначала показать вам конечный продукт. Чтобы вы могли соединить вещи, пока я помещаю здесь код, и вам будет легко понять мои объяснения для каждой строки кода.

Если посмотреть видео. Есть меню, состоящее из 4 кнопок. На каждой кнопке написано название пиццы. Если нажать любую из кнопок, будет размещен заказ пиццы определенного типа. Если вы нажмете кнопку, заказ будет принят Миа и передал его Джону. Джон обработает его и поместит результат в офис сбора. Клиент приедет в офис и заберет свой заказ. Он может получить заказанную пиццу (изображение и сообщение об успехе) или сообщение «Нет в наличии», если пиццы нет в наличии. Клиенты могут заказывать одну и ту же пиццу несколько раз. Но если запас конкретной пиццы закончился, вывод будет «Нет в наличии».

Надеюсь, у вас есть представление о реализации 😄. Если нет, я сделаю все возможное, чтобы подробно объяснить вам полную реализацию.

Давайте код

  1. Создайте новый проект Flutter, используя. Если вы не знаете, как проверить это.
  2. Удалите весь код из main.dart файл и вставьте следующий код:
import 'package:flutter/material.dart';
import 'src/app.dart';

void main() => runApp(new MyApp());
  1. Вы, должно быть, уже видите ошибку в MyApp(). Не волнуйтесь, мы исправим это на следующем шаге.

  2. Создайте пакет с именем src под lib упаковка. Теперь создайте файл дротика app.dart под src упаковка. Вставьте приведенный ниже код внутрь app.dart файл.

import 'package:flutter/material.dart';
import 'pizza_house.dart';
import 'blocs/provider.dart';

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provider( 
      child: MaterialApp(
        home: new PizzaHouse(),
      ),
    );
  }
}

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

  1. Создайте файл дротика под src пакет и назовите его как pizza_house.dart . Давайте пока оставим его пустым. Как только я закончу объяснять вам концепцию и план атаки, мы напишем внутри него некоторый код.

  2. Затем создайте еще один пакет с именем blocs под src упаковка. Создайте два файла дротика внутри blocs пакет и назовите их как bloc.dart , provider.dart . Вставьте ниже код внутри provider.dart файл и давайте сохраним bloc.dart файл пустой:

import 'package:flutter/material.dart';
import 'bloc.dart';
export 'bloc.dart';

class Provider extends InheritedWidget {
  final bloc = Bloc();

  Provider({Key key, Widget child}) : super(key: key, child: child);

  bool updateShouldNotify(_) => true;

  static Bloc of(BuildContext context) {
    return (context.inheritFromWidgetOfExactType(Provider) as Provider).bloc;
  }
}

Этот класс довольно прямолинеен. Его единственная ответственность заключается в предоставлении доступа к экземпляру нашего блока ( bloc.dart ) внутри нашего дерева виджетов.

На всякий случай, если вы хотите увидеть полную структуру проекта:

  1. Теперь пришло время реализовать bloc.dart файл. Если вы видите код ниже, не паникуйте. Я подробно объясню вам каждую строчку, тогда это будет иметь смысл, держу пари. Вот и идет полная реализация bloc.dart файл:
import 'dart:async';

class Bloc {

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

  //Our collect 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": "
    "Neapolitan": "
    "California-style": "
    "Marinara": " };

  //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);
  }
}

В самой первой строке вы, должно быть, ломаете голову 😜. Что за чертовщина StreamController? Я знаю, что это приближалось. Позвольте мне объяснить и облегчить вам 😆.

StreamController

Проще говоря, это наша пиццерия. Он отвечает за прием заказов, их обработку и выдачу результатов. Но какие методы делают StreamController полноценной пиццерией или, другими словами, какие методы отвечают за прием заказов, их обработку и выдачу результатов? Вот небольшая картинка, чтобы ответить на поставленный выше вопрос:


Демистификация StreamController

StreamController имеет два геттера: один sink другой stream .sink является Миа вот кто будет принимать заказы от клиентов и передавать их в поток(Джон потом забирает офис). Простыми словами sink добавит данные в stream StreamController. Теперь поговорим о stream . Он будет передавать данные во внешний мир (сбор офиса) после некоторой обработки ( Джон ). Теперь, если вы видите bloc.dart код. Нижеследующие строки являются sink а также stream StreamController:

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

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

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

sink имеет метод, называемый как add(data) который добавит данные в stream. Здесь он добавляет заказ в stream. orderOffice (коллектор офис) — это геттер, который будет вызываться в нашем pizza_house.dart (еще не реализовано), чтобы передать результат заказчику.

В приведенном выше коде вам должно быть интересно, что это такое transform() для. Это метод, который вызовет Джона для обработки входящих заказов и поместит обработанный вывод в поток (офис сбора). Теперь давайте немного поговорим о Джоне, который является главным сердцем пиццерии. Вот Джон наш validateOrder в приведенном выше коде. validateOrder это СтримТрансформер.

//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");
    }
  });

StreamTransformer

Простыми словами, он будет брать входящие заказы из потока и проверять, действителен ли заказ или нет (можно ли испечь пиццу или нет). Если заказ действителен, он добавит вывод в поток, используя sink.add(successOrder) (пицца успешно выпекается) методом. Здесь мы добавляем изображение испеченной пиццы в поток, чтобы показать покупателю, что его пицца готова. Если заказ недействителен, он добавит его в sink.addError(invalidOrder) сообщая покупателю, что «заказываемой вами пиццы нет в наличии».

Теперь я надеюсь, что у вас все налаживается. Перед внедрением pizza_house.dart есть еще две вещи в bloc.dart файл, который нуждается в некотором объяснении. Это строки кода:

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

  //Different pizza images static final _pizzaImages = {
    "Sushi": "
    "Neapolitan": "
    "California-style": "
    "Marinara": " };

_pizzaList это наше меню, которое будет содержать тип пиццы и общее количество, которое можно испечь в доме. _pizzaImages это карта, которая будет содержать изображения разных видов пиццы, которые пекут в пиццерии. Эти изображения будут показаны покупателю, чтобы он почувствовал, что получил свой заказ 😜.

Теперь последний класс, который мы будем реализовывать, т.е. pizza_house.dart файл. Вот код для него:

import 'package:flutter/material.dart';
import 'blocs/provider.dart';

class PizzaHouse extends StatelessWidget {
  var pizzaName = "";
  @override
  Widget build(BuildContext context) {
    final _bloc = Provider.of(context);
    return Scaffold(
      appBar: AppBar(
        title: Text("Pizza House"),
      ),
      body: Container(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[menu1(_bloc), menu2(_bloc), orderOffice(_bloc)],
        ),
      ),
    );
  }

  menu1(Bloc bloc) {
    return Container(
      margin: EdgeInsets.all(8.0),
      child: Row(
        children: <Widget>[
          Expanded(
            child: RaisedButton(
              child: Text("Neapolitan"),
              onPressed: () {
                bloc.orderItem("Neapolitan");
                pizzaName = "Neapolitan";
              },
            ),
          ),
          Container(
            margin: EdgeInsets.only(left: 2.0, right: 2.0),
          ),
          Expanded(
            child: RaisedButton(
              child: Text("California-style"),
              onPressed: () {
                bloc.orderItem("California-style");
                pizzaName = "California-style";
              },
            ),
          )
        ],
      ),
    );
  }

  menu2(Bloc bloc) {
    return Container(
      margin: EdgeInsets.all(8.0),
      child: Row(
        children: <Widget>[
          Expanded(
            child: RaisedButton(
              child: Text("Sushi"),
              onPressed: () {
                bloc.orderItem("Sushi");
                pizzaName = "Sushi";
              },
            ),
          ),
          Container(
            margin: EdgeInsets.only(left: 2.0, right: 2.0),
          ),
          Expanded(
            child: RaisedButton(
              child: Text("Marinara"),
              onPressed: () {
                bloc.orderItem("Marinara");
                pizzaName = "Marinara";
              },
            ),
          )
        ],
      ),
    );
  }

  orderOffice(Bloc bloc) {
    return StreamBuilder(
      stream: bloc.orderOffice,
      builder: (context, AsyncSnapshot<String> snapshot) {
        if (snapshot.hasData) {
          return Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Image.network(
                snapshot.data,
                fit: BoxFit.fill,
              ),
              Container(
                margin: EdgeInsets.only(top: 8.0),
              ),
              Text("Yay! Collect your $pizzaName pizza")
            ],
          );
        } else if (snapshot.hasError) {
          return Column(
            children: <Widget>[
              Image.network("
              fit: BoxFit.fill),
              Text(
                snapshot.error,
                style: TextStyle(
                  fontSize: 20.0,
                ),
              ),
            ],
          );
        }
        return Text("No item in collect office");
      },
    );
  }
}

Самая первая строка внутри build() метод — это объявление экземпляра блока. Используя этот экземпляр, мы можем размещать заказы и проверять вывод внутри офиса сбора. Сделать заказ определенного вида пиццы. Мы реализовали логику добавления этой конкретной пиццы в поток внутри onPressed() метод каждой кнопки. Внутри onPressed() метод, который мы вызываем orderItem(pizza) который, в свою очередь, вызывает sink.add() метод. Чтобы показать клиентам, каков результат их заказа в офисе сбора, нам нужно заставить наши виджеты работать с потоками. мы будем использовать StreamBuilder для этого .

StreamBuilder будет слушать поток из orderOffice() метод, и если есть какие-либо доступные данные, он вернет виджет на основе данных, поступающих из потока. StreamBuilder имеет два важных параметра. 1) stream и 2) builder. stream взять метод в качестве параметра, который возвращает поток, т.е. orderOffice метод из bloc.dart файл. stream StreamBuilder будет прослушивать любые поступающие новые данные. builder возьмет метод, который имеет два параметра, т.е. context а также snapshot . snapshot — это что-то вроде поставщика данных. Он получит данные из потока и предоставит их, чтобы мы могли их обработать и вернуть правильный виджет для отображения. Как вы можете видеть, есть ли какие-либо данные внутри снимка (мы можем проверить, используя hasData) мы вернем Image и Text виджет. Image это изображение заказанной пиццы. Каждый раз, когда заказывают пиццу, ее количество уменьшается (логика обрабатывается внутри трансформатора). Если пиццы больше не осталось. Вывод будет «Нет в наличии».

Вывод из статьи

Во всем приложении я не использовал ни одного StatefulWidget, но все же смог изменить состояние приложения. Другими словами, я сделал свое приложение реактивным, используя возможности Streams. Потоки очень полезны, когда вы хотите прослушивать изменения в ваших данных и реагировать в соответствии с ними. Надеюсь, теперь понятно, почему мы должны сделать наше приложение как можно более реактивным с помощью Streams.

Вот репозиторий github того же приложения, которое мы создали сегодня.


Фууу!! Мы сделали это. Черт, да, это было удивительное путешествие. Надеюсь, вам тоже понравилось. Дайте мне знать, насколько вам понравилась эта статья, поставив большой громкий хлопок. Знаете ли вы, что вы можете дать максимум 50 хлопков для каждой статьи. Если у вас есть какие-либо сомнения относительно Flutter, свяжитесь со мной по адресу LinkedIn . Увидимся в следующий раз с новой новой статьей. Мир на выход ✌️.


Флаттер Паб — это среднее издание, предлагающее вам последние и удивительные ресурсы, такие как статьи, видео, коды, подкасты и т. д. об этой замечательной технологии, чтобы научить вас создавать с ее помощью красивые приложения. Вы можете найти нас на Фейсбук, Твиттера также Середина или узнайте больше о нас здесь. Мы хотели бы подключиться! И если вы писатель, заинтересованный в том, чтобы писать для нас, вы можете это сделать. с помощью этих руководящих принципов.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *