欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。鸿蒙深色模式与Flutter界面联动适配指南

在鸿蒙(HarmonyOS)应用中集成Flutter时,深色模式的适配是一个关键需求。以下详细说明如何实现鸿蒙深色模式与Flutter界面的联动适配,并提供代码案例。

鸿蒙端深色模式配置实现详解

系统主题状态获取机制

鸿蒙OS通过Configuration类提供系统主题状态信息,开发者可以获取当前系统的UI模式配置。其中深色模式的判断是通过uiModeUI_MODE_NIGHT_MASK的组合运算实现的。

核心实现步骤

  1. 获取当前UI模式配置

    int uiMode = getResourceManager().getConfiguration().uiMode;
    
  2. 判断是否为深色模式

    boolean isDarkMode = (uiMode & Configuration.UI_MODE_NIGHT_MASK) 
                        == Configuration.UI_MODE_NIGHT_YES;
    

与Flutter模块的通信

在Ability的onStart生命周期中,我们需要将主题状态传递给Flutter模块:

  1. 建立通信通道

    FlutterHarmonyAppBundle.getInstance()
        .getFlutterEngine()
        .getDartExecutor()
    
  2. 发送主题状态

    .send("harmony/theme", isDarkMode);
    

完整实现示例

// MainAbility.java
public class MainAbility extends Ability {
    private static final String CHANNEL_NAME = "harmony/theme";
    
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        
        // 1. 获取当前系统主题配置
        Configuration config = getResourceManager().getConfiguration();
        int uiMode = config.uiMode;
        
        // 2. 判断是否为深色模式
        boolean isDarkMode = (uiMode & Configuration.UI_MODE_NIGHT_MASK) 
                            == Configuration.UI_MODE_NIGHT_YES;
        
        // 3. 通过MethodChannel传递给Flutter
        FlutterHarmonyAppBundle.getInstance()
            .getFlutterEngine()
            .getDartExecutor()
            .send(CHANNEL_NAME, isDarkMode);
    }
}

应用场景

  1. 主题切换时的实时响应:当用户在系统设置中切换深色/浅色模式时,应用可以立即获取最新状态
  2. 初始化时的主题适配:应用启动时自动匹配系统当前主题
  3. 跨平台主题同步:保持鸿蒙原生部分和Flutter部分的主题一致性

注意事项

  1. 确保FlutterHarmonyAppBundle已正确初始化
  2. 建议将通道名称定义为常量,避免拼写错误
  3. 在Flutter端需要注册对应的消息处理器来接收主题状态

Flutter端主题状态接收 在Flutter中通过MethodChannel监听鸿蒙端的主题变化,实现步骤如下:

  1. 创建MethodChannel通道
// main.dart
// 定义通道名称,需要与鸿蒙端保持一致
const String CHANNEL_NAME = 'harmony/theme';
final MethodChannel _channel = MethodChannel(CHANNEL_NAME);
  1. 设置方法调用处理器
void _listenThemeChanges() {
  _channel.setMethodCallHandler((MethodCall call) async {
    // 检查调用的方法名称
    if (call.method == 'isDarkMode') {
      // 获取鸿蒙端传递的布尔值参数
      final isDark = call.arguments as bool;
      // 调用主题更新方法
      _updateAppTheme(isDark);
    }
  });
}
  1. 实现主题更新逻辑
void _updateAppTheme(bool isDark) {
  // 根据布尔值确定亮度模式
  final brightness = isDark ? Brightness.dark : Brightness.light;
  
  // 重新构建MaterialApp以应用新主题
  runApp(
    MaterialApp(
      theme: ThemeData(
        brightness: brightness,
        // 可以扩展更多主题配置
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(),
    ),
  );
}
  1. 初始化监听(在应用启动时调用)
void main() {
  WidgetsFlutterBinding.ensureInitialized();
  _listenThemeChanges();
  runApp(MyApp());
}

应用场景说明:

  • 当用户在鸿蒙系统设置中切换深色/浅色模式时
  • 鸿蒙端通过MethodChannel发送当前主题状态
  • Flutter端接收后立即更新整个应用的主题样式
  • 适用于需要跟随系统主题变化的跨平台应用

注意事项:

  1. 通道名称必须与鸿蒙端完全一致
  2. 参数类型转换需要正确处理
  3. 主题更新会重建整个MaterialApp,可能影响性能
  4. 可以考虑使用Provider或Riverpod等状态管理方案优化主题切换

动态主题切换实现方案详解

完整实现方案

1. 主题状态管理实现

// theme_provider.dart
import 'package:flutter/material.dart';

/// 主题状态管理类
class ThemeProvider with ChangeNotifier {
  bool _isDark = false;  // 默认使用亮色主题
  ThemeMode _themeMode = ThemeMode.light;  // 添加主题模式枚举
  
  bool get isDark => _isDark;
  ThemeMode get themeMode => _themeMode;
  
  /// 设置暗黑模式状态
  void setDarkMode(bool value) {
    _isDark = value;
    _themeMode = value ? ThemeMode.dark : ThemeMode.light;
    notifyListeners();  // 通知所有监听者状态已变更
  }
  
  /// 切换主题模式(亮色/暗色)
  void toggleTheme() {
    _isDark = !_isDark;
    _themeMode = _isDark ? ThemeMode.dark : ThemeMode.light;
    notifyListeners();
  }
}

2. 应用入口配置

// main.dart
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => ThemeProvider(),
      child: const MyApp(),
    ),
  );
}

