From 10a251dcf91fa54acaa1a907dca30b825caea48f Mon Sep 17 00:00:00 2001 From: YuanXuan Date: Thu, 28 Aug 2025 10:51:48 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=82=AC=E6=B5=AE=E7=90=83?= =?UTF-8?q?=E4=B8=8A=E4=B8=8B=E6=96=87=E9=97=AE=E9=A2=98=E5=B9=B6=E6=94=B9?= =?UTF-8?q?=E8=BF=9B=E8=AF=A6=E6=83=85=E9=A1=B5=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改example应用结构:将YxNetInspector放到MaterialApp内部 - 修复Navigator和Overlay上下文问题,悬浮球现在能正常点击 - 改进网络检查器面板:详情页现在在检查器内部显示而非主应用导航 - 添加内部页面导航状态管理和返回按钮 - 优化用户体验:保持检查器操作在独立的上下文中 --- debug_test.dart | 73 +++++++++++++++ example/lib/main.dart | 76 +++++++-------- lib/src/widgets/floating_ball.dart | 134 +++++++++++++-------------- lib/src/widgets/inspector_panel.dart | 132 +++++++++++++++++++++----- 4 files changed, 283 insertions(+), 132 deletions(-) create mode 100644 debug_test.dart diff --git a/debug_test.dart b/debug_test.dart new file mode 100644 index 0000000..60725cc --- /dev/null +++ b/debug_test.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:yx_net_inspector/yx_net_inspector.dart'; + +void main() { + runApp(const MyDebugApp()); +} + +class MyDebugApp extends StatelessWidget { + const MyDebugApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'YX 网络检查器调试', + home: YxNetInspector( + config: const YxNetInspectorConfig( + showFloatingBall: true, + ballSize: 60, + ballColor: Colors.blue, + ), + theme: const YxNetInspectorTheme( + primaryColor: Colors.blue, + backgroundColor: Colors.white, + textColor: Colors.black87, + successColor: Colors.green, + errorColor: Colors.red, + warningColor: Colors.orange, + cardColor: Colors.white, + ), + child: Scaffold( + appBar: AppBar( + title: const Text('调试测试'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('点击悬浮球测试'), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () { + // 模拟一个网络请求来测试 + final requestId = + 'test_${DateTime.now().millisecondsSinceEpoch}'; + YxNetInspectorController.instance.logRequest( + id: requestId, + method: 'GET', + url: 'https://jsonplaceholder.typicode.com/posts/1', + headers: const {'Content-Type': 'application/json'}, + ); + + Future.delayed(const Duration(seconds: 1), () { + YxNetInspectorController.instance.logResponse( + id: requestId, + statusCode: 200, + responseData: const { + 'title': 'Test Post', + 'body': 'Test content' + }, + duration: const Duration(seconds: 1), + ); + }); + }, + child: const Text('发送测试请求'), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 1cf3c9f..88f5e24 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -13,50 +13,50 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return YxNetInspector( - config: const YxNetInspectorConfig( - showFloatingBall: true, - ballSize: 70.0, - ballColor: Colors.blue, - showInDebugMode: true, - showInReleaseMode: false, - maxLogs: 1000, - draggable: true, - showBadge: true, - autoHide: false, - ), - theme: const YxNetInspectorTheme( - primaryColor: Colors.blue, - backgroundColor: Colors.white, - textColor: Colors.black87, - successColor: Colors.green, - errorColor: Colors.red, - warningColor: Colors.orange, - cardColor: Colors.white, - ), - child: MaterialApp( - title: 'YX 网络检查器演示', - theme: ThemeData( - primarySwatch: Colors.blue, - visualDensity: VisualDensity.adaptivePlatformDensity, - appBarTheme: const AppBarTheme( + return MaterialApp( + title: 'YX 网络检查器演示', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + appBarTheme: const AppBarTheme( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + elevation: 2, + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, - elevation: 2, - ), - elevatedButtonTheme: ElevatedButtonThemeData( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - foregroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), ), ), ), - home: const MainPage(), - debugShowCheckedModeBanner: false, ), + home: YxNetInspector( + config: const YxNetInspectorConfig( + showFloatingBall: true, + ballSize: 70.0, + ballColor: Colors.blue, + showInDebugMode: true, + showInReleaseMode: false, + maxLogs: 1000, + draggable: true, + showBadge: true, + autoHide: false, + ), + theme: const YxNetInspectorTheme( + primaryColor: Colors.blue, + backgroundColor: Colors.white, + textColor: Colors.black87, + successColor: Colors.green, + errorColor: Colors.red, + warningColor: Colors.orange, + cardColor: Colors.white, + ), + child: const MainPage(), + ), + debugShowCheckedModeBanner: false, ); } } diff --git a/lib/src/widgets/floating_ball.dart b/lib/src/widgets/floating_ball.dart index 5f16a7f..5d1c9f3 100644 --- a/lib/src/widgets/floating_ball.dart +++ b/lib/src/widgets/floating_ball.dart @@ -96,46 +96,63 @@ class _YxFloatingBallState extends State OverlayState? overlay; try { overlay = Overlay.of(context, rootOverlay: true); + print('YxNetInspector: 成功找到根 Overlay'); } catch (e) { + print('YxNetInspector: 根 Overlay 查找失败: $e'); // 如果 Overlay.of 失败,尝试手动查找 overlay = _findOverlayInContext(context); + if (overlay != null) { + print('YxNetInspector: 手动查找 Overlay 成功'); + } else { + print('YxNetInspector: 手动查找 Overlay 也失败'); + } } - + if (overlay == null) { // 如果仍然找不到 Overlay,使用备选方案 _showInspectorDialog(); return; } - final overlayEntry = OverlayEntry( - builder: (context) => Material( - color: Colors.black54, - child: Stack( - children: [ - // 背景遮罩 - GestureDetector( - onTap: _hideInspectorPanel, - child: Container( - color: Colors.transparent, - width: double.infinity, - height: double.infinity, + try { + final overlayEntry = OverlayEntry( + builder: (context) => Material( + color: Colors.black54, + child: Stack( + children: [ + // 背景遮罩 + GestureDetector( + onTap: _hideInspectorPanel, + child: Container( + color: Colors.transparent, + width: double.infinity, + height: double.infinity, + ), ), - ), - // 检查器面板 - Center( - child: YxInspectorPanel( - theme: widget.theme, - controller: widget.controller, - onClose: _hideInspectorPanel, + // 检查器面板 + Center( + child: YxInspectorPanel( + theme: widget.theme, + controller: widget.controller, + onClose: _hideInspectorPanel, + ), ), - ), - ], + ], + ), ), - ), - ); + ); - overlay.insert(overlayEntry); - _currentOverlayEntry = overlayEntry; + overlay.insert(overlayEntry); + _currentOverlayEntry = overlayEntry; + print('YxNetInspector: 检查器面板显示成功'); + } catch (e) { + print('YxNetInspector: 插入 OverlayEntry 失败: $e'); + // 重置状态 + setState(() { + _isExpanded = false; + }); + _animationController.reverse(); + } } OverlayState? _findOverlayInContext(BuildContext context) { @@ -157,53 +174,26 @@ class _YxFloatingBallState extends State } void _createCustomOverlay() { - // 获取屏幕尺寸 - final mediaQuery = MediaQuery.of(context); - final screenSize = mediaQuery.size; - - // 创建一个简单的OverlayEntry但是通过自定义方式 - final overlayEntry = OverlayEntry( - builder: (context) => Positioned.fill( - child: Material( - color: Colors.black54, - child: Stack( - children: [ - // 背景遮罩 - GestureDetector( - onTap: _hideInspectorPanel, - child: Container( - color: Colors.transparent, - width: screenSize.width, - height: screenSize.height, - ), - ), - // 检查器面板 - Center( - child: Container( - width: screenSize.width * 0.9, - height: screenSize.height * 0.8, - child: YxInspectorPanel( - theme: widget.theme, - controller: widget.controller, - onClose: _hideInspectorPanel, - ), - ), - ), - ], - ), - ), - ), - ); - - // 尝试直接插入到根Overlay中 + // 如果所有 Overlay 方法都失败,我们显示一个简单的调试信息 + // 并重置状态,避免悬浮球卡在展开状态 + print('YxNetInspector: 无法找到 Overlay 上下文,请确保 YxNetInspector 在 MaterialApp 内部使用'); + + // 显示一个简单的 SnackBar 或 print 提示 WidgetsBinding.instance.addPostFrameCallback((_) { - try { - final rootOverlay = Overlay.of(context, rootOverlay: true); - rootOverlay.insert(overlayEntry); - _currentOverlayEntry = overlayEntry; - } catch (e) { - // 如果还是失败,记录错误但不崩溃 - print('YxNetInspector: 无法显示检查器面板 - $e'); + if (mounted) { + try { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('网络检查器暂时无法显示,请检查应用结构'), + duration: Duration(seconds: 2), + ), + ); + } catch (e) { + // 如果 ScaffoldMessenger 也不可用,只打印日志 + print('YxNetInspector: 无法显示检查器面板 - 请确保在 MaterialApp 内使用'); + } + + // 重置状态 setState(() { _isExpanded = false; }); diff --git a/lib/src/widgets/inspector_panel.dart b/lib/src/widgets/inspector_panel.dart index 65e1faf..ff8abbe 100644 --- a/lib/src/widgets/inspector_panel.dart +++ b/lib/src/widgets/inspector_panel.dart @@ -26,6 +26,10 @@ class _YxInspectorPanelState extends State { String _searchKeyword = ''; bool _showOnlyErrors = false; bool _isFullScreen = false; + + // 页面导航状态 + NetworkLogEntry? _selectedLog; + bool _showDetailPage = false; @override void dispose() { @@ -33,6 +37,20 @@ class _YxInspectorPanelState extends State { super.dispose(); } + void _showLogDetail(NetworkLogEntry log) { + setState(() { + _selectedLog = log; + _showDetailPage = true; + }); + } + + void _hideLogDetail() { + setState(() { + _selectedLog = null; + _showDetailPage = false; + }); + } + List get _filteredLogs { List logs = widget.controller.logs; @@ -54,6 +72,33 @@ class _YxInspectorPanelState extends State { return ListenableBuilder( listenable: widget.controller, builder: (context, child) { + Widget content; + + if (_showDetailPage && _selectedLog != null) { + // 显示详情页面 + content = Column( + children: [ + _buildDetailHeader(), + Expanded( + child: YxLogDetailPage( + log: _selectedLog!, + theme: widget.theme, + ), + ), + ], + ); + } else { + // 显示主列表页面 + content = Column( + children: [ + _buildHeader(), + _buildStatistics(), + _buildSearchBar(), + Expanded(child: _buildLogList()), + ], + ); + } + if (_isFullScreen) { // 全屏模式 return Material( @@ -62,14 +107,7 @@ class _YxInspectorPanelState extends State { width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, color: widget.theme.backgroundColor, - child: Column( - children: [ - _buildHeader(), - _buildStatistics(), - _buildSearchBar(), - Expanded(child: _buildLogList()), - ], - ), + child: content, ), ); } else { @@ -83,14 +121,7 @@ class _YxInspectorPanelState extends State { color: widget.theme.backgroundColor, borderRadius: BorderRadius.circular(16), ), - child: Column( - children: [ - _buildHeader(), - _buildStatistics(), - _buildSearchBar(), - Expanded(child: _buildLogList()), - ], - ), + child: content, ), ); } @@ -98,6 +129,68 @@ class _YxInspectorPanelState extends State { ); } + Widget _buildDetailHeader() { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: widget.theme.primaryColor, + borderRadius: _isFullScreen + ? BorderRadius.zero + : const BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), + ), + child: Row( + children: [ + IconButton( + onPressed: _hideLogDetail, + icon: const Icon( + Icons.arrow_back, + color: Colors.white, + ), + tooltip: '返回列表', + ), + const SizedBox(width: 8), + const Icon( + Icons.article_outlined, + color: Colors.white, + size: 24, + ), + const SizedBox(width: 8), + const Text( + '请求详情', + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + const Spacer(), + IconButton( + onPressed: () { + _isFullScreen = !_isFullScreen; + setState(() {}); + }, + icon: Icon( + _isFullScreen ? Icons.fullscreen_exit : Icons.fullscreen, + color: Colors.white, + ), + tooltip: _isFullScreen ? '退出全屏' : '全屏显示', + ), + IconButton( + onPressed: widget.onClose, + icon: const Icon( + Icons.close, + color: Colors.white, + ), + tooltip: '关闭', + ), + ], + ), + ); + } + Widget _buildHeader() { return Container( padding: const EdgeInsets.all(16), @@ -309,12 +402,7 @@ class _YxInspectorPanelState extends State { color: widget.theme.cardColor, child: InkWell( onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => - YxLogDetailPage(log: log, theme: widget.theme), - ), - ); + _showLogDetail(log); }, borderRadius: BorderRadius.circular(8), child: Padding(