【Flutter x 鸿蒙】第六篇:状态管理、数据持久化与分布式数据

【Flutter x 鸿蒙】第六篇:状态管理、数据持久化与分布式数据

在掌握了Flutter与鸿蒙的导航和多设备适配后,今天我们深入探讨状态管理数据持久化鸿蒙分布式数据这三个核心主题。这些技术是构建复杂、高性能Flutter应用的基础,也是充分利用鸿蒙分布式特性的关键。

一、Flutter状态管理方案选型

1.1 状态管理方案对比

在Flutter开发中,选择合适的方案至关重要。以下是主流方案的对比:

方案 适用场景 学习曲线 性能表现 代码量
Provider 中小型应用,简单状态共享 优秀
Riverpod 中大型应用,复杂状态管理 优秀 中等
Bloc/Cubit 大型应用,复杂业务逻辑 中高 优秀 较多
GetX 快速开发,全功能框架 优秀
MobX 响应式状态管理 优秀 中等

1.2 Provider实战:计数器应用

Provider是Flutter官方推荐的状态管理方案,简单易用:

复制代码
// lib/models/counter_model.dart
import 'package:flutter/foundation.dart';

class CounterModel with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }

  void decrement() {
    _count--;
    notifyListeners();
  }
}
// lib/pages/counter_page.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/counter_model.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('计数器')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('当前计数:'),
            Consumer<CounterModel>(
              builder: (context, counter, child) {
                return Text(
                  '${counter.count}',
                  style: Theme.of(context).textTheme.headlineMedium,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () => context.read<CounterModel>().increment(),
            child: const Icon(Icons.add),
          ),
          const SizedBox(height: 8),
          FloatingActionButton(
            onPressed: () => context.read<CounterModel>().decrement(),
            child: const Icon(Icons.remove),
          ),
        ],
      ),
    );
  }
}

1.3 Riverpod实战:用户信息管理

Riverpod是Provider的升级版,更加强大和灵活:

复制代码
// lib/providers/user_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';

final userProvider = StateNotifierProvider<UserNotifier, UserState>((ref) {
  return UserNotifier();
});

class UserState {
  final String? name;
  final String? email;
  final bool isLoading;

  UserState({
    this.name,
    this.email,
    this.isLoading = false,
  });

  UserState copyWith({
    String? name,
    String? email,
    bool? isLoading,
  }) {
    return UserState(
      name: name ?? this.name,
      email: email ?? this.email,
      isLoading: isLoading ?? this.isLoading,
    );
  }
}

class UserNotifier extends StateNotifier<UserState> {
  UserNotifier() : super(UserState());

  Future<void> loadUser() async {
    state = state.copyWith(isLoading: true);
    
    // 模拟网络请求
    await Future.delayed(const Duration(seconds: 1));
    
    state = state.copyWith(
      name: '张三',
      email: 'zhangsan@example.com',
      isLoading: false,
    );
  }

  void updateName(String name) {
    state = state.copyWith(name: name);
  }

  void clearUser() {
    state = UserState();
  }
}

二、数据持久化方案

2.1 SharedPreferences:简单键值存储

复制代码
// lib/services/local_storage_service.dart
import 'package:shared_preferences/shared_preferences.dart';

class LocalStorageService {
  static late SharedPreferences _prefs;

  static Future<void> init() async {
    _prefs = await SharedPreferences.getInstance();
  }

  // 保存数据
  static Future<bool> saveString(String key, String value) {
    return _prefs.setString(key, value);
  }

  static Future<bool> saveInt(String key, int value) {
    return _prefs.setInt(key, value);
  }

  static Future<bool> saveBool(String key, bool value) {
    return _prefs.setBool(key, value);
  }

  // 读取数据
  static String getString(String key, [String defaultValue = '']) {
    return _prefs.getString(key) ?? defaultValue;
  }

  static int getInt(String key, [int defaultValue = 0]) {
    return _prefs.getInt(key) ?? defaultValue;
  }

  static bool getBool(String key, [bool defaultValue = false]) {
    return _prefs.getBool(key) ?? defaultValue;
  }

  // 删除数据
  static Future<bool> remove(String key) {
    return _prefs.remove(key);
  }

  static Future<bool> clear() {
    return _prefs.clear();
  }
}

2.2 Hive:高性能NoSQL数据库

Hive是轻量级、高性能的键值数据库:

