Skip to main content

netra_flutter 1.0.1 copy "netra_flutter: ^1.0.1" to clipboard
netra_flutter: ^1.0.1 copied to clipboard

A Flutter networking plugin with offline queueing, slow network strategies, smart caching, circuit breaker, multipart uploads, and event-driven observability. Built on top of OkHttp via a native Andro [...]

Netra Flutter Plugin #

โš ๏ธ Platform Support: Android only. iOS is not currently supported.

Built on top of a native Kotlin networking engine. A powerful Flutter networking plugin with built-in caching, offline queueing, slow network strategies, circuit breaker support, multipart uploads, and event-driven observability.

๐Ÿ“ฑ Check out the example app for a full working demo.


โš™๏ธ Installation #

flutter pub add netra_flutter

Why Netra? #

Netra focuses on real-world mobile networking problems:

  • Unreliable connections
  • Offline-first architecture
  • Slow networks
  • Cache consistency
  • Request recovery
  • Developer observability

Instead of only being an HTTP client, Netra aims to provide a resilient networking layer for modern Flutter applications.

Netra vs Dio Ecosystem #

Most Netra features can be implemented with Dio and additional packages.

The difference is that Netra provides them as a unified networking layer out of the box.

Capability Dio Ecosystem Netra
HTTP Requests Dio Built-in
Multipart Upload Dio Built-in
Request Cancellation Dio Built-in
Caching Dio + dio_cache_interceptor Built-in
Offline Cache Fallback Dio + dio_cache_interceptor configuration Built-in
Offline Queue Custom implementation Built-in
Retry On Reconnect offline_retry_interceptor + custom persistence Built-in
Slow Network Policies Custom implementation Built-in
Circuit Breaker Custom implementation Built-in
Request Lifecycle Events Custom interceptors Built-in
Cache Events Custom implementation Built-in
Queue Events Custom implementation Built-in
Unified API Multiple packages Single SDK

Quick Example #

final client = NetraClient(
  baseUrl: "https://api.example.com",
);

client.on(RequestEvent.requestExecuted((request) {
print("executing: ${request.url}");
}));

client.on(RequestEvent.requestSuccess((request, response) {
print("success: ${response.statusCode} data: ${response.data}");
}));

client.on(RequestEvent.requestFailed((request, response) {
print("failed: ${response?.statusCode}");
}));

final response = await client.get(
  requestOptions: RequestOptions(
    url: "/users",
    cacheOptions: CacheOptions(ttl: 60000),
    offlinePolicyAction: OfflinePolicyAction.queue,
    slowNetworkPolicyAction: SlowNetworkPolicyAction.useCache,
  ),
);

Features #

  • โšก Simple async/await API
  • ๐Ÿ“ฆ GET, POST, PUT, PATCH, DELETE support
  • ๐Ÿง  Smart cache system (memory + disk)
  • ๐Ÿ“ก Offline request handling (queue / retry / cache fallback)
  • ๐Ÿข Slow network strategies (wait / timeout / cache fallback)
  • ๐Ÿ‘€ Event-driven observers (request, cache, queue, network)
  • ๐Ÿ–ผ Multipart file/image upload
  • ๐Ÿ“ฅ Streaming support for large responses
  • ๐Ÿ”„ Automatic queued request restoration
  • ๐Ÿ”ฅ Circuit breaker support
  • ๐Ÿงฉ Converter support (Gson, KotlinX, Moshi)
  • ๐Ÿ›‘ Request cancellation on dispose
  • ๐Ÿงต Fully async Dart API

Create Client #

final netraClient = NetraClient(
  baseUrl: "https://api.example.com",
  headers: {
    "Authorization": "Bearer token",
  },
  converterType: ConverterType.gson,
);

With circuit breaker:

final netraClient = NetraClient(
  baseUrl: "https://api.example.com",
  converterType: ConverterType.gson,
  circuitBreakerOptions: CircuitBreakerOptions(
    failureThreshold: 5,
    retryDelayMs: 1000,
  ),
);

RequestOptions #

All requests are configured via RequestOptions:

RequestOptions(
  url: "/users",                          // required
  body: null,                             // RequestBody?
  headers: {"X-Custom": "value"},         // per-request headers
  offlinePolicyAction: ...,               // OfflinePolicyAction?
  slowNetworkPolicyAction: ...,           // SlowNetworkPolicyAction?
  cacheOptions: CacheOptions(),           // cache config
  cancelOnDispose: true,                  // auto-cancel on widget dispose
)

