修复悬浮球上下文问题并改进详情页显示

- 修改example应用结构:将YxNetInspector放到MaterialApp内部
- 修复Navigator和Overlay上下文问题,悬浮球现在能正常点击
- 改进网络检查器面板:详情页现在在检查器内部显示而非主应用导航
- 添加内部页面导航状态管理和返回按钮
- 优化用户体验:保持检查器操作在独立的上下文中
This commit is contained in:
YuanXuan 2025-08-28 10:51:48 +08:00
parent 31b33ce1e4
commit 10a251dcf9
4 changed files with 283 additions and 132 deletions

73
debug_test.dart Normal file
View File

@ -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('发送测试请求'),
),
],
),
),
),
),
);
}
}

View File

@ -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,
);
}
}

View File

@ -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;
});

View File

@ -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(