鸿蒙 Flutter 开发:离线数据处理与鸿蒙原生存储深度融合实战

在移动应用开发中,离线数据处理是提升用户体验的关键能力,尤其对于鸿蒙生态的全场景应用,用户可能在无网络环境下使用设备(如智能穿戴、车载设备)。Flutter 本身提供了shared_preferencessqflite等离线存储方案,但鸿蒙系统的原生存储能力(如分布式 KvStore、关系型数据库、文件存储)具备更强的分布式特性和系统级性能优化。此前我们探讨了鸿蒙 Flutter 的状态管理、原子化服务、全场景适配等内容,本文将聚焦鸿蒙 Flutter 的离线数据处理,结合鸿蒙原生存储能力(分布式 KvStore、鸿蒙关系型数据库),通过完整代码案例,演示如何实现离线数据的本地存储、同步与跨设备共享,打造支持离线场景的鸿蒙 Flutter 应用。

一、鸿蒙原生存储与 Flutter 离线数据处理的融合逻辑

鸿蒙系统提供了多种原生存储方案,与 Flutter 离线数据处理的融合遵循以下核心逻辑:

  1. 存储层抽象:将鸿蒙原生存储 API(KvStore、RDB)封装为 Flutter 的统一存储接口,使业务逻辑与底层存储实现解耦。
  2. 离线优先策略:Flutter 应用优先读取本地鸿蒙原生存储的离线数据,网络恢复后再同步到云端,并更新鸿蒙分布式存储的跨设备数据。
  3. 数据一致性保障:利用鸿蒙分布式存储的事务能力和监听机制,确保离线数据在多设备间的一致性,同时处理数据冲突。
  4. 轻量级与重量级存储分层:轻量级配置数据使用鸿蒙 KvStore 存储,结构化业务数据使用鸿蒙 RDB 存储,文件数据使用鸿蒙分布式文件存储,与 Flutter 的业务场景匹配。

本文将以离线笔记应用为场景,分别演示鸿蒙 KvStore 与 Flutter 轻量级配置存储、鸿蒙 RDB 与 Flutter 结构化笔记存储的融合开发流程。

二、案例 1:鸿蒙 KvStore + Flutter 轻量级配置存储(离线配置同步)

鸿蒙分布式 KvStore 是轻量级的键值对存储方案,支持跨设备同步,适合存储应用的配置信息(如用户偏好、主题设置、离线状态标记)。本案例将实现 Flutter 应用的主题配置离线存储,通过鸿蒙 KvStore 保存主题设置,实现离线状态下的主题切换,且跨设备同步主题配置。

前置条件

  1. 已配置鸿蒙 DevEco Studio 和 Flutter 环境,开启鸿蒙分布式数据管理能力。
  2. 已在鸿蒙原生项目中初始化分布式 KvStore(参考前文分布式数据管理案例)。

步骤 1:封装鸿蒙 KvStore 的 Flutter 工具类

创建统一的工具类,封装通过MethodChannel调用鸿蒙 KvStore 的读写、监听逻辑,提供 Flutter 端的简洁接口。

// lib/utils/harmony_kvstore_utils.dart
import 'package:flutter/services.dart';

/// 鸿蒙KvStore工具类(轻量级配置存储)
class HarmonyKvStoreUtils {
  static const MethodChannel _channel = MethodChannel('com.example.harmony_flutter/kvstore');

  /// 读取字符串配置
  static Future<String?> getString(String key, {String defaultValue = ''}) async {
    try {
      final String? result = await _channel.invokeMethod('getString', {
        'key': key,
        'defaultValue': defaultValue,
      });
      return result ?? defaultValue;
    } on PlatformException catch (e) {
      print('读取KvStore字符串失败:${e.message}');
      return defaultValue;
    }
  }

  /// 写入字符串配置
  static Future<void> setString(String key, String value) async {
    try {
      await _channel.invokeMethod('setString', {
        'key': key,
        'value': value,
      });
    } on PlatformException catch (e) {
      print('写入KvStore字符串失败:${e.message}');
    }
  }

