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

Как получить доступ к нативным функциям устройства во 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');
}
Как получить доступ к нативным функциям устройства во Flutter? | PrepBro