app_smart_network
A smart Flutter network package with built-in error handling, configurable retry, optional SSL certificate pinning (Android/iOS), and multilingual error messages (en/ar).
2.1.0
536 downloads/mo
#780 most downloaded on pub.dev
devmuhammadreda/app_smart_network
What this package is like to depend on
Last release 5 days ago
19 Aug 2026
Ships fairly regularly
a new release about every 3 months
Most releases are documented
notes for 12 of 14 stable releases
Nothing withdrawn
no release was ever pulled
6 months old
14 releases · first in 2026
14 releases in the last 12 months
see the full history below
Release timeline
14 releases · Feb 2026 to Aug 2026Releases
latest 14-
2.1.019 Aug 2026Release notes
Open source →Changed
CertificatePinningConfignow accepts a single fingerprint. The floor drops from two distinct pins to one (kMinimumFingerprintsis now1); an empty list still throws. Environments with no successor certificate to name — a self-signed staging host, or one rotated in lockstep with the app — can now pin without inventing a second value.- Repeated fingerprints are collapsed instead of rejected. Entries that
normalise to the same digest are one pin, so
allowedSHAFingerprintsstores it once, de-duplicated in the order given.
Existing two-pin configurations are unaffected; this only widens what constructs.
The renewal caveat is unchanged and now rests on you rather than the constructor: a whole-certificate pin stops matching the day the server renews, so a single-pin production app goes dark on renewal day with no fix short of a store release. Pin the current certificate and its successor for anything facing users.
-
2.0.018 Aug 2026Release notes
Open source →Breaking changes
Certificate pinning is now delegated to
http_certificate_pinninginstead of the package's own SPKI implementation. Everything outside pinning is unchanged; apps that do not setcertificatePinningneed no code changes beyond the SDK bump.- Pins are now whole-certificate SHA-256 fingerprints, not SPKI hashes.
sha256/<base64>values from 1.x are rejected at construction. CertificatePinningConfigis reshaped:pins(a per-host map) becomesallowedSHAFingerprints(a flat list applied to every request), and a newtimeoutreplaces nothing.- Removed:
CertificatePinningConfig.fromAssets(),enforce,includeSubdomains,onPinFailure, theOnPinFailureCallbacktypedef, and thekMinimumPinsPerHostconstant (nowkMinimumFingerprints). - Pinning now requires Android or iOS. The plugin has no web or desktop implementation; on other platforms the check cannot run and the request fails rather than silently passing.
- Pinning applies to every request, not to a chosen set of hosts. Point a
separate
NetworkConfigat any host that must not be pinned. - Minimum SDK raised to Dart
^3.5.4/ Flutter>=3.3.0, as required byhttp_certificate_pinning. - Dependencies:
asn1libandcryptodropped;http_certificate_pinningadded.
Migration
Replace each SPKI pin with the SHA-256 fingerprint of the certificate itself:
openssl s_client -connect api.example.com:443 -servername api.example.com \ < /dev/null 2>/dev/null \ | openssl x509 -fingerprint -sha256 -noout// 1.x CertificatePinningConfig( pins: { 'api.example.com': ['sha256/<current>', 'sha256/<backup>'], }, includeSubdomains: true, ) // 2.0.0 CertificatePinningConfig( allowedSHAFingerprints: [ 'AA:BB:CC:...', // certificate in production today 'CC:DD:EE:...', // successor certificate, already issued ], )⚠️ Operational change, not just an API one. A whole-certificate fingerprint stops matching the day the certificate is renewed, even when the key pair is reused — the 1.x SPKI pin survived renewal, this one does not. Both fingerprints must therefore be certificates that already exist and whose renewal you control, and the successor's fingerprint has to ship before the current certificate expires. Plan certificate renewal as a release-coordinated event.
For a staged rollout, previously
enforce: false, gate the wholecertificatePinningfield on a flag instead and watch forCertificatePinningException— see "Shielded and unshielded builds" in the README.Unchanged
CertificatePinningException— same type, samehostfield, still extendsApiException, still locale-aware and never carrying the presented fingerprint.allowBadCertificatecombined withcertificatePinningstill throwsArgumentErrorat startup.- Pin failures are still never retried, and still bypass the 401 handler.
- Pins are now whole-certificate SHA-256 fingerprints, not SPKI hashes.
-
1.4.017 Aug 2026Release notes
Open source →Features
-
CertificatePinningConfig.fromAssets()— configure pinning from certificates bundled as Flutter assets instead of hand-pasted hashes:final pinning = await CertificatePinningConfig.fromAssets( certificatePaths: { 'api.example.com': [ 'assets/certs/api.example.com.pem', 'assets/certs/backup.pub.pem', ], }, );Every asset is read and parsed up front, so a wrong path or an unreadable certificate fails during
initialize()rather than on the first request in production. -
Bare public keys are accepted. An asset may be either a
CERTIFICATEblock (the ASN.1 is walked to locateSubjectPublicKeyInfo) or aPUBLIC KEYblock (already aSubjectPublicKeyInfo, hashed directly). The second form is the point: the mandatory backup pin can be derived from a key pair that has no certificate yet, so pinning ships without waiting on a CA. Both forms yield identical pins for the same key pair. -
bundleparameter — defaults torootBundle; inject anAssetBundlein tests.
Changes
- Certificate pinning is available again. It shipped in 1.3.0 and was removed in 1.3.3; 1.4.0 restores it unchanged. Upgrading from 1.3.3 gains pinning; upgrading from 1.3.0–1.3.2 keeps the behaviour you already had.
- Pinning remains off by default.
certificatePinningdefaults tonulland the package behaves exactly as it does without it.
Notes
fromAssetsdelegates to the unnamed constructor, so all existing validation still applies — including the two-distinct-pins rule. A certificate and a public key extracted from that same certificate are the same key, and therefore one pin, not two.- A file holding more than one PEM block is rejected. Pinning whichever certificate happens to come first in a chain file would be a silent guess at which link was meant.
- Every failure throws
ArgumentErrornaming the offending path: a missing or unreadable asset, a non-PEM file, a label other thanCERTIFICATEorPUBLIC KEY, multiple PEM blocks, a body that is not valid base64, or a certificate that cannot be parsed.
-
-
1.3.316 Aug 2026Release notes
Open source →Changes
- Certificate pinning was removed in this release. It is restored in 1.4.0 — prefer upgrading straight to 1.4.0 over pinning to this version.
-
1.3.216 Aug 2026Nothing published for this version
-
1.3.116 Aug 2026Nothing published for this version
-
1.3.016 Aug 2026Release notes
Open source →Features
- Certificate pinning —
NetworkConfigaccepts an optionalcertificatePinning: CertificatePinningConfig(...). Pins are the base64 SHA-256 of a certificate'sSubjectPublicKeyInfoin the conventionalsha256/<base64>form shared with HPKP and OkHttp'sCertificatePinner. Hashing the SPKI rather than the whole certificate means a pin survives certificate renewal whenever the key pair is reused. - Per-host configuration —
pinsmaps a host to its accepted pins;includeSubdomainsextends a host's pins to its subdomains;enforce: falsereports mismatches without blocking them, for a staged rollout. onPinFailurecallback — reports the host and the pins the server actually presented, for telemetry.CertificatePinningException— a pin failure is distinguishable from a generic network error. It extendsApiException, so existing handlers keep working, and carries the offendinghost. Messages are locale-aware in English and Arabic; the presented pins are never placed in the message.- New public exports —
CertificatePinningConfig,CertificatePinningException,OnPinFailureCallback,kMinimumPinsPerHost.
Fixes
- Certificate failures are no longer retried.
DioExceptionType.badCertificatewas previously treated as a retryable transport error, so a rejected TLS handshake was replayed up to the configured attempt count. Replaying it re-presents the same certificate to the same host for the same verdict, and multiplied a security signal that should be raised once. This applies to all certificate failures, pinned or not.
Changes
- Pinning is off by default;
certificatePinningdefaults tonulland the package behaves exactly as it did in 1.2.0 when it is not set.const NetworkConfig(...)call sites continue to compile. - Only hosts listed in
pinsare pinned. Unlisted hosts — analytics, crash reporting, CDNs — fall through to normal TLS validation rather than failing closed. - New dependencies:
asn1lib(certificate parsing) andcrypto(SHA-256).
Notes
- Every host needs at least two distinct pins. A single pin means a lost or
rotated key bricks every installed app with no recovery path, so fewer than
two throws
ArgumentErrorat startup. Keep the second key offline. - Pin format, pin count, and blank hosts are validated when
CertificatePinningConfigis constructed, so a typo fails at startup rather than on the first API call in production. allowBadCertificate: truecombined withcertificatePinningthrowsArgumentErrorwhen the client is built. The check lives there rather than inNetworkConfig'sconstconstructor because aconstconstructor can onlyassert, and asserts are stripped from release builds — exactly where an app that looks pinned but is not would do the damage.- Pinning uses Dio's
validateCertificate, which evaluates the leaf certificate on every connection, rather thanbadCertificateCallback, which fires only after chain validation has already failed. - Malformed or unparseable certificates are rejected, never treated as a pass.
- Certificate pinning —
-
1.2.011 Aug 2026Release notes
Open source →Features
- Configurable retry —
NetworkConfignow accepts aretrypolicy.RetryPolicyexposesattempts,delays,methodsandstatuses, all of which were previously hardcoded. Passretry: nullto disable retry app-wide. - Per-request retry —
request(),download()anduploadFile()take aretry:argument that overrides the app-wide policy for a single call. UseRetryPolicy.offto opt one request out, or a policy with a higherattemptsto opt one in. - New public export —
RetryPolicy.
Changes
- Retry defaults are unchanged: three retries on idempotent methods with a
1 s / 2 s / 3 s backoff. Code that does not mention
retrybehaves exactly as it did in 1.1.0.
Notes
RetryPolicy.delaysandRetryPolicy.methodsare read from the app-wide policy only. Backoff is fixed when the client is built and cannot vary per request; the method allowlist guards calls that did not supply a policy, so a policy attached to a request bypasses it — that is what makesretry: RetryPolicy(attempts: 5)retry a POST.attemptsis capped atRetryPolicy.maxAttempts(10), asserted at construction.
- Configurable retry —
-
1.0.512 Jul 2026Release notes
Open source →Bug fixes
ErrorHandler—DioExceptionType.transformTimeoutnow returns a proper localized message (TransformTimeout) with status408, instead of leaking the raw'transformTimeout'key as the user-facing message.
Maintenance
equatabledependency removed frompubspec.yaml. It was left behind after theFailureclasses were deleted in 1.0.3 and is no longer used.- Removed the unused
ConnectionErrorlocale key (bothenandar);connectionErrormaps toNoInternetConnection, so the key was dead.
-
1.0.412 Jul 2026Release notes
Open source →Bug fixes
ErrorHandlernow mapsDioExceptionType.connectionErrorto theNoInternetConnectionerror type, so connection failures surface the correct localized "no internet connection" message instead of a generic error.
Maintenance
- Dependencies upgraded (
dart pub upgrade).
-
1.0.301 Mar 2026Release notes
Open source →Breaking changes
-
ServerFailureandCacheFailureremoved —failures.dartand its public exports have been deleted. TheFailureabstraction was out of scope for a network package; it leaked domain-layer concerns into the library and forced an unnecessaryequatabledependency on consumers.-
Migration: catch
ApiExceptiondirectly in your repository layer, or define your ownFailuretypes and map fromApiExceptionthere.// before } on ApiException catch (e) { return Left(ServerFailure.fromException(e)); } // after – option A: catch ApiException directly } on ApiException catch (e) { return Left(MyServerFailure(e.message, e.statusCode)); }
-
-
equatabledependency removed — the package no longer depends onequatable. Remove it from your ownpubspec.yamlif you were relying on the transitive export.
-
-
1.0.225 Feb 2026Release notes
Open source →Bug fixes
ErrorHandler.handleErrornow passes anApiExceptionthrough unchanged instead of re-wrapping it as a generic'UnexpectedError'(status 0). This prevented the real error type, status code, and message from reaching callers whenever anApiExceptionentered the catch block (e.g. thrown by a custom interceptor).RequestService.execute—ensureConnected(),withMobileTimeouts(), andresolveUrl()are now inside the singletry/catchblock, so every error type (connectivity, timeout, bad response, certificate, cancel) flows throughErrorHandlervia one consistent code path.
-
1.0.123 Feb 2026Release notes
Open source →Bug fixes
ApiService.instancenow throws aStateErrorinstead of silently creating a broken client wheninitialize()has not been called yet.ApiService.isInitializedgetter added — use it to safely check whetherinitialize()has been called before accessinginstance.withMobileTimeoutsno longer discards user-suppliedOptionsfields. It now mutates the existing object in-place, only settingreceiveTimeoutwhen the caller has not already provided one.removeAppLocale()now restores the locale that was active atinitialize()time (fromNetworkConfig.defaultHeaders['Accept-Language']) instead of always falling back to'en'.NetworkLocale.clearCustomTranslations([locale])added — removes custom translations for a specific locale, or for all locales when called without an argument.
-
1.0.023 Feb 2026Release notes
Open source →Initial stable release.
Features
ApiServicesingleton withinitialize(NetworkConfig)entry pointNetworkConfig— configure base URL, timeouts, default headers, SSL, and unauthorized callbackHttpMethodenum —get,post,put,patch,deleteRequestService— unified HTTP request execution with connectivity guard and mobile timeout extensionDownloadRequestService— file download with progress callbackUploadRequestService— multipart file upload with extra fields and progress callbackNetworkLocale— locale-aware error messages; built-in English and Arabic translations covering all standard HTTP status codes and network error types; extensible viaaddTranslations()setAppLocale(locale)— setsAccept-Languageheader and switches error-message locale in one callErrorHandler— convertsDioExceptiontoApiExceptionwith translated messagesApiException— rich exception withstatusCode,apiErrorCode,errorCategory,hasApiErrorCode(), andgetResponseField()ServerFailure/CacheFailure— domain-layerFailurewrappers (Equatable)- Automatic retry on idempotent methods (GET, PUT, DELETE) with exponential back-off (3 retries: 1 s, 2 s, 3 s)
- Connectivity check before every request with a single 600 ms retry for transient states
- Background JSON decoding via
computeisolate - Debug request/response logging via
PrettyDioLogger(debug builds only) - Per-request
baseUrloverride, cancel token, query parameters, and send/receive progress callbacks example/app demonstrating all features against JSONPlaceholder API