3. 主题应用实现

// app_widget.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Consumer<ThemeProvider>(
      builder: (_, provider, __) => MaterialApp(
        title: '主题切换示例',
        debugShowCheckedModeBanner: false,
        theme: ThemeData.light().copyWith(
          primaryColor: Colors.blue,
          colorScheme: ColorScheme.light(
            primary: Colors.blue,
            secondary: Colors.blueAccent,
          ),
        ),
        darkTheme: ThemeData.dark().copyWith(
          colorScheme: ColorScheme.dark(
            primary: Colors.blueGrey,
            secondary: Colors.blueGrey[300],
          ),
        ),
        themeMode: provider.themeMode,  // 使用当前主题模式
        home: const HomePage(),
      ),
    );
  }
}

主题切换控制实现

// 切换按钮示例
Switch(
  value: Provider.of<ThemeProvider>(context).isDark,
  onChanged: (value) {
    Provider.of<ThemeProvider>(context, listen: false).setDarkMode(value);
  },
)

// 或者使用IconButton
IconButton(
  icon: Icon(
    Provider.of<ThemeProvider>(context).isDark 
      ? Icons.light_mode 
      : Icons.dark_mode
  ),
  onPressed: () {
    Provider.of<ThemeProvider>(context, listen: false).toggleTheme();
  },
)

高级功能扩展

1. 持久化存储

// 在ThemeProvider中添加持久化逻辑
Future<void> loadThemePref() async {
  final prefs = await SharedPreferences.getInstance();
  _isDark = prefs.getBool('isDark') ?? false;
  _themeMode = _isDark ? ThemeMode.dark : ThemeMode.light;
  notifyListeners();
}

Future<void> saveThemePref() async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setBool('isDark', _isDark);
}

2. 自定义主题配置

// 自定义主题数据
ThemeData customLightTheme = ThemeData(
  primarySwatch: Colors.indigo,
  brightness: Brightness.light,
  // 其他自定义配置...
);

ThemeData customDarkTheme = ThemeData(
  primarySwatch: Colors.amber,
  brightness: Brightness.dark,
  // 其他自定义配置...
);

3. 系统主题跟随

// 检测系统主题偏好
void initSystemTheme() {
  WidgetsBinding.instance.window.platformBrightness.addListener(() {
    final brightness = WidgetsBinding.instance.window.platformBrightness;
    setDarkMode(brightness == Brightness.dark);
  });
}

应用场景示例

  1. 设置页面:在应用设置中提供主题切换选项
  2. 阅读类应用:根据阅读环境切换亮色/暗色主题
  3. 媒体播放器:夜间模式自动切换
  4. 仪表盘应用:根据使用场景选择高对比度主题

动态主题切换实现方案详解

完整实现方案

1. 主题状态管理实现

// theme_provider.dart
import 'package:flutter/material.dart';

/// 主题状态管理类
class ThemeProvider with ChangeNotifier {
  bool _isDark = false;  // 默认使用亮色主题
  ThemeMode _themeMode = ThemeMode.light;  // 添加主题模式枚举
  
  bool get isDark => _isDark;
  ThemeMode get themeMode => _themeMode;
  
  /// 设置暗黑模式状态
  void setDarkMode(bool value) {
    _isDark = value;
    _themeMode = value ? ThemeMode.dark : ThemeMode.light;
    notifyListeners();  // 通知所有监听者状态已变更
  }
  
  /// 切换主题模式(亮色/暗色)
  void toggleTheme() {
    _isDark = !_isDark;
    _themeMode = _isDark ? ThemeMode.dark : ThemeMode.light;
    notifyListeners();
  }
}

2. 应用入口配置

// main.dart
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => ThemeProvider(),
      child: const MyApp(),
    ),
  );
}

3. 主题应用实现

// app_widget.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Consumer<ThemeProvider>(
      builder: (_, provider, __) => MaterialApp(
        title: '主题切换示例',
        debugShowCheckedModeBanner: false,
        theme: ThemeData.light().copyWith(
          primaryColor: Colors.blue,
          colorScheme: ColorScheme.light(
            primary: Colors.blue,
            secondary: Colors.blueAccent,
          ),
        ),
        darkTheme: ThemeData.dark().copyWith(
          colorScheme: ColorScheme.dark(
            primary: Colors.blueGrey,
            secondary: Colors.blueGrey[300],
          ),
        ),
        themeMode: provider.themeMode,  // 使用当前主题模式
        home: const HomePage(),
      ),
    );
  }
}

