В третьей части нашей серии статей мы встраиваем групповой чат в наше социальное приложение. Это позволяет пользователям общаться с несколькими людьми одновременно. Мы используем Stream Chat, чтобы сделать тяжелую работу. В этом посте предполагается, что вы прошли часть 1 и часть 2.

Используя наш код из части 2, мы сосредотачиваемся только на приложении Flutter, поскольку наш бэкэнд уже дает нам все, что нам нужно. Напомним, что серверная часть генерирует токен внешнего интерфейса для Stream Chat, который позволяет приложению Flutter напрямую взаимодействовать с API Stream Chat. Кроме того, поскольку у нас реализован прямой обмен сообщениями, дополнительных библиотек нет. Все, что нам нужно - это установленные ранее библиотеки Stream Chat Android и Swift.

Приложение выполняет следующие действия, чтобы включить групповой чат:

  • Пользователь переходит к списку каналов чата, к которым он может присоединиться. Для начала их не будет, поэтому они должны создать первую.
  • Пользователь нажимает «Создать канал чата» и создает канал чата с идентификатором.
  • Мобильное приложение запрашивает у канала предыдущие сообщения и сообщает Stream, что мы хотели бы посмотреть этот канал в поисках новых сообщений. Мобильное приложение прослушивает новые сообщения.
  • Пользователь создает новое сообщение и отправляет его в Stream Chat API. Stream передает это сообщение всем пользователям, смотрящим этот канал.
  • Когда сообщение транслируется, включая сообщения, созданные пользователем, мобильное приложение использует событие и отображает сообщение.

Мы полагаемся на библиотеки Stream Android / Swift для выполнения большей части работы по взаимодействию с API. Это достигается за счет использования каналов платформы Flutter (Swift / Kotlin) для взаимодействия с собственным кодом (Kotlin / Swift). Если вы хотите продолжить, убедитесь, что и серверная часть, и мобильное приложение запускают часть 2, прежде чем продолжить.

Предпосылки

Для работы с этим руководством необходимы базовые знания Node.js (JavaScript), Flutter (Dart) и Kotlin. Знание Swift полезно, если вы хотите просмотреть реализацию iOS. Этот код предназначен для локального запуска на вашем компьютере.

Если вы хотите продолжить, вам понадобится аккаунт в Stream. Убедитесь, что вы можете запустить приложение Flutter, по крайней мере, на Android. Если вы еще не сделали этого, убедитесь, что у вас установлен Flutter. Если у вас возникли проблемы при создании этого проекта, проверьте, можете ли вы создать запускаемое простое приложение, следуя инструкциям здесь.

