В этом посте мы создадим простую социальную сеть под названием Stream Flutter, которая позволяет пользователю публиковать сообщения для подписчиков.

Приложение позволит пользователю публиковать сообщения своим подписчикам. API канала активности Stream в сочетании с Flutter от Google упрощает создание такого рода сложного взаимодействия. Весь исходный код этого приложения доступен на GitHub. Это приложение полнофункционально как на iOS, так и на Android.

Для краткости, когда нам нужно перейти к собственному коду, мы сосредоточимся только на Android. Вы можете найти соответствующий код iOS, чтобы увидеть, как все реализовано. Чтобы не упустить цель, мы будем показывать наиболее важные фрагменты кода, чтобы получить представление о каждой части. Часто вокруг таких важных фрагментов кода, как макет или навигация, есть контекст. Пожалуйста, обратитесь к полному исходному тексту, если вы не знаете, как что-то работает или как мы попали на экран. Каждый фрагмент будет сопровождаться комментарием, объясняющим, из какого файла и строки он был взят.

Чтобы построить нашу социальную сеть, нам понадобится как серверная часть, так и мобильное приложение. Большая часть работы выполняется в мобильном приложении, но нам нужен бэкэнд для безопасного создания токенов внешнего интерфейса для взаимодействия с Stream API.

В качестве бэкэнда мы будем полагаться на Express (Node.js), использующий библиотеку JavaScript Stream.

Что касается внешнего интерфейса, мы создадим его с помощью Flutter, обертывающего библиотеки Stream Java и Swift.

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

  • Пользователь вводит свое имя в наше мобильное приложение, чтобы войти в систему.
  • Мобильное приложение регистрирует пользователя на нашем сервере и получает токен внешнего интерфейса Stream Activity Feed.
  • Пользователь вводит сообщение и нажимает Опубликовать. Мобильное приложение использует токен Stream для создания активности Stream, используя каналы платформы Flutter для подключения к Stream’s REST API через Java или Swift.
  • Пользователь просматривает свои сообщения. Мобильное приложение делает это, получая свой user канал через Stream.

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

  • Авторизуйтесь (см. Выше).
  • Пользователь переходит к списку пользователей и выбирает пользователя, на которого следует подписаться. Мобильное приложение напрямую взаимодействует с Stream API, создавая отношения подписчиков в их timeline фиде.
  • Пользователь просматривает свою временную шкалу. Мобильное приложение использует Stream API для получения своего timeline фида, который состоит из всех сообщений от тех, на кого они подписаны.

Код разделен между мобильным приложением Flutter, содержащимся в каталоге mobile, и серверной частью Express в каталоге backend. См. README.md в каждой папке, чтобы увидеть инструкции по установке и запуску. Если вы хотите продолжить выполнение кода, убедитесь, что у вас запущены и серверная часть, и мобильное приложение, прежде чем продолжить.

Предпосылки

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

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

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

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

Приступим к строительству!

Начнем с того, что позволим пользователю публиковать сообщения.

Шаг 1. Авторизация

Чтобы взаимодействовать с Stream API, нам нужен защищенный токен внешнего интерфейса, который позволяет нашему мобильному приложению аутентифицироваться напрямую с Stream. Это позволяет избежать использования прокси-сервера через backend. Для этого нам понадобится серверная конечная точка, которая использует секреты нашей учетной записи Stream для генерации этого токена. Как только у нас есть этот токен, нам больше не понадобится серверная часть для каких-либо действий, поскольку мобильное приложение имеет доступ к полному Stream API.

Сначала мы создадим экран входа в систему, который будет выглядеть так:

Для начала давайте разместим нашу форму во Flutter. В нашем main.dart файле мы создадим простую проверку, чтобы увидеть, вошли ли мы в систему, и, если у нас ее нет, покажем пользователю форму входа в систему:

Переменная _account - это простой Map<String, String> объект, который будет содержать серверную часть authToken и поток feedToken. authToken используется для дальнейших запросов к бэкэнду, которые мы будем использовать позже для получения списка пользователей. feedToken - это токен внешнего интерфейса Stream, который обеспечивает доступ к Stream API.

Чтобы установить переменную _account, мы возьмем строку, введенную пользователем после того, как он нажал «Вход», и передадим ее ApiService для выполнения нашей аутентификации. Вот наша _login(..) функция:

Мы используем введенное пользователем имя, чтобы получить наши учетные данные из серверной части и сохранить их в нашей переменной _account. Это два вызова: один для получения нашего backend токена аутентификации, а другой - для получения токена Stream API. Для этого давайте посмотрим на нашу реализацию ApiService#login:

Здесь происходят две вещи. Сначала мы регистрируем пользователя на сервере и получаем authToken. Используя этот authToken, мы просим серверную часть создать токен внешнего интерфейса Stream Activity Feed.

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

Чтобы сгенерировать токен Stream, давайте посмотрим, что делает бэкэнд для его генерации:

Этот код использует наши секретные учетные данные для создания пользователя Stream и регистрации имени пользователя. вызов getOrCreate создает пользователя с именем (или просто извлекает этого пользователя, если мы уже зарегистрировали его). После создания пользователя мы возвращаем необходимые учетные данные в мобильное приложение.

Как только мы вошли в систему, мы готовы опубликовать наше первое сообщение!

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

Теперь мы создадим форму для публикации сообщения о статусе в ленте активности Stream. В этом руководстве мы не будем углубляться в навигацию и макет. Обратитесь к источнику, если вам интересно, как мы попадаем на этот экран. Нам нужно будет создать форму, которая будет принимать то, что пользователь хочет сказать своим подписчикам, и отправить это в Stream.

Во-первых, форма:

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