复制代码
# pubspec.yaml
dependencies:
  hive: ^2.2.3
  hive_flutter: ^1.1.0
  path_provider: ^2.0.15
// lib/services/hive_service.dart
import 'package:hive_flutter/hive_flutter.dart';
import 'package:path_provider/path_provider.dart';

class HiveService {
  static const String userBox = 'user_box';
  static const String settingsBox = 'settings_box';

  static Future<void> init() async {
    final appDocumentDir = await getApplicationDocumentsDirectory();
    Hive.init(appDocumentDir.path);
    
    // 注册适配器(如果需要存储自定义对象)
    // Hive.registerAdapter(UserAdapter());
    
    await Hive.openBox(userBox);
    await Hive.openBox(settingsBox);
  }

  // 保存数据
  static Future<void> saveUserData(String key, dynamic value) async {
    final box = Hive.box(userBox);
    await box.put(key, value);
  }

  // 读取数据
  static dynamic getUserData(String key) {
    final box = Hive.box(userBox);
    return box.get(key);
  }

  // 删除数据
  static Future<void> deleteUserData(String key) async {
    final box = Hive.box(userBox);
    await box.delete(key);
  }

  // 清空数据
  static Future<void> clearUserData() async {
    final box = Hive.box(userBox);
    await box.clear();
  }
}

2.3 SQLite:关系型数据库

对于复杂的数据结构,SQLite是更好的选择:

复制代码
# pubspec.yaml
dependencies:
  sqflite: ^2.3.0
  path: ^1.8.3
// lib/services/database_service.dart
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

class DatabaseService {
  static Database? _database;

  static Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDatabase();
    return _database!;
  }

  static Future<Database> _initDatabase() async {
    final databasePath = await getDatabasesPath();
    final path = join(databasePath, 'app_database.db');

    return openDatabase(
      path,
      version: 1,
      onCreate: (db, version) async {
        await db.execute('''
          CREATE TABLE users(
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            email TEXT NOT NULL,
            created_at INTEGER NOT NULL
          )
        ''');
        
        await db.execute('''
          CREATE TABLE settings(
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            key TEXT NOT NULL,
            value TEXT NOT NULL,
            created_at INTEGER NOT NULL
          )
        ''');
      },
    );
  }

  // 插入用户
  static Future<int> insertUser(Map<String, dynamic> user) async {
    final db = await database;
    return db.insert('users', user);
  }

  // 查询用户
  static Future<List<Map<String, dynamic>>> getUsers() async {
    final db = await database;
    return db.query('users');
  }

  // 更新用户
  static Future<int> updateUser(int id, Map<String, dynamic> user) async {
    final db = await database;
    return db.update('users', user, where: 'id = ?', whereArgs: [id]);
  }

  // 删除用户
  static Future<int> deleteUser(int id) async {
    final db = await database;
    return db.delete('users', where: 'id = ?', whereArgs: [id]);
  }
}

三、鸿蒙分布式数据同步

3.1 分布式数据管理原理

鸿蒙的分布式数据管理(Distributed Data Management, DDM)允许应用在不同设备间同步数据,实现无缝的跨设备体验。

3.2 分布式键值存储

复制代码
// lib/services/distributed_kv_service.dart
import 'package:flutter/services.dart';

class DistributedKVService {
  static const MethodChannel _channel = 
      MethodChannel('com.example/distributed_kv');

  // 保存数据到分布式存储
  static Future<bool> save(String key, String value) async {
    try {
      final result = await _channel.invokeMethod('save', {
        'key': key,
        'value': value,
      });
      return result == true;
    } catch (e) {
      return false;
    }
  }

  // 从分布式存储读取数据
  static Future<String?> get(String key) async {
    try {
      final result = await _channel.invokeMethod('get', {'key': key});
      return result as String?;
    } catch (e) {
      return null;
    }
  }

  // 删除分布式存储中的数据
  static Future<bool> delete(String key) async {
    try {
      final result = await _channel.invokeMethod('delete', {'key': key});
      return result == true;
    } catch (e) {
      return false;
    }
  }

  // 监听数据变化
  static Stream<String> get dataChangeStream {
    return _channel.receiveBroadcastStream().map((event) {
      return event.toString();
    });
  }
}

3.3 鸿蒙端分布式KV实现

复制代码
// ohos/entry/src/main/ets/services/DistributedKVService.ts
import common from '@ohos.app.ability.common';
import distributed from '@ohos.distributed';
import { BusinessError } from '@ohos.base';

