Files
xianyan/lib/features/collaboration/canvas/providers/canvas_provider.dart
Developer 7564e8893d chore: 完成多平台适配与代码优化
此提交包含多项变更:
1. 新增鸿蒙平台支持,完善设备检测与数据库适配
2. 替换旧版分享插件API为SharePlus
3. 批量迁移StateNotifier到Notifier以适配新版Riverpod
4. 修复zip编码判断、图表API参数等bug
5. 更新应用图标、启动页资源与多尺寸适配图标
6. 调整Android最小SDK版本与应用名称
7. 优化日志打印与正则表达式使用
8. 修正编辑器画布样式初始化与配置逻辑
9. 更新依赖与CI插件配置
2026-05-17 07:17:07 +08:00

207 lines
5.7 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// 闲言APP — 协作画布Riverpod状态管理
// 创建时间: 2026-05-14
// 更新时间: 2026-05-15
// 作用: CanvasState + CanvasNotifier封装Engine和SyncService
// 上次更新: 修复participants回调未绑定、joinCanvas传递peerDeviceId
// ============================================================
import 'dart:ui';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:xianyan/core/utils/logger.dart';
import 'package:xianyan/features/file_transfer/providers/shared_signaling_provider.dart';
import 'package:xianyan/features/file_transfer/services/signaling_service.dart';
import '../models/stroke.dart';
import '../services/canvas_engine.dart';
import '../services/canvas_sync_service.dart';
class CanvasState {
const CanvasState({
this.strokes = const [],
this.activeStroke,
this.currentTool = StrokeType.pen,
this.currentColor = '#000000',
this.currentWidth = 3.0,
this.remoteCursors = const {},
this.participants = const [],
this.isConnected = false,
this.canvasId,
});
final List<Stroke> strokes;
final Stroke? activeStroke;
final StrokeType currentTool;
final String currentColor;
final double currentWidth;
final Map<String, Offset> remoteCursors;
final List<String> participants;
final bool isConnected;
final String? canvasId;
CanvasState copyWith({
List<Stroke>? strokes,
Stroke? activeStroke,
StrokeType? currentTool,
String? currentColor,
double? currentWidth,
Map<String, Offset>? remoteCursors,
List<String>? participants,
bool? isConnected,
String? canvasId,
bool clearActiveStroke = false,
}) {
return CanvasState(
strokes: strokes ?? this.strokes,
activeStroke: clearActiveStroke
? null
: (activeStroke ?? this.activeStroke),
currentTool: currentTool ?? this.currentTool,
currentColor: currentColor ?? this.currentColor,
currentWidth: currentWidth ?? this.currentWidth,
remoteCursors: remoteCursors ?? this.remoteCursors,
participants: participants ?? this.participants,
isConnected: isConnected ?? this.isConnected,
canvasId: canvasId ?? this.canvasId,
);
}
}
class CanvasNotifier extends Notifier<CanvasState> {
@override
CanvasState build() {
ref.onDispose(_onDispose);
return const CanvasState();
}
late final SignalingService _signaling = ref.read(sharedSignalingProvider);
late final CanvasEngine _engine;
late final CanvasSyncService _syncService;
CanvasNotifier() {
_engine = CanvasEngine();
_syncService = CanvasSyncService(signaling: _signaling);
_engine.addListener(_onEngineChanged);
_syncService.onRemoteStroke = _handleRemoteStroke;
_syncService.onRemoteSnapshot = _handleRemoteSnapshot;
_syncService.onRemoteCursor = _handleRemoteCursor;
_syncService.onParticipantsChanged = _handleParticipantsChanged;
_syncService.onSnapshotRequest = _handleSnapshotRequest;
}
CanvasEngine get engine => _engine;
void setUserId(String userId) {
_engine.userId = userId;
}
void setTool(StrokeType tool) {
_engine.setTool(tool);
}
void setColor(String color) {
_engine.setColor(color);
}
void setWidth(double width) {
_engine.setWidth(width);
}
void startStroke(Offset point) {
_engine.startStroke(point);
}
void addPoint(Offset point) {
_engine.addPoint(point);
}
void endStroke() {
final stroke = _engine.endStroke();
if (stroke != null) {
_syncService.broadcastStroke(stroke);
}
}
void undo() {
_engine.undo();
}
void redo() {
_engine.redo();
}
void clearCanvas() {
_engine.clearCanvas();
}
void joinCanvas(String canvasId, String deviceId, {String? peerDeviceId}) {
_syncService.joinCanvas(canvasId, deviceId, peerId: peerDeviceId);
state = state.copyWith(canvasId: canvasId, isConnected: true);
Future.delayed(const Duration(milliseconds: 500), () {
_syncService.requestSnapshot();
});
}
void leaveCanvas() {
_syncService.leaveCanvas();
state = state.copyWith(isConnected: false);
}
void broadcastCursor(Offset position) {
_syncService.broadcastCursor(position);
}
void _onEngineChanged() {
state = state.copyWith(
strokes: _engine.strokes,
activeStroke: _engine.activeStroke,
currentTool: _engine.currentTool,
currentColor: _engine.currentColor,
currentWidth: _engine.currentWidth,
clearActiveStroke: _engine.activeStroke == null,
);
}
void _handleRemoteStroke(Stroke stroke) {
Log.d('CanvasProvider: received remote stroke from ${stroke.userId}');
_engine.mergeRemoteStroke(stroke);
}
void _handleRemoteSnapshot(List<Stroke> strokes) {
Log.i('CanvasProvider: received snapshot with ${strokes.length} strokes');
_engine.mergeSnapshot(strokes);
}
void _handleRemoteCursor(String userId, Offset position) {
final updated = Map<String, Offset>.from(state.remoteCursors);
updated[userId] = position;
state = state.copyWith(remoteCursors: updated);
}
void _handleParticipantsChanged(List<String> participants) {
state = state.copyWith(participants: participants);
}
void _handleSnapshotRequest(String requestingDeviceId, String canvasId) {
if (state.canvasId != canvasId) return;
final strokes = _engine.strokes;
Log.i(
'CanvasProvider: sending snapshot response to $requestingDeviceId with ${strokes.length} strokes',
);
_syncService.sendSnapshotResponse(requestingDeviceId, strokes);
}
void _onDispose() {
_engine.removeListener(_onEngineChanged);
_engine.dispose();
_syncService.dispose();
}
}
final canvasProvider = NotifierProvider<CanvasNotifier, CanvasState>(
CanvasNotifier.new,
);