Это простой виджет Flutter, который использует TextEditingController для отслеживания ввода пользователя и использует MaterialButton для запуска публикации.

Теперь давайте посмотрим на реализацию _postMessage, с помощью которой мы создаем действие с нашим сообщением в Stream:

Мы просто берем набранный текст, передаем его нашему ApiService и открываем стек навигации, чтобы вернуться к предыдущему экрану. Вот реализация ApiService#postMessage:

Поскольку мы собираемся использовать библиотеку Streams JavaSwift на iOS), мы вызываем нативную реализацию через встроенные в Flutter каналы платформы. Мы не будем вдаваться в подробности того, как происходит этот вызов, поэтому либо обратитесь к исходному коду, либо прочитайте документацию Flutter, как это сделать. Вот реализация Kotlin (реализация для iOS доступна в исходниках):

Здесь мы используем Stream Java’s CloudClient из Cloud package. Этот набор классов принимает наш токен внешнего интерфейса, который позволяет мобильному приложению напрямую связываться со Stream. Мы аутентифицированы только для публикации действий для актера SU:john. Поскольку мы не храним соответствующий объект в базе данных, мы генерируем идентификатор, чтобы сохранить уникальность каждого сообщения. Мы также передаем полезную нагрузку сообщения, которую увидят наши подписчики.

Вам может быть интересно, что имеет в виду client.flatFeed("user"). Чтобы это работало, нам нужно настроить плоский канал под названием «пользователь» в Stream. Здесь будет храниться лента каждого пользователя, содержащая только их сообщения.

Внутри вашего приложения для разработки Stream создайте плоский канал под названием «пользователь»:

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

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

А вот код:

Мы используем FutureBuilder для асинхронной загрузки наших действий. Мы не будем вдаваться в этот шаблон, поэтому обратитесь к документации или The Boring Flutter Development Show, чтобы понять, как это работает. Мы также используем простой RefreshIndicator для реализации тяги для обновления. Объединение этих двух встроенных виджетов вместе позволяет нам отображать сообщения пользователя и обновлять их, когда они публикуют новое сообщение.

Код, специфичный для Stream, происходит с нашей переменной _activities. Чтобы получить информацию о действиях пользователя (которые содержат наши сообщения), мы еще раз вызываем наш ApiService:

Наш ApiService#getActivities обращается к машинному коду для выполнения работы:

В Kotlin мы используем CloudClient, чтобы запрашивать плоский канал пользователя и получать последние 25 сообщений (действий):

Далее мы увидим, как следить за несколькими пользователями с помощью фида временной шкалы.

Хронология пользователя

Теперь, когда пользователи могут публиковать сообщения, мы хотели бы подписаться на некоторые из них и увидеть объединенный канал всех сообщений для пользователей, на которых мы подписаны.

Шаг 1. Следуйте за пользователем

Первое, что нам нужно сделать, это просмотреть список пользователей и выбрать нескольких, на которых следует подписаться. Мы начнем с создания представления, которое показывает всех пользователей и позволяет пользователю следить за некоторыми. Вот экран, на котором показаны все пользователи:

И вот код, который его поддерживает:

Этот виджет следует тому же шаблону, что и профиль, с использованием FutureBuilder, поддерживаемого users. Переменная users поддерживается просто вызовом нашей службы backend. Поскольку серверная часть не является реальной реализацией, мы пропустим здесь подробности того, как backend хранит и извлекает пользователей. Пожалуйста, обратитесь к первоисточнику. Во Flutter мы просто делаем http-вызов для получения списка, как показано в ApiService#users:

Интересные моменты - это когда мы нажимаем на пользователя. Мы показываем диалог Flutter через showDialog, который содержит единственную кнопку, которая позволяет нам следовать. Когда мы нажимаем эту кнопку, мы запускаем ApiService#follow. Мы можем увидеть, как мы снова используем собственные библиотеки, чтобы следить за пользователем. Вот сторона флаттера:

А вот родная сторона Kotlin, использующая CloudClient:

Здесь мы добавляем связь подписки с user фидом другого пользователя в timeline фид этого пользователя. Все это означает, что каждый раз, когда пользователь публикует свой user канал (реализованный в первой части), мы увидим его в нашей timeline ленте. Самое интересное, что мы можем добавить любое количество каналов пользователей в наш timeline канал, и Stream вернет хорошо упорядоченный список действий.

Поскольку у нас есть новый тип фида, нам нужно настроить его в Stream. Как и в случае с user фидом, перейдите к настроенному вами приложению Stream и создайте группу плоских фидов, называемую временной шкалой:

Шаг 2. Просмотр временной шкалы

Теперь, когда у нас есть способ следить за пользователями, мы можем просматривать нашу временную шкалу. Когда мы закончим, предполагая, что мы следовали за «bob» и «sara», мы увидим экран, который выглядит следующим образом:

Давайте посмотрим на код для отображения нашей временной шкалы:

Он выглядит идентично нашему виджету Profile, в котором используются как FutureBuilder, так и RefreshIndicator, подкрепленные будущим _activities. Поскольку timeline - это просто канал, такой как user, код идентичен тому, как мы получали сообщения наших пользователей. Сначала мы вызываем ApiService#getTimeline, который является простым вызовом нашего собственного кода:

А вот соответствующий собственный код:

Вот и все! Теперь у нас есть полностью функционирующая мини-социальная сеть.

Последние мысли

Flutter и Stream упрощают создание кроссплатформенного мобильного приложения, использующего каналы активности. Оба имеют массу функциональных возможностей из коробки. Если вы ищете альтернативу React Native, Flutter - отличный выбор. Вызвать собственный код просто с помощью каналов платформы, что позволяет нам использовать все замечательные библиотеки, которые нам предоставил Stream.

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