export class DistributedKVService {
  private context: common.UIAbilityContext;
  private channel: any;
  private kvManager: any;
  private kvStore: any;
  private eventSink: any;

  constructor(context: common.UIAbilityContext) {
    this.context = context;
    this.initChannel();
    this.initKVStore();
  }

  private initChannel() {
    this.channel = new MethodChannel(
      this.context,
      'com.example/distributed_kv',
      StandardMethodCodec.INSTANCE
    );

    this.channel.setMethodCallHandler(this.handleMethodCall.bind(this));

    // EventChannel用于数据变化监听
    const eventChannel = new EventChannel(
      this.context,
      'com.example/distributed_kv',
      StandardMessageCodec.INSTANCE
    );

    eventChannel.setStreamHandler({
      onListen: (args: any, events: any) => {
        this.eventSink = events;
      },
      onCancel: (args: any) => {
        this.eventSink = undefined;
      }
    });
  }

  private async initKVStore() {
    try {
      // 创建KVManager
      this.kvManager = distributed.createKVManager({
        context: this.context,
        bundleName: 'com.example.app'
      });

      // 创建KVStore
      this.kvStore = await this.kvManager.getKVStore('app_data', {
        createIfMissing: true
      });

      // 监听数据变化
      this.kvStore.on('dataChange', (data: any) => {
        if (this.eventSink) {
          this.eventSink.success(`数据变化: ${JSON.stringify(data)}`);
        }
      });
    } catch (error) {
      console.error('初始化KVStore失败:', error);
    }
  }

  private async handleMethodCall(call: any, result: any) {
    switch (call.method) {
      case 'save':
        await this.saveData(call.arguments, result);
        break;
      case 'get':
        await this.getData(call.arguments, result);
        break;
      case 'delete':
        await this.deleteData(call.arguments, result);
        break;
      default:
        result.notImplemented();
    }
  }

  private async saveData(args: any, result: any) {
    try {
      const key = args.key;
      const value = args.value;
      await this.kvStore.put(key, value);
      result.success(true);
    } catch (error) {
      result.error('保存数据失败', error.message);
    }
  }

  private async getData(args: any, result: any) {
    try {
      const key = args.key;
      const value = await this.kvStore.get(key);
      result.success(value);
    } catch (error) {
      result.error('获取数据失败', error.message);
    }
  }

  private async deleteData(args: any, result: any) {
    try {
      const key = args.key;
      await this.kvStore.delete(key);
      result.success(true);
    } catch (error) {
      result.error('删除数据失败', error.message);
    }
  }
}

3.4 分布式对象存储

对于复杂对象,可以使用分布式对象存储:

复制代码
// lib/services/distributed_object_service.dart
import 'package:flutter/services.dart';
import 'dart:convert';

class DistributedObjectService {
  static const MethodChannel _channel = 
      MethodChannel('com.example/distributed_object');

  // 保存对象
  static Future<bool> saveObject(String key, Map<String, dynamic> object) async {
    try {
      final result = await _channel.invokeMethod('saveObject', {
        'key': key,
        'value': json.encode(object),
      });
      return result == true;
    } catch (e) {
      return false;
    }
  }

  // 获取对象
  static Future<Map<String, dynamic>?> getObject(String key) async {
    try {
      final result = await _channel.invokeMethod('getObject', {'key': key});
      if (result != null) {
        return json.decode(result as String);
      }
      return null;
    } catch (e) {
      return null;
    }
  }

  // 监听对象变化
  static Stream<Map<String, dynamic>> get objectChangeStream {
    return _channel.receiveBroadcastStream().map((event) {
      return json.decode(event as String);
    });
  }
}

四、状态管理最佳实践

4.1 状态分层架构

将状态按照作用域进行分层管理:

复制代码
// 应用级状态
final appConfigProvider = StateNotifierProvider<AppConfigNotifier, AppConfig>((ref) {
  return AppConfigNotifier();
});

// 页面级状态
final homePageProvider = StateNotifierProvider<HomePageNotifier, HomePageState>((ref) {
  return HomePageNotifier();
});

// 组件级状态
final counterProvider = StateProvider<int>((ref) => 0);

4.2 状态持久化策略

结合Riverpod和Hive实现状态持久化:

复制代码
// lib/providers/persistent_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';

