Browse Source

目录重构,引入模块

master
hejl 2 months ago
parent
commit
55aeafec7a
  1. 104
      win_text_editor/lib/app/app.dart
  2. 41
      win_text_editor/lib/app/components/file_explorer.dart
  3. 0
      win_text_editor/lib/app/components/text_editor.dart
  4. 46
      win_text_editor/lib/app/core/app_scaffold.dart
  5. 0
      win_text_editor/lib/app/core/console_panel.dart
  6. 67
      win_text_editor/lib/app/core/file_explorer_pane.dart
  7. 27
      win_text_editor/lib/app/core/tab_manager.dart
  8. 105
      win_text_editor/lib/app/core/tab_view.dart
  9. 5
      win_text_editor/lib/app/menus/app_menu.dart
  10. 37
      win_text_editor/lib/app/menus/menu_actions.dart
  11. 1
      win_text_editor/lib/app/menus/menu_constants.dart
  12. 4
      win_text_editor/lib/app/models/tab_model.dart
  13. 33
      win_text_editor/lib/app/modules/template_parser/template_parser_controller.dart
  14. 7
      win_text_editor/lib/app/modules/template_parser/template_parser_service.dart
  15. 14
      win_text_editor/lib/app/modules/template_parser/template_parser_view.dart
  16. 84
      win_text_editor/lib/app/widgets/editor_pane.dart
  17. 6
      win_text_editor/lib/main.dart

104
win_text_editor/lib/app/app.dart

@ -1,104 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:win_text_editor/app/menus/app_menu.dart';
import 'package:win_text_editor/app/providers/tab_provider.dart';
import 'package:win_text_editor/app/providers/file_provider.dart';
import 'package:win_text_editor/app/widgets/editor_pane.dart';
import 'package:win_text_editor/app/widgets/file_explorer.dart';
import 'package:win_text_editor/app/widgets/console_panel.dart'; //
class AppScaffold extends StatelessWidget {
const AppScaffold({super.key});
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => FileProvider()),
ChangeNotifierProvider(create: (_) => TabProvider()),
],
child: const Scaffold(
body: Column(
children: [
//
AppMenu(),
//
Expanded(child: _ResizablePanel()),
],
),
),
);
}
}
class _ResizablePanel extends StatefulWidget {
const _ResizablePanel();
@override
State<_ResizablePanel> createState() => _ResizablePanelState();
}
class _ResizablePanelState extends State<_ResizablePanel> {
double _leftWidth = 0.2; // 20%
final double _minWidth = 100; //
final double _maxWidth = 400; //
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return LayoutBuilder(
builder: (context, constraints) {
//
final leftPanelWidth = (_leftWidth * screenWidth).clamp(_minWidth, _maxWidth);
return Column(
children: [
Expanded(
child: Row(
children: [
// - Material小部件包裹
Material(
elevation: 1, //
child: SizedBox(
width: leftPanelWidth,
child: const ClipRect(
//
child: FileExplorer(),
),
),
),
//
GestureDetector(
behavior: HitTestBehavior.translucent,
onPanUpdate: (details) {
setState(() {
_leftWidth = ((leftPanelWidth + details.delta.dx) / screenWidth).clamp(
_minWidth / screenWidth,
_maxWidth / screenWidth,
);
});
},
child: MouseRegion(
cursor: SystemMouseCursors.resizeLeftRight,
child: Container(width: 4, color: Colors.grey[300]),
),
),
// - Material背景
const Expanded(
child: Material(
color: Colors.white,
child: Column(children: [Expanded(child: EditorPane())]),
),
),
],
),
),
//
const ConsolePanel(),
],
);
},
);
}
}

41
win_text_editor/lib/app/widgets/file_explorer.dart → win_text_editor/lib/app/components/file_explorer.dart