  /// 监听配置变化(跨设备同步时触发)
  static void addConfigChangeListener(Function(String key, String value) listener) {
    _channel.setMethodCallHandler((call) async {
      if (call.method == 'onConfigChanged') {
        final String key = call.argument('key');
        final String value = call.argument('value');
        listener(key, value);
      }
      return null;
    });
  }
}

步骤 2:鸿蒙原生端实现 KvStore 的配置读写与监听

在鸿蒙原生项目的MainAbilitySlice.java中,实现 KvStore 的字符串读写、配置变化监听,并通过MethodChannel通知 Flutter 端。

// 鸿蒙KvStore配置处理逻辑
private static final String KVSTORE_CHANNEL = "com.example.harmony_flutter/kvstore";
private SingleKvStore configKvStore;

// 初始化配置KvStore
private void initConfigKvStore() {
    try {
        KvStoreManager manager = KvStoreManagerFactory.getInstance().createKvStoreManager(getContext());
        Options options = new Options();
        options.setCreateIfMissing(true);
        options.setKvStoreType(Options.KvStoreType.SINGLE_VERSION);
        // 配置专用KvStore名称
        Optional<SingleKvStore> optional = manager.getKvStore(options, "config_kvstore");
        if (optional.isPresent()) {
            configKvStore = optional.get();
            // 监听配置变化
            configKvStore.registerChangeListener((key, value) -> {
                // 通知Flutter端配置变化
                new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), KVSTORE_CHANNEL)
                        .invokeMethod("onConfigChanged", Map.of(
                                "key", key,
                                "value", value.getString()
                        ));
            });
        }
    } catch (KvStoreException e) {
        HiLog.error(LABEL, "配置KvStore初始化失败:%{public}s", e.getMessage());
    }
}

// 处理Flutter的KvStore调用
private void handleKvStoreMethodCall(MethodCall call, MethodChannel.Result result) {
    if (configKvStore == null) {
        result.error("KVSTORE_NULL", "配置存储初始化失败", null);
        return;
    }

    String key = call.argument("key");
    switch (call.method) {
        case "getString":
            String defaultValue = call.argument("defaultValue");
            String value = configKvStore.containsKey(key) ? configKvStore.getString(key) : defaultValue;
            result.success(value);
            break;
        case "setString":
            String setValue = call.argument("value");
            configKvStore.putString(key, setValue);
            result.success(null);
            break;
        default:
            result.notImplemented();
            break;
    }
}

步骤 3:Flutter 端实现离线主题配置(融合 KvStore)

在 Flutter 应用中,使用鸿蒙 KvStore 存储主题配置(亮色 / 暗色 / 跟随系统),实现离线状态下的主题切换,并监听跨设备的配置变化,同步主题设置。

// lib/providers/theme_provider.dart
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../utils/harmony_kvstore_utils.dart';

/// 主题模式枚举
enum AppThemeMode {
  light,
  dark,
  system,
}

/// 主题状态管理(融合鸿蒙KvStore)
class ThemeProvider extends ChangeNotifier {
  AppThemeMode _themeMode = AppThemeMode.system;
  static const String _themeKey = "app_theme_mode";

  AppThemeMode get themeMode => _themeMode;

  ThemeProvider() {
    // 初始化:从鸿蒙KvStore读取主题配置(离线优先)
    _initThemeMode();
    // 监听跨设备主题配置变化
    _listenThemeChange();
  }

  /// 初始化主题模式
  Future<void> _initThemeMode() async {
    // 离线读取鸿蒙KvStore的配置
    final String themeStr = await HarmonyKvStoreUtils.getString(_themeKey) ?? "system";
    _themeMode = _parseThemeMode(themeStr);
    notifyListeners();
  }

  /// 监听跨设备主题配置变化
  void _listenThemeChange() {
    HarmonyKvStoreUtils.addConfigChangeListener((key, value) {
      if (key == _themeKey) {
        _themeMode = _parseThemeMode(value);
        notifyListeners();
      }
    });
  }

  /// 切换主题模式(并写入鸿蒙KvStore)
  Future<void> setThemeMode(AppThemeMode mode) async {
    _themeMode = mode;
    notifyListeners();
    // 写入鸿蒙KvStore,支持离线存储和跨设备同步
    await HarmonyKvStoreUtils.setString(_themeKey, _themeModeToString(mode));
  }

