263 lines
6.5 KiB
Dart
263 lines
6.5 KiB
Dart
import 'package:flutter/material.dart';
|
||
|
||
/// 原理图编辑器页面
|
||
/// 核心功能:支持 1000+ 元件流畅编辑、手势操作
|
||
class SchematicEditorScreen extends StatefulWidget {
|
||
final String? projectId;
|
||
|
||
const SchematicEditorScreen({super.key, this.projectId});
|
||
|
||
@override
|
||
State<SchematicEditorScreen> createState() => _SchematicEditorScreenState();
|
||
}
|
||
|
||
class _SchematicEditorScreenState extends State<SchematicEditorScreen> {
|
||
// 当前缩放级别
|
||
double _zoomLevel = 1.0;
|
||
|
||
// 画布偏移
|
||
Offset _offset = Offset.zero;
|
||
|
||
// 是否正在拖拽
|
||
bool _isPanning = false;
|
||
|
||
// 上次触摸位置
|
||
Offset? _lastPanPosition;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
appBar: AppBar(
|
||
title: const Text('原理图编辑器'),
|
||
actions: [
|
||
IconButton(
|
||
icon: const Icon(Icons.save),
|
||
onPressed: _saveProject,
|
||
tooltip: '保存',
|
||
),
|
||
IconButton(
|
||
icon: const Icon(Icons.zoom_in),
|
||
onPressed: () => _zoom(0.1),
|
||
tooltip: '放大',
|
||
),
|
||
IconButton(
|
||
icon: const Icon(Icons.zoom_out),
|
||
onPressed: () => _zoom(-0.1),
|
||
tooltip: '缩小',
|
||
),
|
||
IconButton(
|
||
icon: const Icon(Icons.fit_screen),
|
||
onPressed: _fitToScreen,
|
||
tooltip: '适应屏幕',
|
||
),
|
||
],
|
||
),
|
||
body: GestureDetector(
|
||
// 双指缩放
|
||
onScaleStart: _handleScaleStart,
|
||
onScaleUpdate: _handleScaleUpdate,
|
||
onScaleEnd: _handleScaleEnd,
|
||
|
||
// 单指拖拽
|
||
onPanStart: _handlePanStart,
|
||
onPanUpdate: _handlePanUpdate,
|
||
onPanEnd: _handlePanEnd,
|
||
|
||
// 长按菜单
|
||
onLongPress: _showContextMenu,
|
||
|
||
child: Container(
|
||
color: const Color(0xFFFAFAFA),
|
||
child: CustomPaint(
|
||
size: Size.infinite,
|
||
painter: SchematicCanvasPainter(
|
||
zoomLevel: _zoomLevel,
|
||
offset: _offset,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
floatingActionButton: FloatingActionButton(
|
||
onPressed: _addComponent,
|
||
child: const Icon(Icons.add),
|
||
tooltip: '添加元件',
|
||
),
|
||
);
|
||
}
|
||
|
||
// 缩放处理
|
||
void _handleScaleStart(ScaleStartDetails details) {
|
||
_lastPanPosition = details.focalPoint;
|
||
}
|
||
|
||
void _handleScaleUpdate(ScaleUpdateDetails details) {
|
||
setState(() {
|
||
// 更新缩放级别(限制范围)
|
||
_zoomLevel = (_zoomLevel * details.scale).clamp(0.1, 10.0);
|
||
|
||
// 更新偏移
|
||
if (_lastPanPosition != null) {
|
||
_offset += details.focalPoint - _lastPanPosition!;
|
||
}
|
||
_lastPanPosition = details.focalPoint;
|
||
});
|
||
}
|
||
|
||
void _handleScaleEnd(ScaleEndDetails details) {
|
||
_lastPanPosition = null;
|
||
}
|
||
|
||
// 拖拽处理
|
||
void _handlePanStart(DragStartDetails details) {
|
||
_isPanning = true;
|
||
_lastPanPosition = details.globalPosition;
|
||
}
|
||
|
||
void _handlePanUpdate(DragUpdateDetails details) {
|
||
if (_isPanning && _lastPanPosition != null) {
|
||
setState(() {
|
||
_offset += details.delta;
|
||
});
|
||
}
|
||
}
|
||
|
||
void _handlePanEnd(DragEndDetails details) {
|
||
_isPanning = false;
|
||
_lastPanPosition = null;
|
||
}
|
||
|
||
// 手动缩放
|
||
void _zoom(double delta) {
|
||
setState(() {
|
||
_zoomLevel = (_zoomLevel + delta).clamp(0.1, 10.0);
|
||
});
|
||
}
|
||
|
||
// 适应屏幕
|
||
void _fitToScreen() {
|
||
setState(() {
|
||
_zoomLevel = 1.0;
|
||
_offset = Offset.zero;
|
||
});
|
||
}
|
||
|
||
// 显示上下文菜单
|
||
void _showContextMenu() {
|
||
showModalBottomSheet(
|
||
context: context,
|
||
builder: (context) => SafeArea(
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
ListTile(
|
||
leading: const Icon(Icons.add),
|
||
title: const Text('添加元件'),
|
||
onTap: () {
|
||
Navigator.pop(context);
|
||
_addComponent();
|
||
},
|
||
),
|
||
ListTile(
|
||
leading: const Icon(Icons.content_cut),
|
||
title: const Text('剪切'),
|
||
onTap: () => Navigator.pop(context),
|
||
),
|
||
ListTile(
|
||
leading: const Icon(Icons.content_copy),
|
||
title: const Text('复制'),
|
||
onTap: () => Navigator.pop(context),
|
||
),
|
||
ListTile(
|
||
leading: const Icon(Icons.delete),
|
||
title: const Text('删除'),
|
||
onTap: () => Navigator.pop(context),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
// 添加元件
|
||
void _addComponent() {
|
||
// TODO: 实现元件添加逻辑
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
const SnackBar(content: Text('添加元件功能开发中...')),
|
||
);
|
||
}
|
||
|
||
// 保存项目
|
||
void _saveProject() {
|
||
// TODO: 实现保存逻辑
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
const SnackBar(content: Text('保存功能开发中...')),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 原理图画布绘制器
|
||
class SchematicCanvasPainter extends CustomPainter {
|
||
final double zoomLevel;
|
||
final Offset offset;
|
||
|
||
SchematicCanvasPainter({
|
||
required this.zoomLevel,
|
||
required this.offset,
|
||
});
|
||
|
||
@override
|
||
void paint(Canvas canvas, Size size) {
|
||
// 保存画布状态
|
||
canvas.save();
|
||
|
||
// 应用变换
|
||
canvas.translate(offset.dx, offset.dy);
|
||
canvas.scale(zoomLevel);
|
||
|
||
// 绘制网格
|
||
_drawGrid(canvas, size);
|
||
|
||
// TODO: 绘制元件(1000+ 元件场景)
|
||
// _drawComponents(canvas);
|
||
|
||
// 恢复画布状态
|
||
canvas.restore();
|
||
}
|
||
|
||
void _drawGrid(Canvas canvas, Size size) {
|
||
final paint = Paint()
|
||
..color = const Color(0xFFE0E0E0)
|
||
..strokeWidth = 0.5;
|
||
|
||
const gridSize = 50.0;
|
||
|
||
// 计算可见区域
|
||
final startX = (-offset.dx / zoomLevel).clamp(-1000.0, size.width);
|
||
final startY = (-offset.dy / zoomLevel).clamp(-1000.0, size.height);
|
||
|
||
// 绘制垂直线
|
||
for (double x = startX; x < size.width; x += gridSize) {
|
||
canvas.drawLine(
|
||
Offset(x, 0),
|
||
Offset(x, size.height),
|
||
paint,
|
||
);
|
||
}
|
||
|
||
// 绘制水平线
|
||
for (double y = startY; y < size.height; y += gridSize) {
|
||
canvas.drawLine(
|
||
Offset(0, y),
|
||
Offset(size.width, y),
|
||
paint,
|
||
);
|
||
}
|
||
}
|
||
|
||
@override
|
||
bool shouldRepaint(SchematicCanvasPainter oldDelegate) {
|
||
return oldDelegate.zoomLevel != zoomLevel ||
|
||
oldDelegate.offset != offset;
|
||
}
|
||
}
|