From 185a29e332a8748725fc234986e80b2c1de49501 Mon Sep 17 00:00:00 2001 From: AmisKwok Date: Mon, 14 Sep 2026 08:13:23 +0800 Subject: [PATCH] feat: add Swift Package Manager support for iOS and macOS iOS and macOS now ship a Package.swift alongside the CocoaPods podspec, so the plugin keeps working in projects that have migrated to SwiftPM. Native sources moved to Sources/zero_network_kit/ under each platform directory, and both podspecs point at the new paths. Also fixes the pub.dev description to list all six platforms, and replaces the leftover macOS podspec template metadata plus the iOS author with the real project values, enabling the privacy manifest resource bundle on both platforms. Bump version to 1.0.2 and update README, CHANGELOG and AGENTS accordingly. --- AGENTS.md | 2 +- CHANGELOG.md | 24 ++++++++++++++++ README.md | 4 +-- README_zh.md | 4 +-- docs/404.html | 2 +- docs/404/index.html | 2 +- docs/API-Reference/index.html | 4 +-- docs/Benchmark/index.html | 4 +-- docs/Connectivity/index.html | 4 +-- docs/DNS/index.html | 4 +-- docs/FAQ/index.html | 4 +-- docs/Full-Report/index.html | 4 +-- docs/Getting-Started/index.html | 4 +-- docs/Installation/index.html | 8 +++--- docs/Ping/index.html | 4 +-- docs/Platform-Support/index.html | 4 +-- docs/Ports/index.html | 4 +-- docs/Quality/index.html | 4 +-- docs/Speed-Test/index.html | 4 +-- docs/Usage/index.html | 4 +-- .../static/chunks/nextra-data-en-US.json | 2 +- ...4a.js => Installation-a8871a37d0592eaa.js} | 2 +- .../_buildManifest.js | 2 +- .../_ssgManifest.js | 0 docs/index.html | 4 +-- example/pubspec.lock | 2 +- ios/Assets/.gitkeep | 0 ios/zero_network_kit.podspec | 7 +++-- ios/zero_network_kit/Package.swift | 28 +++++++++++++++++++ .../zero_network_kit}/PrivacyInfo.xcprivacy | 0 .../ZeroNetworkKitPlugin.swift | 0 macos/zero_network_kit.podspec | 20 ++++++------- macos/zero_network_kit/Package.swift | 28 +++++++++++++++++++ .../zero_network_kit}/PrivacyInfo.xcprivacy | 0 .../ZeroNetworkKitPlugin.swift | 0 pubspec.yaml | 4 +-- 36 files changed, 137 insertions(+), 60 deletions(-) rename docs/_next/static/chunks/pages/{Installation-43ade467e1e7ca4a.js => Installation-a8871a37d0592eaa.js} (98%) rename docs/_next/static/{x-nTvOiGfo930-2DM2DH8 => j1hO4dyly2im2OvPwfgSC}/_buildManifest.js (91%) rename docs/_next/static/{x-nTvOiGfo930-2DM2DH8 => j1hO4dyly2im2OvPwfgSC}/_ssgManifest.js (100%) delete mode 100644 ios/Assets/.gitkeep create mode 100644 ios/zero_network_kit/Package.swift rename ios/{Resources => zero_network_kit/Sources/zero_network_kit}/PrivacyInfo.xcprivacy (100%) rename ios/{Classes => zero_network_kit/Sources/zero_network_kit}/ZeroNetworkKitPlugin.swift (100%) create mode 100644 macos/zero_network_kit/Package.swift rename macos/{Resources => zero_network_kit/Sources/zero_network_kit}/PrivacyInfo.xcprivacy (100%) rename macos/{Classes => zero_network_kit/Sources/zero_network_kit}/ZeroNetworkKitPlugin.swift (100%) diff --git a/AGENTS.md b/AGENTS.md index 1a8b4f7..af9480a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,7 +26,7 @@ This file defines the architecture, coding conventions, and required workflows f - DNS uses the system resolver and/or raw UDP queries against explicit servers, encoded/decoded by the pure-Dart `DnsPacket` wire-format codec. - Speed test streams download/upload traffic against configurable endpoints with `SpeedTestProgress` callbacks. - Quality scoring is a pure function (`NetworkQualityEvaluator`) over the available metrics; `QualityService` gathers them. -- Native: Android `android/src/main/kotlin/com/zerolabsco/zero_network_kit/ZeroNetworkKitPlugin.kt` (package `com.zerolabsco.zero_network_kit`); iOS `ios/Classes/ZeroNetworkKitPlugin.swift`. The native side only supplements what the system requires; keep changes minimal and matching the method channel contract. +- Native: Android `android/src/main/kotlin/com/zerolabsco/zero_network_kit/ZeroNetworkKitPlugin.kt` (package `com.zerolabsco.zero_network_kit`); iOS `ios/zero_network_kit/Sources/zero_network_kit/ZeroNetworkKitPlugin.swift`; macOS `macos/zero_network_kit/Sources/zero_network_kit/ZeroNetworkKitPlugin.swift`. iOS and macOS are dual-distributed: a CocoaPods podspec (`ios|macos/zero_network_kit.podspec`, sources under `zero_network_kit/Sources/…`) **and** a Swift Package (`ios|macos/zero_network_kit/Package.swift`). Do not move or rename `Sources/zero_network_kit/` without updating both, plus each podspec's `source_files` / `resource_bundles`. The native side only supplements what the system requires; keep changes minimal and matching the method channel contract. ## Dependencies and SDK constraints - Dart SDK: `>=3.11.0 <4.0.0`; Flutter: `>=3.3.0` (from `pubspec.yaml`). CI pins Flutter `3.41.7` so `dart format` output is identical everywhere. diff --git a/CHANGELOG.md b/CHANGELOG.md index c602e1a..52658dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## 1.0.2 + +### Added / 新增 + +- **Swift Package Manager support** — iOS and macOS now ship a `Package.swift` + next to the CocoaPods podspec, so the plugin keeps working in projects that + have migrated to SwiftPM. Native sources moved to + `/zero_network_kit/Sources/zero_network_kit/`. + - **支持 Swift Package Manager**——iOS 与 macOS 在 CocoaPods podspec 之外新增 + `Package.swift`,使插件在已迁移 SwiftPM 的工程中同样可用。原生源码移至 + `/zero_network_kit/Sources/zero_network_kit/`。 + +### Fixed / 修复 + +- **Package description** — the pub.dev description still advertised only five + platforms after Web support shipped; it now lists all six. + - **包描述**——Web 支持上线后,pub.dev 上的描述仍只宣传五个平台,现已列出全部六个。 +- **podspec metadata** — replaced the leftover macOS template values (placeholder + summary, `example.com` homepage, `Your Company` author) and the iOS author with + the real project metadata, and enabled the privacy manifest resource bundle on + both platforms. + - **podspec 元信息**——把 macOS 残留的模板值(占位 summary、`example.com` 主页、 + `Your Company` 作者)与 iOS 的作者替换为真实项目信息,并在两个平台启用隐私清单资源包。 + ## 1.0.1 ### Added / 新增 diff --git a/README.md b/README.md index 5c0ea78..40e59ad 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ micro-benchmarks — for Android, iOS, macOS, Windows, Linux and Web (partial). [![Dart](https://img.shields.io/badge/Dart-✓-0175C2?logo=dart)](https://dart.dev) [![Style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart) -> **🔔 Upgrade recommended:** `1.0.1` adds partial Web support — the plugin now compiles and runs in the browser, and the capabilities the sandbox forbids degrade gracefully instead of failing. It also fixes the speed test on the web. Upgrade to `^1.0.1`. +> **🔔 Upgrade recommended:** `1.0.2` adds Swift Package Manager support for iOS and macOS, so the plugin also works in projects that have migrated away from CocoaPods. Upgrade to `^1.0.2`. 🌐 **[Official Website](https://www.zerolabsco.com/)**  ·  📦 **[View on pub.dev](https://pub.dev/packages/zero_network_kit)**  ·  🔗 **[View on GitHub](https://github.com/zero-labsco/zero_network_kit)** @@ -74,7 +74,7 @@ Design goals: ```yaml dependencies: - zero_network_kit: ^1.0.1 + zero_network_kit: ^1.0.2 ``` ### Android permissions diff --git a/README_zh.md b/README_zh.md index 6ebb2a7..1ce3690 100644 --- a/README_zh.md +++ b/README_zh.md @@ -18,7 +18,7 @@ [![Dart](https://img.shields.io/badge/Dart-✓-0175C2?logo=dart)](https://dart.dev) [![Style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart) -> **🔔 推荐升级:** `1.0.1` 新增 Web 平台部分支持——插件现已可在浏览器中编译运行,浏览器沙箱禁止的能力会优雅降级而不会报错;同时修复了 Web 端测速失败的问题。建议升级到 `^1.0.1`。 +> **🔔 推荐升级:** `1.0.2` 为 iOS 与 macOS 新增 Swift Package Manager 支持,插件因此同样适用于已从 CocoaPods 迁移走的工程。建议升级到 `^1.0.2`。 🌐 **[官方网站](https://www.zerolabsco.com/)**  ·  📦 **[在 pub.dev 查看](https://pub.dev/packages/zero_network_kit)**  ·  🔗 **[查看 GitHub 仓库](https://github.com/zero-labsco/zero_network_kit)** @@ -72,7 +72,7 @@ ```yaml dependencies: - zero_network_kit: ^1.0.1 + zero_network_kit: ^1.0.2 ``` ### Android 权限 diff --git a/docs/404.html b/docs/404.html index 3b02e2b..69aaa86 100644 --- a/docs/404.html +++ b/docs/404.html @@ -1 +1 @@ -404: This page could not be found

404

This page could not be found.

\ No newline at end of file +404: This page could not be found

404

This page could not be found.

\ No newline at end of file diff --git a/docs/404/index.html b/docs/404/index.html index 3b02e2b..69aaa86 100644 --- a/docs/404/index.html +++ b/docs/404/index.html @@ -1 +1 @@ -404: This page could not be found

404

This page could not be found.

\ No newline at end of file +404: This page could not be found

404

This page could not be found.

\ No newline at end of file diff --git a/docs/API-Reference/index.html b/docs/API-Reference/index.html index 5885879..064c684 100644 --- a/docs/API-Reference/index.html +++ b/docs/API-Reference/index.html @@ -1,4 +1,4 @@ -
📚 API Reference

API Reference / 接口参考

+
📚 API Reference

API Reference / 接口参考

A condensed field reference for every public model. For copy-paste examples of each method, see the capability pages.

这里是每个公开模型的字段速查。每个方法的复制即用示例请见各能力页面。

@@ -95,4 +95,4 @@

API.md 与 API_zh.md。

-


Zero Network Kit · MPL-2.0
\ No newline at end of file +

Zero Network Kit · MPL-2.0
\ No newline at end of file diff --git a/docs/Benchmark/index.html b/docs/Benchmark/index.html index f05c80d..c9b22b0 100644 --- a/docs/Benchmark/index.html +++ b/docs/Benchmark/index.html @@ -1,4 +1,4 @@ -
📊 Benchmarks

Benchmarks / 微基准测试

+
📊 Benchmarks

Benchmarks / 微基准测试

NetworkBenchmark measures how fast the diagnostics API itself runs — useful when deciding whether a diagnostic belongs on your startup path.

NetworkBenchmark 衡量诊断 API 自身的开销——可用在判断某项诊断能否放在启动路径上。

@@ -33,4 +33,4 @@

minDuration, maxDuration, standardDeviation, operationsPerSecond, failures, timestamp.

BenchmarkSuiteResult: suiteName, results, totalDuration, timestamp, and -suite['name'].


Zero Network Kit · MPL-2.0
\ No newline at end of file +suite['name'].


Zero Network Kit · MPL-2.0
\ No newline at end of file diff --git a/docs/Connectivity/index.html b/docs/Connectivity/index.html index 95b88a3..384440d 100644 --- a/docs/Connectivity/index.html +++ b/docs/Connectivity/index.html @@ -1,4 +1,4 @@ -
📡 Connectivity

Connectivity / 连通性

+
📡 Connectivity

Connectivity / 连通性

NetworkDiagnostic.checkConnection() captures a single snapshot of the current network; onConnectivityChanged emits a fresh snapshot on every transport change.

@@ -145,4 +145,4 @@

}, ); } -}


Zero Network Kit · MPL-2.0
\ No newline at end of file +}

Zero Network Kit · MPL-2.0
\ No newline at end of file diff --git a/docs/DNS/index.html b/docs/DNS/index.html index 0d16ac3..2696b3f 100644 --- a/docs/DNS/index.html +++ b/docs/DNS/index.html @@ -1,4 +1,4 @@ -
🌍 DNS

DNS / DNS 解析

+
🌍 DNS

DNS / DNS 解析

NetworkDiagnostic.resolve() tests DNS resolution across multiple servers in parallel, so you can spot the fastest one. Each result is either the system resolver or a raw UDP query against an explicit server.

@@ -72,4 +72,4 @@

final fastest = results
     .where((r) => r.isSuccess)
     .reduce((a, b) => a.responseTimeMs <= b.responseTimeMs ? a : b);
-print('fastest resolver: ${fastest.server}');


Zero Network Kit · MPL-2.0
\ No newline at end of file +print('fastest resolver: ${fastest.server}');

Zero Network Kit · MPL-2.0
\ No newline at end of file diff --git a/docs/FAQ/index.html b/docs/FAQ/index.html index 823addf..9115991 100644 --- a/docs/FAQ/index.html +++ b/docs/FAQ/index.html @@ -1,4 +1,4 @@ -
❓ FAQ

FAQ / 常见问题

+

Zero Network Kit · MPL-2.0
\ No newline at end of file +

采用 MPL-2.0。详见仓库 LICENSE。


Zero Network Kit · MPL-2.0
\ No newline at end of file diff --git a/docs/Full-Report/index.html b/docs/Full-Report/index.html index 422501b..9a66f5e 100644 --- a/docs/Full-Report/index.html +++ b/docs/Full-Report/index.html @@ -1,4 +1,4 @@ -
📋 Full Report

Full Report / 汇总报告

+
📋 Full Report

Full Report / 汇总报告

NetworkDiagnostic.diagnose() runs every probe you ask for and aggregates them into one NetworkDiagnosticReport. A failing sub-test never aborts the run — its field simply stays empty/null.

@@ -80,4 +80,4 @@

includePorts 默认 false,默认运行不会扫描端口。

The report is JSON-encodable via report.toMap(). -报告可通过 report.toMap() 序列化为 JSON。


Zero Network Kit · MPL-2.0
\ No newline at end of file +报告可通过 report.toMap() 序列化为 JSON。


Zero Network Kit · MPL-2.0
\ No newline at end of file diff --git a/docs/Getting-Started/index.html b/docs/Getting-Started/index.html index 007dfb9..10bffd5 100644 --- a/docs/Getting-Started/index.html +++ b/docs/Getting-Started/index.html @@ -1,4 +1,4 @@ -
🚀 Getting Started

Getting Started / 快速开始

+
🚀 Getting Started

Getting Started / 快速开始

Quick Start / 快速开始

Import the package and start diagnosing — no init() required:

导入包即可开始诊断,无需 init():

@@ -83,4 +83,4 @@