  /// 字符串转主题模式
  AppThemeMode _parseThemeMode(String str) {
    switch (str) {
      case "light":
        return AppThemeMode.light;
      case "dark":
        return AppThemeMode.dark;
      default:
        return AppThemeMode.system;
    }
  }

  /// 主题模式转字符串
  String _themeModeToString(AppThemeMode mode) {
    switch (mode) {
      case AppThemeMode.light:
        return "light";
      case AppThemeMode.dark:
        return "dark";
      case AppThemeMode.system:
        return "system";
    }
  }

  /// 获取当前ThemeData
  ThemeData get currentTheme {
    switch (_themeMode) {
      case AppThemeMode.light:
        return ThemeData.light(useMaterial3: true);
      case AppThemeMode.dark:
        return ThemeData.dark(useMaterial3: true);
      case AppThemeMode.system:
        return ThemeData(useMaterial3: true);
    }
  }
}

步骤 4:Flutter UI 层实现主题切换与离线展示

// lib/main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'providers/theme_provider.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => ThemeProvider(),
      child: Consumer<ThemeProvider>(
        builder: (context, provider, child) {
          return MaterialApp(
            title: '鸿蒙Flutter离线主题配置',
            theme: provider.currentTheme,
            home: const ThemeSettingPage(),
          );
        },
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final themeProvider = Provider.of<ThemeProvider>(context);
    return Scaffold(
      appBar: AppBar(title: const Text('离线主题配置(KvStore)')),
      body: ListView(
        padding: const EdgeInsets.all(16.0),
        children: [
          ListTile(
            title: const Text('亮色模式'),
            leading: Radio<AppThemeMode>(
              value: AppThemeMode.light,
              groupValue: themeProvider.themeMode,
              onChanged: (value) {
                if (value != null) {
                  themeProvider.setThemeMode(value);
                }
              },
            ),
          ),
          ListTile(
            title: const Text('暗色模式'),
            leading: Radio<AppThemeMode>(
              value: AppThemeMode.dark,
              groupValue: themeProvider.themeMode,
              onChanged: (value) {
                if (value != null) {
                  themeProvider.setThemeMode(value);
                }
              },
            ),
          ),
          ListTile(
            title: const Text('跟随系统'),
            leading: Radio<AppThemeMode>(
              value: AppThemeMode.system,
              groupValue: themeProvider.themeMode,
              onChanged: (value) {
                if (value != null) {
                  themeProvider.setThemeMode(value);
                }
              },
            ),
          ),
          const Divider(),
          const ListTile(
            title: Text('离线说明:'),
            subtitle: Text('主题配置已存储到鸿蒙KvStore,无网络时仍可切换,跨设备同步配置。'),
          ),
        ],
      ),
    );
  }
}

案例说明

  1. 存储层:鸿蒙 KvStore 负责存储主题配置,支持离线读写和跨设备同步,Flutter 端通过工具类封装调用逻辑,实现与原生存储的解耦。
  2. 状态层ThemeProvider封装主题状态逻辑,初始化时优先读取离线的 KvStore 配置,监听跨设备配置变化并更新状态。
  3. UI 层:用户可在离线状态下切换主题,配置即时生效并写入 KvStore,网络恢复后自动同步到其他设备,实现了离线优先的用户体验。

三、案例 2:鸿蒙 RDB + Flutter 结构化笔记存储(离线笔记管理)

鸿蒙关系型数据库(RDB)是支持 SQL 语法的结构化存储方案,具备事务、索引、批量操作等能力,适合存储复杂的结构化业务数据(如笔记、订单)。本案例将实现 Flutter 离线笔记应用,使用鸿蒙 RDB 存储笔记数据,支持离线创建、编辑、删除笔记,网络恢复后同步到云端,且跨设备共享笔记数据。

核心技术点

  1. 鸿蒙 RDB 的表创建与 CRUD 操作:原生端实现笔记表的创建、增删改查,并提供事务支持。
  2. Flutter 端封装 RDB 接口:通过MethodChannel封装笔记的 CRUD 操作,提供面向对象的调用方式。
  3. 离线优先与数据同步:Flutter 应用优先读取本地 RDB 的笔记数据,网络恢复后同步到云端,并更新分布式存储。

