Ответ
Для взаимодействия с нативным кодом (Android/iOS) в Flutter используется Platform Channels. Вот полный пример получения уровня заряда батареи:
1. Настройка MethodChannel в Dart:
import 'package:flutter/services.dart';
class BatteryService {
static const MethodChannel _channel =
MethodChannel('com.example.app/battery');
static Future<int> getBatteryLevel() async {
try {
final int level = await _channel.invokeMethod('getBatteryLevel');
return level;
} on PlatformException catch (e) {
print('Ошибка получения уровня батареи: ${e.message}');
return -1;
}
}
}
// Использование:
int batteryLevel = await BatteryService.getBatteryLevel();
print('Уровень батареи: $batteryLevel%');
2. Реализация на Android (Kotlin):
package com.example.app
import android.content.Context
import android.content.Context.BATTERY_SERVICE
import android.os.BatteryManager
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity : FlutterActivity() {
private val CHANNEL = "com.example.app/battery"
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
.setMethodCallHandler { call, result ->
when (call.method) {
"getBatteryLevel" -> {
val batteryLevel = getBatteryLevel()
if (batteryLevel != -1) {
result.success(batteryLevel)
} else {
result.error(
"UNAVAILABLE",
"Не удалось получить уровень батареи",
null
)
}
}
else -> result.notImplemented()
}
}
}
private fun getBatteryLevel(): Int {
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
}
}
3. Реализация на iOS (Swift):
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(
name: "com.example.app/battery",
binaryMessenger: controller.binaryMessenger
)
batteryChannel.setMethodCallHandler { [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) in
guard call.method == "getBatteryLevel" else {
result(FlutterMethodNotImplemented)
return
}
self?.receiveBatteryLevel(result: result)
}
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func receiveBatteryLevel(result: FlutterResult) {
let device = UIDevice.current
device.isBatteryMonitoringEnabled = true
if device.batteryState == .unknown {
result(FlutterError(
code: "UNAVAILABLE",
message: "Информация о батарее недоступна",
details: nil
))
} else {
result(Int(device.batteryLevel * 100))
}
}
}
4. Альтернативные подходы:
- EventChannel — для потоковой передачи данных от нативной платформы
- BasicMessageChannel — для простых сообщений с автоматической сериализацией
- Pigeon — кодогенерация для type-safe каналов (рекомендуется для production)
- dart:ffi — для прямого вызова C/C++ библиотек (минуя Platform Channels)
Best Practices:
- Используйте уникальные имена каналов с reverse domain notation
- Всегда обрабатывайте ошибки и случай
notImplemented - Для сложных структур данных используйте JSON-сериализацию
- Тестируйте на обеих платформах, так как поведение может отличаться
- Для часто используемого функционала создавайте плагины