📋 项目概述

本文档详细介绍如何在 HarmonyOS 平台上使用 Flutter 框架集成 shared_preferences 插件,实现应用数据的持久化存储。通过实际开发案例,展示从插件配置到功能实现的完整流程,并记录开发过程中遇到的问题及解决方案。本项目构建了一个现代化的设置管理应用,采用 Material Design 3 设计规范,提供了流畅的用户体验和丰富的交互功能,全面展示了 shared_preferences 支持的各种数据类型(布尔值、整数、浮点数、字符串、字符串列表)的存储和读取。
在这里插入图片描述
在这里插入图片描述

运行截图说明:本文档中的代码已在 HarmonyOS 设备上实际运行测试,功能正常运行。建议读者在阅读时结合实际操作,以获得更好的学习效果。

🎯 项目目标

  • ✅ 在 HarmonyOS 平台上集成 shared_preferences 插件
  • ✅ 实现布尔值(Bool)的存储和读取
  • ✅ 实现整数(Int)的存储和读取
  • ✅ 实现浮点数(Double)的存储和读取
  • ✅ 实现字符串(String)的存储和读取
  • ✅ 实现字符串列表(StringList)的存储和读取
  • ✅ 构建美观的 Material Design 3 风格UI
  • ✅ 实现实时保存和读取功能
  • ✅ 实现清除所有设置功能

🛠️ 技术栈

  • 开发框架: Flutter 3.6.2+
  • 三方库: shared_preferences (OpenHarmony TPC 适配版本)
  • UI 框架: Material Design 3
  • 目标平台: HarmonyOS (OpenHarmony)
  • 开发工具: DevEco Studio / VS Code

📦 一、项目初始化

1.1 创建 Flutter 项目

flutter create --platforms=ohos shared_preferences_demo
cd shared_preferences_demo

1.2 配置依赖

pubspec.yaml 中添加 shared_preferences 依赖:

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.8
  shared_preferences:
    git:
      url: https://atomgit.com/openharmony-tpc/flutter_packages.git
      path: packages/shared_preferences/shared_preferences
      ref: br_shared_preferences-v2.5.3_ohos

重要说明

  • 必须使用 OpenHarmony TPC 提供的适配版本,pub.dev 上的官方版本不支持 HarmonyOS 平台
  • 需要指定正确的分支引用 br_shared_preferences-v2.5.3_ohos
  • shared_preferences 主包会自动处理平台实现包的依赖,无需手动添加 shared_preferences_ohos

1.3 安装依赖

flutter pub get

🔐 二、权限配置

shared_preferences 插件在 HarmonyOS 平台上不需要任何特殊权限。它使用系统提供的键值对存储机制,无需网络权限、文件访问权限或其他敏感权限。

💻 三、核心功能实现

3.1 导入必要的包

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

3.2 初始化 SharedPreferences

在使用 shared_preferences 之前,需要先获取 SharedPreferences 实例:

SharedPreferences prefs = await SharedPreferences.getInstance();

重要提示getInstance() 是一个异步方法,必须使用 await 等待其完成。建议在应用启动时或页面初始化时获取实例,并在整个应用生命周期中复用。

3.3 存储和读取布尔值(Bool)

布尔值通常用于存储开关状态、功能启用/禁用等设置。

存储布尔值
await prefs.setBool('notifications_enabled', true);
await prefs.setBool('dark_mode_enabled', false);
读取布尔值
bool notificationsEnabled = prefs.getBool('notifications_enabled') ?? false;
bool darkModeEnabled = prefs.getBool('dark_mode_enabled') ?? false;

注意事项

  • getBool() 方法可能返回 null(如果键不存在),因此需要使用 ?? 运算符提供默认值
  • 推荐在读取时始终提供默认值,避免空指针异常
完整示例
class SettingsPage extends StatefulWidget {
  const SettingsPage({super.key});

  
  State<SettingsPage> createState() => _SettingsPageState();
}