final persistentCounterProvider = StateNotifierProvider<PersistentCounterNotifier, int>((ref) {
  return PersistentCounterNotifier();
});

class PersistentCounterNotifier extends StateNotifier<int> {
  PersistentCounterNotifier() : super(0) {
    _loadFromStorage();
  }

  Future<void> _loadFromStorage() async {
    final value = HiveService.getUserData('counter') ?? 0;
    state = value;
  }

  void increment() {
    state++;
    _saveToStorage();
  }

  void decrement() {
    state--;
    _saveToStorage();
  }

  Future<void> _saveToStorage() async {
    await HiveService.saveUserData('counter', state);
  }
}

4.3 状态恢复机制

在应用启动时恢复状态:

复制代码
// lib/main.dart
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // 初始化数据存储
  await HiveService.init();
  
  // 恢复应用状态
  final savedCounter = HiveService.getUserData('counter') ?? 0;
  
  runApp(ProviderScope(
    overrides: [
      persistentCounterProvider.overrideWithValue(
        PersistentCounterNotifier()..state = savedCounter,
      ),
    ],
    child: MyApp(),
  ));
}

五、实战案例:跨设备待办事项应用

让我们实现一个完整的跨设备待办事项应用,展示状态管理和分布式数据的实际应用:

复制代码
// lib/models/todo_model.dart
class Todo {
  final String id;
  final String title;
  final bool completed;
  final DateTime createdAt;

  Todo({
    required this.id,
    required this.title,
    this.completed = false,
    required this.createdAt,
  });

  Todo copyWith({
    String? id,
    String? title,
    bool? completed,
    DateTime? createdAt,
  }) {
    return Todo(
      id: id ?? this.id,
      title: title ?? this.title,
      completed: completed ?? this.completed,
      createdAt: createdAt ?? this.createdAt,
    );
  }

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'title': title,
      'completed': completed,
      'createdAt': createdAt.millisecondsSinceEpoch,
    };
  }

  factory Todo.fromMap(Map<String, dynamic> map) {
    return Todo(
      id: map['id'],
      title: map['title'],
      completed: map['completed'],
      createdAt: DateTime.fromMillisecondsSinceEpoch(map['createdAt']),
    );
  }
}
// lib/providers/todo_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:uuid/uuid.dart';
import '../models/todo_model.dart';
import '../services/distributed_kv_service.dart';

final todoListProvider = StateNotifierProvider<TodoListNotifier, List<Todo>>((ref) {
  return TodoListNotifier();
});

class TodoListNotifier extends StateNotifier<List<Todo>> {
  TodoListNotifier() : super([]) {
    _loadFromDistributedStorage();
  }

  Future<void> _loadFromDistributedStorage() async {
    final data = await DistributedKVService.get('todos');
    if (data != null) {
      final List<dynamic> jsonList = json.decode(data);
      final todos = jsonList.map((item) => Todo.fromMap(item)).toList();
      state = todos;
    }
  }

  Future<void> _saveToDistributedStorage() async {
    final jsonList = state.map((todo) => todo.toMap()).toList();
    await DistributedKVService.save('todos', json.encode(jsonList));
  }

  void addTodo(String title) {
    final newTodo = Todo(
      id: const Uuid().v4(),
      title: title,
      createdAt: DateTime.now(),
    );
    state = [...state, newTodo];
    _saveToDistributedStorage();
  }

  void toggleTodo(String id) {
    state = state.map((todo) {
      if (todo.id == id) {
        return todo.copyWith(completed: !todo.completed);
      }
      return todo;
    }).toList();
    _saveToDistributedStorage();
  }

  void deleteTodo(String id) {
    state = state.where((todo) => todo.id != id).toList();
    _saveToDistributedStorage();
  }

  void updateTodo(String id, String newTitle) {
    state = state.map((todo) {
      if (todo.id == id) {
        return todo.copyWith(title: newTitle);
      }
      return todo;
    }).toList();
    _saveToDistributedStorage();
  }
}
// lib/pages/todo_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../providers/todo_provider.dart';

class TodoPage extends ConsumerWidget {
  const TodoPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final todos = ref.watch(todoListProvider);
    final notifier = ref.read(todoListProvider.notifier);