GET Request #

final result = await netraClient.get(
  requestOptions: RequestOptions(
    url: "/users",
  ),
);

print(result?.statusCode);
print(result?.data);

POST Request #

final result = await netraClient.post(
  requestOptions: RequestOptions(
    url: "/users",
    body: RequestBody.createJson(
      jsonEncode({"name": "Sinem", "job": "developer"}),
    ),
  ),
);

PUT Request #

final result = await netraClient.put(
  requestOptions: RequestOptions(
    url: "/users/1",
    body: RequestBody.createJson(
      jsonEncode({"name": "Sinem", "job": "mobile developer"}),
    ),
  ),
);

PATCH Request #

final result = await netraClient.patch(
  requestOptions: RequestOptions(
    url: "/users/1",
    body: RequestBody.createJson(
      jsonEncode({"name": "Sinem"}),
    ),
  ),
);

DELETE Request #

final result = await netraClient.delete(
  requestOptions: RequestOptions(
    url: "/users/1",
  ),
);

Example Response #

result?.statusCode      // int
result?.statusMessage   // String?
result?.headers         // Map<String, String?>?
result?.data            // dynamic
result?.isCache         // bool โ€” true if served from cache

Request Body Types #

JSON #

RequestBody.createJson(jsonEncode({"name": "Sinem"}))

Bytes #

RequestBody.createBytes(
  Uint8List.fromList(utf8.encode(jsonEncode({"name": "Sinem"}))),
)

Multipart #

final part = RequestBodyPart.file(
  name: "image",
  fileName: "photo.jpg",
  bytes: bytes,
  contentType: "image/jpeg",
);

RequestBody.multipart([part])

Multipart Upload #

final XFile? file = await picker.pickImage(source: ImageSource.gallery);
if (file != null) {
  final bytes = await file.readAsBytes();

  final result = await netraClient.post(
    requestOptions: RequestOptions(
      url: "/upload",
      body: RequestBody.multipart([
        RequestBodyPart.file(
          name: "image",
          fileName: file.name,
          bytes: bytes,
          contentType: "image/jpeg",
        ),
      ]),
    ),
  );

  print(result?.statusCode);
}

Streaming #

For large responses (images, files, audio), use getStream to consume data chunk by chunk.

final stream = await netraClient.getStream(
  requestOptions: RequestOptions(
    url: "/image",
    cancelOnDispose: true,
  ),
);

final List<int> imageBytes = [];

stream.listen(
  (data) {
    imageBytes.addAll(data);
  },
  onDone: () {
    final bytes = Uint8List.fromList(imageBytes);
    // use bytes
  },
  onError: (e) {
    print("stream error: $e");
  },
);

Cache Support #

final result = await netraClient.get(
  requestOptions: RequestOptions(
    url: "/users",
    cacheOptions: CacheOptions(),
  ),
);

print(result?.isCache);  // true if served from cache

Custom TTL:

cacheOptions: CacheOptions(ttl: 60000)  // 1 minute in ms

Offline Support #

final result = await netraClient.get(
  requestOptions: RequestOptions(
    url: "/users",
    offlinePolicyAction: OfflinePolicyAction.queue,
  ),
);

Available offline actions:

Action Description
OfflinePolicyAction.queue Stores request, retries automatically when connection is restored
OfflinePolicyAction.useCache Returns cached response if available
OfflinePolicyAction.retry(retries, retryInterval) Retries N times with given interval
OfflinePolicyAction.throwError Immediately throws a network error

RETRY example: #

offlinePolicyAction: OfflinePolicyAction.retry(
  retries: 3,
  retryInterval: Duration(seconds: 2),
),

Slow Network Strategies #

final result = await netraClient.get(
  requestOptions: RequestOptions(
    url: "/users",
    slowNetworkPolicyAction: SlowNetworkPolicyAction.useCache,
  ),
);

Available actions:

Action Description
SlowNetworkPolicyAction.useCache Returns cached response immediately
SlowNetworkPolicyAction.wait(delay) Waits given duration before sending request
SlowNetworkPolicyAction.timeout(timeout) Throws error if request exceeds duration

WAIT example: #

slowNetworkPolicyAction: SlowNetworkPolicyAction.wait(
  delay: Duration(seconds: 2),
),

TIMEOUT example: #