@ -4,11 +4,9 @@ import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:win_text_editor/app/providers/logger.dart'; import 'package:win_text_editor/app/providers/logger.dart';
import 'package:win_text_editor/app/providers/tab_provider.dart';
import '../models/file_node.dart'; import '../models/file_node.dart';
import '../providers/file_provider.dart'; import '../providers/file_provider.dart';
import '../providers/editor_provider.dart';
import 'dart:math'; import 'dart:math';
@ -60,24 +58,6 @@ class _FileExplorerState extends State<FileExplorer> {
await fileProvider.loadDirectoryContents(node); await fileProvider.loadDirectoryContents(node);
} else { } else {
// Handle file opening // Handle file opening
print("No active tab found");
_openFileInEditor(context, node);
}
}
Future<void> _openFileInEditor(BuildContext context, FileNode node) async {
if (!node.isDirectory) {
try {
final editorProvider = Provider.of<TabProvider>(context, listen: false);
await editorProvider.requestLoadFile(context, node.path);
} catch (e) {
Logger().error("打开文件失败: ${e.toString()}");
if (context.mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('打开文件失败: ${e.toString()}')));
}
}
} }
} }
@ -97,17 +77,6 @@ class _FileExplorerState extends State<FileExplorer> {
return maxDepth * 200 + MediaQuery.of(context).size.width * 0.5; return maxDepth * 200 + MediaQuery.of(context).size.width * 0.5;
} }
void _handleNodeDoubleTap(BuildContext context, FileNode node) async {
if (!node.isDirectory) {
//
Logger().info('双击打开文件: ${node.path}', source: 'FileExplorer');
await _openFileInEditor(context, node);
} else {
//
Logger().debug('双击文件夹: ${node.path}', source: 'FileExplorer');
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final fileProvider = Provider.of<FileProvider>(context); final fileProvider = Provider.of<FileProvider>(context);
@ -149,7 +118,6 @@ class _FileExplorerState extends State<FileExplorer> {
key: ValueKey(node.path), key: ValueKey(node.path),
node: node, node: node,
onTap: () => _handleNodeTap(context, node), onTap: () => _handleNodeTap(context, node),
onDoubleTap: () => _handleNodeDoubleTap(context, node),
); );
}, },
), ),
@ -197,14 +165,8 @@ FileNode _getVisibleNode(List<FileNode> nodes, int index) {
class _FileNodeWidget extends StatelessWidget { class _FileNodeWidget extends StatelessWidget {
final FileNode node; final FileNode node;
final VoidCallback onTap; final VoidCallback onTap;
final VoidCallback onDoubleTap; //
const _FileNodeWidget({ const _FileNodeWidget({Key? key, required this.node, required this.onTap}) : super(key: key);
Key? key,
required this.node,
required this.onTap,
required this.onDoubleTap, //
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -212,7 +174,6 @@ class _FileNodeWidget extends StatelessWidget {
return InkWell( return InkWell(
onTap: onTap, onTap: onTap,
onDoubleTap: onDoubleTap,
splashColor: Colors.transparent, // splashColor: Colors.transparent, //
highlightColor: Colors.grey.withOpacity(0.1), // 使 highlightColor: Colors.grey.withOpacity(0.1), // 使
child: Container( child: Container(

0
win_text_editor/lib/app/widgets/text_editor.dart → win_text_editor/lib/app/components/text_editor.dart

46
win_text_editor/lib/app/core/app_scaffold.dart

@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:win_text_editor/app/core/file_explorer_pane.dart';
import 'package:win_text_editor/app/core/tab_manager.dart';
import 'package:win_text_editor/app/core/tab_view.dart';
import 'package:win_text_editor/app/menus/app_menu.dart';
import 'package:win_text_editor/app/providers/file_provider.dart';
import 'package:win_text_editor/app/core/console_panel.dart';
class AppScaffold extends StatelessWidget {
const AppScaffold({super.key});
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => FileProvider()),
ChangeNotifierProvider(create: (_) => TabManager()), // TabManager
],
child: Scaffold(
body: Column(
children: [
const AppMenu(), //
Expanded(
child: Row(
children: [
//
const FileExplorerPane(),
//
Expanded(
child: Consumer<TabManager>(
builder:
(_, manager, __) =>
TabView(tabs: manager.tabs, currentTabId: manager.activeTabId),
),
),
],
),
),
const ConsolePanel(), //
],
),
),
);
}
}

0
win_text_editor/lib/app/widgets/console_panel.dart → win_text_editor/lib/app/core/console_panel.dart

67
win_text_editor/lib/app/core/file_explorer_pane.dart

@ -0,0 +1,67 @@
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:win_text_editor/app/providers/file_provider.dart';
import 'package:win_text_editor/app/widgets/file_explorer.dart';
class FileExplorerPane extends StatelessWidget {
const FileExplorerPane({super.key});
@override
Widget build(BuildContext context) {
return Consumer<FileProvider>(
builder: (context, fileProvider, child) {
return Material(
elevation: 1,
child: SizedBox(
width: _calculateWidth(context, fileProvider),
child: Column(
children: [
//
_buildHeader(context),
//
const Expanded(child: FileExplorer()),
],
),
),
);
},
);
}
Widget _buildHeader(BuildContext context) {
return Container(
height: 40,
padding: const EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
color: Colors.grey[100],
border: const Border(bottom: BorderSide(color: Colors.grey)),
),
child: Row(
children: [
const Text('文件资源管理器', style: TextStyle(fontWeight: FontWeight.bold)),
const Spacer(),
IconButton(
icon: const Icon(Icons.folder_open, size: 20),
tooltip: '打开文件夹',
onPressed: () => _openDirectory(context),
),
],
),
);
}
double _calculateWidth(BuildContext context, FileProvider fileProvider) {
final screenWidth = MediaQuery.of(context).size.width;
final defaultWidth = screenWidth * 0.2; // 20%
return defaultWidth.clamp(200, 400); // 200400
}
Future<void> _openDirectory(BuildContext context) async {
final fileProvider = Provider.of<FileProvider>(context, listen: false);
final String? selectedDirectory = await FilePicker.platform.getDirectoryPath();
if (selectedDirectory != null) {
await fileProvider.setRootPath(selectedDirectory);
}
}
}

27
win_text_editor/lib/app/providers/tab_provider.dart → win_text_editor/lib/app/core/tab_manager.dart

@ -1,18 +1,18 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:win_text_editor/app/models/tab_model.dart'; import 'package:win_text_editor/app/models/tab_model.dart';
import 'package:win_text_editor/app/modules/template_parser/template_parser_view.dart';
import 'package:win_text_editor/app/providers/logger.dart'; import 'package:win_text_editor/app/providers/logger.dart';
import 'package:win_text_editor/app/widgets/template_parser_tab.dart';
class TabProvider with ChangeNotifier { class TabManager with ChangeNotifier {
final List<ContentTab> _tabs = []; final List<AppTab> _tabs = [];
String? _activeTabId; String? _activeTabId;
List<ContentTab> get tabs => _tabs; List<AppTab> get tabs => _tabs;
String? get activeTabId => _activeTabId; String? get activeTabId => _activeTabId;
final Map<String, TemplateParserTabState> _tabControllers = {}; final Map<String, TemplateParserViewState> _tabControllers = {};
void registerTextTabController(String tabId, TemplateParserTabState controller) { void registerTextTabController(String tabId, TemplateParserViewState controller) {
_tabControllers[tabId] = controller; _tabControllers[tabId] = controller;
} }
@ -20,7 +20,7 @@ class TabProvider with ChangeNotifier {
_tabControllers.remove(tabId); _tabControllers.remove(tabId);
} }
ContentTab? get activeTab { AppTab? get activeTab {
if (_activeTabId == null) return null; if (_activeTabId == null) return null;
try { try {
return _tabs.firstWhere((tab) => tab.id == _activeTabId); return _tabs.firstWhere((tab) => tab.id == _activeTabId);
@ -30,26 +30,21 @@ class TabProvider with ChangeNotifier {
} }
} }
Future<void> addTab({ Future<void> addTab(
String id, {
String title = '未命名', String title = '未命名',
String? type, String? type,
IconData? icon, IconData? icon,
String content = '', String content = '',
}) async { }) async {
final newTab = ContentTab( final newTab = AppTab(id: id, title: title, type: type, icon: icon, content: content);
id: DateTime.now().millisecondsSinceEpoch.toString(),
title: title,
type: type,
icon: icon,
content: content,
);
_tabs.add(newTab); _tabs.add(newTab);
_activeTabId = newTab.id; _activeTabId = newTab.id;
notifyListeners(); notifyListeners();
} }
ContentTab? getTabById(String tabId) { AppTab? getTabById(String tabId) {
try { try {
return _tabs.firstWhere((tab) => tab.id == tabId); return _tabs.firstWhere((tab) => tab.id == tabId);
} catch (e) { } catch (e) {

105
win_text_editor/lib/app/core/tab_view.dart

@ -0,0 +1,105 @@
import 'package:flutter/material.dart';
import 'package:win_text_editor/app/components/text_editor.dart';
import 'package:win_text_editor/app/core/tab_manager.dart';
import 'package:win_text_editor/app/models/tab_model.dart';
import 'package:win_text_editor/app/modules/template_parser/template_parser_view.dart';
import 'package:provider/provider.dart';
class TabView extends StatelessWidget {
final List<AppTab> tabs;
final String? currentTabId;
const TabView({super.key, required this.tabs, required this.currentTabId});
@override
Widget build(BuildContext context) {
final activeTab = tabs.firstWhere(
(tab) => tab.id == currentTabId,
orElse: () => AppTab(id: '', title: ''), //
);
return Column(
children: [
//
_buildTabBar(context),
//
Expanded(
child:
activeTab.id.isNotEmpty
? _buildTabContent(activeTab)
: const Center(child: Text('无活动标签页')),
),
],
);
}
Widget _buildTabBar(BuildContext context) {
return SizedBox(
height: 40,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: tabs.length,
itemBuilder: (ctx, index) {
final tab = tabs[index];
return _TabItem(
tab: tab,
isActive: tab.id == currentTabId,
onClose: () => context.read<TabManager>().closeTab(tab.id),
onTap: () => context.read<TabManager>().setActiveTab(tab.id),
);
},
),
);
}
Widget _buildTabContent(AppTab tab) {
switch (tab.type) {
case 'template_parser':
return TemplateParserView(tabId: tab.id); // 使ID而不是新生成
case 'content_search':
// return const ContentSearchView();
return Container(); //
default:
return TextEditor(tabId: tab.id, initialContent: tab.content);
}
}
}
class _TabItem extends StatelessWidget {
final AppTab tab;
final bool isActive;
final VoidCallback onClose;
final VoidCallback onTap;
const _TabItem({
required this.tab,
required this.isActive,
required this.onClose,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: isActive ? Colors.blue[100] : Colors.grey[200],
border: Border(
bottom: BorderSide(color: isActive ? Colors.blue : Colors.transparent, width: 2),
),
),
child: Row(
children: [
if (tab.icon != null) Icon(tab.icon, size: 16),
if (tab.icon != null) const SizedBox(width: 4),
Text(tab.title),
const SizedBox(width: 8),
IconButton(icon: const Icon(Icons.close, size: 16), onPressed: onClose),
],
),
),
);
}
}

5
win_text_editor/lib/app/menus/app_menu.dart

@ -24,7 +24,10 @@ class AppMenu extends StatelessWidget {
} }
List<PopupMenuEntry<String>> _buildToolsMenuItems() { List<PopupMenuEntry<String>> _buildToolsMenuItems() {
return [const PopupMenuItem<String>(value: MenuConstants.templateParser, child: Text('模板解析'))]; return [
const PopupMenuItem<String>(value: MenuConstants.contentSearch, child: Text('内容搜索')),
const PopupMenuItem<String>(value: MenuConstants.templateParser, child: Text('模板解析')),
];
} }
Widget _buildMenuButton(BuildContext context, String label, List<PopupMenuEntry<String>> items) { Widget _buildMenuButton(BuildContext context, String label, List<PopupMenuEntry<String>> items) {

37
win_text_editor/lib/app/menus/menu_actions.dart

@ -1,9 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:win_text_editor/app/core/tab_manager.dart';
import 'package:win_text_editor/app/menus/menu_constants.dart'; import 'package:win_text_editor/app/menus/menu_constants.dart';
import 'package:win_text_editor/app/providers/file_provider.dart'; import 'package:win_text_editor/app/providers/file_provider.dart';
import 'package:win_text_editor/app/providers/tab_provider.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'dart:io'; import 'dart:io';
@ -14,11 +14,20 @@ class MenuActions {
static const IconData templateParserTabIcon = Icons.auto_awesome_mosaic; static const IconData templateParserTabIcon = Icons.auto_awesome_mosaic;
static const String templateParserDefaultContent = ""; static const String templateParserDefaultContent = "";
//
static const String contentSearchTabType = "content_search";
static const String contentSearchTabTitle = "内容搜索";
static const IconData contentSearchTabIcon = Icons.search;
static const String contentSearchDefaultContent = "";
static Future<void> handleMenuAction(String value, BuildContext context) async { static Future<void> handleMenuAction(String value, BuildContext context) async {
switch (value) { switch (value) {
case MenuConstants.openFolder: case MenuConstants.openFolder:
await _openFolder(context); await _openFolder(context);
break; break;
case MenuConstants.contentSearch: //
await _openContentSearch(context);
break;
case MenuConstants.templateParser: case MenuConstants.templateParser:
await _openTemplateParser(context); await _openTemplateParser(context);
break; break;
@ -38,20 +47,36 @@ class MenuActions {
} }
} }
static Future<void> _openContentSearch(BuildContext context) async {
final tabManager = Provider.of<TabManager>(context, listen: false);
// Create new tab with unique ID
final tabId = DateTime.now().millisecondsSinceEpoch.toString();
await tabManager.addTab(
tabId,
title: contentSearchTabTitle,
type: contentSearchTabType,
icon: contentSearchTabIcon,
content: contentSearchDefaultContent,
);
}
static Future<void> _openTemplateParser(BuildContext context) async { static Future<void> _openTemplateParser(BuildContext context) async {
final editorProvider = Provider.of<TabProvider>(context, listen: false); final tabManager = Provider.of<TabManager>(context, listen: false);
// 使 firstWhereOrNull // 使 firstWhereOrNull
final existingTab = editorProvider.tabs.firstWhereOrNull( final existingTab = tabManager.tabs.firstWhereOrNull(
(tab) => tab.type == templateParserTabType, (tab) => tab.type == templateParserTabType,
); );
if (existingTab != null) { if (existingTab != null) {
// //
editorProvider.setActiveTab(existingTab.id); tabManager.setActiveTab(existingTab.id);
} else { } else {
// final tabId = DateTime.now().millisecondsSinceEpoch.toString();
await editorProvider.addTab( await tabManager.addTab(
tabId,
title: templateParserTabTitle, title: templateParserTabTitle,
type: templateParserTabType, type: templateParserTabType,
icon: templateParserTabIcon, icon: templateParserTabIcon,

1
win_text_editor/lib/app/menus/menu_constants.dart

@ -13,6 +13,7 @@ class MenuConstants {
static const String exit = 'exit'; static const String exit = 'exit';
// //
static const String contentSearch = "content_search";
static const String templateParser = 'template_parser'; static const String templateParser = 'template_parser';
// //

4
win_text_editor/lib/app/models/tab_model.dart

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ContentTab { class AppTab {
final String id; final String id;
final String title; final String title;
final String? type; // final String? type; //
@ -8,7 +8,7 @@ class ContentTab {
String content; String content;
String? fileName; String? fileName;
ContentTab({ AppTab({
required this.id, required this.id,
required this.title, required this.title,
this.type, this.type,

33
win_text_editor/lib/app/modules/template_parser/template_parser_controller.dart

@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:win_text_editor/app/modules/template_parser/template_parser_service.dart';
class TemplateParserController with ChangeNotifier {
final TemplateParserService _service = TemplateParserService();
String _sourceContent = '';
String _parsedContent = '';
String _statusMessage = '准备就绪';
String get sourceContent => _sourceContent;
String get parsedContent => _parsedContent;
String get statusMessage => _statusMessage;
set sourceContent(String value) {
_sourceContent = value;
notifyListeners();
}
Future<void> parseTemplates() async {
try {
_statusMessage = '解析中...';
notifyListeners();
_parsedContent = await _service.parse(_sourceContent);
_statusMessage = '解析完成';
} catch (e) {
_statusMessage = '解析失败: $e';
} finally {
notifyListeners();
}
}
}

7
win_text_editor/lib/app/modules/template_parser/template_parser_service.dart

@ -0,0 +1,7 @@
class TemplateParserService {
Future<String> parse(String source) async {
//
await Future.delayed(const Duration(seconds: 1)); //
return 'Parssed: $source';
}
}

14
win_text_editor/lib/app/widgets/template_parser_tab.dart → win_text_editor/lib/app/modules/template_parser/template_parser_view.dart

@ -1,19 +1,19 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:win_text_editor/app/providers/tab_provider.dart'; import 'package:win_text_editor/app/core/tab_manager.dart';
import 'package:win_text_editor/app/widgets/text_editor.dart'; import 'package:win_text_editor/app/widgets/text_editor.dart';
class TemplateParserTab extends StatefulWidget { class TemplateParserView extends StatefulWidget {
final String tabId; final String tabId;
const TemplateParserTab({super.key, required this.tabId}); const TemplateParserView({super.key, required this.tabId});
@override @override
State<TemplateParserTab> createState() => TemplateParserTabState(); State<TemplateParserView> createState() => TemplateParserViewState();
} }
class TemplateParserTabState extends State<TemplateParserTab> { class TemplateParserViewState extends State<TemplateParserView> {
late TabProvider _provider; late TabManager _provider;
String? _editor1FileName; String? _editor1FileName;
String? _editor2FileName; String? _editor2FileName;
String _editor1Content = ''; String _editor1Content = '';
@ -40,7 +40,7 @@ class TemplateParserTabState extends State<TemplateParserTab> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_provider = Provider.of<TabProvider>(context, listen: false); _provider = Provider.of<TabManager>(context, listen: false);
_provider.registerTextTabController(widget.tabId, this); _provider.registerTextTabController(widget.tabId, this);
final tab = _provider.getTabById(widget.tabId); final tab = _provider.getTabById(widget.tabId);
if (tab != null) { if (tab != null) {

84
win_text_editor/lib/app/widgets/editor_pane.dart

@ -1,84 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:win_text_editor/app/providers/tab_provider.dart';
import 'template_parser_tab.dart';
class EditorPane extends StatelessWidget {
const EditorPane({super.key});
@override
Widget build(BuildContext context) {
final provider = Provider.of<TabProvider>(context);
return Column(
children: [
//
SizedBox(
height: 40,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: provider.tabs.length,
itemBuilder: (ctx, index) {
final tab = provider.tabs[index];
return _TabItem(
title: tab.title,
icon: tab.icon, //
isActive: tab.id == provider.activeTabId,
onClose: () => provider.closeTab(tab.id),
onTap: () => provider.setActiveTab(tab.id),
);
},
),
),
//
Expanded(
child:
provider.activeTabId != null && provider.tabs.any((t) => t.id == provider.activeTabId)
? TemplateParserTab(tabId: provider.activeTabId!)
: const Center(child: Text('无活动标签页')),
),
],
);
}
}
class _TabItem extends StatelessWidget {
final String title;
final IconData? icon; //
final bool isActive;
final VoidCallback onClose;
final VoidCallback onTap;
const _TabItem({
required this.title,
this.icon,
required this.isActive,
required this.onClose,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: isActive ? Colors.blue[100] : Colors.grey[200],
border: Border(
bottom: BorderSide(color: isActive ? Colors.blue : Colors.transparent, width: 2),
),
),
child: Row(
children: [
if (icon != null) Icon(icon, size: 16),
if (icon != null) const SizedBox(width: 4),
Text(title),
const SizedBox(width: 8),
IconButton(icon: const Icon(Icons.close, size: 16), onPressed: onClose),
],
),
),
);
}
}

6
win_text_editor/lib/main.dart

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:win_text_editor/app/core/app_scaffold.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
import 'app/app.dart';
import 'app/providers/logger.dart'; // import 'app/providers/logger.dart'; //
void main() async { void main() async {
@ -12,7 +12,7 @@ void main() async {
WindowOptions windowOptions = const WindowOptions( WindowOptions windowOptions = const WindowOptions(
size: Size(1200, 700), size: Size(1200, 700),
center: true, center: true,
title: '文本转换', title: '升级工具',
); );
windowManager.waitUntilReadyToShow(windowOptions, () async { windowManager.waitUntilReadyToShow(windowOptions, () async {
await windowManager.show(); await windowManager.show();
@ -36,7 +36,7 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
title: '文本转换', title: '升级工具',
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
theme: ThemeData( theme: ThemeData(
primarySwatch: Colors.blue, primarySwatch: Colors.blue,

Loading…
Cancel
Save