NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #1656 most downloaded on pub.dev
Multi-platform ICMP ping for Dart and Flutter — Windows, macOS, Linux, Android, and iOS — with live round-trip statistics and packet-loss summaries.
Last release 3 months ago
03 Jul 2026
Ships unpredictably
gaps range from 8 days to 2.7 years
Nearly every release is documented
notes for 30 of 30 stable releases
Nothing withdrawn
no release was ever pulled
8 years old
31 releases · first in 2018
Bug-fix release: the default Ping('google.com') now works out of the box on Android across both mainstream ping dialects, instead of aborting with Pin
Bug-fix release: the default Ping('google.com') now works out of the box on
Android across both mainstream ping dialects, instead of aborting with
Ping process exited with code: 2. Non-breaking; the public API and the
PingResponse / PingError / PingSummary shapes are unchanged, and desktop
Linux / macOS / Windows behavior is byte-for-byte identical.
ping is
toybox, which rejects the iputils-only flags dart_ping was sending (-O /
-n) at argument parsing, and also rejects a valued flag glued into one argv
token ('-i 1' → Not a number ' 1'). On Android, dart_ping now sends only
toybox-accepted flags, each flag and its value as separate argv tokens, and
parses toybox's output — its statistics line has no trailing wall-clock total,
and a name-resolution failure reads <host>: bad address — into the same
PingResponse / PingError / PingSummary events as desktop Linux (a lost
probe surfaces as a lower received + non-zero loss, never a mid-run
requestTimedOut). Desktop Linux is byte-for-byte unchanged. A CI gate runs the
exact argv dart_ping produces against a real toybox ping on loopback, so the
regression cannot silently return.external/ping images (#102). On
images shipping the legacy AOSP external/ping (2008-vintage iputils: a split
IPv4-only ping plus a separate ping6) rather than toybox, the default run
still failed with exit 2 after the #100 fix, because that binary rejects the
-4 / -6 family flag. dart_ping now determines the dialect at runtime — a
network-free ping -4 capability probe, resolved once per process — and adapts
the launched command: toybox keeps the flagged single ping; a legacy image
runs a flagless ping for IPv4 and the separate ping6 binary for
IPv6, so the family stays exclusively pinned without sending a flag the binary
rejects. One superset parser handles both dialects — including IPv6 reply lines
and the ICMPv6 Time exceeded: Hop limit hop-limit wording — mapping them to
the same events as desktop Linux. A second CI gate builds a real legacy iputils
ping / ping6 and proves the produced argv is accepted, with a control case
proving that reintroducing -4 / -6 would be rejected. Public API unchanged;
the change is confined to the Linux/Android subprocess path.One column per quarter.
Each breaking change below carries its migration. Together these are the only source-level changes a consumer must make; everything under Added / Chan…
Each breaking change below carries its migration. Together these are the only source-level changes a consumer must make; everything under Added / Changed / Fixed is additive or a bug fix.
Sealed PingEvent stream (#63). Ping.stream is now
Stream<PingEvent>. The old PingData envelope — a single object whose
response / summary / error fields you null-checked by hand — is replaced
by a sealed class PingEvent with three explicit subtypes: PingResponse (a
successful probe), PingError (a probe/run error, now also carrying optional
seq / ip so a timed-out or TTL-exceeded probe stays a single
self-identifying event), and PingSummary (the terminal run summary, always
the final event before the stream closes). Branch on the type with an
exhaustive switch and the compiler enforces that you handle every case.
// Before (9.x): one PingData with nullable fields, disambiguated by hand
ping.stream.listen((data) {
if (data.response != null) {/* probe reply */}
else if (data.error != null) {/* probe/run error */}
else if (data.summary != null) {/* terminal summary */}
});
// After (10.0.0): a sealed PingEvent — switch on the type
ping.stream.listen((event) {
switch (event) {
case PingResponse(): /* probe reply */
case PingError(): /* probe/run error */
case PingSummary(): /* terminal summary — the final event */
}
});
The per-probe seq / ttl / time / ip and the summary's transmitted /
received / time / errors are all preserved — only the envelope shape
changes. Each variant serializes a 'type' discriminator, so
PingEvent.fromMap / fromJson reconstruct the correct subtype.
ipv6 boolean → IpVersion enum (#69). The ambiguous ipv6 boolean on
Ping is replaced by an explicit, exclusive IpVersion enum. A boolean
false was reasonably misread as "prefer IPv4 / dual-stack"; the library has
always selected a single address family exclusively, and IpVersion makes
that explicit. It has exactly two values — there is no dual-stack/auto value,
and IpVersion.ipv4 excludes IPv6 rather than preferring it.
Ping(host, ipv6: true) → Ping(host, ipVersion: IpVersion.ipv6)Ping(host, ipv6: false) or omitting it → Ping(host, ipVersion: IpVersion.ipv4) (the default)The ping behavior for an equivalent, matched call is unchanged; only the
selector parameter changes shape. IPv6 is now supported on Windows (served via
-6, see #71 under Added); the macOS subprocess path remains IPv4-only and
surfaces an explicit error for an IPv6 selection.
Round-trip durations serialized in microseconds (#63).
PingResponse.toMap, PingSummary.toMap, and RoundTripStats.toMap now write
time / the stat figures as inMicroseconds (decoded as
Duration(microseconds: …)), preserving sub-millisecond resolution end-to-end
— the previous inMilliseconds truncation rounded stddev/jitter toward zero on
fast links. Serialization round-trips within 10.0.0 are consistent, but
JSON/maps persisted by dart_ping ≤ 9.x decode 1000× too small under 10.0.0
(a millisecond magnitude read as microseconds), so do not mix serialized
round-trip values across the major boundary.
dart_ping_ios retired; register() removed (#28, #48). iOS support is
now built into dart_ping itself, so the separate dart_ping_ios package and
its register() step are gone. iOS dispatches internally on
Platform.operatingSystem == 'ios' and auto-wires when the build target is
iOS — no registration call and no conditional import. For existing
dart_ping_ios users:
dart_ping_ios dependency from your pubspec.yaml.DartPingIOS.register() call and its
import 'package:dart_ping_ios/...';.sdk: ">=3.10.0 <4.0.0").No other source change is required: the public Ping API is otherwise
unchanged, iOS now auto-wires, and ping works from any isolate (closes #48)
because the Dart↔Swift seam moved from Flutter platform channels to
dart:ffi. Prior dart_ping_ios releases remain published on pub.dev for
consumers who cannot adopt the raised floor.
Ping(host, ipVersion: IpVersion.ipv6) now works
on Windows, which previously threw UnimplementedError. Windows ping
supports IPv6, so the family is forced with -6 (as -4 is for IPv4). The
Windows reply parser was broadened because IPv6 replies omit bytes= and
TTL= (Reply from ::1: time<1ms), so an IPv6 probe carries no hop TTL. The
macOS subprocess path remains IPv4-only and still surfaces an explicit error.PingResponse and PingError alike — gains an additive nullable
RoundTripStats? stats carrying a running snapshot of the round-trip
figures over all successful replies so far in the run. Consumers can drive a
live latency view and derive packet-loss-so-far without waiting for the
terminal summary. The snapshot reuses the same accumulator that builds the
terminal summary, so the last probe's snapshot equals summary.stats. The
field is null on events not produced by the live run path (e.g. a bare
deserialized event).PingSummary gains a
RoundTripStats? stats field (min / avg / max / population stddev / jitter /
sample count) and a derived packetLoss getter computed on read from
transmitted / received (never stored, so it cannot drift). A zero-reply
run carries the empty snapshot rather than fabricated zeros.interface on the Ping factory
and the platform constructors accepts either an interface name (e.g. eth0)
or a local source IP address (e.g. 192.168.1.5), mapped to each platform's
native flag (Linux/Android -I; macOS -b for a name / -S for an address;
Windows -S, source-address form only). Omitting it leaves the spawned
command byte-for-byte unchanged.listNetworkInterfaces() (#72), exported from
package:dart_ping/dart_ping.dart, returns the host's available network
interfaces so you can present a chooser or validate input and feed a
name/address straight back into Ping(host, interface: ...). A failure to
enumerate is reported to the caller, not swallowed.nat64Synthesis
boolean on the Ping factory. On an IPv6-only (NAT64/DNS64) network an IPv4
literal is otherwise unreachable; synthesis lets the platform reach it. The
active behavior is delivered on iOS by dart_ping's native engine; on the
subprocess platforms it is an inert no-op carried for cross-platform parity
(the spawned command is unchanged and it never raises). Pass
nat64Synthesis: false to restore raw pass-through.dart_ping over dart:ffi (#28). iOS talks to the
bundled native Swift ICMP engine through a dart:ffi code asset
(dart_ping_ffi) compiled by a build hook only when the build target is
iOS, replacing Flutter's MethodChannel / EventChannel. The observable
contract is unchanged: Ping, PingResponse, PingError, and PingSummary
keep the same shapes, event order, and terminal summary. Each iOS Ping owns
its own native run handle and callback — no shared broadcast stream, no
run-id demux — so concurrent pings to distinct hosts cannot
cross-contaminate. The full run config (including ipVersion and
nat64Synthesis) crosses the FFI seam, so iOS NAT64 synthesis, microsecond
RTT precision, and the shared stats accumulator are all preserved. iOS ping
now works from background isolates (the
BackgroundIsolateBinaryMessenger ... is invalid failure, #48, is gone).hooks and code_assets are added as
pure-Dart dependencies and do not pull the flutter SDK into
dart_ping's graph, preserving the pure-Dart gate for CLI/server consumers.-4/-6 to the unified ping, so IpVersion.ipv4 can no longer resolve to
an IPv6 address on a dual-stack host (and vice-versa). macOS IPv6 over the
subprocess path now surfaces an explicit "unsupported" error. More
routing/address-family failures map to ErrorType.noRoute across platforms,
while macOS "Host is down" maps to unknown rather than being mislabelled.
The literal/family mismatch guard now also fires on direct platform-class
construction, not only via the Ping(...) factory.lints 6 and test 1.31, and removed the
leftover dart_code_metrics analysis config (the dev dependency was dropped
in 9.0.0).listNetworkInterfaces() round-trips into Ping(host, interface: ...) by
source address on Windows — a bare interface name is rejected there — while
the address form round-trips on every platform. No behavior change.host is now validated as a
syntactically valid hostname or IPv4/IPv6 literal before anything launches; an
unsafe value throws an ArgumentError on every platform and both the default
and Windows forceCodepage launch paths, so a host can never reach a shell
as code (closing a cmd.exe breakout on the forceCodepage path) and is
never read by the subprocess as an option flag. The allow-list rejects shell
metacharacters, whitespace, and control characters, and — being shaped like a
hostname/IP literal — also rejects leading/trailing-hyphen values (e.g. -f,
--flood) and bracketed IPv6 ([::1]; use the unbracketed ::1). Every
valid host pings exactly as before.Ping stream could hang forever
on a process-launch failure (e.g. a missing ping binary) or an unmapped
non-zero exit code. Both now surface a catchable error through the stream's
error channel and the stream always closes, so consumers (await for,
.drain(), .last, stop()) never deadlock; a missing binary reports that
the ping binary could not be found. stderr/stdout are decoded and line-split
independently before merging, so interleaved writes cannot corrupt, split, or
drop a line. Parser/transform errors route through the error channel instead
of escaping as uncaught async errors. stop() terminates reliably even when
called during process launch, and consumer pause/resume now actually
pause/resume the underlying output.seq capture group
is now read only when the platform's pattern defines it (previously
force-unwrapped, throwing "Not a capture group name: seq").PingSummary.hashCode consistent with ==. Equality already compared
errors element-wise, but hashCode used the list's identity hash, so two
value-equal summaries could produce different hash codes and misbehave as
Set/Map keys. hashCode now hashes errors element-wise.Ping instances with interleaved
per-host output and asserts no field bleeds between runs. The defect did not
reproduce (each instance already owns only instance-local state), so there is
no behavior change — the guard prevents future regressions.dart_ping 9.0.1 (published release)
dart_ping 9.0.1 (published release)
Implement TTL expiration handling
Fix windows timeout flag (Issue #37)
Use named capture groups for regex parsing
Remove windows compatibility warning. Issue #27 / fixed upstream
Add documentation note about apple app sandbox in release mode
Require min dart 2.17 sdk (for enhanced enums)
- Improve documentation - Improve code formatting
Removed unused ios related files that may have not been tree shaken due to MethodChannel
Add static variable to register iOS plugin with
Force timeout and interval to be int instead of double to support ping on all system locales
Fix ping base to expose encoding override
Allow overriding the character decoder via optional encoding flag
- Fix ttl flag on Windows
Implement custom ping parser override to support other languages
Add command getter to output the string command that will be run on the host OS
Accumulate errors into PingSummary
Implement ttl flag (default 255)
Fix response parsing on Windows 10
Leave sequence number intact (don't decrement)
Throw error if stop() is called before process starts
- Fix ping on macOS - Simplify example
Fix parsing bug when linux ping process times out
Fix issue #2 (broken ping command args on windows)
Remove unused cross-platform code stub so pub.dev correctly reports supported platforms
- Improve docs
Drop iOS support to release flutter dependency until an alternative is found
- Package resurrected
Initial version, created by Stagehand
Your coding agent can read these notes before it upgrades. Set up the MCP server →