From c491b2c17e3d39e455b9dc3de0552b553d90f66d Mon Sep 17 00:00:00 2001 From: hejl Date: Sun, 11 May 2025 14:33:02 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BD=93=E4=B8=AA=E7=BC=96=E8=BE=91=E7=AA=97?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E5=8A=A0=E8=BD=BD=E4=B8=8E=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=AE=8C=E6=88=90=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- win_text_editor/lib/app/app.dart | 55 +++---- .../lib/app/providers/editor_provider.dart | 34 +++-- .../lib/app/providers/file_provider.dart | 15 +- win_text_editor/lib/app/providers/logger.dart | 86 +++++++++++ .../lib/app/widgets/console_panel.dart | 130 +++++++++++++++++ .../lib/app/widgets/file_explorer.dart | 134 ++++++++++++------ win_text_editor/lib/app/widgets/text_tab.dart | 62 +++++++- win_text_editor/lib/main.dart | 12 +- 8 files changed, 436 insertions(+), 92 deletions(-) create mode 100644 win_text_editor/lib/app/providers/logger.dart create mode 100644 win_text_editor/lib/app/widgets/console_panel.dart diff --git a/win_text_editor/lib/app/app.dart b/win_text_editor/lib/app/app.dart index 8f40530..d044354 100644 --- a/win_text_editor/lib/app/app.dart +++ b/win_text_editor/lib/app/app.dart @@ -5,6 +5,7 @@ import 'package:win_text_editor/app/providers/editor_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}); @@ -22,9 +23,7 @@ class AppScaffold extends StatelessWidget { // 菜单栏 AppMenu(), // 主内容区域 - Expanded( - child: _ResizablePanel(), - ), + Expanded(child: _ResizablePanel()), ], ), ), @@ -53,34 +52,36 @@ class _ResizablePanelState extends State<_ResizablePanel> { // 计算实际宽度 final leftPanelWidth = (_leftWidth * screenWidth).clamp(_minWidth, _maxWidth); - return Row( + return Column( + children: [ + Expanded( + child: Row( children: [ // 左侧文件资源管理器 - SizedBox( - width: leftPanelWidth, - child: const 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], + SizedBox(width: leftPanelWidth, child: const 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]), + ), + ), + // 右侧编辑器区域 + const Expanded(child: Column(children: [Expanded(child: EditorPane())])), + ], ), ), - ), - // 右侧编辑器区域 - Expanded( - child: const EditorPane(), - ), + // 控制台面板 + const ConsolePanel(), ], ); }, diff --git a/win_text_editor/lib/app/providers/editor_provider.dart b/win_text_editor/lib/app/providers/editor_provider.dart index 7d0085d..00bb593 100644 --- a/win_text_editor/lib/app/providers/editor_provider.dart +++ b/win_text_editor/lib/app/providers/editor_provider.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:win_text_editor/app/providers/logger.dart'; class EditorProvider with ChangeNotifier { final List _tabs = []; @@ -7,20 +8,25 @@ class EditorProvider with ChangeNotifier { List get tabs => _tabs; String? get activeTabId => _activeTabId; + EditorTab? get activeTab { + if (_activeTabId == null) return null; + try { + return _tabs.firstWhere((tab) => tab.id == _activeTabId); + } catch (e) { + Logger().error("找不到活动选项卡: $_activeTabId", source: 'EditorProvider'); + return null; + } + } + void addTab() { final tabId = DateTime.now().millisecondsSinceEpoch.toString(); - _tabs.add( - EditorTab( - id: tabId, - title: '文档 ${_tabs.length + 1}', - content: '', // 初始空内容 - ), - ); + _tabs.add(EditorTab(id: tabId, title: '文档 ${_tabs.length + 1}', content: '')); _activeTabId = tabId; notifyListeners(); } void closeTab(String tabId) { + Logger().info('关闭选项卡: $tabId'); _tabs.removeWhere((tab) => tab.id == tabId); if (_activeTabId == tabId) { _activeTabId = _tabs.isNotEmpty ? _tabs.last.id : null; @@ -29,19 +35,27 @@ class EditorProvider with ChangeNotifier { } void setActiveTab(String tabId) { + Logger().info('设置活动选项卡: $tabId'); _activeTabId = tabId; notifyListeners(); } void updateContent(String tabId, String content) { - final tab = _tabs.firstWhere((t) => t.id == tabId); - tab.content = content; + Logger().debug("更新选项卡内容: $tabId, 长度: ${content.length}"); + try { + final tab = _tabs.firstWhere((t) => t.id == tabId); + tab.content = content; + Logger().debug("内容更新成功,新长度: ${tab.content.length}"); + notifyListeners(); + } catch (e) { + Logger().error("更新内容失败: ${e.toString()}", source: 'EditorProvider'); + } } } class EditorTab { final String id; - final String title; + String title; String content; EditorTab({required this.id, required this.title, required this.content}); diff --git a/win_text_editor/lib/app/providers/file_provider.dart b/win_text_editor/lib/app/providers/file_provider.dart index f4546f4..13c3bd0 100644 --- a/win_text_editor/lib/app/providers/file_provider.dart +++ b/win_text_editor/lib/app/providers/file_provider.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; import 'package:path_provider/path_provider.dart'; import 'package:win_text_editor/app/models/file_node.dart'; +import 'package:win_text_editor/app/providers/logger.dart'; import 'package:win_text_editor/app/services/file_service.dart'; class FileProvider with ChangeNotifier { @@ -47,7 +48,7 @@ class FileProvider with ChangeNotifier { final result = await FilePicker.platform.pickFiles(); if (result != null && result.files.single.path != null) { // 这里需要与编辑器提供者交互来打开文件 - debugPrint('File selected: ${result.files.single.path}'); + Logger().info('File selected: ${result.files.single.path}'); } } @@ -66,7 +67,7 @@ class FileProvider with ChangeNotifier { ); _fileNodes = [rootNode]; // 将根节点作为唯一顶层节点 } catch (e) { - debugPrint('Error loading directory: $e'); + Logger().error('Error loading directory: $e'); _fileNodes = []; } @@ -92,7 +93,7 @@ class FileProvider with ChangeNotifier { ), ]; } catch (e) { - debugPrint('Error loading root directory: $e'); + Logger().error('Error loading root directory: $e'); _fileNodes = []; } @@ -115,7 +116,7 @@ class FileProvider with ChangeNotifier { ), ]; } catch (e) { - debugPrint('Error loading root: $e'); + Logger().error('Error loading root: $e'); _fileNodes = []; } @@ -133,7 +134,7 @@ class FileProvider with ChangeNotifier { dirNode.children = await FileService.listDirectory(dirNode.path); dirNode.isExpanded = true; } catch (e) { - debugPrint('Error loading dir: $e'); + Logger().error('Error loading directory: $e'); dirNode.children = []; } @@ -171,7 +172,7 @@ class FileProvider with ChangeNotifier { _replaceNodeInTree(dirNode, updatedNode); } catch (e) { - debugPrint('Error loading directory contents: $e'); + Logger().error('Error loading directory contents: $e'); final updatedNode = dirNode.copyWith(children: []); _replaceNodeInTree(dirNode, updatedNode); } @@ -217,7 +218,7 @@ class FileProvider with ChangeNotifier { ), ]; } catch (e) { - debugPrint('Error refreshing file tree: $e'); + Logger().error('Error refreshing file tree: $e'); _fileNodes = []; } diff --git a/win_text_editor/lib/app/providers/logger.dart b/win_text_editor/lib/app/providers/logger.dart new file mode 100644 index 0000000..97aafba --- /dev/null +++ b/win_text_editor/lib/app/providers/logger.dart @@ -0,0 +1,86 @@ +import 'package:flutter/foundation.dart'; + +enum LogLevel { debug, info, warning, error } + +class LogEntry { + final DateTime timestamp; + final LogLevel level; + final String message; + final String? source; + + LogEntry({required this.level, required this.message, this.source}) : timestamp = DateTime.now(); + + @override + String toString() { + final levelStr = level.toString().split('.').last.toUpperCase(); + final timeStr = timestamp.toIso8601String(); + final sourceStr = source != null ? ' [$source]' : ''; + return '$timeStr $levelStr$sourceStr: $message'; + } +} + +class Logger with ChangeNotifier { + static final Logger _instance = Logger._internal(); + final List _logs = []; + bool _showTimestamps = true; + bool _showSource = true; + LogLevel _minimumLevel = LogLevel.debug; + + factory Logger() { + return _instance; + } + + Logger._internal(); + + List get logs => List.unmodifiable(_logs); + bool get showTimestamps => _showTimestamps; + bool get showSource => _showSource; + LogLevel get minimumLevel => _minimumLevel; + + void setMinimumLevel(LogLevel level) { + _minimumLevel = level; + notifyListeners(); + } + + void setShowTimestamps(bool show) { + _showTimestamps = show; + notifyListeners(); + } + + void setShowSource(bool show) { + _showSource = show; + notifyListeners(); + } + + void _addLog(LogEntry entry) { + print('Adding log: $entry'); + if (entry.level.index >= _minimumLevel.index) { + _logs.add(entry); + notifyListeners(); + if (kDebugMode) { + print(entry.toString()); + } + } + } + + void debug(String message, {String? source}) { + _addLog(LogEntry(level: LogLevel.debug, message: message, source: source)); + } + + void info(String message, {String? source}) { + _addLog(LogEntry(level: LogLevel.info, message: message, source: source)); + } + + void warning(String message, {String? source}) { + _addLog(LogEntry(level: LogLevel.warning, message: message, source: source)); + } + + void error(String message, {String? source}) { + _addLog(LogEntry(level: LogLevel.error, message: message, source: source)); + } + + void clear() { + _logs.clear(); + notifyListeners(); + } +} diff --git a/win_text_editor/lib/app/widgets/console_panel.dart b/win_text_editor/lib/app/widgets/console_panel.dart new file mode 100644 index 0000000..9bbccdf --- /dev/null +++ b/win_text_editor/lib/app/widgets/console_panel.dart @@ -0,0 +1,130 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:flutter/services.dart'; // 用于复制到剪贴板 +import 'package:win_text_editor/app/providers/logger.dart'; + +class ConsolePanel extends StatefulWidget { + const ConsolePanel({super.key}); + + @override + State createState() => _ConsolePanelState(); +} + +class _ConsolePanelState extends State { + double _height = 100; + final double _minHeight = 50; + final double _maxHeight = 300; + final ScrollController _scrollController = ScrollController(); + String? _selectedLog; // 当前选中的日志内容 + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + Color _getLogColor(LogLevel level) { + switch (level) { + case LogLevel.error: + return Colors.red[400]!; + case LogLevel.warning: + return Colors.orange[400]!; + case LogLevel.info: + return Colors.blue[400]!; + case LogLevel.debug: + return Colors.grey; + } + } + + // 显示右键菜单 + void _showContextMenu(BuildContext context, Offset position, String logContent) async { + final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox; + + await showMenu( + context: context, + position: RelativeRect.fromRect( + Rect.fromPoints(position, position), + Offset.zero & overlay.size, + ), + items: [ + PopupMenuItem( + child: const Text('复制'), + onTap: () async { + await Clipboard.setData(ClipboardData(text: logContent)); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('已复制到剪贴板'), duration: Duration(seconds: 1)), + ); + }, + ), + PopupMenuItem( + child: const Text('清除日志'), + onTap: () { + Provider.of(context, listen: false).clear(); + }, + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + final logger = Provider.of(context); + final consoleHeight = _height.clamp(_minHeight, _maxHeight); + + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_scrollController.hasClients) { + _scrollController.jumpTo(_scrollController.position.maxScrollExtent); + } + }); + + return Column( + children: [ + // 拖拽手柄 + GestureDetector( + behavior: HitTestBehavior.translucent, + onPanUpdate: (details) { + setState(() { + _height = (_height - details.delta.dy).clamp(_minHeight, _maxHeight); + }); + }, + child: MouseRegion( + cursor: SystemMouseCursors.resizeUpDown, + child: Container(height: 4, color: Colors.grey[300]), + ), + ), + // 控制台内容区域 + SizedBox( + height: consoleHeight, + child: Container( + color: Colors.grey[100], + padding: const EdgeInsets.all(8), + child: ListView.builder( + controller: _scrollController, + itemCount: logger.logs.length, + itemBuilder: (context, index) { + final log = logger.logs[index]; + final logContent = log.toString(); + return Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: GestureDetector( + onSecondaryTapDown: (details) { + _showContextMenu(context, details.globalPosition, logContent); + }, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: SelectableText( + // 改为可选择的文本 + logContent, + style: TextStyle(fontFamily: 'monospace', color: _getLogColor(log.level)), + ), + ), + ), + ); + }, + ), + ), + ), + ], + ); + } +} diff --git a/win_text_editor/lib/app/widgets/file_explorer.dart b/win_text_editor/lib/app/widgets/file_explorer.dart index 15598bc..8a7966e 100644 --- a/win_text_editor/lib/app/widgets/file_explorer.dart +++ b/win_text_editor/lib/app/widgets/file_explorer.dart @@ -1,6 +1,9 @@ +import 'dart:io'; + import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:win_text_editor/app/providers/logger.dart'; import '../models/file_node.dart'; import '../providers/file_provider.dart'; @@ -55,10 +58,82 @@ class _FileExplorerState extends State { if (node.isDirectory) { await fileProvider.loadDirectoryContents(node); } else { - // 如果是文件,可以在这里添加打开文件的逻辑 - // 例如: - // final editorProvider = Provider.of(context, listen: false); - // editorProvider.openFile(node.path); + // Handle file opening + print("No active tab found"); + _openFileInEditor(context, node); + } + } + + Future _openFileInEditor(BuildContext context, FileNode node) async { + try { + Logger().info('尝试打开文件: ${node.path}'); + final editorProvider = Provider.of(context, listen: false); + final content = await File(node.path).readAsString(); + + Logger().debug('文件内容读取成功,长度: ${content.length}'); + Logger().debug('当前活动选项卡ID: ${editorProvider.activeTabId}'); + Logger().debug('现有选项卡数量: ${editorProvider.tabs.length}'); + + // 如果没有活动选项卡,先创建一个 + if (editorProvider.activeTabId == null) { + Logger().info('没有活动选项卡,创建新选项卡'); + editorProvider.addTab(); + } + + // 更新内容 + Logger().info('准备更新选项卡内容'); + editorProvider.updateContent(editorProvider.activeTabId!, content); + + // 更新标题为文件名 + final activeTab = editorProvider.activeTab; + if (activeTab != null) { + activeTab.title = node.name; + Logger().debug('更新选项卡标题为: ${node.name}'); + editorProvider.notifyListeners(); + } + + if (context.mounted) { + Logger().debug('已加载: ${node.name}'); + } + } on FormatException { + Logger().warning('文件格式异常: ${node.path}'); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('这不是可读的文本文件'))); + } + } catch (e) { + Logger().error('打开文件失败: ${e.toString()}', source: 'FileExplorer'); + if (context.mounted) { + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text('读取失败: ${e.toString()}'))); + } + } + } + + int getMaxDepth(List nodes) { + int maxDepth = 0; + for (final node in nodes) { + if (node.isDirectory && node.isExpanded) { + maxDepth = max(maxDepth, getMaxDepth(node.children) + 1); + } + } + return maxDepth; + } + + // 动态计算总宽度(示例:根据层级深度调整) + double calculateTotalWidth(BuildContext context, FileProvider fileProvider) { + final maxDepth = getMaxDepth(fileProvider.fileNodes); + 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'); } } @@ -90,7 +165,7 @@ class _FileExplorerState extends State { thumbVisibility: true, scrollbarOrientation: ScrollbarOrientation.bottom, // 横向滚动条在底部 child: SizedBox( - width: _calculateTotalWidth(context, fileProvider), // 动态计算总宽度 + width: calculateTotalWidth(context, fileProvider), // 动态计算总宽度 child: ListView.builder( // 内层:垂直列表 controller: _verticalScrollController, @@ -101,6 +176,7 @@ class _FileExplorerState extends State { key: ValueKey(node.path), node: node, onTap: () => _handleNodeTap(context, node), + onDoubleTap: () => _handleNodeDoubleTap(context, node), ); }, ), @@ -113,22 +189,6 @@ class _FileExplorerState extends State { ), ); } - - // 动态计算总宽度(示例:根据层级深度调整) - double _calculateTotalWidth(BuildContext context, FileProvider fileProvider) { - final maxDepth = _getMaxDepth(fileProvider.fileNodes); - return maxDepth * 200 + MediaQuery.of(context).size.width * 0.5; - } - - int _getMaxDepth(List nodes) { - int maxDepth = 0; - for (final node in nodes) { - if (node.isDirectory && node.isExpanded) { - maxDepth = max(maxDepth, _getMaxDepth(node.children) + 1); - } - } - return maxDepth; - } } // 辅助方法:计算所有可见节点数量 @@ -163,39 +223,31 @@ FileNode _getVisibleNode(List nodes, int index) { class _FileNodeWidget extends StatelessWidget { final FileNode node; final VoidCallback onTap; + final VoidCallback onDoubleTap; // 新增双击回调 const _FileNodeWidget({ - Key? key, // 声明 key 参数 + Key? key, required this.node, required this.onTap, - }) : super(key: key); // 传递给父类 + required this.onDoubleTap, // 新增参数 + }) : super(key: key); @override Widget build(BuildContext context) { //final editorProvider = Provider.of(context, listen: false); return InkWell( - onTap: () { - if (node.isDirectory) { - onTap(); - } else { - //editorProvider.openFile(node.path); - } - }, + onTap: onTap, + onDoubleTap: onDoubleTap, // 添加双击事件 child: Container( - padding: const EdgeInsets.symmetric(vertical: 0), // 缩小垂直间距 + padding: const EdgeInsets.symmetric(vertical: 0), child: ListTile( - dense: true, // 启用紧凑模式 - visualDensity: const VisualDensity( - vertical: -4, // 负值减少高度,正值增加高度 - ), - contentPadding: const EdgeInsets.symmetric(horizontal: 2), // 调整水平内边距 - minVerticalPadding: 0, // 最小垂直内边距设为0 + dense: true, + visualDensity: const VisualDensity(vertical: -4), + contentPadding: const EdgeInsets.symmetric(horizontal: 2), + minVerticalPadding: 0, leading: _buildLeadingWidget(context), - title: Text( - node.name, - style: Theme.of(context).textTheme.bodyMedium, // 使用标准文本样式 - ), + title: Text(node.name, style: Theme.of(context).textTheme.bodyMedium), ), ), ); diff --git a/win_text_editor/lib/app/widgets/text_tab.dart b/win_text_editor/lib/app/widgets/text_tab.dart index 7f38829..c2b96cb 100644 --- a/win_text_editor/lib/app/widgets/text_tab.dart +++ b/win_text_editor/lib/app/widgets/text_tab.dart @@ -46,7 +46,7 @@ class _TextTabState extends State { @override Widget build(BuildContext context) { final tab = _provider.tabs.firstWhere((t) => t.id == widget.tabId); // Add this line - + return Column( children: [ // 新增工具条 @@ -149,12 +149,62 @@ class _TextTabState extends State { } // 保存文件模拟功能 + // 保存文件功能 Future _saveFile(BuildContext context, String content) async { - // 这里是模拟代码,实际应该使用文件保存对话框 - if (context.mounted) { - ScaffoldMessenger.of( - context, - ).showSnackBar(SnackBar(content: Text('已保存 ${content.length} 个字符'))); + try { + // 让用户选择保存位置 + String? outputPath = await FilePicker.platform.saveFile( + dialogTitle: '保存文件', + fileName: 'untitled.txt', + allowedExtensions: ['txt'], + type: FileType.any, + ); + + if (outputPath == null) return; // 用户取消了 + + final file = File(outputPath); + + // 检查文件是否存在 + if (await file.exists()) { + // 如果存在,显示确认对话框 + final shouldOverwrite = await showDialog( + context: context, + builder: + (context) => AlertDialog( + title: const Text('文件已存在'), + content: const Text('要覆盖现有文件吗?'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: const Text('取消'), + ), + TextButton( + onPressed: () => Navigator.pop(context, true), + child: const Text('覆盖'), + ), + ], + ), + ); + + if (shouldOverwrite != true) return; // 用户选择不覆盖 + } + + // 写入文件 + await file.writeAsString(content); + + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('已保存到: ${file.path}'))); + } + } on FileSystemException catch (e) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('保存失败: ${e.message}'))); + } + } catch (e) { + if (context.mounted) { + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text('保存失败: ${e.toString()}'))); + } } } } diff --git a/win_text_editor/lib/main.dart b/win_text_editor/lib/main.dart index 675f8d1..3d3c53a 100644 --- a/win_text_editor/lib/main.dart +++ b/win_text_editor/lib/main.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:window_manager/window_manager.dart'; import 'app/app.dart'; +import 'app/providers/logger.dart'; // 新增导入 void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -17,7 +19,15 @@ void main() async { await windowManager.focus(); }); - runApp(const MyApp()); + runApp( + MultiProvider( + providers: [ + ChangeNotifierProvider(create: (_) => Logger()), // 添加Logger提供者 + // 可以在这里添加其他Provider + ], + child: const MyApp(), + ), + ); } class MyApp extends StatelessWidget {