You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Isar关联数据保存问题及状态管理方案咨询

Isar数据库关联保存与状态管理问题解决

一、Isar中Polygon与Vertex关联保存的正确实现

问题根源

之前的保存逻辑存在两个核心问题:

  1. 首次仅保存Polygon未处理Vertex,导致顶点数据丢失
  2. 拆分多个写事务保存时,关联对象的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 00:34:55