Skip to content

平台差异处理

Flutter 的一大优势是"一套代码,多端运行",但现实中不同平台往往有不同的设计规范和行为习惯——Android 用 Material 风格,iOS 用 Cupertino 风格,鸿蒙有自定义返回逻辑……你需要在代码中识别平台,并做出差异化处理。

判断当前平台

方式一:defaultTargetPlatform(推荐)

dart
import 'package:flutter/foundation.dart';

if (defaultTargetPlatform == TargetPlatform.android) {
  // Android 平台逻辑
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
  // iOS 平台逻辑
}

defaultTargetPlatform 是 Flutter 官方推荐的平台判断方式,返回 TargetPlatform 枚举:

对应平台
TargetPlatform.androidAndroid
TargetPlatform.iOSiOS
TargetPlatform.macOSmacOS
TargetPlatform.windowsWindows
TargetPlatform.linuxLinux
TargetPlatform.fuchsiaFuchsia

鸿蒙平台

使用 flutter-ohos 时,鸿蒙平台返回的也是 TargetPlatform.android。如果需要区分 Android 和鸿蒙,需要额外判断(见下文)。

方式二:dart:io 的 Platform 类

dart
import 'dart:io';

if (Platform.isAndroid) {
  // Android
} else if (Platform.isIOS) {
  // iOS
} else if (Platform.isMacOS) {
  // macOS
}

注意

dart:ioPlatform在 Web 平台不可用,调用会抛异常。如果你的项目需要支持 Web,请使用 defaultTargetPlatform 或先用 kIsWeb 做判断。

方式三:kIsWeb(Web 平台专用)

dart
import 'package:flutter/foundation.dart';

if (kIsWeb) {
  // Web 平台逻辑
}

kIsWeb 是一个 bool 常量,在编译期就能确定,不会抛异常。

区分 Android 与鸿蒙

由于 flutter-ohos 中鸿蒙的 defaultTargetPlatform 也是 TargetPlatform.android,如果需要区分两者,可以通过 MethodChannel 获取平台信息:

dart
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;
  }
}

或者更简单的方式——检查鸿蒙特有目录是否存在:

dart
import 'dart:io';

bool get isHarmony => Directory('harmony').existsSync();

实际建议

大多数情况下不需要区分 Android 和鸿蒙,因为鸿蒙兼容 Android 应用。只有在需要调用鸿蒙特有 API 时才需要区分。

条件渲染:根据平台显示不同 UI

基础写法

dart
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

对于频繁的平台判断,建议封装一个通用组件:

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

使用:

dart
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 中手动覆盖:

dart
MaterialApp(
  theme: ThemeData(
    platform: TargetPlatform.iOS,  // 强制使用 iOS 风格
  ),
)

这在测试时特别有用——你可以在 Android 设备上预览 iOS 风格的 UI。

平台特定的交互适配

不同平台有不同的交互习惯,常见场景:

页面返回

Android 使用系统返回按钮,iOS 使用左滑返回手势,鸿蒙使用侧滑返回:

dart
Widget build(BuildContext context) {
  return WillPopScope(
    onWillPop: () async {
      // 自定义返回逻辑(如:弹出确认框)
      return true;  // true 允许返回,false 阻止返回
    },
    child: Scaffold(...),
  );
}

滚动物理效果

Android 和 iOS 的滚动回弹行为不同。Flutter 默认会根据平台自动选择:

平台滚动效果对应 Physics
Android到顶/底有光晕效果ClampingScrollPhysics
iOS到顶/底有回弹效果BouncingScrollPhysics

手动指定:

dart
ListView(
  physics: BouncingScrollPhysics(),  // 强制 iOS 风格回弹
  children: [...],
)

平台特定的间距与尺寸

dart
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 实现

定义统一接口

dart
// 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();
}
dart
// 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
充分测试每个平台的差异行为都要在真机或模拟器上验证

基于 Flutter 官方文档整理