slowNetworkPolicyAction: SlowNetworkPolicyAction.timeout(
  timeout: Duration(milliseconds: 2000),
),

Per-Request Headers #

requestOptions: RequestOptions(
  url: "/users",
  headers: {
    "X-Custom-Header": "value",
  },
),

Cancel on Dispose #

Requests marked with cancelOnDispose: true are automatically cancelled when the widget is disposed.

requestOptions: RequestOptions(
  url: "/users",
  cancelOnDispose: true,
),

Converter Support #

Converter Enum
Gson ConverterType.gson
Kotlinx Serialization ConverterType.kotlinX
Moshi ConverterType.moshi
final netraClient = NetraClient(
  baseUrl: "https://api.example.com",
  converterType: ConverterType.kotlinX,
);

Observers #

Netra uses an event-driven system. Attach listeners with on() and detach with off().

// attach โ€” returns an eventId
final eventId = netraClient.on(
  CacheEvent.cacheStaleUsed((request, ageMs, ttlMs, expiredByMs) {
    print("stale cache used: ${request.url}");
  }),
);

// detach
netraClient.off(eventId);

Request Events #

netraClient.on(RequestEvent.requestExecuted((request) {
  print("executing: ${request.url}");
}));

netraClient.on(RequestEvent.requestSuccess((request, response) {
  print("success: ${response.statusCode} data: ${response.data}");
}));

netraClient.on(RequestEvent.requestFailed((request, response) {
  print("failed: ${response?.statusCode}");
}));

Cache Events #

netraClient.on(CacheEvent.cacheHit((request, ageMs, ttlMs) {
  print("cache hit: ${request.url}");
}));

netraClient.on(CacheEvent.cacheMiss((request) {
  print("cache miss: ${request.url}");
}));

netraClient.on(CacheEvent.cacheStored((request, ageMs, ttlMs) {
  print("cache stored: ${request.url}");
}));

netraClient.on(CacheEvent.cacheExpired((request, ageMs, ttlMs, expiredByMs) {
  print("cache expired: ${request.url}");
}));

netraClient.on(CacheEvent.cacheStaleUsed((request, ageMs, ttlMs, expiredByMs) {
  print("stale cache used: ${request.url}");
}));
Event Description
cacheHit Valid cached data found and returned
cacheMiss No cache entry exists
cacheStored Response saved to cache
cacheExpired Cache exists but TTL has passed
cacheStaleUsed Expired cache returned as fallback

Queue Events #

netraClient.on(QueueEvent.requestQueued((url, queueOrder, createdAt) {
  print("queued: $url order: $queueOrder");
}));

netraClient.on(QueueEvent.queuedRequestRestored((url) {
  print("restored: $url");
}));

netraClient.on(QueueEvent.queuedRequestExecuted((url, response) {
  print("executed: $url status: ${response.statusCode}");
}));

netraClient.on(QueueEvent.queuedRequestFailed((url) {
  print("queue failed: $url");
}));
Event Description
requestQueued Request stored in offline queue
queuedRequestRestored Connection restored, queue processing started
queuedRequestExecuted Queued request succeeded
queuedRequestFailed Queued request failed on retry

Network Events #

netraClient.on(NetworkEvent.offline(() {
  print("offline");
}));

netraClient.on(NetworkEvent.slowNetwork(() {
  print("slow network");
}));

netraClient.on(NetworkEvent.connectionRestored(() {
  print("connection restored");
}));
Event Description
offline Device lost internet connection
slowNetwork Network is congested or degraded
connectionRestored Internet connection is back

Circuit Breaker #

Automatically stops sending requests after a threshold of consecutive failures.

final netraClient = NetraClient(
  baseUrl: "https://api.example.com",
  circuitBreakerOptions: CircuitBreakerOptions(
    failureThreshold: 5,
    retryDelayMs: 1000,
  ),
);

Roadmap #

  • โŒ iOS support
  • โŒ WebSocket support
  • โŒ Request deduplication
  • โŒ Logging interceptor

License #

MIT License
2
likes
0
points
374
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter networking plugin with offline queueing, slow network strategies, smart caching, circuit breaker, multipart uploads, and event-driven observability. Built on top of OkHttp via a native Android bridge.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

collection, flutter, freezed_annotation, json_annotation, meta, pigeon, plugin_platform_interface, uuid

More

Packages that depend on netra_flutter

Packages that implement netra_flutter