步骤 1:鸿蒙原生端实现 RDB 的笔记存储逻辑

在鸿蒙原生项目中,创建 RDB 管理器,实现笔记表的创建、增删改查操作。

1. 定义笔记实体类
// Note.java
package com.example.harmony_flutter.entity;

public class Note {
    private int id;
    private String title;
    private String content;
    private long createTime;
    private long updateTime;
    private boolean isSync; // 是否同步到云端

    // 构造方法、getter、setter省略
}
2. 实现 RDB 管理器
// NoteRdbManager.java
package com.example.harmony_flutter.manager;

import com.example.harmony_flutter.entity.Note;
import ohos.data.rdb.*;
import ohos.data.resultset.ResultSet;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.app.Context;
import java.util.ArrayList;
import java.util.List;

public class NoteRdbManager {
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0, "NoteRdbManager");
    private static final String DB_NAME = "note_db.db";
    private static final int DB_VERSION = 1;
    private static final String TABLE_NAME = "note";
    // 表结构
    private static final String CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
            "id INTEGER PRIMARY KEY AUTOINCREMENT, " +
            "title TEXT NOT NULL, " +
            "content TEXT, " +
            "create_time LONG NOT NULL, " +
            "update_time LONG NOT NULL, " +
            "is_sync BOOLEAN DEFAULT FALSE)";

    private RdbStore rdbStore;
    private RdbOpenCallback rdbOpenCallback;

    public NoteRdbManager(Context context) {
        initRdbCallback();
        RdbManager rdbManager = RdbManagerFactory.getRdbManager(context);
        rdbStore = rdbManager.getRdbStore(DB_NAME, DB_VERSION, rdbOpenCallback, null);
    }

    /**
     * 初始化RDB回调
     */
    private void initRdbCallback() {
        rdbOpenCallback = new RdbOpenCallback() {
            @Override
            public void onCreate(RdbStore store) {
                store.executeSql(CREATE_TABLE_SQL);
                HiLog.info(LABEL, "笔记表创建成功");
            }

            @Override
            public void onUpgrade(RdbStore store, int oldVersion, int newVersion) {
                // 数据库升级逻辑
            }
        };
    }

    /**
     * 添加笔记(支持事务)
     */
    public long addNote(Note note) {
        ValuesBucket values = new ValuesBucket();
        values.putString("title", note.getTitle());
        values.putString("content", note.getContent());
        values.putLong("create_time", note.getCreateTime());
        values.putLong("update_time", note.getUpdateTime());
        values.putBoolean("is_sync", note.isSync());
        // 开启事务
        rdbStore.beginTransaction();
        try {
            long id = rdbStore.insert(TABLE_NAME, values);
            rdbStore.setTransactionSuccessful();
            return id;
        } catch (Exception e) {
            HiLog.error(LABEL, "添加笔记失败:%{public}s", e.getMessage());
            return -1;
        } finally {
            rdbStore.endTransaction();
        }
    }

    /**
     * 查询所有笔记
     */
    public List<Note> queryAllNotes() {
        List<Note> noteList = new ArrayList<>();
        String[] columns = {"id", "title", "content", "create_time", "update_time", "is_sync"};
        ResultSet resultSet = rdbStore.query(TABLE_NAME, columns, null);
        while (resultSet.goToNextRow()) {
            Note note = new Note();
            note.setId(resultSet.getInt(resultSet.getColumnIndex("id")));
            note.setTitle(resultSet.getString(resultSet.getColumnIndex("title")));
            note.setContent(resultSet.getString(resultSet.getColumnIndex("content")));
            note.setCreateTime(resultSet.getLong(resultSet.getColumnIndex("create_time")));
            note.setUpdateTime(resultSet.getLong(resultSet.getColumnIndex("update_time")));
            note.setSync(resultSet.getBoolean(resultSet.getColumnIndex("is_sync")));
            noteList.add(note);
        }
        resultSet.close();
        return noteList;
    }

    /**
     * 删除笔记
     */
    public int deleteNote(int id) {
        return rdbStore.delete(TABLE_NAME, "id = ?", new String[]{String.valueOf(id)});
    }

    /**
     * 更新笔记
     */
    public int updateNote(Note note) {
        ValuesBucket values = new ValuesBucket();
        values.putString("title", note.getTitle());
        values.putString("content", note.getContent());
        values.putLong("update_time", note.getUpdateTime());
        values.putBoolean("is_sync", note.isSync());
        return rdbStore.update(values, TABLE_NAME, "id = ?", new String[]{String.valueOf(note.getId())});
    }
}

