Skip to Content
Flutter⚡ Nâng cao🔌 Platform-specific Code

Platform-specific Code trong Flutter

1. Platform Detection

import 'dart:io'; if (Platform.isAndroid) { // Android code } else if (Platform.isIOS) { // iOS code } else if (Platform.isWindows) { // Windows code } else if (Platform.isMacOS) { // macOS code } else if (Platform.isLinux) { // Linux code }

Với kIsWeb

import 'package:flutter/foundation.dart'; if (kIsWeb) { // Web code } else if (Platform.isAndroid) { // Android code }

2. Platform Widgets

Cupertino vs Material

import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; Widget buildButton() { if (Platform.isIOS) { return CupertinoButton( onPressed: () {}, child: Text('iOS Button'), ); } else { return ElevatedButton( onPressed: () {}, child: Text('Android Button'), ); } }

Platform-aware Widget

class PlatformButton extends StatelessWidget { final String text; final VoidCallback onPressed; const PlatformButton({ super.key, required this.text, required this.onPressed, }); @override Widget build(BuildContext context) { return Platform.isIOS ? CupertinoButton( onPressed: onPressed, child: Text(text), ) : ElevatedButton( onPressed: onPressed, child: Text(text), ); } }

3. Platform Channels

Gọi native code từ Flutter:

Dart side

import 'package:flutter/services.dart'; class BatteryService { static const platform = MethodChannel('com.example.app/battery'); Future<int> getBatteryLevel() async { try { final result = await platform.invokeMethod('getBatteryLevel'); return result as int; } on PlatformException catch (e) { throw Exception('Failed to get battery: ${e.message}'); } } }

Android (Kotlin)

// MainActivity.kt class MainActivity: FlutterActivity() { private val CHANNEL = "com.example.app/battery" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL) .setMethodCallHandler { call, result -> if (call.method == "getBatteryLevel") { val batteryLevel = getBatteryLevel() if (batteryLevel != -1) { result.success(batteryLevel) } else { result.error("UNAVAILABLE", "Battery level not available", null) } } else { result.notImplemented() } } } private fun getBatteryLevel(): Int { val batteryManager = getSystemService(BATTERY_SERVICE) as BatteryManager return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) } }

iOS (Swift)

// AppDelegate.swift @UIApplicationMain 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 { call, result in if call.method == "getBatteryLevel" { self.receiveBatteryLevel(result: result) } else { result(FlutterMethodNotImplemented) } } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } private func receiveBatteryLevel(result: FlutterResult) { let device = UIDevice.current device.isBatteryMonitoringEnabled = true if device.batteryState == UIDevice.BatteryState.unknown { result(FlutterError(code: "UNAVAILABLE", message: "Battery unavailable", details: nil)) } else { result(Int(device.batteryLevel * 100)) } } }

4. Conditional Imports

// interface.dart abstract class StorageService { Future<void> save(String key, String value); Future<String?> load(String key); } // storage_mobile.dart import 'package:shared_preferences/shared_preferences.dart'; class StorageServiceImpl implements StorageService { Future<void> save(String key, String value) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString(key, value); } // ... } // storage_web.dart import 'dart:html' as html; class StorageServiceImpl implements StorageService { Future<void> save(String key, String value) async { html.window.localStorage[key] = value; } // ... } // storage.dart export 'storage_stub.dart' if (dart.library.io) 'storage_mobile.dart' if (dart.library.html) 'storage_web.dart';

5. Platform-specific Permissions

Android

<!-- android/app/src/main/AndroidManifest.xml --> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.INTERNET" />

iOS

<!-- ios/Runner/Info.plist --> <key>NSCameraUsageDescription</key> <string>This app needs camera access</string> <key>NSPhotoLibraryUsageDescription</key> <string>This app needs photo library access</string>

6. Platform-specific UI Patterns

Back Button

// Android: Hardware back button // iOS: Swipe gesture // Customize với WillPopScope / PopScope PopScope( canPop: false, onPopInvoked: (didPop) async { if (didPop) return; // Show confirmation }, child: Scaffold(...), )

Pull to Refresh

RefreshIndicator( onRefresh: _refresh, // Material style on Android, Cupertino style configurable child: ListView(...), )

7. Theme theo Platform

ThemeData buildTheme() { if (Platform.isIOS) { return ThemeData( // iOS-like styling appBarTheme: AppBarTheme( backgroundColor: Colors.white, foregroundColor: Colors.black, elevation: 0, ), ); } else { return ThemeData( // Material styling useMaterial3: true, ); } }

8. Plugin Packages

Nhiều packages xử lý platform-specific code cho bạn:

dependencies: # Camera camera: ^0.10.0 # Image picker image_picker: ^1.0.0 # Permissions permission_handler: ^11.0.0 # Local storage shared_preferences: ^2.2.0 # Device info device_info_plus: ^9.0.0 # URL launcher url_launcher: ^6.2.0

📝 Tóm tắt

ApproachKhi nào dùng
Platform.isXSimple platform checks
Platform widgetsDifferent UI per platform
Platform channelsNative features không có package
Conditional importsPlatform-specific implementations
PlatformNative Language
AndroidKotlin/Java
iOSSwift/Objective-C
WebJavaScript
DesktopC++/Swift
Last updated on