class _SettingsPageState extends State<SettingsPage> {
  late SharedPreferences _prefs;
  bool _notificationsEnabled = false;

  
  void initState() {
    super.initState();
    _loadSettings();
  }

  Future<void> _loadSettings() async {
    _prefs = await SharedPreferences.getInstance();
    setState(() {
      _notificationsEnabled = _prefs.getBool('notifications_enabled') ?? false;
    });
  }

  Future<void> _toggleNotifications(bool value) async {
    setState(() {
      _notificationsEnabled = value;
    });
    await _prefs.setBool('notifications_enabled', value);
    // 显示保存反馈
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('设置已保存')),
    );
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('设置')),
      body: SwitchListTile(
        title: const Text('通知'),
        value: _notificationsEnabled,
        onChanged: _toggleNotifications,
      ),
    );
  }
}

3.4 存储和读取整数(Int)

整数通常用于存储字体大小、计数、索引等数值。

存储整数
await prefs.setInt('font_size', 16);
await prefs.setInt('counter', 100);
读取整数
int fontSize = prefs.getInt('font_size') ?? 16;
int counter = prefs.getInt('counter') ?? 0;
完整示例
class FontSizeSetting extends StatefulWidget {
  const FontSizeSetting({super.key});

  
  State<FontSizeSetting> createState() => _FontSizeSettingState();
}

class _FontSizeSettingState extends State<FontSizeSetting> {
  late SharedPreferences _prefs;
  int _fontSize = 16;

  
  void initState() {
    super.initState();
    _loadFontSize();
  }

  Future<void> _loadFontSize() async {
    _prefs = await SharedPreferences.getInstance();
    setState(() {
      _fontSize = _prefs.getInt('font_size') ?? 16;
    });
  }

  Future<void> _updateFontSize(int value) async {
    setState(() {
      _fontSize = value;
    });
    await _prefs.setInt('font_size', value);
  }

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('字体大小: $_fontSize'),
        Slider(
          value: _fontSize.toDouble(),
          min: 12,
          max: 24,
          divisions: 12,
          onChanged: (value) => _updateFontSize(value.round()),
        ),
      ],
    );
  }
}

3.5 存储和读取浮点数(Double)

浮点数通常用于存储音量、进度、百分比等需要小数精度的数值。

存储浮点数
await prefs.setDouble('volume_level', 0.75);
await prefs.setDouble('progress', 0.5);
读取浮点数
double volumeLevel = prefs.getDouble('volume_level') ?? 0.5;
double progress = prefs.getDouble('progress') ?? 0.0;
完整示例
class VolumeSetting extends StatefulWidget {
  const VolumeSetting({super.key});

  
  State<VolumeSetting> createState() => _VolumeSettingState();
}

class _VolumeSettingState extends State<VolumeSetting> {
  late SharedPreferences _prefs;
  double _volumeLevel = 0.5;

  
  void initState() {
    super.initState();
    _loadVolume();
  }

  Future<void> _loadVolume() async {
    _prefs = await SharedPreferences.getInstance();
    setState(() {
      _volumeLevel = _prefs.getDouble('volume_level') ?? 0.5;
    });
  }

  Future<void> _updateVolume(double value) async {
    setState(() {
      _volumeLevel = value;
    });
    await _prefs.setDouble('volume_level', value);
  }

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('音量: ${(_volumeLevel * 100).round()}%'),
        Slider(
          value: _volumeLevel,
          min: 0.0,
          max: 1.0,
          divisions: 10,
          onChanged: _updateVolume,
        ),
      ],
    );
  }
}

3.6 存储和读取字符串(String)

字符串通常用于存储用户名、主题名称、配置项等文本数据。

存储字符串
await prefs.setString('username', 'John Doe');
await prefs.setString('selected_theme', 'dark');
读取字符串
String username = prefs.getString('username') ?? '';
String selectedTheme = prefs.getString('selected_theme') ?? 'light';
完整示例
class UsernameSetting extends StatefulWidget {
  const UsernameSetting({super.key});

  
  State<UsernameSetting> createState() => _UsernameSettingState();
}