步骤 2:鸿蒙原生端提供 Flutter 调用的 RDB 接口

MainAbilitySlice.java中,通过MethodChannel暴露笔记的 CRUD 接口给 Flutter 端。

// 笔记RDB的MethodChannel处理
private static final String NOTE_RDB_CHANNEL = "com.example.harmony_flutter/note_rdb";
private NoteRdbManager noteRdbManager;

// 初始化NoteRdbManager
noteRdbManager = new NoteRdbManager(getContext());

// 处理Flutter的RDB调用
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), NOTE_RDB_CHANNEL)
        .setMethodCallHandler((call, result) -> {
            switch (call.method) {
                case "addNote":
                    // 接收Flutter传递的笔记参数
                    String title = call.argument("title");
                    String content = call.argument("content");
                    long createTime = call.argument("createTime");
                    long updateTime = call.argument("updateTime");
                    boolean isSync = call.argument("isSync");
                    Note note = new Note();
                    note.setTitle(title);
                    note.setContent(content);
                    note.setCreateTime(createTime);
                    note.setUpdateTime(updateTime);
                    note.setSync(isSync);
                    long id = noteRdbManager.addNote(note);
                    result.success(id);
                    break;
                case "queryAllNotes":
                    List<Note> noteList = noteRdbManager.queryAllNotes();
                    // 转换为Map列表供Flutter接收
                    List<java.util.Map<String, Object>> noteMapList = new ArrayList<>();
                    for (Note n : noteList) {
                        java.util.Map<String, Object> map = new java.util.HashMap<>();
                        map.put("id", n.getId());
                        map.put("title", n.getTitle());
                        map.put("content", n.getContent());
                        map.put("createTime", n.getCreateTime());
                        map.put("updateTime", n.getUpdateTime());
                        map.put("isSync", n.isSync());
                        noteMapList.add(map);
                    }
                    result.success(noteMapList);
                    break;
                case "deleteNote":
                    int noteId = call.argument("id");
                    int deleteCount = noteRdbManager.deleteNote(noteId);
                    result.success(deleteCount);
                    break;
                case "updateNote":
                    // 处理更新笔记逻辑,类似addNote
                    result.success(0);
                    break;
                default:
                    result.notImplemented();
                    break;
            }
        });

步骤 3:Flutter 端封装笔记实体与 RDB 调用工具类

// lib/entity/note_entity.dart
/// 笔记实体类
class NoteEntity {
  final int id;
  final String title;
  final String content;
  final int createTime;
  final int updateTime;
  final bool isSync;

  NoteEntity({
    this.id = -1,
    required this.title,
    required this.content,
    required this.createTime,
    required this.updateTime,
    this.isSync = false,
  });

  // 从Map转换为NoteEntity
  factory NoteEntity.fromMap(Map<String, dynamic> map) {
    return NoteEntity(
      id: map['id'] as int,
      title: map['title'] as String,
      content: map['content'] as String,
      createTime: map['createTime'] as int,
      updateTime: map['updateTime'] as int,
      isSync: map['isSync'] as bool,
    );
  }

  // 转换为Map
  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'title': title,
      'content': content,
      'createTime': createTime,
      'updateTime': updateTime,
      'isSync': isSync,
    };
  }
}

// lib/utils/harmony_note_rdb_utils.dart
import 'package:flutter/services.dart';
import '../entity/note_entity.dart';

/// 鸿蒙RDB笔记工具类
class HarmonyNoteRdbUtils {
  static const MethodChannel _channel = MethodChannel('com.example.harmony_flutter/note_rdb');

  /// 添加笔记
  static Future<int> addNote(NoteEntity note) async {
    try {
      final int id = await _channel.invokeMethod('addNote', note.toMap());
      return id;
    } on PlatformException catch (e) {
      print('添加笔记失败:${e.message}');
      return -1;
    }
  }

