← Назад к вопросам

Что такое Basic channel?

1.0 Junior🔥 171 комментариев
#Нативная интеграция

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое BasicMessageChannel в Flutter

Общее определение

BasicMessageChannel — это один из встроенных в Flutter механизмов для двусторонней коммуникации между кодом Flutter (Dart) и native кодом платформы (iOS/Android). Это часть Platform Channels — система обмена сообщениями между слоями приложения.

Архитектура Platform Channels

Flutter предоставляет три типа каналов для коммуникации:

  1. BasicMessageChannel — для простых сообщений с ответом
  2. MethodChannel — для вызова методов с параметрами и получения результата
  3. 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
        ))
    }
}

Сравнение каналов

BasicMessageChannelMethodChannelEventChannel
НаправлениеДвустороннийДвустороннийОдносторонний (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.

Что такое Basic channel? | PrepBro