|
|
|
@ -2,17 +2,51 @@ import 'package:flutter/material.dart';
@@ -2,17 +2,51 @@ import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:provider/provider.dart'; |
|
|
|
|
import 'package:flutter/services.dart'; // 复制功能需要 |
|
|
|
|
import 'package:win_text_editor/app/providers/editor_provider.dart'; |
|
|
|
|
import 'package:file_picker/file_picker.dart'; |
|
|
|
|
import 'dart:io'; // 用于文件操作 |
|
|
|
|
|
|
|
|
|
class TextTab extends StatelessWidget { |
|
|
|
|
class TextTab extends StatefulWidget { |
|
|
|
|
final String tabId; |
|
|
|
|
|
|
|
|
|
const TextTab({super.key, required this.tabId}); |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
Widget build(BuildContext context) { |
|
|
|
|
final provider = Provider.of<EditorProvider>(context); |
|
|
|
|
final tab = provider.tabs.firstWhere((t) => t.id == tabId); |
|
|
|
|
State<TextTab> createState() => _TextTabState(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class _TextTabState extends State<TextTab> { |
|
|
|
|
late TextEditingController _controller; |
|
|
|
|
late EditorProvider _provider; |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
void initState() { |
|
|
|
|
super.initState(); |
|
|
|
|
_provider = Provider.of<EditorProvider>(context, listen: false); |
|
|
|
|
_controller = TextEditingController(text: _getCurrentContent()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
String _getCurrentContent() { |
|
|
|
|
return _provider.tabs.firstWhere((t) => t.id == widget.tabId).content; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
void didUpdateWidget(TextTab oldWidget) { |
|
|
|
|
super.didUpdateWidget(oldWidget); |
|
|
|
|
if (oldWidget.tabId != widget.tabId) { |
|
|
|
|
_controller.text = _getCurrentContent(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
void dispose() { |
|
|
|
|
_controller.dispose(); |
|
|
|
|
super.dispose(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
Widget build(BuildContext context) { |
|
|
|
|
final tab = _provider.tabs.firstWhere((t) => t.id == widget.tabId); // Add this line |
|
|
|
|
|
|
|
|
|
return Column( |
|
|
|
|
children: [ |
|
|
|
|
// 新增工具条 |
|
|
|
@ -26,23 +60,27 @@ class TextTab extends StatelessWidget {
@@ -26,23 +60,27 @@ class TextTab extends StatelessWidget {
|
|
|
|
|
child: Row( |
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
|
children: [ |
|
|
|
|
const Text('源文本', style: TextStyle(fontWeight: FontWeight.bold)), |
|
|
|
|
Text( |
|
|
|
|
'源文本${tab.content.isEmpty ? '' : ' (${tab.content.length}字符)'}', |
|
|
|
|
style: const TextStyle(fontWeight: FontWeight.bold), |
|
|
|
|
), |
|
|
|
|
Row( |
|
|
|
|
children: [ |
|
|
|
|
IconButton( |
|
|
|
|
icon: const Icon(Icons.folder_open, size: 20), |
|
|
|
|
tooltip: '打开文件', |
|
|
|
|
onPressed: () => _openFile(context, tabId), |
|
|
|
|
onPressed: () => _openFile(context), |
|
|
|
|
), |
|
|
|
|
IconButton( |
|
|
|
|
icon: const Icon(Icons.content_copy, size: 20), |
|
|
|
|
tooltip: '复制内容', |
|
|
|
|
onPressed: () => _copyToClipboard(context, tab.content), |
|
|
|
|
onPressed: |
|
|
|
|
tab.content.isEmpty ? null : () => _copyToClipboard(context, tab.content), |
|
|
|
|
), |
|
|
|
|
IconButton( |
|
|
|
|
icon: const Icon(Icons.save, size: 20), |
|
|
|
|
tooltip: '保存文件', |
|
|
|
|
onPressed: () => _saveFile(context, tab.content), |
|
|
|
|
tooltip: '保存到文件', |
|
|
|
|
onPressed: tab.content.isEmpty ? null : () => _saveFile(context, tab.content), |
|
|
|
|
), |
|
|
|
|
], |
|
|
|
|
), |
|
|
|
@ -52,12 +90,11 @@ class TextTab extends StatelessWidget {
@@ -52,12 +90,11 @@ class TextTab extends StatelessWidget {
|
|
|
|
|
// 文本编辑区 |
|
|
|
|
Expanded( |
|
|
|
|
child: TextField( |
|
|
|
|
controller: _controller, |
|
|
|
|
maxLines: null, |
|
|
|
|
controller: TextEditingController(text: tab.content), |
|
|
|
|
onChanged: (text) => provider.updateContent(tabId, text), |
|
|
|
|
onChanged: (text) => _provider.updateContent(widget.tabId, text), |
|
|
|
|
decoration: const InputDecoration( |
|
|
|
|
border: InputBorder.none, |
|
|
|
|
hintText: '开始输入内容...', |
|
|
|
|
contentPadding: EdgeInsets.all(16), |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
@ -66,14 +103,41 @@ class TextTab extends StatelessWidget {
@@ -66,14 +103,41 @@ class TextTab extends StatelessWidget {
|
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 打开文件模拟功能 |
|
|
|
|
Future<void> _openFile(BuildContext context, String tabId) async { |
|
|
|
|
final provider = Provider.of<EditorProvider>(context, listen: false); |
|
|
|
|
// 这里是模拟代码,实际应该使用文件选择器 |
|
|
|
|
const mockContent = "从文件加载的示例文本..."; |
|
|
|
|
provider.updateContent(tabId, mockContent); |
|
|
|
|
// _openFile方法现在需要更新控制器 |
|
|
|
|
Future<void> _openFile(BuildContext context) async { |
|
|
|
|
try { |
|
|
|
|
final result = await FilePicker.platform.pickFiles(type: FileType.any, allowMultiple: false); |
|
|
|
|
|
|
|
|
|
if (result != null && result.files.single.path != null) { |
|
|
|
|
final file = File(result.files.single.path!); |
|
|
|
|
|
|
|
|
|
// 尝试读取文件内容 |
|
|
|
|
final content = await file.readAsString(); |
|
|
|
|
|
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('已加载文件内容'))); |
|
|
|
|
// 同时更新Provider和控制器 |
|
|
|
|
_provider.updateContent(widget.tabId, content); |
|
|
|
|
setState(() { |
|
|
|
|
_controller.text = content; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
// 强制刷新文本控制器 |
|
|
|
|
if (context.mounted) { |
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('已加载: ${file.path}'))); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} on FormatException { |
|
|
|
|
// 二进制文件错误 |
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('这不是可读的文本文件'))); |
|
|
|
|
} on FileSystemException catch (e) { |
|
|
|
|
// 文件系统错误 |
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('文件访问错误: ${e.message}'))); |
|
|
|
|
} catch (e) { |
|
|
|
|
if (context.mounted) { |
|
|
|
|
ScaffoldMessenger.of( |
|
|
|
|
context, |
|
|
|
|
).showSnackBar(SnackBar(content: Text('读取失败: ${e.toString()}'))); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 复制到剪贴板 |
|
|
|
|