  /// 查询所有笔记(离线优先)
  static Future<List<NoteEntity>> queryAllNotes() async {
    try {
      final List<dynamic> result = await _channel.invokeMethod('queryAllNotes');
      return result.map((e) => NoteEntity.fromMap(e as Map<String, dynamic>)).toList();
    } on PlatformException catch (e) {
      print('查询笔记失败:${e.message}');
      return [];
    }
  }

  /// 删除笔记
  static Future<int> deleteNote(int id) async {
    try {
      final int count = await _channel.invokeMethod('deleteNote', {'id': id});
      return count;
    } on PlatformException catch (e) {
      print('删除笔记失败:${e.message}');
      return 0;
    }
  }

  /// 更新笔记
  static Future<int> updateNote(NoteEntity note) async {
    try {
      final int count = await _channel.invokeMethod('updateNote', note.toMap());
      return count;
    } on PlatformException catch (e) {
      print('更新笔记失败:${e.message}');
      return 0;
    }
  }
}

步骤 4:Flutter 端实现离线笔记管理的业务逻辑与 UI

// lib/providers/note_provider.dart
import 'package:flutter/foundation.dart';
import '../entity/note_entity.dart';
import '../utils/harmony_note_rdb_utils.dart';

/// 笔记状态管理(融合鸿蒙RDB)
class NoteProvider extends ChangeNotifier {
  List<NoteEntity> _noteList = [];

  List<NoteEntity> get noteList => _noteList;

  NoteProvider() {
    // 初始化:离线读取鸿蒙RDB的笔记数据
    _loadNotes();
  }

  /// 加载所有笔记(离线优先)
  Future<void> _loadNotes() async {
    _noteList = await HarmonyNoteRdbUtils.queryAllNotes();
    notifyListeners();
  }

  /// 添加笔记(离线存储)
  Future<void> addNote(String title, String content) async {
    final now = DateTime.now().millisecondsSinceEpoch;
    final note = NoteEntity(
      title: title,
      content: content,
      createTime: now,
      updateTime: now,
      isSync: false, // 离线状态,未同步到云端
    );
    final int id = await HarmonyNoteRdbUtils.addNote(note);
    if (id != -1) {
      _noteList.add(NoteEntity(
        id: id,
        title: title,
        content: content,
        createTime: now,
        updateTime: now,
        isSync: false,
      ));
      notifyListeners();
      // 网络恢复后同步到云端(此处简化,实际项目中添加网络监听)
      _syncNoteToCloud(note);
    }
  }

  /// 删除笔记
  Future<void> deleteNote(int id) async {
    final int count = await HarmonyNoteRdbUtils.deleteNote(id);
    if (count > 0) {
      _noteList.removeWhere((note) => note.id == id);
      notifyListeners();
    }
  }

  /// 同步笔记到云端(模拟)
  Future<void> _syncNoteToCloud(NoteEntity note) async {
    // 实际项目中添加网络请求逻辑,同步成功后更新isSync状态
    await Future.delayed(const Duration(seconds: 1));
    final updatedNote = NoteEntity(
      id: note.id,
      title: note.title,
      content: note.content,
      createTime: note.createTime,
      updateTime: note.updateTime,
      isSync: true,
    );
    await HarmonyNoteRdbUtils.updateNote(updatedNote);
    // 更新本地列表
    final index = _noteList.indexWhere((n) => n.id == note.id);
    if (index != -1) {
      _noteList[index] = updatedNote;
      notifyListeners();
    }
  }
}

