Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое BasicMessageChannel в Flutter
Общее определение
BasicMessageChannel — это один из встроенных в Flutter механизмов для двусторонней коммуникации между кодом Flutter (Dart) и native кодом платформы (iOS/Android). Это часть Platform Channels — система обмена сообщениями между слоями приложения.
Архитектура Platform Channels
Flutter предоставляет три типа каналов для коммуникации:
- BasicMessageChannel — для простых сообщений с ответом
- MethodChannel — для вызова методов с параметрами и получения результата
- EventChannel — для потока событий от native кода к Flutter
BasicMessageChannel в деталях
Основные характеристики:
- Двусторонняя коммуникация
- Отправка и получение сообщений (любые типы данных)
- Синхронный ответ на сообщение
- Использует message codec для сериализации/десериализации
Когда использовать BasicMessageChannel:
- Отправка простых данных (строки, числа, maps)
- Когда не нужна типизированная информация о методе
- Когда нужна максимальная простота
Когда использовать MethodChannel:
- Вызов конкретного метода native кода
- Когда нужна более структурированная коммуникация
- Когда методов несколько
Практический пример: Отправка конфигурации на native сторону
Flutter (Dart) сторона:
import 'package:flutter/services.dart';
class NativeConfigService {
static const platform = BasicMessageChannel<dynamic>(
'com.example.app/config',
StandardMessageCodec(),
);
// Отправка конфигурации на native
Future<void> sendConfig(Map<String, dynamic> config) async {
try {
final result = await platform.send(config);
print('Native ответил: $result');
} catch (e) {
print('Ошибка: $e');
}
}
// Слушаем сообщения от native
void setupNativeListener() {
platform.setMessageHandler((message) async {
print('Получено от native: $message');
// Обработка сообщения от native
return 'Спасибо за сообщение'; // Ответ native
});
}
}
// Использование
class SettingsScreen extends StatefulWidget {
@override
State<SettingsScreen> createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen> {
final _nativeService = NativeConfigService();
@override
void initState() {
super.initState();
_nativeService.setupNativeListener();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Настройки')),
body: Center(
child: ElevatedButton(
onPressed: () {
_nativeService.sendConfig({
'theme': 'dark',
'language': 'ru',
'apiUrl': 'https://api.example.com',
});
},
child: Text('Отправить конфигурацию'),
),
),
);
}
}
Android (Kotlin) сторона:
import android.os.Handler
import android.os.Looper
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.StringCodec
class MainActivity: FlutterActivity() {
private val CHANNEL = "com.example.app/config"
private lateinit var messageChannel: BasicMessageChannel<Any>
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
messageChannel = BasicMessageChannel(
flutterEngine.dartExecutor.binaryMessenger,
CHANNEL,
StandardMessageCodec()
)
// Слушаем сообщения от Flutter
messageChannel.setMessageHandler { message, reply ->
@Suppress("UNCHECKED_CAST")
val config = message as? Map<String, Any>
if (config != null) {
val theme = config["theme"] as? String
val language = config["language"] as? String
val apiUrl = config["apiUrl"] as? String
// Применяем конфигурацию
applyConfig(theme, language, apiUrl)
// Отправляем ответ в Flutter
reply.reply("Config applied successfully")
} else {
reply.reply(null)
}
}
}
// Отправить сообщение в Flutter
fun notifyFlutter(message: String) {
Handler(Looper.getMainLooper()).post {
messageChannel.send(mapOf("status" to message)) { response ->
android.util.Log.d("MainActivity", "Flutter ответил: $response")
}
}
}
private fun applyConfig(theme: String?, language: String?, apiUrl: String?) {
// Реализация
android.util.Log.d("MainActivity",
"Config: theme=$theme, language=$language, api=$apiUrl"
)
}
}
iOS (Swift) сторона:
import UIKit
import Flutter
@UIApplicationMain
@objc class GeneratedPluginRegistrant: NSObject {}
class AppDelegate: UIResponder, UIApplicationDelegate {
private let channelName = "com.example.app/config"
private var messageChannel: FlutterBasicMessageChannel?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller = window?.rootViewController as! FlutterViewController
messageChannel = FlutterBasicMessageChannel(
name: channelName,
binaryMessenger: controller.binaryMessenger,
codec: FlutterStandardMessageCodec.sharedInstance()
)
// Слушаем сообщения от Flutter
messageChannel?.setMessageHandler { [weak self] (message: Any?) in
guard let message = message as? [String: Any] else {
return nil
}
let theme = message["theme"] as? String
let language = message["language"] as? String
let apiUrl = message["apiUrl"] as? String
// Применяем конфигурацию
self?.applyConfig(theme: theme, language: language, apiUrl: apiUrl)
return "Config applied successfully"
}
GeneratedPluginRegistrant.register(with: self)
return super.application(
application,
didFinishLaunchingWithOptions: launchOptions
)
}
// Отправить сообщение в Flutter
func notifyFlutter(message: String) {
messageChannel?.sendMessage(
["status": message]
) { (response: Any?) in
print("Flutter ответил: \(response ?? "no response")")
}
}
private func applyConfig(theme: String?, language: String?, apiUrl: String?) {
print("Config: theme=\(theme ?? "nil"), language=\(language ?? "nil"), api=\(apiUrl ?? "nil")")
}
}
Практический пример 2: Передача GPS координат
Flutter:
class LocationService {
static const channel = BasicMessageChannel<dynamic>(
'com.example.app/location',
StandardMessageCodec(),
);
Future<void> requestLocationFromNative() async {
try {
final location = await channel.send({'action': 'get_current_location'});
print('Координаты: ${location['latitude']}, ${location['longitude']}');
} catch (e) {
print('Ошибка получения геолокации: $e');
}
}
}
Android (Kotlin):
messageChannel.setMessageHandler { message, reply ->
val map = message as? Map<String, Any>
if (map?.get("action") == "get_current_location") {
// Получаем текущую геолокацию
val latitude = 55.7558
val longitude = 37.6173
reply.reply(mapOf(
"latitude" to latitude,
"longitude" to longitude,
"accuracy" to 10.0
))
}
}
Сравнение каналов
| BasicMessageChannel | MethodChannel | EventChannel | |
|---|---|---|---|
| Направление | Двусторонний | Двусторонний | Односторонний (native → Flutter) |
| Тип | Сообщения | Методы | События |
| Ответ | Да | Да | Да (события) |
| Сложность | Низкая | Средняя | Высокая |
| Примеры | Конфиг, простые данные | API вызовы | Слушатель GPS, sensor |
Важные моменты
1. Codecы для сериализации
// StandardMessageCodec - самый универсальный
BasicMessageChannel<dynamic>('channel', StandardMessageCodec())
// JSONMessageCodec - для JSON данных
BasicMessageChannel<String>('channel', JSONMessageCodec())
// StringCodec - для строк
BasicMessageChannel<String>('channel', StringCodec())
2. Обработка ошибок
try {
final result = await channel.send(data);
} on PlatformException catch (e) {
print('Ошибка платформы: ${e.message}');
} catch (e) {
print('Неизвестная ошибка: $e');
}
3. Жизненный цикл
Чистить listeners при dispose:
@override
void dispose() {
platform.setMessageHandler(null); // Удаляем слушатель
super.dispose();
}
Вывод
BasicMessageChannel — это простой и эффективный инструмент для взаимодействия между Flutter и платформой. Идеален для:
- Передачи конфигурации
- Простых запросов
- Уведомлений
- Случаев, когда MethodChannel слишком heavy
Для более сложных сценариев стоит рассмотреть MethodChannel или EventChannel.