平台差异处理
Flutter 的一大优势是"一套代码,多端运行",但现实中不同平台往往有不同的设计规范和行为习惯——Android 用 Material 风格,iOS 用 Cupertino 风格,鸿蒙有自定义返回逻辑……你需要在代码中识别平台,并做出差异化处理。
判断当前平台
方式一:defaultTargetPlatform(推荐)
import 'package:flutter/foundation.dart';
if (defaultTargetPlatform == TargetPlatform.android) {
// Android 平台逻辑
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
// iOS 平台逻辑
}defaultTargetPlatform 是 Flutter 官方推荐的平台判断方式,返回 TargetPlatform 枚举:
| 值 | 对应平台 |
|---|---|
TargetPlatform.android | Android |
TargetPlatform.iOS | iOS |
TargetPlatform.macOS | macOS |
TargetPlatform.windows | Windows |
TargetPlatform.linux | Linux |
TargetPlatform.fuchsia | Fuchsia |
鸿蒙平台
使用 flutter-ohos 时,鸿蒙平台返回的也是 TargetPlatform.android。如果需要区分 Android 和鸿蒙,需要额外判断(见下文)。
方式二:dart:io 的 Platform 类
import 'dart:io';
if (Platform.isAndroid) {
// Android
} else if (Platform.isIOS) {
// iOS
} else if (Platform.isMacOS) {
// macOS
}注意
dart:io 的 Platform 类在 Web 平台不可用,调用会抛异常。如果你的项目需要支持 Web,请使用 defaultTargetPlatform 或先用 kIsWeb 做判断。
方式三:kIsWeb(Web 平台专用)
import 'package:flutter/foundation.dart';
if (kIsWeb) {
// Web 平台逻辑
}kIsWeb 是一个 bool 常量,在编译期就能确定,不会抛异常。
区分 Android 与鸿蒙
由于 flutter-ohos 中鸿蒙的 defaultTargetPlatform 也是 TargetPlatform.android,如果需要区分两者,可以通过 MethodChannel 获取平台信息:
import 'package:flutter/services.dart';
Future<bool> isHarmonyOS() async {
if (defaultTargetPlatform != TargetPlatform.android) {
return false;
}
try {
final channel = const MethodChannel('device_info');
final result = await channel.invokeMethod<String>('getPlatform');
return result == 'harmony';
} on PlatformException {
return false;
}
}或者更简单的方式——检查鸿蒙特有目录是否存在:
import 'dart:io';
bool get isHarmony => Directory('harmony').existsSync();实际建议
大多数情况下不需要区分 Android 和鸿蒙,因为鸿蒙兼容 Android 应用。只有在需要调用鸿蒙特有 API 时才需要区分。
条件渲染:根据平台显示不同 UI
基础写法
Widget build(BuildContext context) {
if (defaultTargetPlatform == TargetPlatform.iOS) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(middle: Text('设置')),
child: Center(child: Text('iOS 风格页面')),
);
}
return Scaffold(
appBar: AppBar(title: Text('设置')),
body: Center(child: Text('Material 风格页面')),
);
}封装 PlatformWidget
对于频繁的平台判断,建议封装一个通用组件:
class PlatformWidget extends StatelessWidget {
const PlatformWidget({
super.key,
required this.android,
required this.ios,
this.macOS,
this.windows,
});
final WidgetBuilder android;
final WidgetBuilder ios;
final WidgetBuilder? macOS;
final WidgetBuilder? windows;
@override
Widget build(BuildContext context) {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
return ios(context);
case TargetPlatform.macOS:
return macOS?.call(context) ?? ios(context);
case TargetPlatform.windows:
return windows?.call(context) ?? android(context);
default:
return android(context);
}
}
}使用:
PlatformWidget(
android: (_) => Scaffold(
appBar: AppBar(title: Text('首页')),
body: Text('Android'),
),
ios: (_) => CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(middle: Text('首页')),
child: Text('iOS'),
),
)使用 Theme.of(context).platform
在 Widget 的 build 方法中,也可以通过 Theme.of(context).platform 判断平台。它的值默认跟随 defaultTargetPlatform,但可以在 MaterialApp 中手动覆盖:
MaterialApp(
theme: ThemeData(
platform: TargetPlatform.iOS, // 强制使用 iOS 风格
),
)这在测试时特别有用——你可以在 Android 设备上预览 iOS 风格的 UI。
平台特定的交互适配
不同平台有不同的交互习惯,常见场景:
页面返回
Android 使用系统返回按钮,iOS 使用左滑返回手势,鸿蒙使用侧滑返回:
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
// 自定义返回逻辑(如:弹出确认框)
return true; // true 允许返回,false 阻止返回
},
child: Scaffold(...),
);
}滚动物理效果
Android 和 iOS 的滚动回弹行为不同。Flutter 默认会根据平台自动选择:
| 平台 | 滚动效果 | 对应 Physics |
|---|---|---|
| Android | 到顶/底有光晕效果 | ClampingScrollPhysics |
| iOS | 到顶/底有回弹效果 | BouncingScrollPhysics |
手动指定:
ListView(
physics: BouncingScrollPhysics(), // 强制 iOS 风格回弹
children: [...],
)平台特定的间距与尺寸
double getTopPadding(BuildContext context) {
final padding = MediaQuery.of(context).padding;
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
return padding.top; // iOS 状态栏 + 刘海
case TargetPlatform.android:
return padding.top; // Android 状态栏
default:
return padding.top;
}
}平台特定的代码隔离
当不同平台的实现差异很大时,可以将代码分文件组织,利用 Dart 的条件导入:
目录结构
lib/
├── platform_service.dart # 统一接口
├── platform_service_android.dart # Android 实现
└── platform_service_ios.dart # iOS 实现定义统一接口
// lib/platform_service.dart
export 'platform_service_stub.dart'
if (dart.library.io) 'platform_service_io.dart';
abstract class PlatformService {
static PlatformService get instance => _instance;
static late final PlatformService _instance;
/// 获取设备信息
String getDeviceInfo();
}// lib/platform_service_io.dart (移动端实现)
import 'dart:io';
import 'platform_service.dart';
PlatformService get instance => IOPlatformService();
class IOPlatformService implements PlatformService {
@override
String getDeviceInfo() {
if (Platform.isAndroid) return 'Android Device';
if (Platform.isIOS) return 'iOS Device';
return 'Unknown';
}
}这种方式在编译期就会选择对应平台的实现,不会引入不必要的代码,但语法较复杂,一般在大型项目中使用。对于初学者,简单的 if-else 判断已经足够。
平台差异处理原则
| 原则 | 说明 |
|---|---|
| 默认自适应 | Flutter 的 Scaffold、AppBar 等组件已内置平台适配,优先使用默认行为 |
| 按需判断 | 只在确实需要差异的地方做判断,不要过度使用 |
| 集中管理 | 将平台判断逻辑封装到工具类或组件中,避免到处散落 if-else |
| 充分测试 | 每个平台的差异行为都要在真机或模拟器上验证 |