// lib/main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'providers/note_provider.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => NoteProvider(),
      child: MaterialApp(
        title: '鸿蒙Flutter离线笔记',
        theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
        home: const NoteListPage(),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('离线笔记管理(RDB)')),
      body: Consumer<NoteProvider>(
        builder: (context, provider, child) {
          if (provider.noteList.isEmpty) {
            return const Center(child: Text('暂无笔记,点击右下角添加(离线可用)'));
          }
          return ListView.builder(
            itemCount: provider.noteList.length,
            itemBuilder: (context, index) {
              final note = provider.noteList[index];
              return ListTile(
                title: Text(note.title),
                subtitle: Text(
                  note.content,
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                ),
                trailing: Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Icon(
                      note.isSync ? Icons.cloud_done : Icons.cloud_off,
                      color: note.isSync ? Colors.green : Colors.grey,
                    ),
                    IconButton(
                      icon: const Icon(Icons.delete, color: Colors.red),
                      onPressed: () => provider.deleteNote(note.id),
                    ),
                  ],
                ),
              );
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _showAddNoteDialog(context),
        child: const Icon(Icons.add),
      ),
    );
  }

  /// 显示添加笔记对话框
  void _showAddNoteDialog(BuildContext context) {
    final titleController = TextEditingController();
    final contentController = TextEditingController();

    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: const Text('添加离线笔记'),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              TextField(
                controller: titleController,
                decoration: const InputDecoration(hintText: '笔记标题'),
              ),
              const SizedBox(height: 10),
              TextField(
                controller: contentController,
                decoration: const InputDecoration(hintText: '笔记内容'),
                maxLines: 3,
              ),
            ],
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('取消'),
            ),
            TextButton(
              onPressed: () {
                final title = titleController.text.trim();
                final content = contentController.text.trim();
                if (title.isNotEmpty) {
                  Provider.of<NoteProvider>(context, listen: false).addNote(title, content);
                  Navigator.pop(context);
                }
              },
              child: const Text('添加'),
            ),
          ],
        );
      },
    );
  }
}

案例说明

  1. 存储层:鸿蒙 RDB 负责存储结构化的笔记数据,支持事务和批量操作,Flutter 端通过工具类封装调用逻辑,实现离线读写。
  2. 业务层NoteProvider封装笔记的业务逻辑,初始化时优先加载离线的 RDB 数据,添加笔记时先写入本地 RDB,网络恢复后同步到云端。
  3. UI 层:用户可在离线状态下创建、删除笔记,笔记即时生效并存储到 RDB,同步状态通过图标展示,实现了离线优先的笔记管理体验。

四、鸿蒙 Flutter 离线数据处理的最佳实践

  1. 存储方案分层选择
    • 轻量级配置数据(如主题、用户偏好):使用鸿蒙 KvStore,利用其跨设备同步特性。
    • 结构化业务数据(如笔记、订单):使用鸿蒙 RDB,利用其事务和索引能力。
    • 大文件数据(如图片、视频):使用鸿蒙分布式文件存储,支持离线访问和跨设备传输。
  2. 离线优先策略:Flutter 应用启动时优先读取本地鸿蒙原生存储的数据,再尝试从网络获取最新数据,避免网络依赖导致的启动缓慢。
  3. 数据同步策略
    • 增量同步:网络恢复后,只同步离线期间变化的数据,减少网络开销。
    • 冲突处理:使用鸿蒙分布式存储的版本号机制,处理多设备离线数据的冲突(如以最新更新时间为准)。
  4. 性能优化
    • 批量操作:对高频的 CRUD 操作(如批量添加笔记),使用鸿蒙 RDB 的事务能力,减少磁盘 IO 次数。
    • 数据缓存:将常用的离线数据缓存到 Flutter 的内存中,减少原生存储的调用次数。
  5. 网络监听:使用 Flutter 的connectivity_plus插件监听网络状态,网络恢复时自动触发数据同步,更新鸿蒙分布式存储的跨设备数据。

五、总结

本文通过鸿蒙 KvStore 与 Flutter 主题配置存储、鸿蒙 RDB 与 Flutter 笔记存储两个案例,展示了鸿蒙原生存储与 Flutter 离线数据处理的深度融合开发流程。这种融合方式既利用了鸿蒙原生存储的分布式特性和系统级性能优化,又保留了 Flutter 的跨端优势,能够打造出支持离线场景的全场景鸿蒙 Flutter 应用。

在实际项目中,开发者可以结合更多的鸿蒙原生能力(如分布式任务调度、原子化服务),实现离线数据的跨设备同步和轻量化展示。随着鸿蒙系统对 Flutter 支持的不断深化,这种离线数据处理方案将成为鸿蒙 Flutter 应用开发的重要组成部分,尤其适用于智能穿戴、车载设备等鸿蒙生态的离线场景。

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

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