    return Scaffold(
      appBar: AppBar(
        title: const Text('待办事项'),
        actions: [
          IconButton(
            icon: const Icon(Icons.delete),
            onPressed: () {
              // 清空已完成
              final completedIds = todos
                  .where((todo) => todo.completed)
                  .map((todo) => todo.id)
                  .toList();
              for (final id in completedIds) {
                notifier.deleteTodo(id);
              }
            },
          ),
        ],
      ),
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          final todo = todos[index];
          return ListTile(
            leading: Checkbox(
              value: todo.completed,
              onChanged: (value) => notifier.toggleTodo(todo.id),
            ),
            title: Text(
              todo.title,
              style: todo.completed
                  ? TextStyle(decoration: TextDecoration.lineThrough)
                  : null,
            ),
            trailing: IconButton(
              icon: const Icon(Icons.delete),
              onPressed: () => notifier.deleteTodo(todo.id),
            ),
            onTap: () => _editTodo(context, notifier, todo),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _addTodo(context, notifier),
        child: const Icon(Icons.add),
      ),
    );
  }

  void _addTodo(BuildContext context, TodoListNotifier notifier) {
    showDialog(
      context: context,
      builder: (context) {
        final controller = TextEditingController();
        return AlertDialog(
          title: const Text('添加待办'),
          content: TextField(
            controller: controller,
            decoration: const InputDecoration(hintText: '输入待办事项'),
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('取消'),
            ),
            TextButton(
              onPressed: () {
                if (controller.text.isNotEmpty) {
                  notifier.addTodo(controller.text);
                  Navigator.pop(context);
                }
              },
              child: const Text('添加'),
            ),
          ],
        );
      },
    );
  }

  void _editTodo(BuildContext context, TodoListNotifier notifier, Todo todo) {
    showDialog(
      context: context,
      builder: (context) {
        final controller = TextEditingController(text: todo.title);
        return AlertDialog(
          title: const Text('编辑待办'),
          content: TextField(
            controller: controller,
            decoration: const InputDecoration(hintText: '输入待办事项'),
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('取消'),
            ),
            TextButton(
              onPressed: () {
                if (controller.text.isNotEmpty) {
                  notifier.updateTodo(todo.id, controller.text);
                  Navigator.pop(context);
                }
              },
              child: const Text('保存'),
            ),
          ],
        );
      },
    );
  }
}

六、总结与关键要点

通过本篇的学习,你应该已经掌握了:

  1. 状态管理方案选型:Provider、Riverpod等主流方案的特点和适用场景
  2. 数据持久化技术:SharedPreferences、Hive、SQLite的完整实现
  3. 鸿蒙分布式数据:分布式键值存储和对象存储的跨设备同步
  4. 最佳实践:状态分层架构、持久化策略、状态恢复机制

核心价值:通过状态管理和分布式数据同步,你的Flutter应用可以在鸿蒙生态中实现真正的跨设备无缝体验。用户在一台设备上操作的数据,可以实时同步到其他设备,大大提升了应用的使用体验。

下一篇预告 :我们将深入探讨网络请求、API集成与离线缓存,学习如何在Flutter应用中实现高效的网络通信和离线数据管理,让你的应用在网络不稳定的环境下依然能够流畅运行。

相关推荐
解局易否结局4 小时前
Flutter:开启跨平台开发的全新范式
flutter
明月出天山_4 小时前
【金融科技理论与实践】常见知识点汇总——北大软微期末考复习
分布式·科技·金融·区块链·智能合约
2401_860319524 小时前
【精通篇】打造React Native鸿蒙跨平台开发高级复合组件库开发系列:Circle 环形进度条(圆环形的进度条组件)
react native·react.js·harmonyos
爱吃大芒果4 小时前
Flutter 开发环境配置避坑指南:Windows/macOS/Linux 全平台
flutter·华为·harmonyos
花先锋队长4 小时前
华为Mate X7测评:折叠屏的尽头,是让你忘记它在折叠?
科技·华为·智能手机·harmonyos
赵财猫._.4 小时前
React Native鸿蒙开发实战(一):环境搭建与第一个应用
react native·react.js·华为·harmonyos
庄雨山4 小时前
Flutter 通用文本输入框封装实践:兼顾跨平台与开源鸿蒙特性
flutter·openharmonyos
小a彤4 小时前
Flutter跨平台通信机制深度解析
flutter
tangweiguo030519874 小时前
Flutter 轮播图最佳实践:carousel_slider + 精美指示器
flutter
解局易否结局4 小时前
基于 GitCode 口袋工具项目的 Flutter 基础问题解答
flutter·gitcode