← Назад к вопросам
Как получить доступ к нативным функциям устройства во Flutter?
2.3 Middle🔥 112 комментариев
#Архитектура Flutter#Нативная интеграция
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Доступ к нативным функциям устройства во Flutter
Flutter предоставляет механизм Platform Channels для взаимодействия с нативным кодом iOS и Android.
Архитектура Platform Channels
┌─────────────────────────┐
│ Flutter (Dart) │
│ MethodChannel.invoke() │
└────────────┬────────────┘
│
MethodChannel
(сериализация)
│
┌────────────┴────────────┐
│ │
┌─────────────────┐ ┌──────────────┐
│ iOS (Swift) │ │ Android (Kt) │
│ MethodChannel │ │ MethodChannel│
└─────────────────┘ └──────────────┘
1. MethodChannel — синхронные вызовы
Flutter сторона (Dart)
import 'package:flutter/services.dart';
class DeviceNativeBridge {
static const platform = MethodChannel('com.example.app/device');
// Получить информацию о батарее
static Future<int> getBatteryLevel() async {
try {
final int result = await platform.invokeMethod('getBatteryLevel');
return result;
} catch (e) {
print('Ошибка: $e');
return -1;
}
}
// Отправить параметры на нативную сторону
static Future<String> getUserInfo(String userId) async {
try {
final String result = await platform.invokeMethod(
'getUserInfo',
{'userId': userId},
);
return result;
} catch (e) {
return 'Error';
}
}
// Открыть нативный экран
static Future<void> openNativeScreen(Map<String, dynamic> params) async {
await platform.invokeMethod('openScreen', params);
}
}
// Использование
class HomeScreen extends StatefulWidget {
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int batteryLevel = 0;
@override
void initState() {
super.initState();
_getBattery();
}
Future<void> _getBattery() async {
final level = await DeviceNativeBridge.getBatteryLevel();
setState(() => batteryLevel = level);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text('Батарея: $batteryLevel%')),
);
}
}
Android сторона (Kotlin)
// MainActivity.kt
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager
import io.flutter.embedding.v1.FlutterFragment
import io.flutter.embedding.v1.FlutterView
import io.flutter.plugin.common.MethodChannel
import android.os.BatteryManager
import android.content.IntentFilter
class MainActivity: FlutterActivity() {
private val CHANNEL = "com.example.app/device"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
CHANNEL
).setMethodCallHandler { call, result ->
when (call.method) {
"getBatteryLevel" -> {
val batteryLevel = getBatteryLevel()
result.success(batteryLevel)
}
"getUserInfo" -> {
val userId = call.argument<String>("userId")
val info = getUserInfo(userId)
result.success(info)
}
else -> result.notImplemented()
}
}
}
private fun getBatteryLevel(): Int {
val batteryManager = getSystemService(BATTERY_SERVICE) as BatteryManager
return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER)
}
private fun getUserInfo(userId: String?): String {
return if (userId != null) {
"User: $userId"
} else {
"Unknown"
}
}
}
iOS сторона (Swift)
import UIKit
import Flutter
@UIApplicationMain
@objc class GeneratedPluginRegistrant: NSObject {}
class ViewController: FlutterViewController {
override func viewDidLoad() {
super.viewDidLoad()
let channel = FlutterMethodChannel(
name: "com.example.app/device",
binaryMessenger: binaryMessenger
)
channel.setMethodCallHandler { call, result in
switch call.method {
case "getBatteryLevel":
let level = UIDevice.current.batteryLevel * 100
result(Int(level))
case "getUserInfo":
if let userId = call.arguments as? [String: Any],
let id = userId["userId"] as? String {
result("User: \(id)")
} else {
result(FlutterMethodNotImplemented)
}
default:
result(FlutterMethodNotImplemented)
}
}
}
}
2. EventChannel — асинхронный поток событий
Для получения непрерывного потока данных (GPS, ускорение, и т.д.).
Flutter (Dart)
class LocationNativeBridge {
static const eventChannel = EventChannel('com.example.app/location');
static Stream<LocationData> getLocationStream() {
return eventChannel.receiveBroadcastStream().map((dynamic event) {
return LocationData.fromMap(Map<String, dynamic>.from(event));
});
}
}
class LocationData {
final double latitude;
final double longitude;
final double accuracy;
LocationData({
required this.latitude,
required this.longitude,
required this.accuracy,
});
factory LocationData.fromMap(Map<String, dynamic> json) {
return LocationData(
latitude: json['latitude'] as double,
longitude: json['longitude'] as double,
accuracy: json['accuracy'] as double,
);
}
}
// Использование
class LocationScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<LocationData>(
stream: LocationNativeBridge.getLocationStream(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
final location = snapshot.data!;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Latitude: ${location.latitude}'),
Text('Longitude: ${location.longitude}'),
Text('Accuracy: ${location.accuracy}'),
],
),
);
},
),
);
}
}
Android (Kotlin)
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager
import io.flutter.embedding.v1.FlutterFragment
import io.flutter.embedding.v1.FlutterView
import io.flutter.plugin.common.EventChannel
import android.location.LocationManager
import android.location.Location
class MainActivity: FlutterActivity() {
private val EVENT_CHANNEL = "com.example.app/location"
private var eventSink: EventChannel.EventSink? = null
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
EventChannel(flutterEngine.dartExecutor.binaryMessenger, EVENT_CHANNEL)
.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
eventSink = events
startLocationUpdates()
}
override fun onCancel(arguments: Any?) {
stopLocationUpdates()
eventSink = null
}
})
}
private fun startLocationUpdates() {
val locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
try {
val location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
if (location != null) {
val event = mapOf(
"latitude" to location.latitude,
"longitude" to location.longitude,
"accuracy" to location.accuracy
)
eventSink?.success(event)
}
} catch (e: Exception) {
eventSink?.error("LOCATION_ERROR", e.message, null)
}
}
private fun stopLocationUpdates() {
// Остановить получение координат
}
}
3. Плагины Flutter
Для частых операций используй готовые плагины вместо своих Platform Channels:
// GPS
import 'package:geolocator/geolocator.dart';
Future<Position> getLocation() async {
return await Geolocator.getCurrentPosition();
}
// Камера
import 'package:image_picker/image_picker.dart';
Final picker = ImagePicker();
final pickedFile = await picker.pickImage(source: ImageSource.camera);
// Контакты
import 'package:contacts_service/contacts_service.dart';
final contacts = await ContactsService.getContacts();
// Батарея
import 'package:battery_plus/battery_plus.dart';
final battery = Battery();
final level = await battery.batteryLevel;
// Интернет
import 'package:connectivity_plus/connectivity_plus.dart';
final connectivity = Connectivity();
final result = await connectivity.checkConnectivity();
if (result == ConnectivityResult.wifi) {
print('Connected to WiFi');
}
4. Platform-specific UI
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class PlatformSpecificWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Разные UI для iOS и Android
return Theme.of(context).platform == TargetPlatform.iOS
? CupertinoButton(
onPressed: () {},
child: Text('iOS Button'),
)
: ElevatedButton(
onPressed: () {},
child: Text('Android Button'),
);
}
}
Чеклист для работы с нативным кодом
- Всегда используй try-catch при вызове нативного кода
- Тестируй на реальных устройствах
- Запроси необходимые permissions (AndroidManifest.xml, Info.plist)
- Используй готовые плагины из pub.dev когда возможно
- Документируй ошибки нативного кода
- Обработай случаи, когда функция недоступна
- Используй platform detection для разных реализаций
// Проверить платформу
if (defaultTargetPlatform == TargetPlatform.iOS) {
print('Running on iOS');
} else if (defaultTargetPlatform == TargetPlatform.android) {
print('Running on Android');
}