Installation — Detailed installation methods / 详细安装方式
  • Usage — Global config & dependency injection / 全局配置与依赖注入
  • Platform Support — Capability matrix / 能力矩阵
  • -


    Zero Network Kit · MPL-2.0
    \ No newline at end of file +

    Zero Network Kit · MPL-2.0
    \ No newline at end of file diff --git a/docs/Installation/index.html b/docs/Installation/index.html index 08fb703..6602968 100644 --- a/docs/Installation/index.html +++ b/docs/Installation/index.html @@ -1,9 +1,9 @@ -
    📦 Installation

    Installation / 安装

    +

    Then run:

    然后运行:

    flutter pub get
    @@ -14,7 +14,7 @@

    zero_network_kit: git: url: https://github.com/zero-labsco/zero_network_kit.git - ref: release/v1.0.1

    + ref: release/v1.0.2

    Platform Setup / 平台配置

    Android

    The plugin manifest already declares INTERNET, ACCESS_NETWORK_STATE and @@ -62,4 +62,4 @@

  • Getting Started — Quick start guide / 快速开始
  • Usage — Full usage guide / 完整使用指南
  • -


    Zero Network Kit · MPL-2.0
    \ No newline at end of file +

    Zero Network Kit · MPL-2.0
    \ No newline at end of file diff --git a/docs/Ping/index.html b/docs/Ping/index.html index 924668c..47ecf03 100644 --- a/docs/Ping/index.html +++ b/docs/Ping/index.html @@ -1,4 +1,4 @@ -
    ⏱ Latency / Ping

    Latency / Ping / 延迟探测

    +
    ⏱ Latency / Ping

    Latency / Ping / 延迟探测

    NetworkDiagnostic.ping() measures round-trip time towards a target host and reports sent / received counts, packet loss, min/avg/max and jitter.

    NetworkDiagnostic.ping() 测量到目标主机的往返时间,并给出发送/接收次数、丢包率、 @@ -75,4 +75,4 @@

    mode: PingMode.icmp on desktop to get closer to real ICMP. TCP 探测需要一个在监听的端口。对不接受 TCP/443 的主机探测 port: 443 会失败—— 那是“被过滤”,不是“离线”。在桌面上用 PingMode.icmp 更接近真实 ICMP。

    -


    Zero Network Kit · MPL-2.0
    \ No newline at end of file +

    Zero Network Kit · MPL-2.0
    \ No newline at end of file diff --git a/docs/Platform-Support/index.html b/docs/Platform-Support/index.html index 352eda4..e5f8963 100644 --- a/docs/Platform-Support/index.html +++ b/docs/Platform-Support/index.html @@ -1,4 +1,4 @@ -
    🖥 Platform Support

    Platform Support / 平台支持

    +
    🖥 Platform Support

    Platform Support / 平台支持

    zero_network_kit is declared on six plugin platforms: Android, iOS, macOS, Windows, Linux and Web. Web support is partial: the services that depend on dart:io are swapped for browser-safe equivalents, and the @@ -276,4 +276,4 @@

    }

    details is always null on the web, and usually null on desktop too (see above).

    -

    Web 上 details 恒为 null,桌面端通常也为 null(见上文)。


    Zero Network Kit · MPL-2.0
    \ No newline at end of file +

    Web 上 details 恒为 null,桌面端通常也为 null(见上文)。


    Zero Network Kit · MPL-2.0
    \ No newline at end of file diff --git a/docs/Ports/index.html b/docs/Ports/index.html index dfc3dac..6f50efe 100644 --- a/docs/Ports/index.html +++ b/docs/Ports/index.html @@ -1,4 +1,4 @@ -
    🔌 Ports

    Ports / 端口检测

    +
    🔌 Ports

    Ports / 端口检测

    checkPort() returns a full PortCheckResult for one port; isPortOpen() is the plain boolean convenience; scanPorts() runs many ports concurrently with bounded in-flight connections.

    @@ -65,4 +65,4 @@

    checkPort() 返回 PortCheckResult(含 isOpen、responseTimeMs、 errorMessage);单端口布尔诉求请用 isPortOpen()。需要 RTT 或失败原因时可直接 调用 checkPort(),或给 scanPorts(..., ports: [443]) 取单条结果。

    -


    Zero Network Kit · MPL-2.0
    \ No newline at end of file +

    Zero Network Kit · MPL-2.0
    \ No newline at end of file diff --git a/docs/Quality/index.html b/docs/Quality/index.html index abbc709..06d267c 100644 --- a/docs/Quality/index.html +++ b/docs/Quality/index.html @@ -1,4 +1,4 @@ -
    ⭐ Quality Score

    Quality Score / 质量评分

    +
    ⭐ Quality Score

    Quality Score / 质量评分

    NetworkDiagnostic.evaluateQuality() returns a weighted 0–100 score plus a level and human-readable suggestions, computed over whatever metrics are available.

    @@ -128,4 +128,4 @@

    ); print('${score.score.toStringAsFixed(1)} → ${score.level.label}');

    Tune the ideal values globally through NetworkDiagnosticConfig(qualityTargets: ...). -调整理想值请通过 NetworkDiagnosticConfig(qualityTargets: ...) 全局设置。


    Zero Network Kit · MPL-2.0
    \ No newline at end of file +调整理想值请通过 NetworkDiagnosticConfig(qualityTargets: ...) 全局设置。


    Zero Network Kit · MPL-2.0
    \ No newline at end of file diff --git a/docs/Speed-Test/index.html b/docs/Speed-Test/index.html index 215d430..def1b34 100644 --- a/docs/Speed-Test/index.html +++ b/docs/Speed-Test/index.html @@ -1,4 +1,4 @@ -
    🚀 Speed Test

    Speed Test / 测速

    +
    🚀 Speed Test

    Speed Test / 测速

    NetworkDiagnostic.runSpeedTest() measures download and upload throughput and samples latency during the run, reporting progress through onProgress.

    NetworkDiagnostic.runSpeedTest() 测量下载与上传吞吐量,并在过程中采样延迟,通过 @@ -87,4 +87,4 @@

    downloadUrl / uploadUrl for production. 默认端点指向 Cloudflare 公共服务并会产生真实流量(默认下载约 25 MB)。在按量计费 网络上运行前请征得同意,并为生产环境替换 downloadUrl / uploadUrl。

    -


    Zero Network Kit · MPL-2.0
    \ No newline at end of file +

    Zero Network Kit · MPL-2.0
    \ No newline at end of file diff --git a/docs/Usage/index.html b/docs/Usage/index.html index fe50b5b..da42dba 100644 --- a/docs/Usage/index.html +++ b/docs/Usage/index.html @@ -1,4 +1,4 @@ -
    📖 Usage

    Usage / 用法

    +

    Zero Network Kit · MPL-2.0
    \ No newline at end of file +

    Zero Network Kit · MPL-2.0
    \ No newline at end of file diff --git a/docs/_next/static/chunks/nextra-data-en-US.json b/docs/_next/static/chunks/nextra-data-en-US.json index 63fc8a0..33a151a 100644 --- a/docs/_next/static/chunks/nextra-data-en-US.json +++ b/docs/_next/static/chunks/nextra-data-en-US.json @@ -1 +1 @@ -{"/API-Reference":{"title":"API Reference / 接口参考","data":{"":"A condensed field reference for every public model. For copy-paste examples of\neach method, see the capability pages.这里是每个公开模型的字段速查。每个方法的复制即用示例请见各能力页面。","networktype#NetworkType":"Member\tNotes\tvalues\tnone, wifi, mobile, ethernet, vpn, bluetooth, other\tlabel\tHuman readable: 'Wi-Fi', 'Mobile', …\tid\tStable English identifier (name), safe to persist\tisConnected\ttrue unless none\tNetworkType.fromRaw(Object?)\tParses a raw platform string","networkconnectioninfo#NetworkConnectionInfo":"isConnected, type, ssid, signalStrength (dBm), ipAddress,\nipv6Address, gateway, macAddress, isVpn, isReachable, timestamp,\nplus copyWith({bool? isReachable}).","pingresult#PingResult":"Fields host, port, mode, sent, received, times, timestamp;\nderived getters lost, packetLoss (%), minTime, maxTime, averageTime,\njitter, isSuccess.","dnstestresult#DnsTestResult":"Fields server, domain, isSuccess, responseTime, resolvedIps,\nerrorMessage, timestamp; derived responseTimeMs, primaryAddress.","portcheckresult#PortCheckResult":"Fields host, port, isOpen, responseTime, errorMessage, timestamp;\nderived responseTimeMs.","speedtestresult#SpeedTestResult":"Fields downloadSpeed, uploadSpeed (Mbps), ping (ms), jitter (ms),\npacketLoss (%), downloadedBytes, uploadedBytes, downloadDuration,\nuploadDuration, server, timestamp; derived duration; static helper\nSpeedTestResult.mbpsFromBytes(bytes, elapsed).","speedtestprogress--speedtestphase#SpeedTestProgress / SpeedTestPhase":"phase (download, upload, completed), bytes, elapsed, speedMbps.","networkqualityscore--networkqualitylevel#NetworkQualityScore / NetworkQualityLevel":"score (0–100), level, metrics (Map), suggestions,\ntimestamp; NetworkQualityLevel.fromScore(double), level.label.","networkdiagnosticreport#NetworkDiagnosticReport":"connection, ping, dnsResults, portResults, speedTest, quality,\ntimestamp.","benchmarkresult--benchmarksuiteresult#BenchmarkResult / BenchmarkSuiteResult":"BenchmarkResult: testName, iterations, totalDuration,\naverageDuration, minDuration, maxDuration, standardDeviation,\noperationsPerSecond, failures, timestamp.\nBenchmarkSuiteResult: suiteName, results, totalDuration, timestamp,\nand suite['name'].","gotchas--常见陷阱#Gotchas / 常见陷阱":"type.label, not displayName. NetworkType exposes label / id.\ncheckPort() returns PortCheckResult. Use isPortOpen() for a plain\nboolean, or scanPorts(..., ports: [p]).first for RTT / error message.\nincludeSystemResolver defaults to false. Pass true to add the\nsystem DNS row.\nA TCP ping needs a listening port. Probing host with port: 443 fails\nif that host does not accept TCP/443 — that is \"filtered\", not \"offline\". Use\nmode: PingMode.icmp on desktop to get closer to real ICMP.\nPingMode.icmp is desktop-only and silently degrades when the ping\nbinary is unavailable.\nThe speed test moves real traffic (≈25 MB download by default). Ask for\nconsent before running it on a metered connection.\nMissing permissions degrade, they never throw. A missing location\npermission yields ssid == null; the rest of the snapshot still arrives.\nincludePorts is false by default in diagnose().\ninit() is optional, but dispose() only closes the client the plugin\nowns — an httpClient you injected stays open, so close it yourself.\nFull source-level API docs live in the repo:\nAPI.md\n(English) and\nAPI_zh.md\n(简体中文).\n完整源码级 API 文档见仓库内的\nAPI.md\n与 API_zh.md。"}},"/Benchmark":{"title":"Benchmarks / 微基准测试","data":{"":"NetworkBenchmark measures how fast the diagnostics API itself runs — useful\nwhen deciding whether a diagnostic belongs on your startup path.NetworkBenchmark 衡量诊断 API 自身的开销——可用在判断某项诊断能否放在启动路径上。","all-at-once--一次性全跑#All at once / 一次性全跑":"final suite = await NetworkBenchmark.runAll(\n iterations: 20,\n warmupIterations: 3,\n host: '1.1.1.1',\n dnsDomain: 'example.com',\n port: 443,\n);\nfor (final result in suite.results) {\n print('${result.testName.padRight(12)} '\n 'avg ${(result.averageDuration.inMicroseconds / 1000).toStringAsFixed(2)} ms '\n '± ${(result.standardDeviation.inMicroseconds / 1000).toStringAsFixed(2)} ms '\n '${result.operationsPerSecond.toStringAsFixed(1)} ops/s '\n 'failures=${result.failures}');\n}\n// Look up one benchmark by name / 按名称查单个结果\nprint(suite['ping']?.averageDuration);\nprint(suite.totalDuration);","individual-suites--单个基准#Individual suites / 单个基准":"await NetworkBenchmark.benchmarkConnection(iterations: 20);\nawait NetworkBenchmark.benchmarkPing(iterations: 20, host: '1.1.1.1');\nawait NetworkBenchmark.benchmarkDns(iterations: 20, domain: 'example.com');\nawait NetworkBenchmark.benchmarkPortCheck(iterations: 20, port: 443);\nawait NetworkBenchmark.benchmarkPlatformChannel(iterations: 20);","result-fields--结果字段#Result fields / 结果字段":"BenchmarkResult: testName, iterations, totalDuration, averageDuration,\nminDuration, maxDuration, standardDeviation, operationsPerSecond,\nfailures, timestamp.BenchmarkSuiteResult: suiteName, results, totalDuration, timestamp, and\nsuite['name']."}},"/Connectivity":{"title":"Connectivity / 连通性","data":{"":"NetworkDiagnostic.checkConnection() captures a single snapshot of the current\nnetwork; onConnectivityChanged emits a fresh snapshot on every transport\nchange.NetworkDiagnostic.checkConnection() 捕获当前网络的一次快照;onConnectivityChanged\n在每次传输方式变化时推送新快照。","snapshot--快照#Snapshot / 快照":"final connection = await NetworkDiagnostic.checkConnection();\nprint('type : ${connection.type.label}'); // Wi-Fi / Mobile / Ethernet …\nprint('connected : ${connection.isConnected}');\nprint('ipv4 : ${connection.ipAddress}');\nprint('ipv6 : ${connection.ipv6Address}');\nprint('gateway : ${connection.gateway}');\nprint('ssid : ${connection.ssid}');\nprint('rssi : ${connection.signalStrength} dBm');\nprint('mac : ${connection.macAddress}');\nprint('vpn : ${connection.isVpn}');\nprint('timestamp : ${connection.timestamp}');\nField\tNotes\ttype\tNetworkType (none, wifi, mobile, ethernet, vpn, bluetooth, other)\tisConnected\ttrue unless type is none\tipAddress / ipv6Address\tLocal IPv4 / IPv6 (Dart NetworkInterface)\tgateway / macAddress\tNative; null on desktop except Windows\tssid / signalStrength\tNative; always null on desktop\tisVpn\tNative VPN detection\tisReachable\tOnly filled when probeReachability: true\ttimestamp\tWhen the snapshot was taken\t\nUse connection.type.label / connection.type.id. There is no\ndisplayName.\n请使用 connection.type.label / connection.type.id,没有 displayName。","parameters--参数#Parameters / 参数":"Parameter\tDefault\tMeaning\tincludeNativeDetails\ttrue\tAlso read SSID / gateway / MAC / VPN from the native side\tprobeReachability\tfalse\tMake a real request and fill isReachable\tprobeTimeout\t3s\tTimeout of that reachability probe\t\n// Skip the native channel (cheapest call — good on hot paths).\nfinal quick = await NetworkDiagnostic.checkConnection(includeNativeDetails: false);\n// Prove the internet is actually reachable, not just that a NIC is up.\nfinal verified = await NetworkDiagnostic.checkConnection(\n probeReachability: true,\n probeTimeout: const Duration(seconds: 5),\n);\nif (verified.isReachable == false) {\n print('Interface is up but the internet is unreachable.');\n}","change-stream--变化监听#Change stream / 变化监听":"final subscription = NetworkDiagnostic.onConnectivityChanged.listen(\n (info) => print('now on ${info.type.id} · ${info.ipAddress}'),\n);\n// Later:\nawait subscription.cancel();\nIn a widget:\nStreamBuilder(\n stream: NetworkDiagnostic.onConnectivityChanged,\n builder: (context, snapshot) {\n final info = snapshot.data;\n if (info == null) return const Text('Checking…');\n return Text('${info.type.label} · ${info.isConnected}');\n },\n)","connectivity-only-minimal-example--仅连通性最小示例#Connectivity-only: minimal example / 仅连通性:最小示例":"If all you need is \"am I online, on what transport, what IP, and react to\nchanges\", you only need this page — no init() required.如果只需要「在不在線、走什么網络、IP 是多少、切换时通知」,只看本页即可——\n无需 init()。\nclass ConnectivityScreen extends StatelessWidget {\n const ConnectivityScreen({super.key});\n @override\n Widget build(BuildContext context) {\n return StreamBuilder(\n stream: NetworkDiagnostic.onConnectivityChanged,\n builder: (context, snap) {\n final c = snap.data;\n if (c == null) {\n return const Center(child: CircularProgressIndicator());\n }\n return ListView(\n children: [\n ListTile(title: const Text('Type / 类型'), trailing: Text(c.type.label)),\n ListTile(title: const Text('Online / 在线'), trailing: Text('${c.isConnected}')),\n ListTile(title: const Text('IPv4'), trailing: Text(c.ipAddress ?? '—')),\n ListTile(title: const Text('IPv6'), trailing: Text(c.ipv6Address ?? '—')),\n ],\n );\n },\n );\n }\n}"}},"/DNS":{"title":"DNS / DNS 解析","data":{"":"NetworkDiagnostic.resolve() tests DNS resolution across multiple servers in\nparallel, so you can spot the fastest one. Each result is either the system\nresolver or a raw UDP query against an explicit server.NetworkDiagnostic.resolve() 并行测试多台 DNS 服务器的解析表现,从而挑出最快的一台。\n每条结果要么来自系统解析器,要么是对指定服务器的原始 UDP 查询。","basic--基础#Basic / 基础":"final results = await NetworkDiagnostic.resolve(\n domain: 'example.com',\n dnsServers: const ['1.1.1.1', '8.8.8.8', '114.114.114.114'],\n timeout: const Duration(seconds: 5),\n concurrent: true,\n includeSystemResolver: true,\n);\nfor (final r in results) {\n if (r.isSuccess) {\n print('${r.server.padRight(16)} → ${r.resolvedIps.join(\", \")} '\n '(${r.responseTimeMs.toStringAsFixed(1)} ms)');\n } else {\n print('${r.server.padRight(16)} ✗ ${r.errorMessage}');\n }\n}\nParameter\tDefault\tMeaning\tdomain\tconfig.dnsDomain\tDomain to resolve\tdnsServers\tconfig.dnsServers\tServers queried in parallel (or in series)\ttimeout\tconfig.dnsTimeout\tTimeout of one query\tconcurrent\ttrue\tQuery all servers at once\tincludeSystemResolver\tfalse\tAlso add a system row\t\nNotes / 说明:\nA row with server == 'system' comes from the OS resolver; the others are raw\nUDP queries against the listed IPs, encoded by the built-in DnsPacket wire\ncodec.\nserver == 'system' 的行来自系统解析器,其余是对指定 IP 的原始 UDP 查询,由内置\nDnsPacket 编解码。\nincludeSystemResolver defaults to false at the facade level — pass\ntrue to include it.\n门面层默认不包含系统解析器,需显式传 true。","pick-the-fastest--挑出最快的#Pick the fastest / 挑出最快的":"final fastest = results\n .where((r) => r.isSuccess)\n .reduce((a, b) => a.responseTimeMs <= b.responseTimeMs ? a : b);\nprint('fastest resolver: ${fastest.server}');"}},"/FAQ":{"title":"FAQ / 常见问题","data":{"do-i-need-to-call-init--需要调用-init-吗#Do I need to call init()? / 需要调用 init() 吗?":"No. Every API falls back to built-in defaults. Call ZeroNetworkKit.init() only\nwhen you want to override defaults (e.g. a custom reachability host or DNS\nservers). Connectivity alone works with zero setup.不需要。每个 API 都走内置默认值。只有在想覆盖默认参数(如自定义可达性主机或 DNS\n服务器)时才调用 ZeroNetworkKit.init()。单用连通性完全零配置。","which-platforms-are-supported--支持哪些平台#Which platforms are supported? / 支持哪些平台?":"Android, iOS, macOS, Windows, Linux and Web (partial — see\nPlatform Support).支持 Android、iOS、macOS、Windows、Linux 与 Web(部分支持)。详见\nPlatform Support。","why-is-ssid--signalstrength-null-on-desktop--为什么桌面上的-ssid信号强度是-null#Why is ssid / signalStrength null on desktop? / 为什么桌面上的 ssid/信号强度是 null?":"Desktop uses tier A: SSID and RSSI require system APIs that aren't exposed on\nmacOS/Linux/Windows the same way. They are always null on desktop; Windows\nadditionally provides gateway / MAC / DNS / VPN via GetAdaptersAddresses.桌面采用 A 档:SSID 与 RSSI 依赖系统 API,在桌面上不以同样方式暴露,因此恒为 null;\nWindows 额外通过 GetAdaptersAddresses 提供网关 / MAC / DNS / VPN。","why-does-getnativenetworkdetails-return-null--为什么-getnativenetworkdetails-返回-null#Why does getNativeNetworkDetails() return null? / 为什么 getNativeNetworkDetails 返回 null?":"Either the platform has no such data (e.g. desktop tier A), or the required\npermission is missing (e.g. location for Wi-Fi SSID on Android/iOS). The call\nnever throws — it degrades gracefully.要么是平台没有该数据(如桌面 A 档),要么是缺权限(如 Android/iOS 上读取 Wi-Fi SSID\n需定位权限)。该调用绝不抛异常,会优雅降级。","does-the-speed-test-use-my-bandwidth--测速会消耗我的流量吗#Does the speed test use my bandwidth? / 测速会消耗我的流量吗?":"Yes — the default endpoints are Cloudflare's public service and download ≈25 MB\nby default. Ask for consent on metered connections and override downloadUrl /\nuploadUrl for production.会——默认端点指向 Cloudflare 公共服务,默认下载约 25 MB。在按量计费网络上请征得同意,\n生产环境请替换 downloadUrl / uploadUrl。","how-do-i-test-my-integration-without-the-network--如何脱离网络测试我的集成#How do I test my integration without the network? / 如何脱离网络测试我的集成?":"Every service is injectable. Implement ConnectivityAdapter (or pass fake\nservice instances) and call NetworkDiagnostic.configure(...), then\nNetworkDiagnostic.reset() to restore defaults. See\nUsage → Dependency injection.每个服务都可注入。实现 ConnectivityAdapter(或直接传入假服务实例)后调用\nNetworkDiagnostic.configure(...),再用 NetworkDiagnostic.reset() 恢复默认。详见\n用法 → 依赖注入。","how-is-this-licensed--采用什么许可证#How is this licensed? / 采用什么许可证?":"MPL-2.0. See the repository LICENSE.采用 MPL-2.0。详见仓库 LICENSE。"}},"/Full-Report":{"title":"Full Report / 汇总报告","data":{"":"NetworkDiagnostic.diagnose() runs every probe you ask for and aggregates them\ninto one NetworkDiagnosticReport. A failing sub-test never aborts the run — its\nfield simply stays empty/null.NetworkDiagnostic.diagnose() 运行你指定的各项探测,并汇总为一份\nNetworkDiagnosticReport。任何子项失败都不会中断整体流程,对应字段保持空。","basic--基础#Basic / 基础":"final report = await NetworkDiagnostic.diagnose(\n includePing: true,\n includeDns: true,\n includePorts: true,\n includeSpeedTest: true,\n includeUpload: true,\n host: '1.1.1.1',\n dnsDomain: 'example.com',\n dnsServers: const ['1.1.1.1', '8.8.8.8'],\n ports: const [80, 443],\n);\nprint(report); // one-line summary\nprint(report.connection.type.label);\nprint(report.ping?.averageTime);\nprint(report.dnsResults.length);\nprint(report.portResults.where((p) => p.isOpen).length);\nprint(report.speedTest?.downloadSpeed);\nprint(report.quality.level.label);\nParameter\tDefault\tMeaning\tincludePing\ttrue\tSample latency / jitter / loss\tincludeDns\ttrue\tSample DNS latency\tincludePorts\tfalse\tScan ports (off by default)\tincludeSpeedTest\ttrue\tSample download / upload\tincludeUpload\ttrue\tInclude the upload metric\thost\tconfig.pingHost\tLatency / port target\tdnsDomain / dnsServers\tfrom config\tDNS target\tports\tconfig.probePorts\tPorts scanned when includePorts is true\t\nincludePorts defaults to false, so a default run does not scan ports.\nincludePorts 默认 false,默认运行不会扫描端口。\nThe report is JSON-encodable via report.toMap().\n报告可通过 report.toMap() 序列化为 JSON。"}},"/Getting-Started":{"title":"Getting Started / 快速开始","data":{"quick-start--快速开始#Quick Start / 快速开始":"Import the package and start diagnosing — no init() required:导入包即可开始诊断,无需 init():\nimport 'package:flutter/material.dart';\nimport 'package:zero_network_kit/zero_network_kit.dart';\nvoid main() {\n // Optional: apply your own global defaults once.\n // 可选:全局定制一次默认参数。\n ZeroNetworkKit.init(\n config: const NetworkDiagnosticConfig(pingHost: '1.1.1.1'),\n );\n runApp(const MyApp());\n}\nIf you only need connectivity, you don't even need init():如果只需要连通性,连 init() 都不需要:\nfinal connection = await NetworkDiagnostic.checkConnection();\nprint('${connection.type.label} · ${connection.ipAddress}');","what-you-get--你能得到什么#What you get / 你能得到什么":"Every method is a static, non-blocking call on the NetworkDiagnostic facade.\nThe plugin is pure Dart under the hood, so it is fully injectable and testable\nwithout touching the network.每个方法都是 NetworkDiagnostic 门面上的静态、非阻塞调用。插件内核是纯 Dart,\n因此完全可注入、可脱离网络做测试。\nNeed\tCall\tAm I online, on what transport?\tNetworkDiagnostic.checkConnection()\tReact to Wi-Fi ⇄ cellular switches\tNetworkDiagnostic.onConnectivityChanged\tLatency, jitter, packet loss\tNetworkDiagnostic.ping()\tIs DNS slow or broken?\tNetworkDiagnostic.resolve()\tIs host:port reachable?\tNetworkDiagnostic.checkPort() / isPortOpen()\tHow fast is down/up?\tNetworkDiagnostic.runSpeedTest()\tOne number for \"good or bad\"\tNetworkDiagnostic.evaluateQuality()\tEverything at once\tNetworkDiagnostic.diagnose()\tNative version / SSID / gateway / MAC\tZeroNetworkKit.getNativeNetworkDetails()\t\nSee Which API do I need? for the full map.","lifecycle--生命周期#Lifecycle / 生命周期":"ZeroNetworkKit.isInitialized; // false until init() runs\nZeroNetworkKit.config; // the effective NetworkDiagnosticConfig\nawait ZeroNetworkKit.dispose(); // releases the HTTP client the plugin owns\ndispose() only closes the HTTP client the plugin created itself. An\nhttp.Client you injected stays open — close it yourself.dispose() 只会关闭插件自己创建的 HTTP 客户端;你注入的 http.Client 不会被关闭。","next-steps--下一步#Next Steps / 下一步":"Installation — Detailed installation methods / 详细安装方式\nUsage — Global config & dependency injection / 全局配置与依赖注入\nPlatform Support — Capability matrix / 能力矩阵"}},"/Installation":{"title":"Installation / 安装","data":{"from-pubdev-recommended--从-pubdev-安装推荐#From pub.dev (Recommended) / 从 pub.dev 安装(推荐)":"Add the following to your pubspec.yaml:在 pubspec.yaml 中添加以下依赖:\ndependencies:\n zero_network_kit: ^1.0.1\nThen run:然后运行:\nflutter pub get","from-github--从-github-安装#From GitHub / 从 GitHub 安装":"Alternatively, install from GitHub:或者从 GitHub 安装:\ndependencies:\n zero_network_kit:\n git:\n url: https://github.com/zero-labsco/zero_network_kit.git\n ref: release/v1.0.1","platform-setup--平台配置#Platform Setup / 平台配置":"","android#Android":"The plugin manifest already declares INTERNET, ACCESS_NETWORK_STATE and\nACCESS_WIFI_STATE. Reading the Wi-Fi SSID additionally requires the\nlocation permission (ACCESS_FINE_LOCATION) on Android 8.1+, otherwise the\nsnapshot reports ssid: null.插件清单已声明 INTERNET、ACCESS_NETWORK_STATE 与 ACCESS_WIFI_STATE。读取 Wi-Fi\nSSID 还需在 Android 8.1+ 申请定位权限(ACCESS_FINE_LOCATION),否则快照中\nssid 为 null。","ios#iOS":"No additional configuration needed. Reading the Wi-Fi SSID additionally\nrequires the Access WiFi Information capability plus location authorisation.无需额外配置。读取 Wi-Fi SSID 还需开启 Access WiFi Information 能力并授权定位。","macos--windows--linux#macOS / Windows / Linux":"The plugin is declared on all three desktop platforms. On desktop, native\ndetails such as SSID / RSSI are always null (tier A). Windows additionally\nprovides gateway / MAC / DNS / VPN through GetAdaptersAddresses; macOS and\nLinux report only the Dart-side IP/IPv6. See\nPlatform Support for the full matrix.插件已在三个桌面平台声明。桌面上 SSID / 信号强度等原生详情恒为 null(A 档)。\nWindows 额外通过 GetAdaptersAddresses 提供网关 / MAC / DNS / VPN;macOS 与 Linux\n仅提供 Dart 侧的 IP/IPv6。完整矩阵见平台支持。","import--导入#Import / 导入":"import 'package:zero_network_kit/zero_network_kit.dart';","requirements--环境要求#Requirements / 环境要求":"Requirement\tVersion\tFlutter\t>= 3.3.0\tDart SDK\t>= 3.11.0 < 4.0.0","next-steps--下一步#Next Steps / 下一步":"Getting Started — Quick start guide / 快速开始\nUsage — Full usage guide / 完整使用指南"}},"/Ping":{"title":"Latency / Ping / 延迟探测","data":{"":"NetworkDiagnostic.ping() measures round-trip time towards a target host and\nreports sent / received counts, packet loss, min/avg/max and jitter.NetworkDiagnostic.ping() 测量到目标主机的往返时间,并给出发送/接收次数、丢包率、\n最小/平均/最大耗时与抖动。","basic--基础#Basic / 基础":"final ping = await NetworkDiagnostic.ping(\n host: '1.1.1.1',\n count: 5,\n timeout: const Duration(seconds: 2),\n interval: const Duration(milliseconds: 200),\n port: 443,\n);\nprint('received : ${ping.received}/${ping.sent}');\nprint('loss : ${ping.packetLoss.toStringAsFixed(1)} %');\nprint('min/avg/max: ${ping.minTime.toStringAsFixed(1)} / '\n '${ping.averageTime.toStringAsFixed(1)} / '\n '${ping.maxTime.toStringAsFixed(1)} ms');\nprint('jitter : ${ping.jitter.toStringAsFixed(2)} ms');\nprint('samples : ${ping.times}');\nParameter\tDefault\tMeaning\thost\tconfig.pingHost\tTarget host\tcount\tconfig.pingCount\tNumber of probes\ttimeout\tconfig.pingTimeout\tPer-probe timeout\tinterval\tconfig.pingInterval\tDelay between probes\tport\tconfig.pingPort\tTCP port probed in PingMode.tcp\tmode\tPingMode.tcp\tPingMode.tcp or PingMode.icmp","tcp-vs-icmp--tcp-与-icmp#TCP vs ICMP / TCP 与 ICMP":"PingMode.tcp performs a TCP handshake to host:port — the portable equivalent\nof ICMP and the only mode available on Android/iOS.PingMode.tcp 通过 TCP 握手到 host:port 测量往返,是移动端唯一可用的模式。\n// Desktop only — falls back gracefully if the `ping` binary is unavailable.\nawait NetworkDiagnostic.ping(host: '1.1.1.1', count: 4, mode: PingMode.icmp);\nping.isSuccess is true when at least one probe answered; check it before\ntrusting the averages.至少一次成功响应时 isSuccess 为 true,读平均值前建议先判断它。\nA TCP ping needs a listening port. Probing host with port: 443 fails if\nthat host does not accept TCP/443 — that is \"filtered\", not \"offline\". Use\nmode: PingMode.icmp on desktop to get closer to real ICMP.\nTCP 探测需要一个在监听的端口。对不接受 TCP/443 的主机探测 port: 443 会失败——\n那是“被过滤”,不是“离线”。在桌面上用 PingMode.icmp 更接近真实 ICMP。"}},"/Platform-Support":{"title":"Platform Support / 平台支持","data":{"":"zero_network_kit is declared on six plugin platforms: Android, iOS,\nmacOS, Windows, Linux and Web. Web support is partial: the services that\ndepend on dart:io are swapped for browser-safe equivalents, and the\ncapabilities the sandbox forbids degrade gracefully instead of failing.zero_network_kit 在 六 个插件平台上声明:Android、iOS、macOS、Windows、Linux\n与 Web。Web 为部分支持:依赖 dart:io 的服务已替换为浏览器安全的等价实现,\n浏览器沙箱禁止的能力会优雅降级而不会失败。","capability-matrix--能力矩阵#Capability matrix / 能力矩阵":"Capability\tAndroid / iOS\tDesktop (macOS / Windows / Linux)\tWeb\tConnectivity (connectivity_plus)\t✅\t✅\t✅\tLocal IP / IPv6 (NetworkInterface)\t✅\t✅\t❌\tNative details (SSID / gateway / MAC / VPN)\t✅\t⚠️ see below\t❌\tTCP ping\t✅\t✅\t⚠️ HTTPS round trip\tICMP ping (Process.run('ping'))\t❌\t✅\t❌\tHTTP ping\t✅\t✅\t✅\tDNS system resolver\t✅\t✅\t✅ DoH\tDNS raw UDP\t✅\t✅\t⚠️ DoH endpoint required\tPort check / scan\t✅\t✅\t❌\tSpeed test\t✅\t✅\t✅\tQuality score\t✅\t✅\t✅\tBenchmarks\t✅\t✅\t✅","networkcapabilities--能力查询#NetworkCapabilities / 能力查询":"Query the host platform before calling, so your UI can hide unsupported cards:在调用前查询当前平台能力,UI 即可据此隐藏不支持的卡片:\nfinal caps = NetworkDiagnostic.capabilities;\nprint(caps.platform); // 'android' | 'ios' | 'macos' | 'windows' | 'linux' | 'web'\nprint(caps.supports(NetworkCapability.nativeDetails)); // mobile: true, desktop: false\nprint(caps.supports(NetworkCapability.icmpPing)); // desktop: true, mobile: false\nRules baked into NetworkCapabilities.current():\nnativeDetails is mobile-only (SSID / gateway / MAC / VPN need system APIs).\nicmpPing is desktop-only (uses the system ping binary).\nOn web the supported set narrows to connectivity, tcpPing,\ndnsSystem, speedTest, quality and benchmark; every other capability is\nabsent.\nNetworkCapabilities.current() 内置规则:\nnativeDetails 仅移动端(SSID / 网关 / MAC / VPN 需要系统 API)。\nicmpPing 仅桌面(使用系统 ping 命令)。\nWeb 上的支持集合收缩为 connectivity、tcpPing、dnsSystem、speedTest、\nquality 与 benchmark,其余能力均不存在。","native-details-on-desktop--桌面原生详情#Native details on desktop / 桌面原生详情":"Desktop uses tier A by default: the native layer only reports the platform\nversion and an (often empty) details map. IP/IPv6 come from Dart\nNetworkInterface. As a result:桌面默认采用 A 档:原生层只报告平台版本与(通常为空的)详情 map,IP/IPv6 由 Dart\nNetworkInterface 兜底。因此:\nSSID / signal strength are always null on desktop.\n桌面上的 SSID / 信号强度恒为 null。\ngateway / macAddress / isVpn are null on macOS and Linux.\nmacOS 与 Linux 上 gateway / macAddress / isVpn 为 null。\nWindows additionally implements GetAdaptersAddresses, so it reports\ngateway / macAddress / DNS / isVpn (SSID still null). This is a bonus\ntier-B fragment kept as-is.\nWindows 额外实现了 GetAdaptersAddresses,因此上报 gateway / macAddress /\nDNS / isVpn(SSID 仍为 null)。这是保留的 B 档赠品。\nWhatever the platform, a missing permission or an unreachable native call never\nthrows — the field simply stays null and the rest of the result still arrives.无论在哪个平台,缺权限或原生调用不可达都不会抛异常——对应字段保持 null,其余\n结果照常返回。","web-support--web-支持#Web support / Web 支持":"The web build exposes the same static API. Capabilities that the browser sandbox\nforbids degrade gracefully — they return null or an \"unavailable\" result\ninstead of throwing:\nCapability\tWeb\tNotes\tConnectivity\t✅\tvia connectivity_plus\tPing (PingMode.tcp)\t⚠️\tmeasured as an HTTPS round trip; the target must send CORS headers\tPing (PingMode.icmp)\t❌\tthrows UnsupportedError\tDNS (system resolver)\t✅\tvia DNS-over-HTTPS\tDNS (explicit server)\t⚠️\tneeds a DoH endpoint, otherwise \"unsupported\"\tSpeed test\t✅\tHTTP download / upload\tQuality score\t✅\tpure function\tBenchmarks\t✅\tpure function\tPort check / scan\t❌\treturns \"unavailable\" results\tNative details (SSID, gateway, MAC, VPN)\t❌\tnull\t\nBrowsers expose no VPN API, so isVpn stays false on the web even when a\nsystem VPN or a local HTTP proxy is active.Web 构建提供同样的静态 API;浏览器沙箱禁止的能力会优雅降级(返回 null 或\"不可用\"\n结果,而不是抛异常):\n能力\tWeb\t说明\t连通性检测\t✅\t通过 connectivity_plus\tPing(PingMode.tcp)\t⚠️\t以 HTTPS 往返耗时度量,目标主机需下发 CORS 头\tPing(PingMode.icmp)\t❌\t抛出 UnsupportedError\tDNS(系统解析器)\t✅\t通过 DNS-over-HTTPS\tDNS(指定服务器)\t⚠️\t需要 DoH 端点,否则返回\"不支持\"\t带宽测速\t✅\tHTTP 下载 / 上传\t质量评分\t✅\t纯函数\t基准测试\t✅\t纯函数\t端口检测 / 扫描\t❌\t返回\"不可用\"结果\t原生详情(SSID、网关、MAC、VPN)\t❌\t返回 null\t\n浏览器不暴露 VPN 接口,因此即使系统开启了 VPN 或本地 HTTP 代理,Web 上的\nisVpn 仍为 false。","reading-native-data--读取原生数据#Reading native data / 读取原生数据":"final version = await ZeroNetworkKit.getPlatformVersion();\nprint(version); // e.g. 'Android 14' / 'iOS 18.0' / 'Web'\nfinal details = await ZeroNetworkKit.getNativeNetworkDetails();\nif (details != null) {\n print(details); // SSID, BSSID, gateway, MAC, VPN flag, RSSI …\n}\ndetails is always null on the web, and usually null on desktop too (see\nabove).Web 上 details 恒为 null,桌面端通常也为 null(见上文)。"}},"/Ports":{"title":"Ports / 端口检测","data":{"":"checkPort() returns a full PortCheckResult for one port; isPortOpen() is the\nplain boolean convenience; scanPorts() runs many ports concurrently with\nbounded in-flight connections.checkPort() 返回单个端口的完整 PortCheckResult;isPortOpen() 是便捷的布尔版;\nscanPorts() 以有界并发批量扫描多个端口。","single-port--单端口#Single port / 单端口":"// Boolean convenience / 便捷布尔版\nfinal open = await NetworkDiagnostic.isPortOpen(\n host: 'example.com',\n port: 443,\n timeout: const Duration(seconds: 3),\n);\nprint(open ? 'HTTPS reachable' : 'HTTPS unreachable');\n// Full result with RTT and error message / 含 RTT 与错误信息的完整结果\nfinal result = await NetworkDiagnostic.checkPort(host: 'example.com', port: 443);\nprint('open=${result.isOpen} rtt=${result.responseTimeMs} ms '\n 'err=${result.errorMessage}');","bulk-scan--批量扫描#Bulk scan / 批量扫描":"final scan = await NetworkDiagnostic.scanPorts(\n host: 'example.com',\n ports: const [22, 80, 443, 8080, 8443],\n concurrency: 8,\n);\nfor (final r in scan) {\n print('${r.host}:${r.port} '\n '${r.isOpen ? \"open\" : \"closed\"} '\n '${r.responseTimeMs.toStringAsFixed(1)} ms '\n '${r.errorMessage ?? \"\"}');\n}\nParameter\tDefault\tMeaning\tports (scanPorts)\tconfig.probePorts ([80, 443])\tPorts to probe\tconcurrency\t12\tMax in-flight connections\ttimeout\tconfig.portCheckTimeout\tTimeout per port\t\ncheckPort() returns a PortCheckResult (with isOpen, responseTimeMs\nand errorMessage); use isPortOpen() for a plain boolean. When you need the\nround-trip time or the failure reason for a single port, call checkPort()\ndirectly or pass a one-element list to scanPorts():\nscanPorts(host: 'example.com', ports: [443]).first.\ncheckPort() 返回 PortCheckResult(含 isOpen、responseTimeMs、\nerrorMessage);单端口布尔诉求请用 isPortOpen()。需要 RTT 或失败原因时可直接\n调用 checkPort(),或给 scanPorts(..., ports: [443]) 取单条结果。"}},"/Quality":{"title":"Quality Score / 质量评分","data":{"":"NetworkDiagnostic.evaluateQuality() returns a weighted 0–100 score plus a\nlevel and human-readable suggestions, computed over whatever metrics are\navailable.NetworkDiagnostic.evaluateQuality() 返回一个加权 0–100 分,附带等级与可读建议,\n它基于当前可用的各项指标计算得出。","basic--基础#Basic / 基础":"final quality = await NetworkDiagnostic.evaluateQuality(\n includePing: true,\n includeDns: true,\n includeSpeedTest: true,\n includeUpload: true,\n);\nprint('${quality.score.toStringAsFixed(1)}/100 — ${quality.level.label}');\nquality.metrics.forEach((metric, value) {\n print(' $metric = ${value.toStringAsFixed(2)}');\n});\nfor (final suggestion in quality.suggestions) {\n print('• $suggestion');\n}\nParameter\tDefault\tMeaning\tincludePing\ttrue\tSample latency / jitter / loss\tincludeDns\ttrue\tSample DNS latency\tincludeSpeedTest\ttrue\tSample download / upload (heavy)\tincludeUpload\ttrue\tInclude the upload metric\tpingHost / pingCount\tfrom config\tLatency target\tdnsDomain / dnsServers\tfrom config\tDNS target\tdownloadUrl / uploadUrl\tfrom config\tBandwidth endpoints","lightweight-variant--轻量变体#Lightweight variant / 轻量变体":"final quality = await NetworkDiagnostic.evaluateQuality(includeSpeedTest: false);","level-thresholds--等级阈值#Level thresholds / 等级阈值":"≥90 excellent · ≥75 good · ≥60 fair · ≥40 poor · otherwise bad\n(NetworkQualityLevel.fromScore).≥90 极佳 · ≥75 良好 · ≥60 一般 · ≥40 较差 · 其余为极差。","weights--权重#Weights / 权重":"Metric\tWeight\tSource\tlatency\t0.25\tPingResult.averageTime\tjitter\t0.10\tPingResult.jitter\tpacketLoss\t0.15\tPingResult.packetLoss\tdownload\t0.25\tSpeedTestResult.downloadSpeed\tupload\t0.15\tSpeedTestResult.uploadSpeed\tdns\t0.10\tmean of successful DnsTestResult.responseTimeMs\tsignalStrength\t0.10\tNetworkConnectionInfo.signalStrength","pure-function--纯函数#Pure function / 纯函数":"If you already have the metrics, skip the network entirely:如果你已有指标,可完全跳过网络:\nfinal score = NetworkQualityEvaluator.evaluate(\n latency: 42, jitter: 6, packetLoss: 0,\n download: 88.4, upload: 12.1, dns: 25, signalStrength: -55,\n targets: const QualityTargets(),\n);\nprint('${score.score.toStringAsFixed(1)} → ${score.level.label}');\nTune the ideal values globally through NetworkDiagnosticConfig(qualityTargets: ...).\n调整理想值请通过 NetworkDiagnosticConfig(qualityTargets: ...) 全局设置。"}},"/Speed-Test":{"title":"Speed Test / 测速","data":{"":"NetworkDiagnostic.runSpeedTest() measures download and upload throughput and\nsamples latency during the run, reporting progress through onProgress.NetworkDiagnostic.runSpeedTest() 测量下载与上传吞吐量,并在过程中采样延迟,通过\nonProgress 汇报进度。","basic--基础#Basic / 基础":"final speed = await NetworkDiagnostic.runSpeedTest(\n includeUpload: true,\n includePing: true,\n onProgress: (progress) {\n print('${progress.phase.name}: '\n '${progress.speedMbps.toStringAsFixed(1)} Mbps '\n '(${progress.bytes} bytes, ${progress.elapsed.inMilliseconds} ms)');\n },\n);\nprint('download : ${speed.downloadSpeed.toStringAsFixed(2)} Mbps');\nprint('upload : ${speed.uploadSpeed.toStringAsFixed(2)} Mbps');\nprint('ping : ${speed.ping.toStringAsFixed(1)} ms');\nprint('jitter : ${speed.jitter.toStringAsFixed(2)} ms');\nprint('loss : ${speed.packetLoss.toStringAsFixed(1)} %');\nprint('server : ${speed.server}');\nprint('duration : ${speed.duration.inMilliseconds} ms');\nParameter\tDefault\tMeaning\tdownloadUrl / uploadUrl\tfrom config\tEndpoints used\ttimeout\tconfig.speedTestTimeout\tPer-request timeout\tmaxDuration\tconfig.speedTestMaxDuration\tSampling window per phase\tuploadPayloadBytes\tconfig.uploadPayloadBytes\tUpload size\tpingHost / pingCount\tfrom config\tLatency sampled during the test\tincludeUpload\ttrue\tSkip the upload phase\tincludePing\ttrue\tSkip the latency sample\tonProgress\tnull\tCalled repeatedly during both phases\t\nProgress phases: SpeedTestPhase.download → SpeedTestPhase.upload →\nSpeedTestPhase.completed.进度阶段依次为 download → upload → completed。","fast-low-traffic-variant--轻量低流量变体#Fast, low-traffic variant / 轻量低流量变体":"final quick = await NetworkDiagnostic.runSpeedTest(\n includeUpload: false,\n includePing: false,\n maxDuration: const Duration(seconds: 5),\n);\nThe default endpoints point at Cloudflare's public speed service and move real\ntraffic (≈25 MB download by default). Ask for consent before running it on a\nmetered connection, and override downloadUrl / uploadUrl for production.\n默认端点指向 Cloudflare 公共服务并会产生真实流量(默认下载约 25 MB)。在按量计费\n网络上运行前请征得同意,并为生产环境替换 downloadUrl / uploadUrl。"}},"/Usage":{"title":"Usage / 用法","data":{"which-api-do-i-need--该用哪个-api#Which API do I need? / 该用哪个 API?":"I want to know…\tCall\tAm I online, and over what transport?\tNetworkDiagnostic.checkConnection()\tReact to Wi-Fi ⇄ cellular switches\tNetworkDiagnostic.onConnectivityChanged\tLatency, jitter, packet loss\tNetworkDiagnostic.ping()\tIs DNS slow or broken?\tNetworkDiagnostic.resolve()\tIs host:port reachable?\tNetworkDiagnostic.checkPort()\tWhich of these ports are open?\tNetworkDiagnostic.scanPorts()\tHow fast is down/up?\tNetworkDiagnostic.runSpeedTest()\tOne number for “good or bad”\tNetworkDiagnostic.evaluateQuality()\tEverything at once, as a report\tNetworkDiagnostic.diagnose()\tIs the diagnostics API itself expensive?\tNetworkBenchmark.runAll()\tNative version / SSID / gateway / MAC\tZeroNetworkKit.getPlatformVersion(), getNativeNetworkDetails()","global-configuration--全局配置#Global configuration / 全局配置":"NetworkDiagnosticConfig centralises every default. Override only what you\nneed; the rest keep the defaults below.NetworkDiagnosticConfig 集中了全部默认参数,只需覆盖你关心的字段。\nZeroNetworkKit.init(\n config: const NetworkDiagnosticConfig(\n pingHost: '1.1.1.1',\n pingPort: 443,\n pingCount: 5,\n dnsDomain: 'example.com',\n dnsServers: ['1.1.1.1', '8.8.8.8'],\n downloadUrl: 'https://my-cdn.example.com/speedtest.bin',\n uploadUrl: 'https://my-cdn.example.com/upload',\n ),\n);\nField\tDefault\tMeaning\tpingHost\t'1.1.1.1'\tDefault ping / port-scan target\tpingPort\t443\tPort used by the TCP ping\tpingCount\t4\tProbes per ping run\tpingTimeout\t3s\tTimeout of one probe\tpingInterval\t200ms\tDelay between probes\tdnsDomain\t'www.google.com'\tDefault domain for DNS tests\tdnsServers\t['1.1.1.1', '8.8.8.8', '114.114.114.114']\tServers queried by default\tdnsTimeout\t5s\tTimeout per DNS server\tdownloadUrl\tCloudflare __down?bytes=25000000\tDownload endpoint\tuploadUrl\tCloudflare __up\tUpload endpoint\tuploadPayloadBytes\t1048576 (1 MiB)\tUpload payload size\tspeedTestTimeout\t30s\tPer-request timeout\tspeedTestMaxDuration\t10s\tSampling window per phase\tportCheckTimeout\t3s\tTimeout of one port check\tprobePorts\t[80, 443]\tPorts used by scanPorts() by default\tqualityTargets\tconst QualityTargets()\tIdeal values for scoring\t\nThe default speed-test endpoints are Cloudflare's public service. Swap them for\nyour own before shipping production traffic.\n默认测速端点是 Cloudflare 的公共服务,正式项目请替换为自建端点。","quality-targets--质量评分理想值#Quality targets / 质量评分理想值":"const NetworkDiagnosticConfig(\n qualityTargets: QualityTargets(\n excellentLatency: 30, // ms\n acceptableLatency: 150, // ms\n excellentJitter: 5, // ms\n acceptableJitter: 40, // ms\n acceptablePacketLoss: 5, // %\n excellentDownload: 50, // Mbps\n acceptableDownload: 5, // Mbps\n excellentUpload: 20, // Mbps\n acceptableUpload: 2, // Mbps\n excellentDns: 30, // ms\n acceptableDns: 200, // ms\n ),\n);","json-serialisation--序列化#JSON serialisation / 序列化":"Every result object implements toMap() and is JSON encodable:每个结果对象都实现了 toMap(),可直接 JSON 序列化:\nimport 'dart:convert';\nfinal report = await NetworkDiagnostic.diagnose(includeSpeedTest: false);\nfinal json = jsonEncode(report.toMap());\nprint(json);\n// Round-trip a connection snapshot.\nfinal decoded = NetworkConnectionInfo.fromMap(jsonDecode(jsonEncode(\n report.connection.toMap(),\n)) as Map);\nType\tSerialisation\tNetworkConnectionInfo\ttoMap() + fromMap()\tPingResult\ttoMap()\tDnsTestResult\ttoMap()\tPortCheckResult\ttoMap()\tSpeedTestResult\ttoMap()\tNetworkQualityScore\ttoMap()\tNetworkDiagnosticReport\ttoMap()\tBenchmarkResult / BenchmarkSuiteResult\ttoMap()","dependency-injection--tests--注入与测试#Dependency injection & tests / 注入与测试":"Every service accepts its collaborators, so your integration points are\nunit-testable without touching the network.每个服务都支持注入协作对象,可以完全脱离网络做单测。\nclass FakeConnectivityAdapter implements ConnectivityAdapter {\n @override\n Future> checkConnectivity() async => ['wifi'];\n @override\n Stream> get onConnectivityChanged => const Stream.empty();\n}\nNetworkDiagnostic.configure(\n config: const NetworkDiagnosticConfig(pingHost: '127.0.0.1'),\n connectivity: ConnectivityService(adapter: FakeConnectivityAdapter()),\n ping: PingService(),\n dns: DnsService(),\n ports: PortService(),\n speedTest: SpeedTestService(client: fakeClient),\n);\n// Restore defaults afterwards.\nNetworkDiagnostic.reset();\nSwap individual services directly when you prefer:也可以直接替换单个服务:\nNetworkDiagnostic.pingService = MyFakePingService();\nNetworkDiagnostic.dnsService = MyFakeDnsService();\nFor advanced service classes, the DNS wire codec and low-level APIs, import\npackage:zero_network_kit/advanced.dart.\n进阶的服务类、DNS 报文编解码器等底层 API 请通过\npackage:zero_network_kit/advanced.dart 引入。"}},"/":{"title":"Zero Network Kit","data":{"":"A Flutter plugin for network diagnostics: connectivity inspection, latency\nprobing, DNS resolution, port checks, bandwidth measurement, quality scoring and\nmicro-benchmarks — on Android, iOS, macOS, Windows and Linux.一个 Flutter 插件,提供网络诊断能力:连通性检测、延迟探测、DNS 解析、端口\n检测、带宽测速、质量评分与微基准测试——覆盖 Android、iOS、macOS、Windows 与 Linux。","-features--功能特性#✨ Features / 功能特性":"Feature\tDescription\tZero setup\tWorks out of the box; init() only to override defaults / 开箱即用,仅需在想覆盖默认值时才 init()\tConnectivity\tTransport type, IPv4/IPv6, gateway, SSID, RSSI, MAC, VPN / 传输类型、IPv4/IPv6、网关、SSID、信号强度、MAC、VPN\tLatency\tTCP handshake RTT everywhere; system ICMP on desktop / TCP 握手 RTT 全平台可用,桌面额外支持系统 ICMP\tDNS\tSystem resolver + raw UDP against explicit servers / 系统解析器 + 针对指定服务器的原始 UDP 查询\tPorts\tBounded-concurrency TCP reachability / 有界并发的 TCP 可达性检测\tSpeed test\tDownload/upload throughput with progress callbacks / 上下行测速,带进度回调\tQuality\tWeighted 0–100 score + level + suggestions / 加权 0–100 分 + 等级 + 建议\tFull report\tOne-shot aggregate of every probe / 一次性汇总所有探测\tBenchmarks\tMeasures how fast the diagnostics API itself runs / 衡量诊断 API 自身开销\tCapabilities\tQuery-then-call; hide unsupported cards (e.g. SSID on desktop) / 先查询后调用,隐藏不支持的卡片\tGraceful degradation\tMissing permission or unreachable sub-service never throws / 缺权限或子服务不可达时只返回 null,绝不抛异常\tHermetic tests\tEvery service accepts injected collaborators / 每个服务都支持注入协作对象,便于单测","-table-of-contents--目录#📚 Table of Contents / 目录":"Page\tDescription\tGetting Started\tQuick start guide / 快速开始\tInstallation\tHow to install / 安装方式\tUsage\tGlobal config, serialisation, dependency injection / 全局配置、序列化、依赖注入\tConnectivity\tConnection snapshot & change stream / 连通性快照与变化监听\tLatency / Ping\tTCP & ICMP latency probing / 延迟探测\tDNS\tResolver comparison across servers / DNS 解析\tPorts\tSingle port & bulk scan / 端口检测\tSpeed Test\tDownload/upload throughput / 测速\tQuality Score\tWeighted scoring / 质量评分\tFull Report\tOne-shot aggregate / 汇总报告\tBenchmarks\tMicro-benchmarks / 微基准\tPlatform Support\tCapability matrix & native details / 能力矩阵与原生详情\tAPI Reference\tModel field reference & gotchas / 模型字段速查与陷阱\tFAQ\tFrequently asked questions / 常见问题","-links--链接#🔗 Links / 链接":"GitHub\nOfficial Website\npub.dev","-license--许可证#📄 License / 许可证":"This project is licensed under the MPL-2.0.本项目采用 MPL-2.0 许可证。This plugin is provided \"as is\", without warranty of any kind. The author assumes\nno responsibility or liability for the functionality, security, or any\nconsequences arising from the use of modified versions or derivative projects.本插件按\"原样\"提供,不提供任何担保。作者不对修改版或衍生项目的功能、安全性及\n任何使用后果承担责任。"}}} \ No newline at end of file +{"/API-Reference":{"title":"API Reference / 接口参考","data":{"":"A condensed field reference for every public model. For copy-paste examples of\neach method, see the capability pages.这里是每个公开模型的字段速查。每个方法的复制即用示例请见各能力页面。","networktype#NetworkType":"Member\tNotes\tvalues\tnone, wifi, mobile, ethernet, vpn, bluetooth, other\tlabel\tHuman readable: 'Wi-Fi', 'Mobile', …\tid\tStable English identifier (name), safe to persist\tisConnected\ttrue unless none\tNetworkType.fromRaw(Object?)\tParses a raw platform string","networkconnectioninfo#NetworkConnectionInfo":"isConnected, type, ssid, signalStrength (dBm), ipAddress,\nipv6Address, gateway, macAddress, isVpn, isReachable, timestamp,\nplus copyWith({bool? isReachable}).","pingresult#PingResult":"Fields host, port, mode, sent, received, times, timestamp;\nderived getters lost, packetLoss (%), minTime, maxTime, averageTime,\njitter, isSuccess.","dnstestresult#DnsTestResult":"Fields server, domain, isSuccess, responseTime, resolvedIps,\nerrorMessage, timestamp; derived responseTimeMs, primaryAddress.","portcheckresult#PortCheckResult":"Fields host, port, isOpen, responseTime, errorMessage, timestamp;\nderived responseTimeMs.","speedtestresult#SpeedTestResult":"Fields downloadSpeed, uploadSpeed (Mbps), ping (ms), jitter (ms),\npacketLoss (%), downloadedBytes, uploadedBytes, downloadDuration,\nuploadDuration, server, timestamp; derived duration; static helper\nSpeedTestResult.mbpsFromBytes(bytes, elapsed).","speedtestprogress--speedtestphase#SpeedTestProgress / SpeedTestPhase":"phase (download, upload, completed), bytes, elapsed, speedMbps.","networkqualityscore--networkqualitylevel#NetworkQualityScore / NetworkQualityLevel":"score (0–100), level, metrics (Map), suggestions,\ntimestamp; NetworkQualityLevel.fromScore(double), level.label.","networkdiagnosticreport#NetworkDiagnosticReport":"connection, ping, dnsResults, portResults, speedTest, quality,\ntimestamp.","benchmarkresult--benchmarksuiteresult#BenchmarkResult / BenchmarkSuiteResult":"BenchmarkResult: testName, iterations, totalDuration,\naverageDuration, minDuration, maxDuration, standardDeviation,\noperationsPerSecond, failures, timestamp.\nBenchmarkSuiteResult: suiteName, results, totalDuration, timestamp,\nand suite['name'].","gotchas--常见陷阱#Gotchas / 常见陷阱":"type.label, not displayName. NetworkType exposes label / id.\ncheckPort() returns PortCheckResult. Use isPortOpen() for a plain\nboolean, or scanPorts(..., ports: [p]).first for RTT / error message.\nincludeSystemResolver defaults to false. Pass true to add the\nsystem DNS row.\nA TCP ping needs a listening port. Probing host with port: 443 fails\nif that host does not accept TCP/443 — that is \"filtered\", not \"offline\". Use\nmode: PingMode.icmp on desktop to get closer to real ICMP.\nPingMode.icmp is desktop-only and silently degrades when the ping\nbinary is unavailable.\nThe speed test moves real traffic (≈25 MB download by default). Ask for\nconsent before running it on a metered connection.\nMissing permissions degrade, they never throw. A missing location\npermission yields ssid == null; the rest of the snapshot still arrives.\nincludePorts is false by default in diagnose().\ninit() is optional, but dispose() only closes the client the plugin\nowns — an httpClient you injected stays open, so close it yourself.\nFull source-level API docs live in the repo:\nAPI.md\n(English) and\nAPI_zh.md\n(简体中文).\n完整源码级 API 文档见仓库内的\nAPI.md\n与 API_zh.md。"}},"/Benchmark":{"title":"Benchmarks / 微基准测试","data":{"":"NetworkBenchmark measures how fast the diagnostics API itself runs — useful\nwhen deciding whether a diagnostic belongs on your startup path.NetworkBenchmark 衡量诊断 API 自身的开销——可用在判断某项诊断能否放在启动路径上。","all-at-once--一次性全跑#All at once / 一次性全跑":"final suite = await NetworkBenchmark.runAll(\n iterations: 20,\n warmupIterations: 3,\n host: '1.1.1.1',\n dnsDomain: 'example.com',\n port: 443,\n);\nfor (final result in suite.results) {\n print('${result.testName.padRight(12)} '\n 'avg ${(result.averageDuration.inMicroseconds / 1000).toStringAsFixed(2)} ms '\n '± ${(result.standardDeviation.inMicroseconds / 1000).toStringAsFixed(2)} ms '\n '${result.operationsPerSecond.toStringAsFixed(1)} ops/s '\n 'failures=${result.failures}');\n}\n// Look up one benchmark by name / 按名称查单个结果\nprint(suite['ping']?.averageDuration);\nprint(suite.totalDuration);","individual-suites--单个基准#Individual suites / 单个基准":"await NetworkBenchmark.benchmarkConnection(iterations: 20);\nawait NetworkBenchmark.benchmarkPing(iterations: 20, host: '1.1.1.1');\nawait NetworkBenchmark.benchmarkDns(iterations: 20, domain: 'example.com');\nawait NetworkBenchmark.benchmarkPortCheck(iterations: 20, port: 443);\nawait NetworkBenchmark.benchmarkPlatformChannel(iterations: 20);","result-fields--结果字段#Result fields / 结果字段":"BenchmarkResult: testName, iterations, totalDuration, averageDuration,\nminDuration, maxDuration, standardDeviation, operationsPerSecond,\nfailures, timestamp.BenchmarkSuiteResult: suiteName, results, totalDuration, timestamp, and\nsuite['name']."}},"/Connectivity":{"title":"Connectivity / 连通性","data":{"":"NetworkDiagnostic.checkConnection() captures a single snapshot of the current\nnetwork; onConnectivityChanged emits a fresh snapshot on every transport\nchange.NetworkDiagnostic.checkConnection() 捕获当前网络的一次快照;onConnectivityChanged\n在每次传输方式变化时推送新快照。","snapshot--快照#Snapshot / 快照":"final connection = await NetworkDiagnostic.checkConnection();\nprint('type : ${connection.type.label}'); // Wi-Fi / Mobile / Ethernet …\nprint('connected : ${connection.isConnected}');\nprint('ipv4 : ${connection.ipAddress}');\nprint('ipv6 : ${connection.ipv6Address}');\nprint('gateway : ${connection.gateway}');\nprint('ssid : ${connection.ssid}');\nprint('rssi : ${connection.signalStrength} dBm');\nprint('mac : ${connection.macAddress}');\nprint('vpn : ${connection.isVpn}');\nprint('timestamp : ${connection.timestamp}');\nField\tNotes\ttype\tNetworkType (none, wifi, mobile, ethernet, vpn, bluetooth, other)\tisConnected\ttrue unless type is none\tipAddress / ipv6Address\tLocal IPv4 / IPv6 (Dart NetworkInterface)\tgateway / macAddress\tNative; null on desktop except Windows\tssid / signalStrength\tNative; always null on desktop\tisVpn\tNative VPN detection\tisReachable\tOnly filled when probeReachability: true\ttimestamp\tWhen the snapshot was taken\t\nUse connection.type.label / connection.type.id. There is no\ndisplayName.\n请使用 connection.type.label / connection.type.id,没有 displayName。","parameters--参数#Parameters / 参数":"Parameter\tDefault\tMeaning\tincludeNativeDetails\ttrue\tAlso read SSID / gateway / MAC / VPN from the native side\tprobeReachability\tfalse\tMake a real request and fill isReachable\tprobeTimeout\t3s\tTimeout of that reachability probe\t\n// Skip the native channel (cheapest call — good on hot paths).\nfinal quick = await NetworkDiagnostic.checkConnection(includeNativeDetails: false);\n// Prove the internet is actually reachable, not just that a NIC is up.\nfinal verified = await NetworkDiagnostic.checkConnection(\n probeReachability: true,\n probeTimeout: const Duration(seconds: 5),\n);\nif (verified.isReachable == false) {\n print('Interface is up but the internet is unreachable.');\n}","change-stream--变化监听#Change stream / 变化监听":"final subscription = NetworkDiagnostic.onConnectivityChanged.listen(\n (info) => print('now on ${info.type.id} · ${info.ipAddress}'),\n);\n// Later:\nawait subscription.cancel();\nIn a widget:\nStreamBuilder(\n stream: NetworkDiagnostic.onConnectivityChanged,\n builder: (context, snapshot) {\n final info = snapshot.data;\n if (info == null) return const Text('Checking…');\n return Text('${info.type.label} · ${info.isConnected}');\n },\n)","connectivity-only-minimal-example--仅连通性最小示例#Connectivity-only: minimal example / 仅连通性:最小示例":"If all you need is \"am I online, on what transport, what IP, and react to\nchanges\", you only need this page — no init() required.如果只需要「在不在線、走什么網络、IP 是多少、切换时通知」,只看本页即可——\n无需 init()。\nclass ConnectivityScreen extends StatelessWidget {\n const ConnectivityScreen({super.key});\n @override\n Widget build(BuildContext context) {\n return StreamBuilder(\n stream: NetworkDiagnostic.onConnectivityChanged,\n builder: (context, snap) {\n final c = snap.data;\n if (c == null) {\n return const Center(child: CircularProgressIndicator());\n }\n return ListView(\n children: [\n ListTile(title: const Text('Type / 类型'), trailing: Text(c.type.label)),\n ListTile(title: const Text('Online / 在线'), trailing: Text('${c.isConnected}')),\n ListTile(title: const Text('IPv4'), trailing: Text(c.ipAddress ?? '—')),\n ListTile(title: const Text('IPv6'), trailing: Text(c.ipv6Address ?? '—')),\n ],\n );\n },\n );\n }\n}"}},"/DNS":{"title":"DNS / DNS 解析","data":{"":"NetworkDiagnostic.resolve() tests DNS resolution across multiple servers in\nparallel, so you can spot the fastest one. Each result is either the system\nresolver or a raw UDP query against an explicit server.NetworkDiagnostic.resolve() 并行测试多台 DNS 服务器的解析表现,从而挑出最快的一台。\n每条结果要么来自系统解析器,要么是对指定服务器的原始 UDP 查询。","basic--基础#Basic / 基础":"final results = await NetworkDiagnostic.resolve(\n domain: 'example.com',\n dnsServers: const ['1.1.1.1', '8.8.8.8', '114.114.114.114'],\n timeout: const Duration(seconds: 5),\n concurrent: true,\n includeSystemResolver: true,\n);\nfor (final r in results) {\n if (r.isSuccess) {\n print('${r.server.padRight(16)} → ${r.resolvedIps.join(\", \")} '\n '(${r.responseTimeMs.toStringAsFixed(1)} ms)');\n } else {\n print('${r.server.padRight(16)} ✗ ${r.errorMessage}');\n }\n}\nParameter\tDefault\tMeaning\tdomain\tconfig.dnsDomain\tDomain to resolve\tdnsServers\tconfig.dnsServers\tServers queried in parallel (or in series)\ttimeout\tconfig.dnsTimeout\tTimeout of one query\tconcurrent\ttrue\tQuery all servers at once\tincludeSystemResolver\tfalse\tAlso add a system row\t\nNotes / 说明:\nA row with server == 'system' comes from the OS resolver; the others are raw\nUDP queries against the listed IPs, encoded by the built-in DnsPacket wire\ncodec.\nserver == 'system' 的行来自系统解析器,其余是对指定 IP 的原始 UDP 查询,由内置\nDnsPacket 编解码。\nincludeSystemResolver defaults to false at the facade level — pass\ntrue to include it.\n门面层默认不包含系统解析器,需显式传 true。","pick-the-fastest--挑出最快的#Pick the fastest / 挑出最快的":"final fastest = results\n .where((r) => r.isSuccess)\n .reduce((a, b) => a.responseTimeMs <= b.responseTimeMs ? a : b);\nprint('fastest resolver: ${fastest.server}');"}},"/FAQ":{"title":"FAQ / 常见问题","data":{"do-i-need-to-call-init--需要调用-init-吗#Do I need to call init()? / 需要调用 init() 吗?":"No. Every API falls back to built-in defaults. Call ZeroNetworkKit.init() only\nwhen you want to override defaults (e.g. a custom reachability host or DNS\nservers). Connectivity alone works with zero setup.不需要。每个 API 都走内置默认值。只有在想覆盖默认参数(如自定义可达性主机或 DNS\n服务器)时才调用 ZeroNetworkKit.init()。单用连通性完全零配置。","which-platforms-are-supported--支持哪些平台#Which platforms are supported? / 支持哪些平台?":"Android, iOS, macOS, Windows, Linux and Web (partial — see\nPlatform Support).支持 Android、iOS、macOS、Windows、Linux 与 Web(部分支持)。详见\nPlatform Support。","why-is-ssid--signalstrength-null-on-desktop--为什么桌面上的-ssid信号强度是-null#Why is ssid / signalStrength null on desktop? / 为什么桌面上的 ssid/信号强度是 null?":"Desktop uses tier A: SSID and RSSI require system APIs that aren't exposed on\nmacOS/Linux/Windows the same way. They are always null on desktop; Windows\nadditionally provides gateway / MAC / DNS / VPN via GetAdaptersAddresses.桌面采用 A 档:SSID 与 RSSI 依赖系统 API,在桌面上不以同样方式暴露,因此恒为 null;\nWindows 额外通过 GetAdaptersAddresses 提供网关 / MAC / DNS / VPN。","why-does-getnativenetworkdetails-return-null--为什么-getnativenetworkdetails-返回-null#Why does getNativeNetworkDetails() return null? / 为什么 getNativeNetworkDetails 返回 null?":"Either the platform has no such data (e.g. desktop tier A), or the required\npermission is missing (e.g. location for Wi-Fi SSID on Android/iOS). The call\nnever throws — it degrades gracefully.要么是平台没有该数据(如桌面 A 档),要么是缺权限(如 Android/iOS 上读取 Wi-Fi SSID\n需定位权限)。该调用绝不抛异常,会优雅降级。","does-the-speed-test-use-my-bandwidth--测速会消耗我的流量吗#Does the speed test use my bandwidth? / 测速会消耗我的流量吗?":"Yes — the default endpoints are Cloudflare's public service and download ≈25 MB\nby default. Ask for consent on metered connections and override downloadUrl /\nuploadUrl for production.会——默认端点指向 Cloudflare 公共服务,默认下载约 25 MB。在按量计费网络上请征得同意,\n生产环境请替换 downloadUrl / uploadUrl。","how-do-i-test-my-integration-without-the-network--如何脱离网络测试我的集成#How do I test my integration without the network? / 如何脱离网络测试我的集成?":"Every service is injectable. Implement ConnectivityAdapter (or pass fake\nservice instances) and call NetworkDiagnostic.configure(...), then\nNetworkDiagnostic.reset() to restore defaults. See\nUsage → Dependency injection.每个服务都可注入。实现 ConnectivityAdapter(或直接传入假服务实例)后调用\nNetworkDiagnostic.configure(...),再用 NetworkDiagnostic.reset() 恢复默认。详见\n用法 → 依赖注入。","how-is-this-licensed--采用什么许可证#How is this licensed? / 采用什么许可证?":"MPL-2.0. See the repository LICENSE.采用 MPL-2.0。详见仓库 LICENSE。"}},"/Full-Report":{"title":"Full Report / 汇总报告","data":{"":"NetworkDiagnostic.diagnose() runs every probe you ask for and aggregates them\ninto one NetworkDiagnosticReport. A failing sub-test never aborts the run — its\nfield simply stays empty/null.NetworkDiagnostic.diagnose() 运行你指定的各项探测,并汇总为一份\nNetworkDiagnosticReport。任何子项失败都不会中断整体流程,对应字段保持空。","basic--基础#Basic / 基础":"final report = await NetworkDiagnostic.diagnose(\n includePing: true,\n includeDns: true,\n includePorts: true,\n includeSpeedTest: true,\n includeUpload: true,\n host: '1.1.1.1',\n dnsDomain: 'example.com',\n dnsServers: const ['1.1.1.1', '8.8.8.8'],\n ports: const [80, 443],\n);\nprint(report); // one-line summary\nprint(report.connection.type.label);\nprint(report.ping?.averageTime);\nprint(report.dnsResults.length);\nprint(report.portResults.where((p) => p.isOpen).length);\nprint(report.speedTest?.downloadSpeed);\nprint(report.quality.level.label);\nParameter\tDefault\tMeaning\tincludePing\ttrue\tSample latency / jitter / loss\tincludeDns\ttrue\tSample DNS latency\tincludePorts\tfalse\tScan ports (off by default)\tincludeSpeedTest\ttrue\tSample download / upload\tincludeUpload\ttrue\tInclude the upload metric\thost\tconfig.pingHost\tLatency / port target\tdnsDomain / dnsServers\tfrom config\tDNS target\tports\tconfig.probePorts\tPorts scanned when includePorts is true\t\nincludePorts defaults to false, so a default run does not scan ports.\nincludePorts 默认 false,默认运行不会扫描端口。\nThe report is JSON-encodable via report.toMap().\n报告可通过 report.toMap() 序列化为 JSON。"}},"/Getting-Started":{"title":"Getting Started / 快速开始","data":{"quick-start--快速开始#Quick Start / 快速开始":"Import the package and start diagnosing — no init() required:导入包即可开始诊断,无需 init():\nimport 'package:flutter/material.dart';\nimport 'package:zero_network_kit/zero_network_kit.dart';\nvoid main() {\n // Optional: apply your own global defaults once.\n // 可选:全局定制一次默认参数。\n ZeroNetworkKit.init(\n config: const NetworkDiagnosticConfig(pingHost: '1.1.1.1'),\n );\n runApp(const MyApp());\n}\nIf you only need connectivity, you don't even need init():如果只需要连通性,连 init() 都不需要:\nfinal connection = await NetworkDiagnostic.checkConnection();\nprint('${connection.type.label} · ${connection.ipAddress}');","what-you-get--你能得到什么#What you get / 你能得到什么":"Every method is a static, non-blocking call on the NetworkDiagnostic facade.\nThe plugin is pure Dart under the hood, so it is fully injectable and testable\nwithout touching the network.每个方法都是 NetworkDiagnostic 门面上的静态、非阻塞调用。插件内核是纯 Dart,\n因此完全可注入、可脱离网络做测试。\nNeed\tCall\tAm I online, on what transport?\tNetworkDiagnostic.checkConnection()\tReact to Wi-Fi ⇄ cellular switches\tNetworkDiagnostic.onConnectivityChanged\tLatency, jitter, packet loss\tNetworkDiagnostic.ping()\tIs DNS slow or broken?\tNetworkDiagnostic.resolve()\tIs host:port reachable?\tNetworkDiagnostic.checkPort() / isPortOpen()\tHow fast is down/up?\tNetworkDiagnostic.runSpeedTest()\tOne number for \"good or bad\"\tNetworkDiagnostic.evaluateQuality()\tEverything at once\tNetworkDiagnostic.diagnose()\tNative version / SSID / gateway / MAC\tZeroNetworkKit.getNativeNetworkDetails()\t\nSee Which API do I need? for the full map.","lifecycle--生命周期#Lifecycle / 生命周期":"ZeroNetworkKit.isInitialized; // false until init() runs\nZeroNetworkKit.config; // the effective NetworkDiagnosticConfig\nawait ZeroNetworkKit.dispose(); // releases the HTTP client the plugin owns\ndispose() only closes the HTTP client the plugin created itself. An\nhttp.Client you injected stays open — close it yourself.dispose() 只会关闭插件自己创建的 HTTP 客户端;你注入的 http.Client 不会被关闭。","next-steps--下一步#Next Steps / 下一步":"Installation — Detailed installation methods / 详细安装方式\nUsage — Global config & dependency injection / 全局配置与依赖注入\nPlatform Support — Capability matrix / 能力矩阵"}},"/Installation":{"title":"Installation / 安装","data":{"from-pubdev-recommended--从-pubdev-安装推荐#From pub.dev (Recommended) / 从 pub.dev 安装(推荐)":"Add the following to your pubspec.yaml:在 pubspec.yaml 中添加以下依赖:\ndependencies:\n zero_network_kit: ^1.0.2\nThen run:然后运行:\nflutter pub get","from-github--从-github-安装#From GitHub / 从 GitHub 安装":"Alternatively, install from GitHub:或者从 GitHub 安装:\ndependencies:\n zero_network_kit:\n git:\n url: https://github.com/zero-labsco/zero_network_kit.git\n ref: release/v1.0.2","platform-setup--平台配置#Platform Setup / 平台配置":"","android#Android":"The plugin manifest already declares INTERNET, ACCESS_NETWORK_STATE and\nACCESS_WIFI_STATE. Reading the Wi-Fi SSID additionally requires the\nlocation permission (ACCESS_FINE_LOCATION) on Android 8.1+, otherwise the\nsnapshot reports ssid: null.插件清单已声明 INTERNET、ACCESS_NETWORK_STATE 与 ACCESS_WIFI_STATE。读取 Wi-Fi\nSSID 还需在 Android 8.1+ 申请定位权限(ACCESS_FINE_LOCATION),否则快照中\nssid 为 null。","ios#iOS":"No additional configuration needed. Reading the Wi-Fi SSID additionally\nrequires the Access WiFi Information capability plus location authorisation.无需额外配置。读取 Wi-Fi SSID 还需开启 Access WiFi Information 能力并授权定位。","macos--windows--linux#macOS / Windows / Linux":"The plugin is declared on all three desktop platforms. On desktop, native\ndetails such as SSID / RSSI are always null (tier A). Windows additionally\nprovides gateway / MAC / DNS / VPN through GetAdaptersAddresses; macOS and\nLinux report only the Dart-side IP/IPv6. See\nPlatform Support for the full matrix.插件已在三个桌面平台声明。桌面上 SSID / 信号强度等原生详情恒为 null(A 档)。\nWindows 额外通过 GetAdaptersAddresses 提供网关 / MAC / DNS / VPN;macOS 与 Linux\n仅提供 Dart 侧的 IP/IPv6。完整矩阵见平台支持。","import--导入#Import / 导入":"import 'package:zero_network_kit/zero_network_kit.dart';","requirements--环境要求#Requirements / 环境要求":"Requirement\tVersion\tFlutter\t>= 3.3.0\tDart SDK\t>= 3.11.0 < 4.0.0","next-steps--下一步#Next Steps / 下一步":"Getting Started — Quick start guide / 快速开始\nUsage — Full usage guide / 完整使用指南"}},"/Ping":{"title":"Latency / Ping / 延迟探测","data":{"":"NetworkDiagnostic.ping() measures round-trip time towards a target host and\nreports sent / received counts, packet loss, min/avg/max and jitter.NetworkDiagnostic.ping() 测量到目标主机的往返时间,并给出发送/接收次数、丢包率、\n最小/平均/最大耗时与抖动。","basic--基础#Basic / 基础":"final ping = await NetworkDiagnostic.ping(\n host: '1.1.1.1',\n count: 5,\n timeout: const Duration(seconds: 2),\n interval: const Duration(milliseconds: 200),\n port: 443,\n);\nprint('received : ${ping.received}/${ping.sent}');\nprint('loss : ${ping.packetLoss.toStringAsFixed(1)} %');\nprint('min/avg/max: ${ping.minTime.toStringAsFixed(1)} / '\n '${ping.averageTime.toStringAsFixed(1)} / '\n '${ping.maxTime.toStringAsFixed(1)} ms');\nprint('jitter : ${ping.jitter.toStringAsFixed(2)} ms');\nprint('samples : ${ping.times}');\nParameter\tDefault\tMeaning\thost\tconfig.pingHost\tTarget host\tcount\tconfig.pingCount\tNumber of probes\ttimeout\tconfig.pingTimeout\tPer-probe timeout\tinterval\tconfig.pingInterval\tDelay between probes\tport\tconfig.pingPort\tTCP port probed in PingMode.tcp\tmode\tPingMode.tcp\tPingMode.tcp or PingMode.icmp","tcp-vs-icmp--tcp-与-icmp#TCP vs ICMP / TCP 与 ICMP":"PingMode.tcp performs a TCP handshake to host:port — the portable equivalent\nof ICMP and the only mode available on Android/iOS.PingMode.tcp 通过 TCP 握手到 host:port 测量往返,是移动端唯一可用的模式。\n// Desktop only — falls back gracefully if the `ping` binary is unavailable.\nawait NetworkDiagnostic.ping(host: '1.1.1.1', count: 4, mode: PingMode.icmp);\nping.isSuccess is true when at least one probe answered; check it before\ntrusting the averages.至少一次成功响应时 isSuccess 为 true,读平均值前建议先判断它。\nA TCP ping needs a listening port. Probing host with port: 443 fails if\nthat host does not accept TCP/443 — that is \"filtered\", not \"offline\". Use\nmode: PingMode.icmp on desktop to get closer to real ICMP.\nTCP 探测需要一个在监听的端口。对不接受 TCP/443 的主机探测 port: 443 会失败——\n那是“被过滤”,不是“离线”。在桌面上用 PingMode.icmp 更接近真实 ICMP。"}},"/Platform-Support":{"title":"Platform Support / 平台支持","data":{"":"zero_network_kit is declared on six plugin platforms: Android, iOS,\nmacOS, Windows, Linux and Web. Web support is partial: the services that\ndepend on dart:io are swapped for browser-safe equivalents, and the\ncapabilities the sandbox forbids degrade gracefully instead of failing.zero_network_kit 在 六 个插件平台上声明:Android、iOS、macOS、Windows、Linux\n与 Web。Web 为部分支持:依赖 dart:io 的服务已替换为浏览器安全的等价实现,\n浏览器沙箱禁止的能力会优雅降级而不会失败。","capability-matrix--能力矩阵#Capability matrix / 能力矩阵":"Capability\tAndroid / iOS\tDesktop (macOS / Windows / Linux)\tWeb\tConnectivity (connectivity_plus)\t✅\t✅\t✅\tLocal IP / IPv6 (NetworkInterface)\t✅\t✅\t❌\tNative details (SSID / gateway / MAC / VPN)\t✅\t⚠️ see below\t❌\tTCP ping\t✅\t✅\t⚠️ HTTPS round trip\tICMP ping (Process.run('ping'))\t❌\t✅\t❌\tHTTP ping\t✅\t✅\t✅\tDNS system resolver\t✅\t✅\t✅ DoH\tDNS raw UDP\t✅\t✅\t⚠️ DoH endpoint required\tPort check / scan\t✅\t✅\t❌\tSpeed test\t✅\t✅\t✅\tQuality score\t✅\t✅\t✅\tBenchmarks\t✅\t✅\t✅","networkcapabilities--能力查询#NetworkCapabilities / 能力查询":"Query the host platform before calling, so your UI can hide unsupported cards:在调用前查询当前平台能力,UI 即可据此隐藏不支持的卡片:\nfinal caps = NetworkDiagnostic.capabilities;\nprint(caps.platform); // 'android' | 'ios' | 'macos' | 'windows' | 'linux' | 'web'\nprint(caps.supports(NetworkCapability.nativeDetails)); // mobile: true, desktop: false\nprint(caps.supports(NetworkCapability.icmpPing)); // desktop: true, mobile: false\nRules baked into NetworkCapabilities.current():\nnativeDetails is mobile-only (SSID / gateway / MAC / VPN need system APIs).\nicmpPing is desktop-only (uses the system ping binary).\nOn web the supported set narrows to connectivity, tcpPing,\ndnsSystem, speedTest, quality and benchmark; every other capability is\nabsent.\nNetworkCapabilities.current() 内置规则:\nnativeDetails 仅移动端(SSID / 网关 / MAC / VPN 需要系统 API)。\nicmpPing 仅桌面(使用系统 ping 命令)。\nWeb 上的支持集合收缩为 connectivity、tcpPing、dnsSystem、speedTest、\nquality 与 benchmark,其余能力均不存在。","native-details-on-desktop--桌面原生详情#Native details on desktop / 桌面原生详情":"Desktop uses tier A by default: the native layer only reports the platform\nversion and an (often empty) details map. IP/IPv6 come from Dart\nNetworkInterface. As a result:桌面默认采用 A 档:原生层只报告平台版本与(通常为空的)详情 map,IP/IPv6 由 Dart\nNetworkInterface 兜底。因此:\nSSID / signal strength are always null on desktop.\n桌面上的 SSID / 信号强度恒为 null。\ngateway / macAddress / isVpn are null on macOS and Linux.\nmacOS 与 Linux 上 gateway / macAddress / isVpn 为 null。\nWindows additionally implements GetAdaptersAddresses, so it reports\ngateway / macAddress / DNS / isVpn (SSID still null). This is a bonus\ntier-B fragment kept as-is.\nWindows 额外实现了 GetAdaptersAddresses,因此上报 gateway / macAddress /\nDNS / isVpn(SSID 仍为 null)。这是保留的 B 档赠品。\nWhatever the platform, a missing permission or an unreachable native call never\nthrows — the field simply stays null and the rest of the result still arrives.无论在哪个平台,缺权限或原生调用不可达都不会抛异常——对应字段保持 null,其余\n结果照常返回。","web-support--web-支持#Web support / Web 支持":"The web build exposes the same static API. Capabilities that the browser sandbox\nforbids degrade gracefully — they return null or an \"unavailable\" result\ninstead of throwing:\nCapability\tWeb\tNotes\tConnectivity\t✅\tvia connectivity_plus\tPing (PingMode.tcp)\t⚠️\tmeasured as an HTTPS round trip; the target must send CORS headers\tPing (PingMode.icmp)\t❌\tthrows UnsupportedError\tDNS (system resolver)\t✅\tvia DNS-over-HTTPS\tDNS (explicit server)\t⚠️\tneeds a DoH endpoint, otherwise \"unsupported\"\tSpeed test\t✅\tHTTP download / upload\tQuality score\t✅\tpure function\tBenchmarks\t✅\tpure function\tPort check / scan\t❌\treturns \"unavailable\" results\tNative details (SSID, gateway, MAC, VPN)\t❌\tnull\t\nBrowsers expose no VPN API, so isVpn stays false on the web even when a\nsystem VPN or a local HTTP proxy is active.Web 构建提供同样的静态 API;浏览器沙箱禁止的能力会优雅降级(返回 null 或\"不可用\"\n结果,而不是抛异常):\n能力\tWeb\t说明\t连通性检测\t✅\t通过 connectivity_plus\tPing(PingMode.tcp)\t⚠️\t以 HTTPS 往返耗时度量,目标主机需下发 CORS 头\tPing(PingMode.icmp)\t❌\t抛出 UnsupportedError\tDNS(系统解析器)\t✅\t通过 DNS-over-HTTPS\tDNS(指定服务器)\t⚠️\t需要 DoH 端点,否则返回\"不支持\"\t带宽测速\t✅\tHTTP 下载 / 上传\t质量评分\t✅\t纯函数\t基准测试\t✅\t纯函数\t端口检测 / 扫描\t❌\t返回\"不可用\"结果\t原生详情(SSID、网关、MAC、VPN)\t❌\t返回 null\t\n浏览器不暴露 VPN 接口,因此即使系统开启了 VPN 或本地 HTTP 代理,Web 上的\nisVpn 仍为 false。","reading-native-data--读取原生数据#Reading native data / 读取原生数据":"final version = await ZeroNetworkKit.getPlatformVersion();\nprint(version); // e.g. 'Android 14' / 'iOS 18.0' / 'Web'\nfinal details = await ZeroNetworkKit.getNativeNetworkDetails();\nif (details != null) {\n print(details); // SSID, BSSID, gateway, MAC, VPN flag, RSSI …\n}\ndetails is always null on the web, and usually null on desktop too (see\nabove).Web 上 details 恒为 null,桌面端通常也为 null(见上文)。"}},"/Ports":{"title":"Ports / 端口检测","data":{"":"checkPort() returns a full PortCheckResult for one port; isPortOpen() is the\nplain boolean convenience; scanPorts() runs many ports concurrently with\nbounded in-flight connections.checkPort() 返回单个端口的完整 PortCheckResult;isPortOpen() 是便捷的布尔版;\nscanPorts() 以有界并发批量扫描多个端口。","single-port--单端口#Single port / 单端口":"// Boolean convenience / 便捷布尔版\nfinal open = await NetworkDiagnostic.isPortOpen(\n host: 'example.com',\n port: 443,\n timeout: const Duration(seconds: 3),\n);\nprint(open ? 'HTTPS reachable' : 'HTTPS unreachable');\n// Full result with RTT and error message / 含 RTT 与错误信息的完整结果\nfinal result = await NetworkDiagnostic.checkPort(host: 'example.com', port: 443);\nprint('open=${result.isOpen} rtt=${result.responseTimeMs} ms '\n 'err=${result.errorMessage}');","bulk-scan--批量扫描#Bulk scan / 批量扫描":"final scan = await NetworkDiagnostic.scanPorts(\n host: 'example.com',\n ports: const [22, 80, 443, 8080, 8443],\n concurrency: 8,\n);\nfor (final r in scan) {\n print('${r.host}:${r.port} '\n '${r.isOpen ? \"open\" : \"closed\"} '\n '${r.responseTimeMs.toStringAsFixed(1)} ms '\n '${r.errorMessage ?? \"\"}');\n}\nParameter\tDefault\tMeaning\tports (scanPorts)\tconfig.probePorts ([80, 443])\tPorts to probe\tconcurrency\t12\tMax in-flight connections\ttimeout\tconfig.portCheckTimeout\tTimeout per port\t\ncheckPort() returns a PortCheckResult (with isOpen, responseTimeMs\nand errorMessage); use isPortOpen() for a plain boolean. When you need the\nround-trip time or the failure reason for a single port, call checkPort()\ndirectly or pass a one-element list to scanPorts():\nscanPorts(host: 'example.com', ports: [443]).first.\ncheckPort() 返回 PortCheckResult(含 isOpen、responseTimeMs、\nerrorMessage);单端口布尔诉求请用 isPortOpen()。需要 RTT 或失败原因时可直接\n调用 checkPort(),或给 scanPorts(..., ports: [443]) 取单条结果。"}},"/Quality":{"title":"Quality Score / 质量评分","data":{"":"NetworkDiagnostic.evaluateQuality() returns a weighted 0–100 score plus a\nlevel and human-readable suggestions, computed over whatever metrics are\navailable.NetworkDiagnostic.evaluateQuality() 返回一个加权 0–100 分,附带等级与可读建议,\n它基于当前可用的各项指标计算得出。","basic--基础#Basic / 基础":"final quality = await NetworkDiagnostic.evaluateQuality(\n includePing: true,\n includeDns: true,\n includeSpeedTest: true,\n includeUpload: true,\n);\nprint('${quality.score.toStringAsFixed(1)}/100 — ${quality.level.label}');\nquality.metrics.forEach((metric, value) {\n print(' $metric = ${value.toStringAsFixed(2)}');\n});\nfor (final suggestion in quality.suggestions) {\n print('• $suggestion');\n}\nParameter\tDefault\tMeaning\tincludePing\ttrue\tSample latency / jitter / loss\tincludeDns\ttrue\tSample DNS latency\tincludeSpeedTest\ttrue\tSample download / upload (heavy)\tincludeUpload\ttrue\tInclude the upload metric\tpingHost / pingCount\tfrom config\tLatency target\tdnsDomain / dnsServers\tfrom config\tDNS target\tdownloadUrl / uploadUrl\tfrom config\tBandwidth endpoints","lightweight-variant--轻量变体#Lightweight variant / 轻量变体":"final quality = await NetworkDiagnostic.evaluateQuality(includeSpeedTest: false);","level-thresholds--等级阈值#Level thresholds / 等级阈值":"≥90 excellent · ≥75 good · ≥60 fair · ≥40 poor · otherwise bad\n(NetworkQualityLevel.fromScore).≥90 极佳 · ≥75 良好 · ≥60 一般 · ≥40 较差 · 其余为极差。","weights--权重#Weights / 权重":"Metric\tWeight\tSource\tlatency\t0.25\tPingResult.averageTime\tjitter\t0.10\tPingResult.jitter\tpacketLoss\t0.15\tPingResult.packetLoss\tdownload\t0.25\tSpeedTestResult.downloadSpeed\tupload\t0.15\tSpeedTestResult.uploadSpeed\tdns\t0.10\tmean of successful DnsTestResult.responseTimeMs\tsignalStrength\t0.10\tNetworkConnectionInfo.signalStrength","pure-function--纯函数#Pure function / 纯函数":"If you already have the metrics, skip the network entirely:如果你已有指标,可完全跳过网络:\nfinal score = NetworkQualityEvaluator.evaluate(\n latency: 42, jitter: 6, packetLoss: 0,\n download: 88.4, upload: 12.1, dns: 25, signalStrength: -55,\n targets: const QualityTargets(),\n);\nprint('${score.score.toStringAsFixed(1)} → ${score.level.label}');\nTune the ideal values globally through NetworkDiagnosticConfig(qualityTargets: ...).\n调整理想值请通过 NetworkDiagnosticConfig(qualityTargets: ...) 全局设置。"}},"/Speed-Test":{"title":"Speed Test / 测速","data":{"":"NetworkDiagnostic.runSpeedTest() measures download and upload throughput and\nsamples latency during the run, reporting progress through onProgress.NetworkDiagnostic.runSpeedTest() 测量下载与上传吞吐量,并在过程中采样延迟,通过\nonProgress 汇报进度。","basic--基础#Basic / 基础":"final speed = await NetworkDiagnostic.runSpeedTest(\n includeUpload: true,\n includePing: true,\n onProgress: (progress) {\n print('${progress.phase.name}: '\n '${progress.speedMbps.toStringAsFixed(1)} Mbps '\n '(${progress.bytes} bytes, ${progress.elapsed.inMilliseconds} ms)');\n },\n);\nprint('download : ${speed.downloadSpeed.toStringAsFixed(2)} Mbps');\nprint('upload : ${speed.uploadSpeed.toStringAsFixed(2)} Mbps');\nprint('ping : ${speed.ping.toStringAsFixed(1)} ms');\nprint('jitter : ${speed.jitter.toStringAsFixed(2)} ms');\nprint('loss : ${speed.packetLoss.toStringAsFixed(1)} %');\nprint('server : ${speed.server}');\nprint('duration : ${speed.duration.inMilliseconds} ms');\nParameter\tDefault\tMeaning\tdownloadUrl / uploadUrl\tfrom config\tEndpoints used\ttimeout\tconfig.speedTestTimeout\tPer-request timeout\tmaxDuration\tconfig.speedTestMaxDuration\tSampling window per phase\tuploadPayloadBytes\tconfig.uploadPayloadBytes\tUpload size\tpingHost / pingCount\tfrom config\tLatency sampled during the test\tincludeUpload\ttrue\tSkip the upload phase\tincludePing\ttrue\tSkip the latency sample\tonProgress\tnull\tCalled repeatedly during both phases\t\nProgress phases: SpeedTestPhase.download → SpeedTestPhase.upload →\nSpeedTestPhase.completed.进度阶段依次为 download → upload → completed。","fast-low-traffic-variant--轻量低流量变体#Fast, low-traffic variant / 轻量低流量变体":"final quick = await NetworkDiagnostic.runSpeedTest(\n includeUpload: false,\n includePing: false,\n maxDuration: const Duration(seconds: 5),\n);\nThe default endpoints point at Cloudflare's public speed service and move real\ntraffic (≈25 MB download by default). Ask for consent before running it on a\nmetered connection, and override downloadUrl / uploadUrl for production.\n默认端点指向 Cloudflare 公共服务并会产生真实流量(默认下载约 25 MB)。在按量计费\n网络上运行前请征得同意,并为生产环境替换 downloadUrl / uploadUrl。"}},"/Usage":{"title":"Usage / 用法","data":{"which-api-do-i-need--该用哪个-api#Which API do I need? / 该用哪个 API?":"I want to know…\tCall\tAm I online, and over what transport?\tNetworkDiagnostic.checkConnection()\tReact to Wi-Fi ⇄ cellular switches\tNetworkDiagnostic.onConnectivityChanged\tLatency, jitter, packet loss\tNetworkDiagnostic.ping()\tIs DNS slow or broken?\tNetworkDiagnostic.resolve()\tIs host:port reachable?\tNetworkDiagnostic.checkPort()\tWhich of these ports are open?\tNetworkDiagnostic.scanPorts()\tHow fast is down/up?\tNetworkDiagnostic.runSpeedTest()\tOne number for “good or bad”\tNetworkDiagnostic.evaluateQuality()\tEverything at once, as a report\tNetworkDiagnostic.diagnose()\tIs the diagnostics API itself expensive?\tNetworkBenchmark.runAll()\tNative version / SSID / gateway / MAC\tZeroNetworkKit.getPlatformVersion(), getNativeNetworkDetails()","global-configuration--全局配置#Global configuration / 全局配置":"NetworkDiagnosticConfig centralises every default. Override only what you\nneed; the rest keep the defaults below.NetworkDiagnosticConfig 集中了全部默认参数,只需覆盖你关心的字段。\nZeroNetworkKit.init(\n config: const NetworkDiagnosticConfig(\n pingHost: '1.1.1.1',\n pingPort: 443,\n pingCount: 5,\n dnsDomain: 'example.com',\n dnsServers: ['1.1.1.1', '8.8.8.8'],\n downloadUrl: 'https://my-cdn.example.com/speedtest.bin',\n uploadUrl: 'https://my-cdn.example.com/upload',\n ),\n);\nField\tDefault\tMeaning\tpingHost\t'1.1.1.1'\tDefault ping / port-scan target\tpingPort\t443\tPort used by the TCP ping\tpingCount\t4\tProbes per ping run\tpingTimeout\t3s\tTimeout of one probe\tpingInterval\t200ms\tDelay between probes\tdnsDomain\t'www.google.com'\tDefault domain for DNS tests\tdnsServers\t['1.1.1.1', '8.8.8.8', '114.114.114.114']\tServers queried by default\tdnsTimeout\t5s\tTimeout per DNS server\tdownloadUrl\tCloudflare __down?bytes=25000000\tDownload endpoint\tuploadUrl\tCloudflare __up\tUpload endpoint\tuploadPayloadBytes\t1048576 (1 MiB)\tUpload payload size\tspeedTestTimeout\t30s\tPer-request timeout\tspeedTestMaxDuration\t10s\tSampling window per phase\tportCheckTimeout\t3s\tTimeout of one port check\tprobePorts\t[80, 443]\tPorts used by scanPorts() by default\tqualityTargets\tconst QualityTargets()\tIdeal values for scoring\t\nThe default speed-test endpoints are Cloudflare's public service. Swap them for\nyour own before shipping production traffic.\n默认测速端点是 Cloudflare 的公共服务,正式项目请替换为自建端点。","quality-targets--质量评分理想值#Quality targets / 质量评分理想值":"const NetworkDiagnosticConfig(\n qualityTargets: QualityTargets(\n excellentLatency: 30, // ms\n acceptableLatency: 150, // ms\n excellentJitter: 5, // ms\n acceptableJitter: 40, // ms\n acceptablePacketLoss: 5, // %\n excellentDownload: 50, // Mbps\n acceptableDownload: 5, // Mbps\n excellentUpload: 20, // Mbps\n acceptableUpload: 2, // Mbps\n excellentDns: 30, // ms\n acceptableDns: 200, // ms\n ),\n);","json-serialisation--序列化#JSON serialisation / 序列化":"Every result object implements toMap() and is JSON encodable:每个结果对象都实现了 toMap(),可直接 JSON 序列化:\nimport 'dart:convert';\nfinal report = await NetworkDiagnostic.diagnose(includeSpeedTest: false);\nfinal json = jsonEncode(report.toMap());\nprint(json);\n// Round-trip a connection snapshot.\nfinal decoded = NetworkConnectionInfo.fromMap(jsonDecode(jsonEncode(\n report.connection.toMap(),\n)) as Map);\nType\tSerialisation\tNetworkConnectionInfo\ttoMap() + fromMap()\tPingResult\ttoMap()\tDnsTestResult\ttoMap()\tPortCheckResult\ttoMap()\tSpeedTestResult\ttoMap()\tNetworkQualityScore\ttoMap()\tNetworkDiagnosticReport\ttoMap()\tBenchmarkResult / BenchmarkSuiteResult\ttoMap()","dependency-injection--tests--注入与测试#Dependency injection & tests / 注入与测试":"Every service accepts its collaborators, so your integration points are\nunit-testable without touching the network.每个服务都支持注入协作对象,可以完全脱离网络做单测。\nclass FakeConnectivityAdapter implements ConnectivityAdapter {\n @override\n Future> checkConnectivity() async => ['wifi'];\n @override\n Stream> get onConnectivityChanged => const Stream.empty();\n}\nNetworkDiagnostic.configure(\n config: const NetworkDiagnosticConfig(pingHost: '127.0.0.1'),\n connectivity: ConnectivityService(adapter: FakeConnectivityAdapter()),\n ping: PingService(),\n dns: DnsService(),\n ports: PortService(),\n speedTest: SpeedTestService(client: fakeClient),\n);\n// Restore defaults afterwards.\nNetworkDiagnostic.reset();\nSwap individual services directly when you prefer:也可以直接替换单个服务:\nNetworkDiagnostic.pingService = MyFakePingService();\nNetworkDiagnostic.dnsService = MyFakeDnsService();\nFor advanced service classes, the DNS wire codec and low-level APIs, import\npackage:zero_network_kit/advanced.dart.\n进阶的服务类、DNS 报文编解码器等底层 API 请通过\npackage:zero_network_kit/advanced.dart 引入。"}},"/":{"title":"Zero Network Kit","data":{"":"A Flutter plugin for network diagnostics: connectivity inspection, latency\nprobing, DNS resolution, port checks, bandwidth measurement, quality scoring and\nmicro-benchmarks — on Android, iOS, macOS, Windows and Linux.一个 Flutter 插件,提供网络诊断能力:连通性检测、延迟探测、DNS 解析、端口\n检测、带宽测速、质量评分与微基准测试——覆盖 Android、iOS、macOS、Windows 与 Linux。","-features--功能特性#✨ Features / 功能特性":"Feature\tDescription\tZero setup\tWorks out of the box; init() only to override defaults / 开箱即用,仅需在想覆盖默认值时才 init()\tConnectivity\tTransport type, IPv4/IPv6, gateway, SSID, RSSI, MAC, VPN / 传输类型、IPv4/IPv6、网关、SSID、信号强度、MAC、VPN\tLatency\tTCP handshake RTT everywhere; system ICMP on desktop / TCP 握手 RTT 全平台可用,桌面额外支持系统 ICMP\tDNS\tSystem resolver + raw UDP against explicit servers / 系统解析器 + 针对指定服务器的原始 UDP 查询\tPorts\tBounded-concurrency TCP reachability / 有界并发的 TCP 可达性检测\tSpeed test\tDownload/upload throughput with progress callbacks / 上下行测速,带进度回调\tQuality\tWeighted 0–100 score + level + suggestions / 加权 0–100 分 + 等级 + 建议\tFull report\tOne-shot aggregate of every probe / 一次性汇总所有探测\tBenchmarks\tMeasures how fast the diagnostics API itself runs / 衡量诊断 API 自身开销\tCapabilities\tQuery-then-call; hide unsupported cards (e.g. SSID on desktop) / 先查询后调用,隐藏不支持的卡片\tGraceful degradation\tMissing permission or unreachable sub-service never throws / 缺权限或子服务不可达时只返回 null,绝不抛异常\tHermetic tests\tEvery service accepts injected collaborators / 每个服务都支持注入协作对象,便于单测","-table-of-contents--目录#📚 Table of Contents / 目录":"Page\tDescription\tGetting Started\tQuick start guide / 快速开始\tInstallation\tHow to install / 安装方式\tUsage\tGlobal config, serialisation, dependency injection / 全局配置、序列化、依赖注入\tConnectivity\tConnection snapshot & change stream / 连通性快照与变化监听\tLatency / Ping\tTCP & ICMP latency probing / 延迟探测\tDNS\tResolver comparison across servers / DNS 解析\tPorts\tSingle port & bulk scan / 端口检测\tSpeed Test\tDownload/upload throughput / 测速\tQuality Score\tWeighted scoring / 质量评分\tFull Report\tOne-shot aggregate / 汇总报告\tBenchmarks\tMicro-benchmarks / 微基准\tPlatform Support\tCapability matrix & native details / 能力矩阵与原生详情\tAPI Reference\tModel field reference & gotchas / 模型字段速查与陷阱\tFAQ\tFrequently asked questions / 常见问题","-links--链接#🔗 Links / 链接":"GitHub\nOfficial Website\npub.dev","-license--许可证#📄 License / 许可证":"This project is licensed under the MPL-2.0.本项目采用 MPL-2.0 许可证。This plugin is provided \"as is\", without warranty of any kind. The author assumes\nno responsibility or liability for the functionality, security, or any\nconsequences arising from the use of modified versions or derivative projects.本插件按\"原样\"提供,不提供任何担保。作者不对修改版或衍生项目的功能、安全性及\n任何使用后果承担责任。"}}} \ No newline at end of file diff --git a/docs/_next/static/chunks/pages/Installation-43ade467e1e7ca4a.js b/docs/_next/static/chunks/pages/Installation-a8871a37d0592eaa.js similarity index 98% rename from docs/_next/static/chunks/pages/Installation-43ade467e1e7ca4a.js rename to docs/_next/static/chunks/pages/Installation-a8871a37d0592eaa.js index b653e6c..574edf4 100644 --- a/docs/_next/static/chunks/pages/Installation-43ade467e1e7ca4a.js +++ b/docs/_next/static/chunks/pages/Installation-a8871a37d0592eaa.js @@ -1 +1 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[139],{9720:function(e,i,t){(window.__NEXT_P=window.__NEXT_P||[]).push(["/Installation",function(){return t(1220)}])},1220:function(e,i,t){"use strict";t.r(i),t.d(i,{useTOC:function(){return l}});var n=t(5893),s=t(7812),r=t(7080),d=t(8925),a=t(5192);function l(e){return[{value:"From pub.dev (Recommended) / 从 pub.dev 安装(推荐)",id:"from-pubdev-recommended--从-pubdev-安装推荐",depth:2},{value:"From GitHub / 从 GitHub 安装",id:"from-github--从-github-安装",depth:2},{value:"Platform Setup / 平台配置",id:"platform-setup--平台配置",depth:2},{value:"Android",id:"android",depth:3},{value:"iOS",id:"ios",depth:3},{value:"macOS / Windows / Linux",id:"macos--windows--linux",depth:3},{value:"Import / 导入",id:"import--导入",depth:2},{value:"Requirements / 环境要求",id:"requirements--环境要求",depth:2},{value:"Next Steps / 下一步",id:"next-steps--下一步",depth:2}]}i.default=(0,s.c)(function(e){let{toc:i=l(e)}=e,t={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,d.a)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.h1,{children:"Installation / 安装"}),"\n",(0,n.jsx)(t.h2,{id:i[0].id,children:i[0].value}),"\n",(0,n.jsxs)(t.p,{children:["Add the following to your ",(0,n.jsx)(t.code,{children:"pubspec.yaml"}),":"]}),"\n",(0,n.jsxs)(t.p,{children:["在 ",(0,n.jsx)(t.code,{children:"pubspec.yaml"})," 中添加以下依赖:"]}),"\n",(0,n.jsx)(t.pre,{tabIndex:"0","data-language":"yaml","data-word-wrap":"","data-copy":"",children:(0,n.jsxs)(t.code,{children:[(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#22863A","--shiki-dark":"#85E89D"},children:"dependencies"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:":"})]}),"\n",(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#22863A","--shiki-dark":"#85E89D"},children:" zero_network_kit"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:": "}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:"^1.0.1"})]})]})}),"\n",(0,n.jsx)(t.p,{children:"Then run:"}),"\n",(0,n.jsx)(t.p,{children:"然后运行:"}),"\n",(0,n.jsx)(t.pre,{icon:a.Fx,tabIndex:"0","data-language":"bash","data-word-wrap":"","data-copy":"",children:(0,n.jsx)(t.code,{children:(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#6F42C1","--shiki-dark":"#B392F0"},children:"flutter"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:" pub"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:" get"})]})})}),"\n",(0,n.jsx)(t.h2,{id:i[1].id,children:i[1].value}),"\n",(0,n.jsx)(t.p,{children:"Alternatively, install from GitHub:"}),"\n",(0,n.jsx)(t.p,{children:"或者从 GitHub 安装:"}),"\n",(0,n.jsx)(t.pre,{tabIndex:"0","data-language":"yaml","data-word-wrap":"","data-copy":"",children:(0,n.jsxs)(t.code,{children:[(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#22863A","--shiki-dark":"#85E89D"},children:"dependencies"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:":"})]}),"\n",(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#22863A","--shiki-dark":"#85E89D"},children:" zero_network_kit"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:":"})]}),"\n",(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#22863A","--shiki-dark":"#85E89D"},children:" git"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:":"})]}),"\n",(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#22863A","--shiki-dark":"#85E89D"},children:" url"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:": "}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:"https://github.com/zero-labsco/zero_network_kit.git"})]}),"\n",(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#22863A","--shiki-dark":"#85E89D"},children:" ref"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:": "}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:"release/v1.0.1"})]})]})}),"\n",(0,n.jsx)(t.h2,{id:i[2].id,children:i[2].value}),"\n",(0,n.jsx)(t.h3,{id:i[3].id,children:i[3].value}),"\n",(0,n.jsxs)(t.p,{children:["The plugin manifest already declares ",(0,n.jsx)(t.code,{children:"INTERNET"}),", ",(0,n.jsx)(t.code,{children:"ACCESS_NETWORK_STATE"})," and\n",(0,n.jsx)(t.code,{children:"ACCESS_WIFI_STATE"}),". Reading the Wi-Fi ",(0,n.jsx)(t.strong,{children:"SSID"})," additionally requires the\nlocation permission (",(0,n.jsx)(t.code,{children:"ACCESS_FINE_LOCATION"}),") on Android 8.1+, otherwise the\nsnapshot reports ",(0,n.jsx)(t.code,{children:"ssid: null"}),"."]}),"\n",(0,n.jsxs)(t.p,{children:["插件清单已声明 ",(0,n.jsx)(t.code,{children:"INTERNET"}),"、",(0,n.jsx)(t.code,{children:"ACCESS_NETWORK_STATE"})," 与 ",(0,n.jsx)(t.code,{children:"ACCESS_WIFI_STATE"}),"。读取 Wi-Fi\n",(0,n.jsx)(t.strong,{children:"SSID"})," 还需在 Android 8.1+ 申请定位权限(",(0,n.jsx)(t.code,{children:"ACCESS_FINE_LOCATION"}),"),否则快照中\n",(0,n.jsx)(t.code,{children:"ssid"})," 为 ",(0,n.jsx)(t.code,{children:"null"}),"。"]}),"\n",(0,n.jsx)(t.h3,{id:i[4].id,children:i[4].value}),"\n",(0,n.jsxs)(t.p,{children:["No additional configuration needed. Reading the Wi-Fi ",(0,n.jsx)(t.strong,{children:"SSID"})," additionally\nrequires the ",(0,n.jsx)(t.em,{children:"Access WiFi Information"})," capability plus location authorisation."]}),"\n",(0,n.jsxs)(t.p,{children:["无需额外配置。读取 Wi-Fi ",(0,n.jsx)(t.strong,{children:"SSID"})," 还需开启 ",(0,n.jsx)(t.em,{children:"Access WiFi Information"})," 能力并授权定位。"]}),"\n",(0,n.jsx)(t.h3,{id:i[5].id,children:i[5].value}),"\n",(0,n.jsxs)(t.p,{children:["The plugin is declared on all three desktop platforms. On desktop, native\ndetails such as SSID / RSSI are ",(0,n.jsxs)(t.strong,{children:["always ",(0,n.jsx)(t.code,{children:"null"})]})," (tier A). Windows additionally\nprovides gateway / MAC / DNS / VPN through ",(0,n.jsx)(t.code,{children:"GetAdaptersAddresses"}),"; macOS and\nLinux report only the Dart-side IP/IPv6. See\n",(0,n.jsx)(t.a,{href:"Platform-Support",children:"Platform Support"})," for the full matrix."]}),"\n",(0,n.jsxs)(t.p,{children:["插件已在三个桌面平台声明。桌面上 SSID / 信号强度等原生详情",(0,n.jsxs)(t.strong,{children:["恒为 ",(0,n.jsx)(t.code,{children:"null"})]}),"(A 档)。\nWindows 额外通过 ",(0,n.jsx)(t.code,{children:"GetAdaptersAddresses"})," 提供网关 / MAC / DNS / VPN;macOS 与 Linux\n仅提供 Dart 侧的 IP/IPv6。完整矩阵见",(0,n.jsx)(t.a,{href:"Platform-Support",children:"平台支持"}),"。"]}),"\n",(0,n.jsx)(t.h2,{id:i[6].id,children:i[6].value}),"\n",(0,n.jsx)(t.pre,{tabIndex:"0","data-language":"dart","data-word-wrap":"","data-copy":"",children:(0,n.jsx)(t.code,{children:(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:" 'package:zero_network_kit/zero_network_kit.dart'"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]})})}),"\n",(0,n.jsx)(t.h2,{id:i[7].id,children:i[7].value}),"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",(0,n.jsxs)(t.table,{children:[(0,n.jsx)(t.thead,{children:(0,n.jsxs)(t.tr,{children:[(0,n.jsx)(t.th,{children:"Requirement"}),(0,n.jsx)(t.th,{children:"Version"})]})}),(0,n.jsxs)(t.tbody,{children:[(0,n.jsxs)(t.tr,{children:[(0,n.jsx)(t.td,{children:"Flutter"}),(0,n.jsx)(t.td,{children:">= 3.3.0"})]}),(0,n.jsxs)(t.tr,{children:[(0,n.jsx)(t.td,{children:"Dart SDK"}),(0,n.jsx)(t.td,{children:">= 3.11.0 < 4.0.0"})]})]})]}),"\n",(0,n.jsx)(t.h2,{id:i[8].id,children:i[8].value}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.a,{href:"Getting-Started",children:"Getting Started"})," — Quick start guide / 快速开始"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.a,{href:"Usage",children:"Usage"})," — Full usage guide / 完整使用指南"]}),"\n"]})]})},"/Installation",{filePath:"pages/Installation.md",timestamp:1789334081e3,pageMap:r.v,frontMatter:{},title:"Installation / 安装"},"undefined"==typeof RemoteContent?l:RemoteContent.useTOC)},7080:function(e,i,t){"use strict";t.d(i,{v:function(){return n}});let n=[{data:{index:"\uD83C\uDFE0 Home","Getting-Started":"\uD83D\uDE80 Getting Started",Installation:"\uD83D\uDCE6 Installation",Usage:"\uD83D\uDCD6 Usage","--capabilities":{type:"separator",title:"\uD83D\uDD27 Capabilities"},Connectivity:"\uD83D\uDCE1 Connectivity",Ping:"⏱ Latency / Ping",DNS:"\uD83C\uDF0D DNS",Ports:"\uD83D\uDD0C Ports","Speed-Test":"\uD83D\uDE80 Speed Test",Quality:"⭐ Quality Score","Full-Report":"\uD83D\uDCCB Full Report",Benchmark:"\uD83D\uDCCA Benchmarks","--advanced":{type:"separator",title:"\uD83D\uDEE0 Advanced"},"Platform-Support":"\uD83D\uDDA5 Platform Support","API-Reference":"\uD83D\uDCDA API Reference","--info":{type:"separator",title:"ℹ️ Info"},FAQ:"❓ FAQ"}},{name:"API-Reference",route:"/API-Reference",frontMatter:{sidebarTitle:"API Reference"}},{name:"Benchmark",route:"/Benchmark",frontMatter:{sidebarTitle:"Benchmark"}},{name:"Connectivity",route:"/Connectivity",frontMatter:{sidebarTitle:"Connectivity"}},{name:"DNS",route:"/DNS",frontMatter:{sidebarTitle:"DNS"}},{name:"FAQ",route:"/FAQ",frontMatter:{sidebarTitle:"Faq"}},{name:"Full-Report",route:"/Full-Report",frontMatter:{sidebarTitle:"Full Report"}},{name:"Getting-Started",route:"/Getting-Started",frontMatter:{sidebarTitle:"Getting Started"}},{name:"index",route:"/",frontMatter:{sidebarTitle:"Index"}},{name:"Installation",route:"/Installation",frontMatter:{sidebarTitle:"Installation"}},{name:"Ping",route:"/Ping",frontMatter:{sidebarTitle:"Ping"}},{name:"Platform-Support",route:"/Platform-Support",frontMatter:{sidebarTitle:"Platform Support"}},{name:"Ports",route:"/Ports",frontMatter:{sidebarTitle:"Ports"}},{name:"Quality",route:"/Quality",frontMatter:{sidebarTitle:"Quality"}},{name:"Speed-Test",route:"/Speed-Test",frontMatter:{sidebarTitle:"Speed Test"}},{name:"Usage",route:"/Usage",frontMatter:{sidebarTitle:"Usage"}}]}},function(e){e.O(0,[812,888,774,179],function(){return e(e.s=9720)}),_N_E=e.O()}]); \ No newline at end of file +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[139],{9720:function(e,i,t){(window.__NEXT_P=window.__NEXT_P||[]).push(["/Installation",function(){return t(1220)}])},1220:function(e,i,t){"use strict";t.r(i),t.d(i,{useTOC:function(){return l}});var n=t(5893),s=t(7812),r=t(7080),d=t(8925),a=t(5192);function l(e){return[{value:"From pub.dev (Recommended) / 从 pub.dev 安装(推荐)",id:"from-pubdev-recommended--从-pubdev-安装推荐",depth:2},{value:"From GitHub / 从 GitHub 安装",id:"from-github--从-github-安装",depth:2},{value:"Platform Setup / 平台配置",id:"platform-setup--平台配置",depth:2},{value:"Android",id:"android",depth:3},{value:"iOS",id:"ios",depth:3},{value:"macOS / Windows / Linux",id:"macos--windows--linux",depth:3},{value:"Import / 导入",id:"import--导入",depth:2},{value:"Requirements / 环境要求",id:"requirements--环境要求",depth:2},{value:"Next Steps / 下一步",id:"next-steps--下一步",depth:2}]}i.default=(0,s.c)(function(e){let{toc:i=l(e)}=e,t={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,d.a)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.h1,{children:"Installation / 安装"}),"\n",(0,n.jsx)(t.h2,{id:i[0].id,children:i[0].value}),"\n",(0,n.jsxs)(t.p,{children:["Add the following to your ",(0,n.jsx)(t.code,{children:"pubspec.yaml"}),":"]}),"\n",(0,n.jsxs)(t.p,{children:["在 ",(0,n.jsx)(t.code,{children:"pubspec.yaml"})," 中添加以下依赖:"]}),"\n",(0,n.jsx)(t.pre,{tabIndex:"0","data-language":"yaml","data-word-wrap":"","data-copy":"",children:(0,n.jsxs)(t.code,{children:[(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#22863A","--shiki-dark":"#85E89D"},children:"dependencies"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:":"})]}),"\n",(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#22863A","--shiki-dark":"#85E89D"},children:" zero_network_kit"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:": "}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:"^1.0.2"})]})]})}),"\n",(0,n.jsx)(t.p,{children:"Then run:"}),"\n",(0,n.jsx)(t.p,{children:"然后运行:"}),"\n",(0,n.jsx)(t.pre,{icon:a.Fx,tabIndex:"0","data-language":"bash","data-word-wrap":"","data-copy":"",children:(0,n.jsx)(t.code,{children:(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#6F42C1","--shiki-dark":"#B392F0"},children:"flutter"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:" pub"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:" get"})]})})}),"\n",(0,n.jsx)(t.h2,{id:i[1].id,children:i[1].value}),"\n",(0,n.jsx)(t.p,{children:"Alternatively, install from GitHub:"}),"\n",(0,n.jsx)(t.p,{children:"或者从 GitHub 安装:"}),"\n",(0,n.jsx)(t.pre,{tabIndex:"0","data-language":"yaml","data-word-wrap":"","data-copy":"",children:(0,n.jsxs)(t.code,{children:[(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#22863A","--shiki-dark":"#85E89D"},children:"dependencies"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:":"})]}),"\n",(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#22863A","--shiki-dark":"#85E89D"},children:" zero_network_kit"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:":"})]}),"\n",(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#22863A","--shiki-dark":"#85E89D"},children:" git"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:":"})]}),"\n",(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#22863A","--shiki-dark":"#85E89D"},children:" url"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:": "}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:"https://github.com/zero-labsco/zero_network_kit.git"})]}),"\n",(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#22863A","--shiki-dark":"#85E89D"},children:" ref"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:": "}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:"release/v1.0.2"})]})]})}),"\n",(0,n.jsx)(t.h2,{id:i[2].id,children:i[2].value}),"\n",(0,n.jsx)(t.h3,{id:i[3].id,children:i[3].value}),"\n",(0,n.jsxs)(t.p,{children:["The plugin manifest already declares ",(0,n.jsx)(t.code,{children:"INTERNET"}),", ",(0,n.jsx)(t.code,{children:"ACCESS_NETWORK_STATE"})," and\n",(0,n.jsx)(t.code,{children:"ACCESS_WIFI_STATE"}),". Reading the Wi-Fi ",(0,n.jsx)(t.strong,{children:"SSID"})," additionally requires the\nlocation permission (",(0,n.jsx)(t.code,{children:"ACCESS_FINE_LOCATION"}),") on Android 8.1+, otherwise the\nsnapshot reports ",(0,n.jsx)(t.code,{children:"ssid: null"}),"."]}),"\n",(0,n.jsxs)(t.p,{children:["插件清单已声明 ",(0,n.jsx)(t.code,{children:"INTERNET"}),"、",(0,n.jsx)(t.code,{children:"ACCESS_NETWORK_STATE"})," 与 ",(0,n.jsx)(t.code,{children:"ACCESS_WIFI_STATE"}),"。读取 Wi-Fi\n",(0,n.jsx)(t.strong,{children:"SSID"})," 还需在 Android 8.1+ 申请定位权限(",(0,n.jsx)(t.code,{children:"ACCESS_FINE_LOCATION"}),"),否则快照中\n",(0,n.jsx)(t.code,{children:"ssid"})," 为 ",(0,n.jsx)(t.code,{children:"null"}),"。"]}),"\n",(0,n.jsx)(t.h3,{id:i[4].id,children:i[4].value}),"\n",(0,n.jsxs)(t.p,{children:["No additional configuration needed. Reading the Wi-Fi ",(0,n.jsx)(t.strong,{children:"SSID"})," additionally\nrequires the ",(0,n.jsx)(t.em,{children:"Access WiFi Information"})," capability plus location authorisation."]}),"\n",(0,n.jsxs)(t.p,{children:["无需额外配置。读取 Wi-Fi ",(0,n.jsx)(t.strong,{children:"SSID"})," 还需开启 ",(0,n.jsx)(t.em,{children:"Access WiFi Information"})," 能力并授权定位。"]}),"\n",(0,n.jsx)(t.h3,{id:i[5].id,children:i[5].value}),"\n",(0,n.jsxs)(t.p,{children:["The plugin is declared on all three desktop platforms. On desktop, native\ndetails such as SSID / RSSI are ",(0,n.jsxs)(t.strong,{children:["always ",(0,n.jsx)(t.code,{children:"null"})]})," (tier A). Windows additionally\nprovides gateway / MAC / DNS / VPN through ",(0,n.jsx)(t.code,{children:"GetAdaptersAddresses"}),"; macOS and\nLinux report only the Dart-side IP/IPv6. See\n",(0,n.jsx)(t.a,{href:"Platform-Support",children:"Platform Support"})," for the full matrix."]}),"\n",(0,n.jsxs)(t.p,{children:["插件已在三个桌面平台声明。桌面上 SSID / 信号强度等原生详情",(0,n.jsxs)(t.strong,{children:["恒为 ",(0,n.jsx)(t.code,{children:"null"})]}),"(A 档)。\nWindows 额外通过 ",(0,n.jsx)(t.code,{children:"GetAdaptersAddresses"})," 提供网关 / MAC / DNS / VPN;macOS 与 Linux\n仅提供 Dart 侧的 IP/IPv6。完整矩阵见",(0,n.jsx)(t.a,{href:"Platform-Support",children:"平台支持"}),"。"]}),"\n",(0,n.jsx)(t.h2,{id:i[6].id,children:i[6].value}),"\n",(0,n.jsx)(t.pre,{tabIndex:"0","data-language":"dart","data-word-wrap":"","data-copy":"",children:(0,n.jsx)(t.code,{children:(0,n.jsxs)(t.span,{children:[(0,n.jsx)(t.span,{style:{"--shiki-light":"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:" 'package:zero_network_kit/zero_network_kit.dart'"}),(0,n.jsx)(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]})})}),"\n",(0,n.jsx)(t.h2,{id:i[7].id,children:i[7].value}),"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",(0,n.jsxs)(t.table,{children:[(0,n.jsx)(t.thead,{children:(0,n.jsxs)(t.tr,{children:[(0,n.jsx)(t.th,{children:"Requirement"}),(0,n.jsx)(t.th,{children:"Version"})]})}),(0,n.jsxs)(t.tbody,{children:[(0,n.jsxs)(t.tr,{children:[(0,n.jsx)(t.td,{children:"Flutter"}),(0,n.jsx)(t.td,{children:">= 3.3.0"})]}),(0,n.jsxs)(t.tr,{children:[(0,n.jsx)(t.td,{children:"Dart SDK"}),(0,n.jsx)(t.td,{children:">= 3.11.0 < 4.0.0"})]})]})]}),"\n",(0,n.jsx)(t.h2,{id:i[8].id,children:i[8].value}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.a,{href:"Getting-Started",children:"Getting Started"})," — Quick start guide / 快速开始"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.a,{href:"Usage",children:"Usage"})," — Full usage guide / 完整使用指南"]}),"\n"]})]})},"/Installation",{filePath:"pages/Installation.md",timestamp:1789334081e3,pageMap:r.v,frontMatter:{},title:"Installation / 安装"},"undefined"==typeof RemoteContent?l:RemoteContent.useTOC)},7080:function(e,i,t){"use strict";t.d(i,{v:function(){return n}});let n=[{data:{index:"\uD83C\uDFE0 Home","Getting-Started":"\uD83D\uDE80 Getting Started",Installation:"\uD83D\uDCE6 Installation",Usage:"\uD83D\uDCD6 Usage","--capabilities":{type:"separator",title:"\uD83D\uDD27 Capabilities"},Connectivity:"\uD83D\uDCE1 Connectivity",Ping:"⏱ Latency / Ping",DNS:"\uD83C\uDF0D DNS",Ports:"\uD83D\uDD0C Ports","Speed-Test":"\uD83D\uDE80 Speed Test",Quality:"⭐ Quality Score","Full-Report":"\uD83D\uDCCB Full Report",Benchmark:"\uD83D\uDCCA Benchmarks","--advanced":{type:"separator",title:"\uD83D\uDEE0 Advanced"},"Platform-Support":"\uD83D\uDDA5 Platform Support","API-Reference":"\uD83D\uDCDA API Reference","--info":{type:"separator",title:"ℹ️ Info"},FAQ:"❓ FAQ"}},{name:"API-Reference",route:"/API-Reference",frontMatter:{sidebarTitle:"API Reference"}},{name:"Benchmark",route:"/Benchmark",frontMatter:{sidebarTitle:"Benchmark"}},{name:"Connectivity",route:"/Connectivity",frontMatter:{sidebarTitle:"Connectivity"}},{name:"DNS",route:"/DNS",frontMatter:{sidebarTitle:"DNS"}},{name:"FAQ",route:"/FAQ",frontMatter:{sidebarTitle:"Faq"}},{name:"Full-Report",route:"/Full-Report",frontMatter:{sidebarTitle:"Full Report"}},{name:"Getting-Started",route:"/Getting-Started",frontMatter:{sidebarTitle:"Getting Started"}},{name:"index",route:"/",frontMatter:{sidebarTitle:"Index"}},{name:"Installation",route:"/Installation",frontMatter:{sidebarTitle:"Installation"}},{name:"Ping",route:"/Ping",frontMatter:{sidebarTitle:"Ping"}},{name:"Platform-Support",route:"/Platform-Support",frontMatter:{sidebarTitle:"Platform Support"}},{name:"Ports",route:"/Ports",frontMatter:{sidebarTitle:"Ports"}},{name:"Quality",route:"/Quality",frontMatter:{sidebarTitle:"Quality"}},{name:"Speed-Test",route:"/Speed-Test",frontMatter:{sidebarTitle:"Speed Test"}},{name:"Usage",route:"/Usage",frontMatter:{sidebarTitle:"Usage"}}]}},function(e){e.O(0,[812,888,774,179],function(){return e(e.s=9720)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/docs/_next/static/x-nTvOiGfo930-2DM2DH8/_buildManifest.js b/docs/_next/static/j1hO4dyly2im2OvPwfgSC/_buildManifest.js similarity index 91% rename from docs/_next/static/x-nTvOiGfo930-2DM2DH8/_buildManifest.js rename to docs/_next/static/j1hO4dyly2im2OvPwfgSC/_buildManifest.js index f0dc45e..093e5b5 100644 --- a/docs/_next/static/x-nTvOiGfo930-2DM2DH8/_buildManifest.js +++ b/docs/_next/static/j1hO4dyly2im2OvPwfgSC/_buildManifest.js @@ -1 +1 @@ -self.__BUILD_MANIFEST=function(e){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":[e,"static/chunks/pages/index-2a81fce2c8fa4aa7.js"],"/API-Reference":[e,"static/chunks/pages/API-Reference-e13a2917d7546319.js"],"/Benchmark":[e,"static/chunks/pages/Benchmark-a292e8cb0a504f00.js"],"/Connectivity":[e,"static/chunks/pages/Connectivity-f9b385023ad97f07.js"],"/DNS":[e,"static/chunks/pages/DNS-93340032eaa9ffdd.js"],"/FAQ":[e,"static/chunks/pages/FAQ-bccbbb52a0b53bb8.js"],"/Full-Report":[e,"static/chunks/pages/Full-Report-8fdda01136483e9e.js"],"/Getting-Started":[e,"static/chunks/pages/Getting-Started-0b9a79d0ebc87cdf.js"],"/Installation":[e,"static/chunks/pages/Installation-43ade467e1e7ca4a.js"],"/Ping":[e,"static/chunks/pages/Ping-e7614d3667bef7af.js"],"/Platform-Support":[e,"static/chunks/pages/Platform-Support-3994cdd8603e191b.js"],"/Ports":[e,"static/chunks/pages/Ports-743ec1523389b840.js"],"/Quality":[e,"static/chunks/pages/Quality-ac653fb19750eaeb.js"],"/Speed-Test":[e,"static/chunks/pages/Speed-Test-03d333b534600839.js"],"/Usage":[e,"static/chunks/pages/Usage-668e8555c3b8432b.js"],"/_error":["static/chunks/pages/_error-7a92967bea80186d.js"],"/_meta":["static/chunks/pages/_meta-b47e7a01bb6b7929.js"],sortedPages:["/","/API-Reference","/Benchmark","/Connectivity","/DNS","/FAQ","/Full-Report","/Getting-Started","/Installation","/Ping","/Platform-Support","/Ports","/Quality","/Speed-Test","/Usage","/_app","/_error","/_meta"]}}("static/chunks/812-02e96d01ac591260.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); \ No newline at end of file +self.__BUILD_MANIFEST=function(e){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":[e,"static/chunks/pages/index-2a81fce2c8fa4aa7.js"],"/API-Reference":[e,"static/chunks/pages/API-Reference-e13a2917d7546319.js"],"/Benchmark":[e,"static/chunks/pages/Benchmark-a292e8cb0a504f00.js"],"/Connectivity":[e,"static/chunks/pages/Connectivity-f9b385023ad97f07.js"],"/DNS":[e,"static/chunks/pages/DNS-93340032eaa9ffdd.js"],"/FAQ":[e,"static/chunks/pages/FAQ-bccbbb52a0b53bb8.js"],"/Full-Report":[e,"static/chunks/pages/Full-Report-8fdda01136483e9e.js"],"/Getting-Started":[e,"static/chunks/pages/Getting-Started-0b9a79d0ebc87cdf.js"],"/Installation":[e,"static/chunks/pages/Installation-a8871a37d0592eaa.js"],"/Ping":[e,"static/chunks/pages/Ping-e7614d3667bef7af.js"],"/Platform-Support":[e,"static/chunks/pages/Platform-Support-3994cdd8603e191b.js"],"/Ports":[e,"static/chunks/pages/Ports-743ec1523389b840.js"],"/Quality":[e,"static/chunks/pages/Quality-ac653fb19750eaeb.js"],"/Speed-Test":[e,"static/chunks/pages/Speed-Test-03d333b534600839.js"],"/Usage":[e,"static/chunks/pages/Usage-668e8555c3b8432b.js"],"/_error":["static/chunks/pages/_error-7a92967bea80186d.js"],"/_meta":["static/chunks/pages/_meta-b47e7a01bb6b7929.js"],sortedPages:["/","/API-Reference","/Benchmark","/Connectivity","/DNS","/FAQ","/Full-Report","/Getting-Started","/Installation","/Ping","/Platform-Support","/Ports","/Quality","/Speed-Test","/Usage","/_app","/_error","/_meta"]}}("static/chunks/812-02e96d01ac591260.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); \ No newline at end of file diff --git a/docs/_next/static/x-nTvOiGfo930-2DM2DH8/_ssgManifest.js b/docs/_next/static/j1hO4dyly2im2OvPwfgSC/_ssgManifest.js similarity index 100% rename from docs/_next/static/x-nTvOiGfo930-2DM2DH8/_ssgManifest.js rename to docs/_next/static/j1hO4dyly2im2OvPwfgSC/_ssgManifest.js diff --git a/docs/index.html b/docs/index.html index 0dc976a..8be3baa 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,4 +1,4 @@ -
    🏠 Home

    Zero Network Kit

    +
    🏠 Home

    Zero Network Kit

    A Flutter plugin for network diagnostics: connectivity inspection, latency probing, DNS resolution, port checks, bandwidth measurement, quality scoring and micro-benchmarks — on Android, iOS, macOS, Windows and Linux.

    @@ -143,4 +143,4 @@

    本插件按”原样”提供,不提供任何担保。作者不对修改版或衍生项目的功能、安全性及 -任何使用后果承担责任。


    Zero Network Kit · MPL-2.0
    \ No newline at end of file +任何使用后果承担责任。


    Zero Network Kit · MPL-2.0
    \ No newline at end of file diff --git a/example/pubspec.lock b/example/pubspec.lock index fa466ad..3f6cf53 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -378,7 +378,7 @@ packages: path: ".." relative: true source: path - version: "1.0.0" + version: "1.0.2" sdks: dart: ">=3.11.5 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/ios/Assets/.gitkeep b/ios/Assets/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/ios/zero_network_kit.podspec b/ios/zero_network_kit.podspec index 4719561..293ca3f 100644 --- a/ios/zero_network_kit.podspec +++ b/ios/zero_network_kit.podspec @@ -4,7 +4,7 @@ # Pod::Spec.new do |s| s.name = 'zero_network_kit' - s.version = '1.0.1' + s.version = '1.0.2' s.summary = 'Flutter network diagnostics: ping, DNS, speed test, port scan and quality score.' s.description = <<-DESC A Flutter plugin for network diagnostics: connectivity check, ping, DNS @@ -12,9 +12,10 @@ resolution, speed test, port scanning, quality scoring and micro-benchmarks. DESC s.homepage = 'https://github.com/zero-labsco/zero_network_kit' s.license = { :file => '../LICENSE' } - s.author = { 'Zero Labs' => 'dev@zerolabsco.com' } + s.author = { 'AmisKwok' => 'amiskwok@zerolabsco.com' } s.source = { :path => '.' } - s.source_files = 'Classes/**/*' + s.source_files = 'zero_network_kit/Sources/zero_network_kit/**/*.swift' + s.resource_bundles = { 'zero_network_kit_privacy' => ['zero_network_kit/Sources/zero_network_kit/PrivacyInfo.xcprivacy'] } s.dependency 'Flutter' s.platform = :ios, '13.0' diff --git a/ios/zero_network_kit/Package.swift b/ios/zero_network_kit/Package.swift new file mode 100644 index 0000000..f27ed13 --- /dev/null +++ b/ios/zero_network_kit/Package.swift @@ -0,0 +1,28 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "zero_network_kit", + platforms: [ + .iOS("13.0") + ], + products: [ + .library(name: "zero-network-kit", targets: ["zero_network_kit"]) + ], + dependencies: [ + .package(name: "FlutterFramework", path: "../FlutterFramework") + ], + targets: [ + .target( + name: "zero_network_kit", + dependencies: [ + .product(name: "FlutterFramework", package: "FlutterFramework") + ], + resources: [ + .process("PrivacyInfo.xcprivacy") + ] + ) + ] +) diff --git a/ios/Resources/PrivacyInfo.xcprivacy b/ios/zero_network_kit/Sources/zero_network_kit/PrivacyInfo.xcprivacy similarity index 100% rename from ios/Resources/PrivacyInfo.xcprivacy rename to ios/zero_network_kit/Sources/zero_network_kit/PrivacyInfo.xcprivacy diff --git a/ios/Classes/ZeroNetworkKitPlugin.swift b/ios/zero_network_kit/Sources/zero_network_kit/ZeroNetworkKitPlugin.swift similarity index 100% rename from ios/Classes/ZeroNetworkKitPlugin.swift rename to ios/zero_network_kit/Sources/zero_network_kit/ZeroNetworkKitPlugin.swift diff --git a/macos/zero_network_kit.podspec b/macos/zero_network_kit.podspec index b00bc32..008aa3d 100644 --- a/macos/zero_network_kit.podspec +++ b/macos/zero_network_kit.podspec @@ -4,23 +4,19 @@ # Pod::Spec.new do |s| s.name = 'zero_network_kit' - s.version = '1.0.1' - s.summary = 'A new Flutter plugin project.' + s.version = '1.0.2' + s.summary = 'Flutter network diagnostics: ping, DNS, speed test, port scan and quality score.' s.description = <<-DESC -A new Flutter plugin project. +A Flutter plugin for network diagnostics: connectivity check, ping, DNS +resolution, speed test, port scanning, quality scoring and micro-benchmarks. DESC - s.homepage = 'http://example.com' + s.homepage = 'https://github.com/zero-labsco/zero_network_kit' s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } + s.author = { 'AmisKwok' => 'amiskwok@zerolabsco.com' } s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - - # If your plugin requires a privacy manifest, for example if it collects user - # data, update the PrivacyInfo.xcprivacy file to describe your plugin's - # privacy impact, and then uncomment this line. For more information, - # see https://developer.apple.com/documentation/bundleresources/privacy_manifest_files - # s.resource_bundles = {'zero_network_kit_privacy' => ['Resources/PrivacyInfo.xcprivacy']} + s.source_files = 'zero_network_kit/Sources/zero_network_kit/**/*.swift' + s.resource_bundles = { 'zero_network_kit_privacy' => ['zero_network_kit/Sources/zero_network_kit/PrivacyInfo.xcprivacy'] } s.dependency 'FlutterMacOS' diff --git a/macos/zero_network_kit/Package.swift b/macos/zero_network_kit/Package.swift new file mode 100644 index 0000000..19dde24 --- /dev/null +++ b/macos/zero_network_kit/Package.swift @@ -0,0 +1,28 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "zero_network_kit", + platforms: [ + .macOS("10.15") + ], + products: [ + .library(name: "zero-network-kit", targets: ["zero_network_kit"]) + ], + dependencies: [ + .package(name: "FlutterFramework", path: "../FlutterFramework") + ], + targets: [ + .target( + name: "zero_network_kit", + dependencies: [ + .product(name: "FlutterFramework", package: "FlutterFramework") + ], + resources: [ + .process("PrivacyInfo.xcprivacy") + ] + ) + ] +) diff --git a/macos/Resources/PrivacyInfo.xcprivacy b/macos/zero_network_kit/Sources/zero_network_kit/PrivacyInfo.xcprivacy similarity index 100% rename from macos/Resources/PrivacyInfo.xcprivacy rename to macos/zero_network_kit/Sources/zero_network_kit/PrivacyInfo.xcprivacy diff --git a/macos/Classes/ZeroNetworkKitPlugin.swift b/macos/zero_network_kit/Sources/zero_network_kit/ZeroNetworkKitPlugin.swift similarity index 100% rename from macos/Classes/ZeroNetworkKitPlugin.swift rename to macos/zero_network_kit/Sources/zero_network_kit/ZeroNetworkKitPlugin.swift diff --git a/pubspec.yaml b/pubspec.yaml index f82b807..cda892c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: zero_network_kit -description: "Network diagnostics toolkit for Flutter: connectivity, ping, DNS, speed test, port scan and quality scoring across Android, iOS, macOS, Windows and Linux." -version: 1.0.1 +description: "Network diagnostics toolkit for Flutter: connectivity, ping, DNS, speed test, port scan and quality scoring across Android, iOS, macOS, Windows, Linux and Web." +version: 1.0.2 topics: - network - diagnostics