class _UsernameSettingState extends State<UsernameSetting> {
  late SharedPreferences _prefs;
  final TextEditingController _controller = TextEditingController();

  
  void initState() {
    super.initState();
    _loadUsername();
  }

  Future<void> _loadUsername() async {
    _prefs = await SharedPreferences.getInstance();
    final username = _prefs.getString('username') ?? '';
    _controller.text = username;
  }

  Future<void> _saveUsername(String value) async {
    await _prefs.setString('username', value);
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('用户名已保存')),
    );
  }

  
  Widget build(BuildContext context) {
    return TextField(
      controller: _controller,
      decoration: const InputDecoration(
        labelText: '用户名',
        hintText: '请输入用户名',
      ),
      onChanged: _saveUsername,
    );
  }

  
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

3.7 存储和读取字符串列表(StringList)

字符串列表通常用于存储标签、收藏列表、历史记录等多项数据。

存储字符串列表
List<String> tags = ['工作', '学习', '娱乐'];
await prefs.setStringList('selected_tags', tags);
读取字符串列表
List<String> tags = prefs.getStringList('selected_tags') ?? [];
完整示例
class TagsSelector extends StatefulWidget {
  const TagsSelector({super.key});

  
  State<TagsSelector> createState() => _TagsSelectorState();
}

class _TagsSelectorState extends State<TagsSelector> {
  late SharedPreferences _prefs;
  List<String> _selectedTags = [];
  final List<String> _availableTags = ['工作', '学习', '娱乐', '运动', '旅行', '美食'];

  
  void initState() {
    super.initState();
    _loadTags();
  }

  Future<void> _loadTags() async {
    _prefs = await SharedPreferences.getInstance();
    setState(() {
      _selectedTags = _prefs.getStringList('selected_tags') ?? [];
    });
  }

  Future<void> _toggleTag(String tag) async {
    setState(() {
      if (_selectedTags.contains(tag)) {
        _selectedTags.remove(tag);
      } else {
        _selectedTags.add(tag);
      }
    });
    await _prefs.setStringList('selected_tags', _selectedTags);
  }

  
  Widget build(BuildContext context) {
    return Wrap(
      spacing: 8,
      children: _availableTags.map((tag) {
        final isSelected = _selectedTags.contains(tag);
        return FilterChip(
          label: Text(tag),
          selected: isSelected,
          onSelected: (_) => _toggleTag(tag),
        );
      }).toList(),
    );
  }
}

3.8 清除数据

清除单个键值对
await prefs.remove('username');
清除所有数据
await prefs.clear();

注意事项

  • remove() 方法只删除指定的键值对
  • clear() 方法会删除所有存储的数据,请谨慎使用
  • 建议在清除所有数据前显示确认对话框
完整示例
Future<void> _clearAllSettings() async {
  final confirmed = await showDialog<bool>(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('确认清除'),
      content: const Text('确定要清除所有设置吗?此操作不可恢复。'),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context, false),
          child: const Text('取消'),
        ),
        TextButton(
          onPressed: () => Navigator.pop(context, true),
          style: TextButton.styleFrom(foregroundColor: Colors.red),
          child: const Text('确定'),
        ),
      ],
    ),
  );

  if (confirmed == true) {
    await _prefs.clear();
    // 重新加载设置
    _loadSettings();
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('所有设置已清除')),
    );
  }
}

3.9 检查键是否存在

bool exists = prefs.containsKey('username');

3.10 获取所有键

Set<String> keys = prefs.getKeys();

🎨 四、UI设计实现

4.1 动态渐变背景

使用 AnimationController 创建持续变化的渐变背景:

