修复悬浮球上下文问题并改进详情页显示
- 修改example应用结构:将YxNetInspector放到MaterialApp内部 - 修复Navigator和Overlay上下文问题,悬浮球现在能正常点击 - 改进网络检查器面板:详情页现在在检查器内部显示而非主应用导航 - 添加内部页面导航状态管理和返回按钮 - 优化用户体验:保持检查器操作在独立的上下文中
This commit is contained in:
parent
31b33ce1e4
commit
10a251dcf9
|
|
@ -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('发送测试请求'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,46 +96,63 @@ class _YxFloatingBallState extends State<YxFloatingBall>
|
|||
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<YxFloatingBall>
|
|||
}
|
||||
|
||||
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;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ class _YxInspectorPanelState extends State<YxInspectorPanel> {
|
|||
String _searchKeyword = '';
|
||||
bool _showOnlyErrors = false;
|
||||
bool _isFullScreen = false;
|
||||
|
||||
// 页面导航状态
|
||||
NetworkLogEntry? _selectedLog;
|
||||
bool _showDetailPage = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
|
|
@ -33,6 +37,20 @@ class _YxInspectorPanelState extends State<YxInspectorPanel> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
void _showLogDetail(NetworkLogEntry log) {
|
||||
setState(() {
|
||||
_selectedLog = log;
|
||||
_showDetailPage = true;
|
||||
});
|
||||
}
|
||||
|
||||
void _hideLogDetail() {
|
||||
setState(() {
|
||||
_selectedLog = null;
|
||||
_showDetailPage = false;
|
||||
});
|
||||
}
|
||||
|
||||
List<NetworkLogEntry> get _filteredLogs {
|
||||
List<NetworkLogEntry> logs = widget.controller.logs;
|
||||
|
||||
|
|
@ -54,6 +72,33 @@ class _YxInspectorPanelState extends State<YxInspectorPanel> {
|
|||
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<YxInspectorPanel> {
|
|||
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<YxInspectorPanel> {
|
|||
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<YxInspectorPanel> {
|
|||
);
|
||||
}
|
||||
|
||||
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<YxInspectorPanel> {
|
|||
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(
|
||||
|
|
|
|||
Loading…
Reference in New Issue