WebSockets в Flutter
FlutterPulseЭта статья переведена специально для канала FlutterPulse. В этом канале вы найдёте много интересных вещей, связанных с Flutter. Не забывайте подписываться! 🚀

Помимо установления соединений с серверами с помощью обычных HTTP-запросов, вы также можете использовать WebSockets. WebSockets позволяют иметь двустороннюю (двухстороннюю) связь с сервером без опроса. Поэтому использование WebSockets в Flutter позволит вам создавать интерактивные и отзывчивые приложения, которые могут обрабатывать обновления данных в реальном времени.
WebSockets vs HTTP
Как упоминалось выше, обе эти технологии могут использоваться для связи с сервером. Но хотя обе стремятся доставлять актуальную информацию, они значительно отличаются следующими способами.
Модель соединения:
WebSockets устанавливают постоянное и непрерывное двустороннее соединение между клиентом и сервером. Таким образом, нет накладных расходов, связанных с повторным установлением новых соединений каждый раз.
HTTP-опрос основывается на модели запроса и ответа. То есть, клиент периодически отправляет запросы на сервер, чтобы проверять и получать обновления.
Задержка:
Из-за того, что WebSockets поддерживают постоянное соединение между клиентом и сервером, задержка минимальна. Постоянный HTTP-запрос-ответ вносит задержку.
Использование данных:
WebSockets потребляют меньше ресурсов и более эффективны при обработке больших объемов данных. С HTTP использование данных выше, так как частые запросы создают значительную нагрузку на сервер.
Сложность:
HTTP проще реализовать на базовом уровне по сравнению с WebSockets, которые требуют поддержки на стороне сервера.
WebSockets революционизировали веб, превратив неудобные, медленные взаимодействия в реальном времени в плавные, низкозадержевые опыты, сделав их основным выбором для динамичных, дружелюбных к пользователю приложений.
Реализация
Для этой демонстрации мы будем использовать эхо-WebSocket. Это сервер, который просто возвращает (или отражает) любое полученное сообщение. Клиент (мы) отправит сообщение на сервер, и сервер немедленно вернет то же сообщение. Таким образом, он используется в основном для тестирования и отладки. Например, wss://ws.postman-echo.com/raw или ws://echo.websocket.org
Начнем с реализации WebSocket в Flutter. Добавьте последний пакет web_socket_channel в зависимости и не забудьте запустить flutter pub get для его установки.
dependencies:
flutter:
sdk: flutter
web_socket_channel: ^3.0.2
В вашем файле Dart добавьте импорт пакета в начало: import 'package:web_socket_channel/io.dart'; и продолжайте инициализировать канал WebSocket вместе с другими переменными — TextEditingController для обработки ввода клиента в TextField и List для хранения отправленных и полученных сообщений.
final channel = IOWebSocketChannel.connect('wss://ws.postman-echo.com/raw');
final TextEditingController _controller = TextEditingController();
final List<String> _messages = [];
В функции initState() которая выполняется при первом создании виджета, мы будем слушать входящие сообщения WebSocket и обновлять список _messages с помощью setState() чтобы интерфейс отражал изменения. Вы также можете обрабатывать ошибки канала здесь.
@override
void initState() {
super.initState();
channel.stream.listen(
(message) {
setState(() {
_messages.add('[recv]: $message');
});
},
onError: (error) {
print('WebSocket Error: $error');
},
onDone: () {
print('WebSocket Closed');
},
);
}
Интерфейс будет в основном состоять из двух частей: ListView для отображения отправленных и полученных сообщений,Row с TextFormField для ввода пользователем фактических сообщений, которые будут отправлены на сервер, и IconButton для вызова функции _sendMessage.
...
Column(
children: [
Expanded(
child: ListView.builder(
itemCount: _messages.length,
itemBuilder: (context, index) {
return ListTile(title: Text(_messages[index])); // отображает отправленные и полученные сообщения
},
),
),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black12),
borderRadius: BorderRadius.only(
topRight: Radius.circular(10),
topLeft: Radius.circular(10),
),
),
child: Row(
children: [
Expanded(
child: Padding(
padding: EdgeInsets.fromLTRB(10, 0, 0, 10),
child: TextFormField(
controller: _controller,
maxLines: 1,
style: TextStyle(fontSize: 14),
decoration: InputDecoration(
hintText: 'Отправить сообщение для эха...',
border: InputBorder.none,
),
),
),
),
IconButton(
onPressed: _sendMessage, // функция для отправки сообщения на сервер
icon: Icon(Icons.send),
),
],
),
),
],
),
...
Функция _sendMessage начинается с проверки, что поле ввода не пустое. Если оно не пустое, мы сохраняем сообщение в списке, чтобы оно появилось в чате, а затем отправляем сообщение на сервер через channel.sink.add(). В конце мы просто очищаем поле ввода.
void _sendMessage() {
if (_controller.text.isNotEmpty) {
String text = _controller.text;
setState(() {
_messages.add('[send]: $text');
});
channel.sink.add(text);
_controller.clear();
}
}
Как всегда, не забудьте закрыть каналы и освободить контроллеры, чтобы избежать утечек памяти.
@override
void dispose() {
channel.sink.close();
_controller.dispose();
super.dispose();
}
Вот как это будет работать.

Полный код этой реализации можно найти в моем репозитории здесь. Проверьте и поделитесь своими мыслями.
"Когда вы можете связаться с живой аудиторией и получаете мгновенный отклик, это просто великолепно."
— Limahl
Спасибо за чтение ❤
Ссылки1. https://docs.flutter.dev/cookbook/networking/web-sockets
2. https://websocket.org/
3. https://www.videosdk.live/developer-hub/websocket/flutter-websockets