class _SettingsPageState extends State<SettingsPage>
    with TickerProviderStateMixin {
  late AnimationController _backgroundController;

  
  void initState() {
    super.initState();
    _backgroundController = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 20),
    )..repeat();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: AnimatedBuilder(
        animation: _backgroundController,
        builder: (context, child) {
          return Container(
            decoration: BoxDecoration(
              gradient: LinearGradient(
                begin: Alignment.topLeft,
                end: Alignment.bottomRight,
                colors: [
                  Color.lerp(
                    Colors.deepPurple.shade300,
                    Colors.pink.shade300,
                    (_backgroundController.value * 2) % 1,
                  )!,
                  Color.lerp(
                    Colors.blue.shade300,
                    Colors.purple.shade300,
                    (_backgroundController.value * 2 + 0.5) % 1,
                  )!,
                ],
              ),
            ),
            child: child,
          );
        },
        child: SafeArea(
          child: Column(
            children: [
              // 设置项内容
            ],
          ),
        ),
      ),
    );
  }

  
  void dispose() {
    _backgroundController.dispose();
    super.dispose();
  }
}

4.2 设置卡片设计

创建美观的设置卡片组件:

class _SettingCard extends StatelessWidget {
  final Widget child;
  final Color color;

  const _SettingCard({
    required this.child,
    required this.color,
  });

  
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(20),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(20),
        boxShadow: [
          BoxShadow(
            color: color.withOpacity(0.2),
            blurRadius: 20,
            offset: const Offset(0, 10),
          ),
        ],
      ),
      child: child,
    );
  }
}

4.3 开关设置项

Widget _buildSwitchCard({
  required String title,
  required String subtitle,
  required IconData icon,
  required bool value,
  required Color color,
  required ValueChanged<bool> onChanged,
}) {
  return _SettingCard(
    color: color,
    child: Row(
      children: [
        Container(
          padding: const EdgeInsets.all(12),
          decoration: BoxDecoration(
            color: color.withOpacity(0.2),
            borderRadius: BorderRadius.circular(12),
          ),
          child: Icon(icon, color: color, size: 24),
        ),
        const SizedBox(width: 16),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                title,
                style: const TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                  color: Colors.black87,
                ),
              ),
              const SizedBox(height: 4),
              Text(
                subtitle,
                style: TextStyle(
                  fontSize: 12,
                  color: Colors.grey.shade600,
                ),
              ),
            ],
          ),
        ),
        Switch(
          value: value,
          onChanged: onChanged,
          activeColor: color,
        ),
      ],
    ),
  );
}

4.4 滑块设置项

Widget _buildSliderCard({
  required String title,
  required String subtitle,
  required IconData icon,
  required double value,
  required Color color,
  required double min,
  required double max,
  required int divisions,
  required ValueChanged<double> onChanged,
  String Function(double)? formatValue,
}) {
  return _SettingCard(
    color: color,
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            Container(
              padding: const EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: color.withOpacity(0.2),
                borderRadius: BorderRadius.circular(12),
              ),
              child: Icon(icon, color: color, size: 24),
            ),
            const SizedBox(width: 16),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    title,
                    style: const TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                      color: Colors.black87,
                    ),
                  ),
                  const SizedBox(height: 4),
                  Text(
                    subtitle,
                    style: TextStyle(
                      fontSize: 12,
                      color: Colors.grey.shade600,
                    ),
                  ),
                ],
              ),
            ),
            Container(
              padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
              decoration: BoxDecoration(
                color: color.withOpacity(0.2),
                borderRadius: BorderRadius.circular(20),
              ),
              child: Text(
                formatValue != null
                    ? formatValue(value)
                    : '${(value * 100).round()}%',
                style: TextStyle(
                  fontSize: 14,
                  fontWeight: FontWeight.bold,
                  color: color,
                ),
              ),
            ),
          ],
        ),
        const SizedBox(height: 16),
        Slider(
          value: value,
          min: min,
          max: max,
          divisions: divisions,
          activeColor: color,
          inactiveColor: color.withOpacity(0.3),
          onChanged: onChanged,
        ),
      ],
    ),
  );
}