Если у вас есть учетная запись в Stream, вам необходимо настроить приложение для разработки (см. Часть 1:

Вам нужно будет добавить учетные данные из приложения Stream в исходный код, чтобы оно работало. См. Как mobile, так и backend README.

Сначала мы рассмотрим, как пользователь создает групповой канал.

Для начала добавим в нижнюю панель новый элемент навигации:

Для этого в main.dart мы просто добавляем новый BottomNavigationItem:

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

Это загружает виджет Channels, который показывает список каналов и позволяет пользователю создать новый.

Шаг 2: Создание группового канала

Когда пользователь впервые попадает на этот экран, он будет пустым, если никто другой не создал никаких каналов. Мы добавим в виджет кнопку нового канала. Так как это будет список групповых каналов, мы пока будем использовать ListView с одним элементом, нашей новой кнопкой. Мы поговорим немного о том, как FutureBuilder и RefreshIndicator с состоянием _channel. Вот структурный код с кнопкой «Новый канал»:

Наш первый элемент списка - это кнопка. Когда пользователь нажимает кнопку, мы переходим к новому виджету с именем NewChannel. Мы проверяем возвращаемое значение Navigator.push, чтобы проверить создание канала. Если он был создан, обновим список каналов (мы его немного рассмотрим).

Во время навигации пользователь видит форму для создания канала. Это простой виджет, в котором пользователь вводит идентификатор канала и создает канал:

Давайте посмотрим на определение виджета:

Здесь мы видим простую форму Flutter с TextEditingController. Прежде всего, проверьте, является ли текст действительным идентификатором канала. В Stream Chat есть правила относительно того, как может выглядеть идентификатор канала, и для простоты мы просто создадим и перечислим каналы по этому идентификатору. Вы можете обратиться к документации, если хотите добавить отдельное название канала.

Как только пользователь отправляет идентификатор канала, мы просто переходим к виджету LivestreamChannel. Обратите внимание, что здесь мы не создаем канал в Stream. Stream лениво создает каналы при первом взаимодействии с ними. LivestreamChannel будет запрашивать и смотреть канал, который заставит его создать. Кроме того, мы используем имя «Livestream», чтобы отразить тип канала, который мы будем использовать в Stream. Livestream - это тип канала по умолчанию, который нам нужен, поскольку в части 4 мы внедрим живое видео в наш групповой канал. Если ни один из типов по умолчанию не работает для вашего приложения, вы можете создать свои собственные типы каналов.

Вот что видит пользователь при первом присоединении к групповому каналу:

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

Это простой шаблон, который показывает идентификатор канала вверху и две части, список сообщений и ввод нового сообщения. Когда мы инициализируем этот виджет, мы слушаем канал, очень похоже на то, как мы слушали прямые каналы сообщений в части 2. Мы делаем это в методе initState:

Мы вызываем метод .listenToChannel на ApiService. Это устанавливает запросы и отслеживает соответствующий канал Stream. Это означает, что он предоставит нам начальный набор сообщений и последующие сообщения. Каждый раз, когда мы получаем сообщения, мы объединяем их в ранее отображаемый набор. Мы увидим, как отобразить эти сообщения в несколько шагов.

Мы также храним функцию cancelChannel, которая позволяет виджету прекратить прослушивание после удаления:

Это важно, иначе мы бы вели себя странно из-за того, что слушатели-сироты слонялись поблизости. Давайте посмотрим на реализацию .listenToChannel:

Это идентично тому, как мы настраивали вещи в части 3, за исключением идентификатора канала. Поскольку нам дается идентификатор пользователя, нам не нужно его создавать. Этот метод сообщает собственной стороне настроить канал с помощью Stream и запускает EventChannel с этим идентификатором канала. Как только это будет сделано, мы подписываемся на EventChannel, который позволяет нативной стороне передавать нам сообщения. Мы берем этот поток, слушаем его, анализируем все встречающиеся результаты и передаем их виджету.

Затем мы переходим к нашей реализации setupChannel в Котлине. Этот метод координируется с Stream, устанавливает соединение с каналом и создает поток событий для отправки данных обратно на сторону Flutter:

Этот код и общается со Stream. Сначала мы создаем объект Channel с типом livestream и нашим идентификатором канала. Как описано ранее, livestream является подходящим типом канала по умолчанию для нашего группового чата. Это позволяет любому пользователю присоединиться к каналу и пообщаться с другими.

Затем мы запускаем Flutter EventChannel в Котлине. Это позволяет нам передавать данные обратно на сторону Flutter. В нашем .onListen методе, который вызывается, когда сторона Flutter подписывается на EventChannel, мы запрашиваем канал для начального набора сообщений и приказываем Stream следить за будущими сообщениями. Этот первоначальный запрос создаст канал в Stream, если он не существует. Первоначальный набор сообщений запустит наш QueryChannelCallback, и они будут отправлены через EventChannel в виде строки JSON.

Чтобы получать сообщения в будущем, нам нужно зарегистрировать обработчик событий в канале. Это делается по телефону channel.addEventHandler. Поскольку мы указали, что хотим посмотреть канал, когда выполняли наш первоначальный запрос, все будущие сообщения будут отправлены на наш обратный вызов ChatChannelEventHandler. Мы отправляем их через EventChannel в виде строки JSON, как указано выше.

Когда сторона Flutter указывает, что они хотят отменить, вызывается .onCancel. Мы просто прекращаем наблюдение и очищаем обработчики событий. Теперь пользователь готов отправить свое первое сообщение.

Шаг 3. Отправка сообщения

Для начала нам понадобится кнопка ввода и отправки:

Это выглядит странно, но все, что происходит, - это простой гибкий макет с двумя элементами, полем ввода и отправкой. Кнопка отправки - это размер значка, а ввод текста является гибким, что позволяет ему занимать оставшееся место. Мы также заключили его в Container с некоторым запасом, чтобы избежать специфичных для телефона функций, которые занимают нижнюю часть пространства, таких как индикатор дома в iOS.

Когда пользователь набрал сообщение и нажал «Отправить», мы готовы отправить сообщение на канал Stream. Вот реализация _sendMessage:

Это проверяет длину и передает ее методу API .postChannelMessage:

Которая затем передает его методу Kotlin .postChannelMessage:

Здесь мы создаем Stream Message и отправляем его на канал. Нашему коду особо нечего делать, поскольку библиотеки Stream берут на себя всю работу. Мы просто возвращаем true, когда Stream сообщает об успехе с помощью нашего обратного вызова.

Шаг 4. Просмотр сообщений

Наконец-то мы готовы ознакомиться со всей нашей тяжелой работой. Поскольку мы уже слушаем канал, который был настроен в части 2, все, что нам нужно сделать, это отобразить сообщения. Мы создаем представление списка, которое занимает остальное пространство, не занятое нашим вводом сообщения создания:

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

Шаг 5. Просмотр других каналов

Теперь пользователь может вернуться к списку каналов и просмотреть все каналы, в том числе созданные другими пользователями. Давайте вернемся к виджету, который мы начали на шаге 1, и обновим его, чтобы он запрашивал и отображал список каналов:

Все, что осталось для полноценной работы этого виджета, - это подключить вызовы к ApiService.channels и добавить их в наш список. Во-первых, когда мы впервые загружаем виджет в initState, нам нужно загрузить каналы:

Простые вызовы ApiService.channel выполняются на нативной стороне и декодируют результат:

Собственная реализация для получения каналов аналогична тому, как мы запрашиваем сообщения выше:

Мы просим библиотеку Stream запросить все livestream канала. Мы ищем только livestream, поэтому не указываем частные каналы прямого обмена сообщениями. Как только библиотека вернет нам эти данные, мы передаем их в виде строки JSON на сторону Flutter.

Поскольку наш ApiService возвращает будущее, которое будет содержать каналы, мы подключаем это Future к FutureBuilder, чтобы иметь дело с изменениями состояния:

После загрузки данных мы можем использовать snapshot.data из FutureBuilder, чтобы добавить к нашему ListView внутри нашего .build метода:

Когда мы щелкаем ListTile, мы просто переходим к соответствующему виджету LivestreamChat. Теперь пользователь будет видеть все каналы и иметь возможность общаться в чате на любом из них.

Последняя оставшаяся часть - подключить вызов ApiService.channels, где нам нужно обновить список. RefreshIndicator вызывает метод _refreshChannels, когда пользователь нажимает кнопку для обновления. Мы также называем это, когда успешно создаем канал. Вот реализация _refreshChannels:

Этот метод просто устанавливает новое состояние Future, которое будет дополнено последним списком каналов. После этого мы увидим обновленный список.

Вот и все! Теперь у нас есть полностью функционирующий групповой чат через потоковые каналы!

В дополнение к приведенному выше руководству Stream предлагает исчерпывающее руководство Flutter SDK Tutorial, чтобы вы могли узнать, как быстро приступить к работе с Flutter.