主题切换控制实现

// 切换按钮示例
Switch(
  value: Provider.of<ThemeProvider>(context).isDark,
  onChanged: (value) {
    Provider.of<ThemeProvider>(context, listen: false).setDarkMode(value);
  },
)

// 或者使用IconButton
IconButton(
  icon: Icon(
    Provider.of<ThemeProvider>(context).isDark 
      ? Icons.light_mode 
      : Icons.dark_mode
  ),
  onPressed: () {
    Provider.of<ThemeProvider>(context, listen: false).toggleTheme();
  },
)

高级功能扩展

1. 持久化存储

// 在ThemeProvider中添加持久化逻辑
Future<void> loadThemePref() async {
  final prefs = await SharedPreferences.getInstance();
  _isDark = prefs.getBool('isDark') ?? false;
  _themeMode = _isDark ? ThemeMode.dark : ThemeMode.light;
  notifyListeners();
}

Future<void> saveThemePref() async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setBool('isDark', _isDark);
}

2. 自定义主题配置

// 自定义主题数据
ThemeData customLightTheme = ThemeData(
  primarySwatch: Colors.indigo,
  brightness: Brightness.light,
  // 其他自定义配置...
);

ThemeData customDarkTheme = ThemeData(
  primarySwatch: Colors.amber,
  brightness: Brightness.dark,
  // 其他自定义配置...
);

3. 系统主题跟随

// 检测系统主题偏好
void initSystemTheme() {
  WidgetsBinding.instance.window.platformBrightness.addListener(() {
    final brightness = WidgetsBinding.instance.window.platformBrightness;
    setDarkMode(brightness == Brightness.dark);
  });
}

应用场景示例

  1. 设置页面:在应用设置中提供主题切换选项
  2. 阅读类应用:根据阅读环境切换亮色/暗色主题
  3. 媒体播放器:夜间模式自动切换
  4. 仪表盘应用:根据使用场景选择高对比度主题

组件级适配技巧

对于自定义组件,通过MediaQuery获取当前主题亮度:

Widget build(BuildContext context) {
  final brightness = MediaQuery.of(context).platformBrightness;
  final isDark = brightness == Brightness.dark;
  
  return Container(
    color: isDark ? Colors.grey[900] : Colors.white,
    child: Text(
      'Adaptive Text',
      style: TextStyle(
        color: isDark ? Colors.white : Colors.black,
      ),
    ),
  );
}
完整代码示例

鸿蒙与Flutter协同的完整示例结构:

harmony_app/
├── java/               # 鸿蒙端代码
│   └── MainAbility.java
└── flutter/
    ├── lib/
    │   ├── main.dart
    │   ├── theme_provider.dart
    │   └── app_widget.dart
    └── pubspec.yaml

注意事项

  1. 鸿蒙端初始化
  • 确保FlutterHarmonyAppBundle在鸿蒙端正确初始化
  • 在鸿蒙应用的MainAbility入口处调用FlutterHarmonyAppBundle的初始化方法
  • 示例代码:
@Override
public void onStart(Intent intent) {
    super.onStart(intent);
    FlutterHarmonyAppBundle.init(this);
}
  1. 状态管理组件包裹
  • Flutter的MaterialApp必须包裹在状态管理组件外层
  • 推荐使用Provider或GetX等状态管理方案
  • 典型结构:
Provider(
  create: (_) => ThemeNotifier(),
  child: MaterialApp(
    theme: ThemeData.light(),
    darkTheme: ThemeData.dark(),
    home: MyHomePage(),
  )
)
  1. 测试流程
  • 测试时需切换鸿蒙系统的显示设置
  • 具体路径:设置 > 显示 > 深色模式
  • 建议测试场景:
    • 系统浅色模式 + 应用浅色主题
    • 系统深色模式 + 应用深色主题
    • 系统模式切换时应用主题即时响应
  1. 资源准备
  • 图片资源建议准备light/dark两套版本
  • 推荐目录结构:
assets/
  images/
    light/
      logo.png
    dark/
      logo.png
  • 加载示例:
Image.asset(
  isDarkMode ? 'assets/images/dark/logo.png' : 'assets/images/light/logo.png'
)
  1. 扩展建议
  • 可动态适配的主题属性包括:
    • 配色方案(主色、强调色等)
    • 字体样式(大小、字重等)
    • 控件外观(按钮圆角、阴影等)
    • 过渡动画效果
  • 建议通过ThemeExtension实现自定义主题属性
  • 可结合系统API获取鸿蒙当前的深色模式状态

通过以上方法可以实现:

  • 鸿蒙系统深色模式与Flutter界面的完美联动
  • 跨平台一致的用户体验
  • 平滑的主题切换过渡效果

实际开发中可根据项目需求:

  • 扩展更多主题属性的动态适配
  • 实现主题配置的持久化存储
  • 添加主题切换的过渡动画
  • 开发自定义主题选择器

欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