⚠️ 五、常见问题与解决方案

5.1 数据未保存

问题:调用 setBool()setString() 等方法后,数据没有保存。

解决方案

  1. 确保使用 await:所有 set* 方法都是异步的,必须使用 await 等待完成

    // 错误
    prefs.setBool('key', true);
    
    // 正确
    await prefs.setBool('key', true);
    
  2. 确保方法标记为 async:调用 set* 方法的方法必须标记为 async

    // 错误
    void _saveSetting() {
      prefs.setBool('key', true);
    }
    
    // 正确
    Future<void> _saveSetting() async {
      await prefs.setBool('key', true);
    }
    

5.2 读取数据返回 null

问题:调用 getBool()getString() 等方法返回 null

解决方案

使用 ?? 运算符提供默认值:

// 错误:可能返回 null
bool value = prefs.getBool('key');

// 正确:提供默认值
bool value = prefs.getBool('key') ?? false;
String username = prefs.getString('username') ?? '';
int fontSize = prefs.getInt('font_size') ?? 16;
double volume = prefs.getDouble('volume_level') ?? 0.5;
List<String> tags = prefs.getStringList('tags') ?? [];

5.3 数据丢失

问题:应用重启后,之前保存的数据丢失。

解决方案

  1. 检查键名是否正确:确保存储和读取使用相同的键名

    // 存储
    await prefs.setString('username', 'John');
    
    // 读取(键名必须一致)
    String username = prefs.getString('username') ?? '';
    
  2. 检查应用是否被卸载:卸载应用会清除所有 SharedPreferences 数据

  3. 检查是否调用了 clear():确保没有意外调用 clear() 方法

5.4 性能问题

问题:频繁读写导致性能问题。

解决方案

  1. 复用 SharedPreferences 实例:在应用启动时获取实例,并在整个生命周期中复用

    class _SettingsPageState extends State<SettingsPage> {
      late SharedPreferences _prefs;
    
      
      void initState() {
        super.initState();
        _loadPreferences();
      }
    
      Future<void> _loadPreferences() async {
        _prefs = await SharedPreferences.getInstance();
      }
    
      // 后续操作都使用 _prefs
    }
    
  2. 批量操作:如果需要保存多个值,可以连续调用多个 set* 方法,它们会批量提交

    await Future.wait([
      prefs.setBool('notifications', true),
      prefs.setString('username', 'John'),
      prefs.setInt('font_size', 16),
    ]);
    

5.5 MissingPluginException 错误

问题:运行时出现 MissingPluginException: No implementation found for method getAll on channel plugins.flutter.io/shared_preferences

解决方案

这是 HarmonyOS 平台上最常见的问题,原因是插件没有正确注册到原生平台。需要完成以下步骤:

  1. 确保依赖正确配置:在 pubspec.yaml 中正确配置了 shared_preferences 依赖

  2. 重新构建项目

    flutter clean
    flutter pub get
    cd ohos/entry && ohpm install
    
  3. 检查插件注册:检查 ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets 文件是否包含 shared_preferences_ohos 的注册

  4. 验证配置:检查 .flutter-plugins 文件是否包含 shared_preferences_ohos

    cat .flutter-plugins | grep shared_preferences
    

重要提示

  • GeneratedPluginRegistrant.ets 文件虽然标记为自动生成,但在 HarmonyOS 平台上,如果插件没有自动注册,可能需要手动添加注册代码
  • 如果使用相对路径导入插件,需要确保路径正确

📝 六、最佳实践总结

6.1 数据存储规范

  1. 使用有意义的键名:键名应该清晰、有意义,便于维护

    // 推荐
    'notifications_enabled'
    'user_name'
    'font_size'
    
    // 不推荐
    'flag1'
    'data'
    'value'
    
  2. 提供默认值:读取数据时始终提供默认值

    bool value = prefs.getBool('key') ?? false;
    
  3. 类型一致性:确保存储和读取使用相同的数据类型

    // 存储为字符串
    await prefs.setString('count', '10');
    
    // 读取时也要使用字符串
    String count = prefs.getString('count') ?? '0';
    

