zero_network_kit 1.0.1
zero_network_kit: ^1.0.1 copied to clipboard
Network diagnostics toolkit for Flutter: connectivity, ping, DNS, speed test, port scan and quality scoring across Android, iOS, macOS, Windows and Linux.
zero_network_kit #
English | 简体中文
A Flutter plugin for network diagnostics: connectivity inspection, latency probing, DNS resolution, port checks, bandwidth measurement, quality scoring and micro-benchmarks — for Android, iOS, macOS, Windows, Linux and Web (partial).
🔔 Upgrade recommended:
1.0.1adds 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.
🌐 Official Website · 📦 View on pub.flutter-io.cn · 🔗 View on GitHub
Table of Contents #
- Features
- Getting started
- Usage
- Testing your own code against it
- Platform support
- Documentation
- License
Features #
| Capability | API | Notes |
|---|---|---|
| Connectivity | NetworkDiagnostic.checkConnection() |
Transport, IPv4/IPv6, gateway, SSID, RSSI, MAC, VPN |
| Connectivity stream | NetworkDiagnostic.onConnectivityChanged |
Emits a fresh snapshot on every change |
| Latency | NetworkDiagnostic.ping() |
TCP handshake RTT everywhere; system ICMP on desktop |
| DNS | NetworkDiagnostic.resolve() |
system resolver + raw UDP against explicit servers |
| Bandwidth | NetworkDiagnostic.runSpeedTest() |
Download/upload throughput with progress callbacks |
| Ports | NetworkDiagnostic.checkPort() / scanPorts() |
Bounded-concurrency TCP reachability |
| Quality | NetworkDiagnostic.evaluateQuality() |
Weighted 0–100 score + level + suggestions |
| Full report | NetworkDiagnostic.diagnose() |
One-shot aggregate of every probe |
| Capabilities | NetworkDiagnostic.capabilities |
Query-then-call; hide unsupported cards (e.g. SSID on desktop) |
| Benchmarks | NetworkBenchmark.runAll() |
Measures how fast the diagnostics API itself runs |
Design goals:
- Hermetic unit tests — every service accepts injected collaborators
(
ConnectivityAdapter,http.Client,PingService, …), so the whole test suite runs without touching the network. - No hidden state — results are immutable value objects with
toMap(). - Graceful degradation — a missing permission or an unreachable sub-service
never throws through the public API; the corresponding field stays
null.
Getting started #
dependencies:
zero_network_kit: ^1.0.1
Android permissions #
The plugin manifest already declares INTERNET, ACCESS_NETWORK_STATE and
ACCESS_WIFI_STATE. Reading the Wi-Fi SSID additionally requires location
permission (ACCESS_FINE_LOCATION) on Android 8.1+ and the Access WiFi
Information entitlement plus location authorisation on iOS. Without it the
snapshot simply reports ssid: null.
Usage #
The snippets below cover the common paths. For every method, parameter, default and result field, see the API guide.
import 'package:zero_network_kit/zero_network_kit.dart';
void main() {
// Optional: apply global defaults once.
ZeroNetworkKit.init(
config: const NetworkDiagnosticConfig(
pingHost: '1.1.1.1',
dnsServers: <String>['1.1.1.1', '8.8.8.8'],
),
);
runApp(const MyApp());
}
Connectivity #
final info = await NetworkDiagnostic.checkConnection(probeReachability: true);
print('${info.type.label} · ${info.ipAddress} · ${info.signalStrength} dBm');
print('gateway=${info.gateway} vpn=${info.isVpn} reachable=${info.isReachable}');
await for (final change in NetworkDiagnostic.onConnectivityChanged) {
print('now on ${change.type.id}');
}
Just need connectivity? You don't need
init()or any of the other APIs —checkConnection()andonConnectivityChangedwork out of the box. See the Connectivity-only: minimal example in the Connectivity chapter of API.md.
Ping #
final result = await NetworkDiagnostic.ping(host: '1.1.1.1', count: 5);
print('${result.received}/${result.sent} replies, '
'loss ${result.packetLoss.toStringAsFixed(1)}%, '
'avg ${result.averageTime.toStringAsFixed(1)} ms, '
'jitter ${result.jitter.toStringAsFixed(2)} ms');
On desktop you can switch to the system ICMP binary:
await NetworkDiagnostic.ping(host: '1.1.1.1', mode: PingMode.icmp);
DNS #
final results = await NetworkDiagnostic.resolve(
domain: 'example.com',
dnsServers: const <String>['1.1.1.1', '8.8.8.8'],
includeSystemResolver: true,
);
for (final r in results) {
print('${r.server}: ${r.isSuccess ? r.resolvedIps.join(", ") : r.errorMessage}'
' (${r.responseTimeMs.toStringAsFixed(1)} ms)');
}
Speed test #
final speed = await NetworkDiagnostic.runSpeedTest(
onProgress: (progress) => print(
'${progress.phase.name}: ${progress.speedMbps.toStringAsFixed(1)} Mbps',
),
);
print('↓ ${speed.downloadSpeed.toStringAsFixed(2)} Mbps '
'↑ ${speed.uploadSpeed.toStringAsFixed(2)} Mbps');
Download and upload phases write a meaningful amount of traffic. Defaults point
at the public Cloudflare speed endpoints; override downloadUrl / uploadUrl
to use your own.
Ports #
if (await NetworkDiagnostic.isPortOpen(host: 'example.com', port: 443)) {
print('HTTPS reachable');
}
final scan = await NetworkDiagnostic.scanPorts(
host: 'example.com',
ports: const <int>[22, 80, 443, 8080],
concurrency: 8,
);
Quality score #
final score = await NetworkDiagnostic.evaluateQuality(includeSpeedTest: false);
print('${score.score}/100 (${score.level.label})');
for (final suggestion in score.suggestions) {
print('• $suggestion');
}
The score is a weighted average of the metrics that are available:
| Metric | Weight | Source |
|---|---|---|
latency |
0.25 | PingResult.averageTime |
jitter |
0.10 | PingResult.jitter |
packetLoss |
0.15 | PingResult.packetLoss |
download |
0.25 | SpeedTestResult.downloadSpeed |
upload |
0.15 | SpeedTestResult.uploadSpeed |
dns |
0.10 | mean successful DnsTestResult.responseTimeMs |
signalStrength |
0.10 | NetworkConnectionInfo.signalStrength |
Tune the ideal values with NetworkDiagnosticConfig(qualityTargets: ...).
Full report #
final report = await NetworkDiagnostic.diagnose(includePorts: true);
print(report); // NetworkDiagnosticReport(type: wifi, connected: true, score: 87.5)
print(report.toMap()); // JSON encodable
Benchmarks #
final suite = await NetworkBenchmark.runAll(iterations: 20, warmupIterations: 3);
for (final result in suite.results) {
print('${result.testName}: avg '
'${(result.averageDuration.inMicroseconds / 1000).toStringAsFixed(2)} ms, '
'${result.operationsPerSecond.toStringAsFixed(1)} ops/s');
}
Benchmarks answer questions like "how expensive is one checkConnection() call
on this device?" — useful when deciding whether a diagnostic belongs on the
startup path.
Testing your own code against it #
Every service is injectable, so you can unit-test your integration points without a network:
class FakeConnectivityAdapter implements ConnectivityAdapter {
@override
Future<List<String>> checkConnectivity() async => <String>['wifi'];
@override
Stream<List<String>> get onConnectivityChanged => const Stream.empty();
}
NetworkDiagnostic.configure(
connectivity: ConnectivityService(adapter: FakeConnectivityAdapter()),
);
NetworkDiagnostic.reset() restores the built-in defaults.
Platform support #
| Platform | Status |
|---|---|
| Android | ✅ Supported (Kotlin native side) |
| iOS | ✅ Supported (Swift native side) |
| macOS | ✅ Supported (Swift native side) |
| Windows | ✅ Supported (C++ native side) |
| Linux | ✅ Supported (C++ native side) |
| Web | ⚠️ Partial — see Web support below |
Web support #
The web build exposes the same static API. Capabilities that the browser sandbox
forbids degrade gracefully — they return null or an "unavailable" result
instead of throwing:
| Capability | Web | Notes |
|---|---|---|
| Connectivity | ✅ | via connectivity_plus |
Ping (PingMode.tcp) |
⚠️ | HTTPS round trip; the target must send CORS headers |
Ping (PingMode.icmp) |
❌ | throws UnsupportedError |
| DNS (system resolver) | ✅ | via DNS-over-HTTPS |
| DNS (explicit server) | ⚠️ | needs a DoH endpoint, otherwise "unsupported" |
| Speed test | ✅ | HTTP download / upload |
| Quality score | ✅ | pure function |
| Benchmark | ✅ | pure function |
| Port check / scan | ❌ | returns "unavailable" results |
| Native details (SSID, gateway, MAC, VPN) | ❌ | null |
ZeroNetworkKit.getNativeNetworkDetails() returns null on the web and
ZeroNetworkKit.getPlatformVersion() returns Web. Call
NetworkCapabilities.current() to discover the supported set at runtime.
Documentation #
- API.md — complete usage guide: every public API with copy-paste examples, parameter defaults, model reference and common pitfalls.
- AGENTS.md — engineering contract: architecture, conventions and the new-feature checklist.
- CONTRIBUTING.md — how to set up and submit changes.
- CHANGELOG.md — release history.
License #
MPL-2.0 © Zero Labs
Third-party dependency, trademark and endorsement notices live in NOTICE.