mobile-eda/lib/presentation/screens/schematic_editor_screen.dart

263 lines
6.5 KiB
Dart
Raw Permalink 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.

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;
}
}