Bỏ qua để đến nội dung

🔌 Platform-specific Code

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
}
import 'package:flutter/foundation.dart';
if (kIsWeb) {
// Web code
} else if (Platform.isAndroid) {
// Android code
}

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'),
);
}
}
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),
);
}
}

Gọi native code từ Flutter:

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}');
}
}
}
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)
}
}
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))
}
}
}

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';

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

// 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(...),
)
RefreshIndicator(
onRefresh: _refresh,
// Material style on Android, Cupertino style configurable
child: ListView(...),
)

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,
);
}
}

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

Approach Khi nào dùng
Platform.isX Simple platform checks
Platform widgets Different UI per platform
Platform channels Native features không có package
Conditional imports Platform-specific implementations
Platform Native Language
Android Kotlin/Java
iOS Swift/Objective-C
Web JavaScript
Desktop C++/Swift