Flutter Isar关联数据保存问题及状态管理方案咨询
Isar数据库关联保存与状态管理问题解决
一、Isar中Polygon与Vertex关联保存的正确实现
问题根源
之前的保存逻辑存在两个核心问题:
- 首次仅保存Polygon未处理Vertex,导致顶点数据丢失
- 拆分多个写事务保存时,关联对象的ID尚未生成,导致双向关联(
IsarLink/IsarLinks)无法正确建立
正确实现代码
修改addVertex方法中的闭合多边形保存逻辑,必须在同一个写事务内完成Polygon和Vertex的关联与保存:
void addVertex(details) async { Offset pos = details.localPosition; if (_currentVertices == null) { _currentVertices = [Vertex()..point = pos]; } else { if (_currentVertices!.length > 2 && (_currentVertices!.first.point - pos).distance <= 20) { // 创建Polygon并设置颜色(注意late变量必须初始化) Polygon p = Polygon()..color = Colors.red; // 将顶点添加到Polygon的vertices链接中,Isar会自动设置每个Vertex的polygon反向关联 p.vertices.addAll(_currentVertices!); // 同一个写事务内完成所有保存操作 await _isar.writeTxn(() async { // 保存Polygon时,Isar会自动保存关联的Vertex并建立双向链接 await _isar.polygons.put(p); }); _currentVertices = null; } else { _currentVertices!.add(Vertex()..point = pos); } } }
关键说明
- Isar的双向关联依赖对象ID,必须在同一个写事务内完成关联与保存,确保ID生成后能同步到关联字段
- 使用
p.vertices.addAll(_currentVertices!)可以自动完成Vertex的polygon反向关联,无需手动循环设置v.polygon.value = p
二、状态管理方案选择(Provider/Riverpod)
方案对比与推荐
优先选择方案一,理由如下:
- Polygon与Vertex是强聚合关系,Vertex是Polygon的组成部分,将两者的添加逻辑放在同一个方法中,符合业务逻辑的一致性
- 无需单独维护Vertex的状态管理类,减少复杂度——视图仅需监听Polygon的变化,Vertex作为附属数据随Polygon一起更新
Provider实现示例
1. 定义PolygonModel状态管理类
import 'package:flutter/material.dart'; import 'package:isar/isar.dart'; class PolygonModel extends ChangeNotifier { final Isar _isar; List<Polygon> _polygons = []; PolygonModel(this._isar) { _loadPolygons(); } List<Polygon> get polygons => _polygons; // 从数据库加载所有多边形,预加载关联的顶点 Future<void> _loadPolygons() async { _polygons = await _isar.polygons.where().includeVertices().findAll(); notifyListeners(); } // 添加多边形及关联顶点的方法 Future<void> addPolygon(Polygon polygon, List<Vertex> vertices) async { await _isar.writeTxn(() async { polygon.vertices.addAll(vertices); await _isar.polygons.put(polygon); }); // 重新加载数据并通知视图更新 await _loadPolygons(); } }
2. 注册与使用Provider
// 在main.dart中注册Provider void main() async { final isar = await Isar.open([PolygonSchema, VertexSchema]); runApp( ChangeNotifierProvider( create: (context) => PolygonModel(isar), child: const MyApp(), ), ); } // 在页面中调用添加方法 final polygonModel = Provider.of<PolygonModel>(context, listen: false); polygonModel.addPolygon(p, _currentVertices!);
Riverpod实现示例(更推荐)
Riverpod相比Provider更灵活,无上下文依赖:
1. 定义Provider与StateNotifier
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; // 提供Isar实例的Provider final isarProvider = Provider<Isar>((ref) { // 这里返回初始化好的Isar实例,需提前完成Isar的open操作 throw UnimplementedError("请初始化Isar实例"); }); // 管理Polygon状态的StateNotifier class PolygonNotifier extends StateNotifier<List<Polygon>> { final Isar _isar; PolygonNotifier(this._isar) : super([]) { _loadPolygons(); } Future<void> _loadPolygons() async { final polygons = await _isar.polygons.where().includeVertices().findAll(); state = polygons; } Future<void> addPolygon(Polygon polygon, List<Vertex> vertices) async { await _isar.writeTxn(() async { polygon.vertices.addAll(vertices); await _isar.polygons.put(polygon); }); await _loadPolygons(); } } // 注册Polygon状态的Provider final polygonProvider = StateNotifierProvider<PolygonNotifier, List<Polygon>>((ref) { return PolygonNotifier(ref.watch(isarProvider)); });
2. 在UI中使用Riverpod
// 在ConsumerWidget中调用 class PolygonPage extends ConsumerWidget { const PolygonPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final polygons = ref.watch(polygonProvider); // 在addVertex方法中调用添加逻辑 void handleAddVertex(TapUpDetails details) async { // ... 省略顶点收集逻辑 ... if (/* 多边形闭合条件满足 */) { Polygon p = Polygon()..color = Colors.blue; ref.read(polygonProvider.notifier).addPolygon(p, _currentVertices!); _currentVertices = null; } } return GestureDetector( onTapUp: handleAddVertex, child: /* 绘制区域 */, ); } }
内容的提问来源于stack exchange,提问作者Gordon Hollingworth
相关产品推荐
相关产品推荐