6.2 错误处理

  1. 处理异步操作:所有 set*getInstance() 方法都是异步的,需要正确处理

    try {
      await prefs.setBool('key', true);
    } catch (e) {
      // 处理错误
      print('保存失败: $e');
    }
    
  2. 处理空值:读取数据时始终处理可能的 null

6.3 性能优化

  1. 复用实例:在应用启动时获取 SharedPreferences 实例,并在整个生命周期中复用

  2. 避免频繁读写:不要在循环中频繁读写数据

  3. 批量操作:如果需要保存多个值,可以连续调用多个 set* 方法

6.4 用户体验优化

  1. 显示保存反馈:保存数据后显示提示信息

    await prefs.setBool('key', true);
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('设置已保存')),
    );
    
  2. 加载状态:在加载数据时显示加载指示器

    bool _isLoading = true;
    
    Future<void> _loadSettings() async {
      setState(() => _isLoading = true);
      _prefs = await SharedPreferences.getInstance();
      // 加载数据...
      setState(() => _isLoading = false);
    }
    
  3. 确认清除:清除所有数据前显示确认对话框

🔗 七、相关资源

📄 八、完整代码示例

以下是一个最小化的完整示例,可以直接复制使用:

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Shared Preferences Demo',
      theme: ThemeData(useMaterial3: true),
      home: const SettingsPage(),
    );
  }
}

class SettingsPage extends StatefulWidget {
  const SettingsPage({super.key});

  
  State<SettingsPage> createState() => _SettingsPageState();
}

class _SettingsPageState extends State<SettingsPage> {
  late SharedPreferences _prefs;
  bool _notificationsEnabled = false;
  String _username = '';

  
  void initState() {
    super.initState();
    _loadSettings();
  }

  Future<void> _loadSettings() async {
    _prefs = await SharedPreferences.getInstance();
    setState(() {
      _notificationsEnabled = _prefs.getBool('notifications_enabled') ?? false;
      _username = _prefs.getString('username') ?? '';
    });
  }

  Future<void> _saveBool(String key, bool value) async {
    await _prefs.setBool(key, value);
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('设置已保存')),
    );
  }

  Future<void> _saveString(String key, String value) async {
    await _prefs.setString(key, value);
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('设置已保存')),
    );
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('设置')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          SwitchListTile(
            title: const Text('通知'),
            value: _notificationsEnabled,
            onChanged: (value) {
              setState(() {
                _notificationsEnabled = value;
              });
              _saveBool('notifications_enabled', value);
            },
          ),
          TextField(
            decoration: const InputDecoration(
              labelText: '用户名',
              hintText: '请输入用户名',
            ),
            onChanged: (value) {
              setState(() {
                _username = value;
              });
              _saveString('username', value);
            },
            controller: TextEditingController(text: _username)
              ..selection = TextSelection.fromPosition(
                TextPosition(offset: _username.length),
              ),
          ),
        ],
      ),
    );
  }
}

使用说明

  1. 将上述代码保存到 lib/main.dart 文件中
  2. 确保 pubspec.yaml 中已添加 shared_preferences 依赖
  3. 运行 flutter pub get 安装依赖
  4. 运行 flutter run 启动应用

🌐 社区支持

欢迎加入开源鸿蒙跨平台社区,与其他开发者交流学习,共同推进鸿蒙跨平台生态建设:

开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net

在这里你可以:

  • 📚 获取最新的跨平台开发技术文档
  • 💬 与其他开发者交流开发经验
  • 🐛 反馈问题和建议
  • 🎯 参与开源项目贡献
  • 📖 学习更多跨平台开发最佳实践

享受你的设置管理开发之旅! 🚀✨

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