diff --git a/README.md b/README.md
index 2becdf5..7b8e059 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,19 @@
-# network proxy
+## 免费开源Http、Https抓包工具,支持Windows、Mac、Android、IOS, 全平台系统
-## 免费开全Http、Https抓包工具,支持Windows、Mac、Android、IOS, 支持全平台系统
+**Mac首次打开会提示已损坏,需要到系统偏好设置-安全性与隐私-允许任何来源。**
-> ios先没有上架AppStore, 后面在处理,目前是通过分发平台签名安装
+国内下载地址:https://gitee.com/wanghongenpin/network-proxy-flutter/releases/tag/0.0.1
+
+> ios先没有上架AppStore, 后面在处理。
-- mac会提示已损坏,需要到系统偏好设置-安全性与隐私-允许任何来源
- Mac先点左上角关闭按钮退出,直接Command+q可能不会清理系统代理。如果退出上不了网,请检查系统代理设置,清除即可。
-- [ ] 接下来会完善功能体验,JSON格式化展示,URL解码,UI优化。
+- [ ] 接下来会完善功能体验,JSON格式化展示,URL解码,UI优化。
- [ ] IOS上架app Store。
- [ ] 后面桌面端和手机端整合,扫码连接啥的,不用手动配置Wifi代理,包括配置同步。
- [ ] 支持安卓微信小程序抓包,安卓分为系统证书和用户证书,下载的自签名根证书安装都是用户证书,微信不信任用户证书,不Root导致Https抓不了了, 目前市场上所有抓包软件抓不了微信的包,后面单独做个运行空间插件,动态反编译修改配置,信任用户证书来解决。
+
+
+
.
+
+
diff --git a/ios/ProxyPin/PacketTunnelProvider.swift b/ios/ProxyPin/PacketTunnelProvider.swift
index 378c5d8..7fd3da7 100644
--- a/ios/ProxyPin/PacketTunnelProvider.swift
+++ b/ios/ProxyPin/PacketTunnelProvider.swift
@@ -18,9 +18,10 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
exit(EXIT_FAILURE)
}
let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
-
+ networkSettings.mtu = 1500
+ NSLog(conf.debugDescription)
//http代理
- let host = conf["proxtHost"] as! String
+ let host = conf["proxyHost"] as! String
let proxyPort = conf["proxyPort"] as! Int
let proxySettings = NEProxySettings()
proxySettings.httpEnabled = true
@@ -28,8 +29,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
proxySettings.httpsEnabled = true
proxySettings.httpsServer = NEProxyServer(address: host, port: proxyPort)
proxySettings.matchDomains = [""]
+
networkSettings.proxySettings = proxySettings
-
+
setTunnelNetworkSettings(networkSettings) {
error in
guard error == nil else {
@@ -43,23 +45,24 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}
NSLog("startTunnelend")
}
-
+
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
completionHandler()
}
-
+
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
// Add code here to handle the message.
if let handler = completionHandler {
+ NSLog("handleAppMessage ", messageData.debugDescription)
handler(messageData)
}
}
-
+
override func sleep(completionHandler: @escaping () -> Void) {
// Add code here to get ready to sleep.
completionHandler()
}
-
+
override func wake() {
// Add code here to wake up.
}
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 3b21ed3..b5eed67 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 54;
+ objectVersion = 55;
objects = {
/* Begin PBXBuildFile section */
@@ -19,6 +19,8 @@
9B0912222A54593A001108B7 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B0912212A54593A001108B7 /* NetworkExtension.framework */; };
9B0912252A54593A001108B7 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0912242A54593A001108B7 /* PacketTunnelProvider.swift */; };
9B09122A2A54593A001108B7 /* ProxyPin.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 9B0912202A54593A001108B7 /* ProxyPin.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ 9B70772D2A5718FB00F184A9 /* AudioManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B70772C2A5718FB00F184A9 /* AudioManager.swift */; };
+ 9B7077362A5728B900F184A9 /* silence.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 9B7077352A5728B900F184A9 /* silence.mp3 */; };
B375908E625E0AED772FA2C0 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D37E307095F2B3E689A68827 /* Pods_RunnerTests.framework */; };
/* End PBXBuildFile section */
@@ -92,6 +94,8 @@
9B0912242A54593A001108B7 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = ""; };
9B0912262A54593A001108B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
9B0912272A54593A001108B7 /* ProxyPin.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ProxyPin.entitlements; sourceTree = ""; };
+ 9B70772C2A5718FB00F184A9 /* AudioManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioManager.swift; sourceTree = ""; };
+ 9B7077352A5728B900F184A9 /* silence.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = silence.mp3; sourceTree = ""; };
D37E307095F2B3E689A68827 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E328C7F89A365CDC0EAD15C6 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
@@ -192,6 +196,7 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
+ 9B7077352A5728B900F184A9 /* silence.mp3 */,
9B09121A2A5457B3001108B7 /* VpnManager.swift */,
9B0912192A545757001108B7 /* Runner.entitlements */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
@@ -202,6 +207,7 @@
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ 9B70772C2A5718FB00F184A9 /* AudioManager.swift */,
);
path = Runner;
sourceTree = "";
@@ -303,7 +309,7 @@
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
- compatibilityVersion = "Xcode 9.3";
+ compatibilityVersion = "Xcode 13.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -334,6 +340,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 9B7077362A5728B900F184A9 /* silence.mp3 in Resources */,
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
@@ -460,6 +467,7 @@
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ 9B70772D2A5718FB00F184A9 /* AudioManager.swift in Sources */,
9B09121B2A5457B3001108B7 /* VpnManager.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -805,7 +813,7 @@
INFOPLIST_FILE = ProxyPin/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ProxyPin;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
- IPHONEOS_DEPLOYMENT_TARGET = 16.2;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -843,7 +851,7 @@
INFOPLIST_FILE = ProxyPin/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ProxyPin;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
- IPHONEOS_DEPLOYMENT_TARGET = 16.2;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -878,7 +886,7 @@
INFOPLIST_FILE = ProxyPin/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ProxyPin;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
- IPHONEOS_DEPLOYMENT_TARGET = 16.2;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
index 17ddef1..329d614 100644
--- a/ios/Runner/AppDelegate.swift
+++ b/ios/Runner/AppDelegate.swift
@@ -29,15 +29,47 @@ import NetworkExtension
}
})
-
-
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
+ override func applicationWillTerminate(_ application: UIApplication) {
+ VpnManager.shared.disconnect()
+ }
- override func applicationWillTerminate(_ application: UIApplication) {
- VpnManager.shared.disconnect()
- }
+ var timer: Timer?
+ var bgTask: UIBackgroundTaskIdentifier?
+ override func applicationDidEnterBackground(_ application: UIApplication) {
+// timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
+// RunLoop.current.add(timer!, forMode: RunLoop.Mode.common)
+// bgTask = application.beginBackgroundTask(expirationHandler: nil)
+ }
+
+ @objc func timerAction() {
+ print(UIApplication.shared.backgroundTimeRemaining )
+ if UIApplication.shared.backgroundTimeRemaining < 60.0 {
+ let application = UIApplication.shared
+ bgTask = application.beginBackgroundTask(expirationHandler: nil)
+ }
+ }
+
+ var backgroundUpdateTask: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier(rawValue: 0)
+ func endBackgroundUpdateTask() {
+ AudioManager.shared.openBackgroundAudioAutoplay = false
+ UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask)
+ self.backgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
+ }
+
+ override func applicationWillResignActive(_ application: UIApplication) {
+ AudioManager.shared.openBackgroundAudioAutoplay = true
+ self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
+ self.endBackgroundUpdateTask()
+ })
+ }
+ override func applicationDidBecomeActive(_ application: UIApplication) {
+ self.endBackgroundUpdateTask()
+
+ }
+
}
diff --git a/ios/Runner/AudioManager.swift b/ios/Runner/AudioManager.swift
new file mode 100644
index 0000000..042506d
--- /dev/null
+++ b/ios/Runner/AudioManager.swift
@@ -0,0 +1,157 @@
+import Foundation
+import AVFoundation
+import UIKit
+class AudioManager: NSObject {
+
+ static let shared = AudioManager()
+ fileprivate let audioSession = AVAudioSession.sharedInstance()
+ fileprivate var backgroundAudioPlayer: AVAudioPlayer?
+ fileprivate var backgroundTimeLength = 0
+ fileprivate var timer: Timer?
+
+ static let audioName = ""
+ // 是否开启后台自动播放无声音乐
+ var openBackgroundAudioAutoplay = false {
+ didSet {
+ if self.openBackgroundAudioAutoplay {
+ self.setupAudioSession()
+ self.setupBackgroundAudioPlayer()
+ } else {
+ if let player = self.backgroundAudioPlayer {
+ if player.isPlaying {
+ player.stop()
+ }
+ }
+ self.backgroundAudioPlayer = nil
+ try? self.audioSession.setActive(false, options: AVAudioSession.SetActiveOptions.notifyOthersOnDeactivation)
+ }
+ }
+ }
+
+ override init() {
+ super.init()
+ self.setupListener()
+ }
+ deinit {
+ NotificationCenter.default.removeObserver(self)
+ }
+ private func setupAudioSession() {
+ do {
+ try self.audioSession.setCategory(AVAudioSession.Category.playback, options: AVAudioSession.CategoryOptions.mixWithOthers)
+ try self.audioSession.setActive(false)
+ } catch let error {
+ debugPrint("\(type(of:self)):\(error)")
+ }
+ }
+ private func setupBackgroundAudioPlayer() {
+ do {
+ self.backgroundAudioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: Bundle.main.path(forResource: "silence", ofType: "mp3")!))
+ } catch let error {
+ debugPrint("\(type(of:self)):\(error)")
+ }
+ self.backgroundAudioPlayer?.numberOfLoops = -1
+ self.backgroundAudioPlayer?.volume = 1
+ self.backgroundAudioPlayer?.delegate = self
+ }
+
+//主要修改了这里
+ private func setupListener() {
+
+ NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
+
+ NotificationCenter.default.addObserver(self, selector: #selector(audioSessionInterruption(notification:)), name: AVAudioSession.interruptionNotification, object: nil)
+
+
+ }
+
+}
+// MARK: - 扩展 监听通知
+extension AudioManager {
+ /// 进入后台 播放无声音乐
+ @objc public func didEnterBackground() {
+ self.setupTimer()
+ guard self.openBackgroundAudioAutoplay else {return}
+
+ do {
+ try self.audioSession.setActive(true)
+ } catch let error {
+ debugPrint("\(type(of:self)):\(error))")
+ }
+ self.backgroundAudioPlayer?.prepareToPlay()
+ self.backgroundAudioPlayer?.play()
+ }
+
+ /// 进入前台,暂停播放音乐
+ @objc public func didBecomeActive() {
+ self.removeTimer()
+ self.hintBackgroundTimeLength()
+ self.backgroundTimeLength = 0
+ guard self.openBackgroundAudioAutoplay else {return}
+
+ self.backgroundAudioPlayer?.pause()
+ do {
+ try self.audioSession.setActive(false, options: AVAudioSession.SetActiveOptions.notifyOthersOnDeactivation)
+ } catch let error {
+ debugPrint("\(type(of:self)):\(error))")
+ }
+
+
+ }
+
+ /// 音乐中断处理
+ @objc fileprivate func audioSessionInterruption(notification: NSNotification) {
+ guard self.openBackgroundAudioAutoplay else {return}
+ guard let userinfo = notification.userInfo else {return}
+ guard let interruptionType: UInt = userinfo[AVAudioSessionInterruptionTypeKey] as! UInt? else {return}
+ if interruptionType == AVAudioSession.InterruptionType.began.rawValue {
+ // 中断开始,音乐被暂停
+ debugPrint("\(type(of:self)): 中断开始 userinfo:\(userinfo)")
+ } else if interruptionType == AVAudioSession.InterruptionType.ended.rawValue {
+ // 中断结束,恢复播放
+ debugPrint("\(type(of:self)): 中断结束 userinfo:\(userinfo)")
+ guard let player = self.backgroundAudioPlayer else {return}
+ if player.isPlaying == false {
+ debugPrint("\(type(of:self)): 音乐未播放,准备开始播放")
+ do {
+ try self.audioSession.setActive(true)
+ } catch let error {
+ debugPrint("\(type(of:self)):\(error)")
+ }
+ player.prepareToPlay()
+ player.play()
+ } else {
+ debugPrint("\(type(of:self)): 音乐正在播放")
+ }
+ }
+ }
+}
+// MARK: - 扩展 定时器任务
+extension AudioManager {
+ fileprivate func setupTimer() {
+ self.removeTimer()
+ self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerTask), userInfo: nil, repeats: true)
+ RunLoop.main.add(self.timer!, forMode: RunLoop.Mode.common)
+// RunLoop.main.add(self.timer!, forMode: RunLoop.Mode.init(rawValue: ""))
+ }
+ fileprivate func removeTimer() {
+ self.timer?.invalidate()
+ self.timer = nil;
+ }
+ @objc func timerTask() {
+ self.backgroundTimeLength += 1
+ }
+ fileprivate func hintBackgroundTimeLength() {
+ let message = "本次后台持续时间:\(self.backgroundTimeLength)s"
+ print(message)
+ }
+}
+// MARK: - 扩展 播放代理
+extension AudioManager: AVAudioPlayerDelegate {
+ func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
+
+ }
+ func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
+ debugPrint("\(type(of:self))" + error.debugDescription)
+ }
+}
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index ccbe8f4..4702458 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -2,6 +2,10 @@
+ BGTaskSchedulerPermittedIdentifiers
+
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+
CADisableMinimumFrameDurationOnPhone
CFBundleDevelopmentRegion
@@ -26,8 +30,18 @@
$(FLUTTER_BUILD_NUMBER)
LSRequiresIPhoneOS
+ NSAppTransportSecurity
+
+ NSAllowsArbitraryLoads
+
+
UIApplicationSupportsIndirectInputEvents
+ UIBackgroundModes
+
+ audio
+ processing
+
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
@@ -45,11 +59,6 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
- NSAppTransportSecurity
-
- NSAllowsArbitraryLoads
-
-
UIViewControllerBasedStatusBarAppearance
diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements
index 70e1d98..0e27801 100644
--- a/ios/Runner/Runner.entitlements
+++ b/ios/Runner/Runner.entitlements
@@ -10,7 +10,6 @@
allow-vpn
-
com.apple.security.application-groups
group.com.proxy.pin
diff --git a/ios/Runner/VpnManager.swift b/ios/Runner/VpnManager.swift
index de32809..c8ab2db 100755
--- a/ios/Runner/VpnManager.swift
+++ b/ios/Runner/VpnManager.swift
@@ -14,7 +14,7 @@ enum VPNStatus {
class VpnManager{
var activeVPN: NETunnelProviderManager?;
- public var proxtHost: String = "127.0.0.01"
+ public var proxyHost: String = "127.0.0.01"
public var proxyPort: Int = 8888
static let shared = VpnManager()
@@ -94,7 +94,7 @@ extension VpnManager{
}
var conf = [String:AnyObject]()
- conf["proxtHost"] = self.proxtHost as AnyObject
+ conf["proxyHost"] = self.proxyHost as AnyObject
conf["proxyPort"] = self.proxyPort as AnyObject
let orignConf = manager.protocolConfiguration as! NETunnelProviderProtocol
@@ -141,7 +141,7 @@ extension VpnManager{
extension VpnManager{
func connect(host: String?, port: Int?){
- self.proxtHost = host ?? self.proxtHost
+ self.proxyHost = host ?? self.proxyHost
self.proxyPort = port ?? self.proxyPort
self.loadAndCreatePrividerManager { (manager) in
diff --git a/ios/Runner/silence.mp3 b/ios/Runner/silence.mp3
new file mode 100644
index 0000000..cf2063b
Binary files /dev/null and b/ios/Runner/silence.mp3 differ
diff --git a/lib/network/channel.dart b/lib/network/channel.dart
index 6a16e95..8e9b2a9 100644
--- a/lib/network/channel.dart
+++ b/lib/network/channel.dart
@@ -124,7 +124,7 @@ class ChannelPipeline extends ChannelHandler {
}
if (data is HttpRequest) {
data.hostAndPort = channel.getAttribute(AttributeKeys.host) ?? getHostAndPort(data);
- if (data.headers.host() != null) {
+ if (data.headers.host() != null && data.headers.host()?.contains(":") == false) {
data.hostAndPort?.host = data.headers.host()!;
}
diff --git a/lib/ui/desktop/toolbar/launch/launch.dart b/lib/ui/desktop/toolbar/launch/launch.dart
index 80d2e0d..4694603 100644
--- a/lib/ui/desktop/toolbar/launch/launch.dart
+++ b/lib/ui/desktop/toolbar/launch/launch.dart
@@ -46,10 +46,9 @@ class _SocketLaunchState extends State with WindowListener, Widget
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
- //页面即将关闭
if (state == AppLifecycleState.detached) {
print('AppLifecycleState.detached');
- // widget.onStop?.call();
+ widget.onStop?.call();
widget.proxyServer.stop();
started = false;
}
diff --git a/lib/ui/panel.dart b/lib/ui/panel.dart
index 84953ef..df70722 100644
--- a/lib/ui/panel.dart
+++ b/lib/ui/panel.dart
@@ -107,11 +107,21 @@ class NetworkTabState extends State with SingleTickerProvi
}
Widget request() {
- return message(widget.request.get(), "Request");
+ return ListView(children: [
+ Padding(
+ padding: const EdgeInsetsDirectional.only(start: 20, top: 20),
+ child: rowWidget("Path", widget.request.get()?.path.toString())),
+ ...message(widget.request.get(), "Request")
+ ]);
}
Widget response() {
- return message(widget.response.get(), "Response");
+ return ListView(children: [
+ Padding(
+ padding: const EdgeInsetsDirectional.only(start: 20, top: 20),
+ child: rowWidget("StatusCode", widget.response.get()?.status.code.toString())),
+ ...message(widget.response.get(), "Response")
+ ]);
}
Widget cookies() {
@@ -126,7 +136,7 @@ class NetworkTabState extends State with SingleTickerProvi
]);
}
- Widget message(HttpMessage? message, String type) {
+ List message(HttpMessage? message, String type) {
var headers = [];
message?.headers.forEach((name, values) {
for (var v in values) {
@@ -140,15 +150,14 @@ class NetworkTabState extends State with SingleTickerProvi
Widget? bodyWidgets = message == null ? null : getBody(type, message);
- return ListView(children: [
- ExpansionTile(
- title: Text("$type Headers", style: const TextStyle(fontWeight: FontWeight.bold)),
- initiallyExpanded: true,
- shape: const Border(),
- childrenPadding: const EdgeInsets.only(left: 20, bottom: 20),
- children: headers),
- bodyWidgets ?? const SizedBox()
- ]);
+ Widget headerWidget = ExpansionTile(
+ title: Text("$type Headers", style: const TextStyle(fontWeight: FontWeight.bold)),
+ initiallyExpanded: true,
+ shape: const Border(),
+ childrenPadding: const EdgeInsets.only(left: 20, bottom: 20),
+ children: headers);
+
+ return [headerWidget, bodyWidgets ?? const SizedBox()];
}
Widget expansionTile(String title, List content) {