zhengzaihong/rxnet
zhengzaihong/rxnet has 45 GitHub stars and 0 forks. GitHot measured 0 stars in 24 hours and 0 in 7 days. Last checked 2026-11-11 19:12 UTC. Last push 2026-10-28.
RxNet 是一款专为 Flutter 开发的跨平台网络请求工具,贴合原生开发习惯,几乎零学习成本即可上手。它不仅让网络通信更丝滑,还支持丰富的功能组合,助你构建高性能、可维护的移动应用,已经支持Android、ios、windows、linux、macos、Web、HarmonyOS
What you can do
- 0.7.1: Web compile fix (stub import path) — prefer this over 0.7.0 for Web
- 0.6.x: Pluggable Adapter Architecture (DioAdapter, HttpAdapter, MockAdapter)
- 0.5.0: RESTful auto-detection, parameter separation, breakpoint upload/download
측정된 스타 증가
- in 24h
- 0
- in 7 days
- 0
- in 15 days
- 업데이트 지연
- in 1 month
- 업데이트 지연
서로 겹치는 기간의 순증가량이며 일별 합계가 아닙니다. 측정값이 없으면 비워 둡니다. 스타는 관심도를 나타내며 품질을 보장하지 않습니다.
README.md
OriginalRxNet
Language: English | 简体中文
RxNet: Extremely Easy-to-Use, Powerful, Native-Style Flutter Network Communication Framework
RxNet is a cross-platform network request tool specially built for Flutter. It conforms to native development habits, can be started with almost zero learning cost, and supports rich function combinations to help you build high-performance, maintainable applications.
Latest: 0.7.1
dependencies:
rxnet_plus: ^0.7.1
New in :
- RxNetConfig - Clean configuration class with Builder pattern, replacing long parameter lists
- Cache Eviction - LRU / LFU / FIFO policies with configurable
cacheMaxSize - RetryPolicy - Exponential backoff, jitter strategies beyond fixed interval
- AdapterBaseOptions - Adapter-agnostic global request defaults (timeout, headers, etc.)
- TokenRefreshInterceptor - Automatic token refresh on 401/403 with concurrent deduplication
- DeduplicateInterceptor - Prevents duplicate requests within configurable time window
- ThrottleInterceptor - Request rate limiting with configurable window
- RxResult.success() - Guaranteed non-null value factory +
requiredValuegetter
100% backward compatible - All existing code works without changes. New features are opt-in.
Previous Highlights
- 0.7.1: Web compile fix (stub import path) — prefer this over 0.7.0 for Web
- 0.6.x: Pluggable Adapter Architecture (DioAdapter, HttpAdapter, MockAdapter)
- 0.5.0: RESTful auto-detection, parameter separation, breakpoint upload/download
Table of Contents
- Quick Start
- RxNetConfig
- Cache Eviction
- Retry Policy
- AdapterBaseOptions
- Adapter Selection
- Request Examples
- Built-in Interceptors
- Concurrent Requests
- Upload & Download
- Certificate Validation
- Migration from 0.6.x to 0.7.0
Quick Start
Option 1: RxNetConfig (Recommended)
await RxNet.init(config: RxNetConfig(
baseUrl: "https://api.example.com",
cacheMode: CacheMode.CACHE_EMPTY_OR_EXPIRED_THEN_REQUEST,
cacheInvalidationTime: 24 * 60 * 60 * 1000,
cacheMaxSize: 500,
cacheEvictionPolicy: CacheEvictionPolicy.lru,
adapterBaseOptions: AdapterBaseOptions(
connectTimeout: Duration(seconds: 10),
sendTimeout: Duration(seconds: 10),
receiveTimeout: Duration(seconds: 30),
),
interceptors: [
RxNetLogAdapterInterceptor(),
],
));
Option 2: Builder Pattern
await RxNet.init(config: RxNetConfig.builder()
.baseUrl("https://api.example.com")
.cacheMode(CacheMode.CACHE_EMPTY_OR_EXPIRED_THEN_REQUEST)
.cacheInvalidationTime(24 * 60 * 60 * 1000)
.cacheMaxSize(500)
.cacheEvictionPolicy(CacheEvictionPolicy.lru)
.baseOptions(AdapterBaseOptions(
connectTimeout: Duration(seconds: 10),
sendTimeout: Duration(seconds: 10),
receiveTimeout: Duration(seconds: 30),
))
.addInterceptor(RxNetLogAdapterInterceptor())
.build());
RxNetConfig
RxNetConfig is the recommended way to configure RxNet. It provides a clean, type-safe configuration object with Builder pattern support.
All Configuration Options
final config = RxNetConfig(
baseUrl: "https://api.example.com", // Required
adapter: DioAdapter(), // Optional, defaults to DioAdapter
adapterBaseOptions: AdapterBaseOptions( // Optional, global request defaults
connectTimeout: Duration(seconds: 10),
receiveTimeout: Duration(seconds: 30),
sendTimeout: Duration(seconds: 10),
headers: {'Authorization': 'Bearer token'},
),
cacheMode: CacheMode.CACHE_EMPTY_OR_EXPIRED_THEN_REQUEST,
cacheInvalidationTime: 60 * 1000, // 1 minute
cacheMaxSize: 500, // Max 500 entries
cacheEvictionPolicy: CacheEvictionPolicy.lru, // LRU eviction
interceptors: [RxNetLogAdapterInterceptor()],
cachePath: '/path/to/cache',
cacheName: 'network_cache',
databaseName: 'rxnet_cache.db',
ignoreCacheKeys: ['token'],
baseUrlEnv: {'dev': 'https://dev.api.com', 'prod': 'https://api.com'},
baseCheckNet: myCheckNetFunction,
);
copyWith
final updatedConfig = config.copyWith(
baseUrl: "https://new-api.example.com",
cacheMaxSize: 1000,
);
Cache Eviction & Hit Rate
Cache Eviction Policies
await RxNet.init(config: RxNetConfig(
baseUrl: "https://api.example.com",
cacheMaxSize: 200,
cacheEvictionPolicy: CacheEvictionPolicy.lru,
));
| Policy | Behavior |
|---|---|
CacheEvictionPolicy.none |
No eviction (default, backward compatible) |
CacheEvictionPolicy.lru |
Evicts least recently accessed entries |
CacheEvictionPolicy.lfu |
Evicts least frequently accessed entries |
CacheEvictionPolicy.fifo |
Evicts oldest entries by creation time |
Cache Hit Rate Statistics
final hitRate = RxNet.I.cacheManager.hitRate; // 0.0 ~ 1.0
final hits = RxNet.I.cacheManager.hitCount;
final misses = RxNet.I.cacheManager.missCount;
RxNet.I.cacheManager.resetStats(); // Reset counters
Programmatic Cache Management
final cache = RxNet.I.cacheManager;
// Save/read network cache
await cache.saveNetworkCache(path: '/api/data', params: {'page': 1}, responseData: {...});
final data = await cache.readNetworkCache(path: '/api/data', params: {'page': 1});
// Save/read key-value cache
await cache.put('user_token', 'abc123');
final token = await cache.get<String>('user_token');
// Clear cache
await cache.clearAll();
await cache.clearByPrefix('/api/users');
await cache.clearByPattern(r'^/api/v\d+/');
// Query cache
final size = await cache.getCacheSize();
final keys = await cache.getAllKeys();
Retry Policy
Fixed Interval (Default)
RxNet.get()
.setPath("/api/data")
.setRetryCount(3, interval: Duration(seconds: 2))
.request();
Advanced RetryPolicy
// Exponential backoff
RxNet.get()
.setPath("/api/data")
.setRetryPolicy(RetryPolicy.exponentialBackoff(
maxRetries: 3,
baseInterval: Duration(seconds: 1),
maxInterval: Duration(seconds: 30),
))
.request();
// Exponential backoff with jitter (recommended for distributed systems)
RxNet.get()
.setPath("/api/data")
.setRetryPolicy(RetryPolicy.exponentialBackoffWithJitter(
maxRetries: 5,
baseInterval: Duration(seconds: 1),
maxInterval: Duration(seconds: 30),
))
.request();
| Strategy | Attempt 1 | Attempt 2 | Attempt 3 | Best For |
|---|---|---|---|---|
fixed |
1s | 1s | 1s | Simple cases |
exponentialBackoff |
1s | 2s | 4s | Server overload recovery |
exponentialBackoffWithJitter |
~0.5s | ~1.5s | ~3s | Distributed systems |
AdapterBaseOptions
Set global request defaults that apply to all requests across all adapters:
await RxNet.init(config: RxNetConfig(
baseUrl: "https://api.example.com",
adapterBaseOptions: AdapterBaseOptions(
connectTimeout: Duration(seconds: 10),
receiveTimeout: Duration(seconds: 30),
sendTimeout: Duration(seconds: 30),
headers: {'Authorization': 'Bearer token'},
contentType: 'application/json',
responseType: ResponseType.json,
followRedirects: true,
maxRedirects: 5,
receiveDataWhenStatusError: true,
persistentConnection: true,
),
));
Request-level parameters always override global defaults
Adapter Selection
| Adapter | Package | Features | Cancellation | Best For |
|---|---|---|---|---|
| DioAdapter | dio: ^5.8.0+1 |
All features, interceptors | True (aborts connection) | Production apps (default) |
| HttpAdapter | http: ^1.2.0 |
Basic HTTP, interceptors, multipart, stream | Pseudo (marks cancelled) | Lightweight apps |
| MockAdapter | Built-in | Testing, no network | Simulated | Unit/integration tests |
Default: DioAdapter is used if no adapter is specified.
Multiple Instances
final mainApi = RxNet.create();
await mainApi.initNet(config:RxNetConfig(baseUrl: "https://api.main.com", adapter: DioAdapter()));
final analyticsApi = RxNet.create();
await analyticsApi.initNet(config:RxNetConfig(baseUrl: "https://analytics.example.com", adapter: HttpAdapter()));
Request Examples
Supported Methods
get, post, delete, put, patch, head, options
Cache Modes
enum CacheMode {
ONLY_REQUEST, // No caching, always network
ONLY_CACHE, // Cache only, no network
REQUEST_FAILED_READ_CACHE, // Network first, fallback to cache
FIRST_USE_CACHE_THEN_REQUEST, // Cache first, then network
CACHE_EMPTY_OR_EXPIRED_THEN_REQUEST, // Network only if cache empty/expired
}
1. Callback Mode
RxNet.get<WeatherInfo>()
.setPath('/api/weather/city/{id}')
.setPathParam("id", "101030100")
.setCacheMode(CacheMode.CACHE_EMPTY_OR_EXPIRED_THEN_REQUEST)
.setJsonConvert(WeatherInfo.fromJson)
.setRetryPolicy(RetryPolicy.exponentialBackoff(maxRetries: 3))
.execute(
success: (data, source) {
setState(() { weather = data; });
},
failure: (e) {
setState(() { error = e.toString(); });
},
completed: () { /* Always called */ },
);
2. async/await Mode
final result = await RxNet.get<WeatherInfo>()
.setPath('/api/weather/city/{id}')
.setPathParam("id", "101030100")
.setJsonConvert(WeatherInfo.fromJson)
.request();
if (result.isSuccess) {
print(result.value);
}
3. Stream Mode
StreamSubscription? _subscription;
_subscription = RxNet.get()
.setPath('/api/weather/city/{id}')
.setPathParam("id", "101030100")
.setLoop(true, interval: const Duration(seconds: 5))
.executeStream()
.listen((result) {
if (result.isSuccess) { setState(() { weather = result.value; }); }
});
// Don't forget to cancel in dispose()
@override
void dispose() { _subscription?.cancel(); super.dispose(); }
Built-in Interceptors
TokenRefreshInterceptor
TokenRefreshInterceptor(
tokenProvider: () async { ... },
isUnauthorized: (error, request) => error.statusCode == 401,
onRequestUpdated: (request, newToken) {
return request.copyWith(headers: {...request.headers, 'Authorization': 'Bearer $newToken'});
},
onTokenRefreshed: (token) => currentToken = token,
);
DeduplicateInterceptor
DeduplicateInterceptor(windowDuration: Duration(milliseconds: 500))
ThrottleInterceptor
ThrottleInterceptor(throttleDuration: Duration(seconds: 1))
Concurrent Requests
For async/await (Recommended)
final (weather, user) = await (
RxNet.get<Weather>().setPath('/weather').request(),
RxNet.get<User>().setPath('/user').request(),
).wait;
For Callback Requests (zipRequest)
final results = await RxNet.zipRequest([
ZipRequest<Weather>(request: ..., tag: 'weather'),
ZipRequest<User>(request: ..., tag: 'user'),
]);
final weather = results.getRequestByTag<Weather>('weather');
Upload & Download
Recommended usage by platform
| Feature | IO (Android / iOS / desktop, etc.) | Web |
|---|---|---|
download / downloadFile |
Save to a real local path (savePath) |
Prefer Downloader.downloadFile(url: ...). With DioAdapter, savePath is only a suggested filename (browser chooses the real location). HttpAdapter download is not supported on Web. |
breakPointDownload |
Supported (local file read/write) | Not supported (no filesystem) |
upload / uploadFile |
MultipartFile.fromFile / path-based APIs OK |
Use MultipartFile.fromBytes / fromStream (e.g. with file_picker). Do not use fromFile / fromPath / raw dart:io File. |
breakPointUpload |
Supported | Not supported (no filesystem) |
Web requests are still subject to browser rules (CORS, mixed content, etc.).
IO examples
// Download to a local path
await RxNet.get().setPath("https://example.com/file.zip")
.downloadFile(savePath: "${appDocPath}/file.zip");
// Breakpoint download
RxNet.get().setPath("https://example.com/large-file.zip")
.breakPointDownload(savePath: "...", onReceiveProgress: (len, total) {});
// Upload from a local file path
final file = await MultipartFile.fromFile("/path/to/file.jpg");
await RxNet.post()
.setPath("/api/upload")
.setBodyParam("file", file)
.asFormData()
.uploadFile();
// Breakpoint upload
RxNet.post().setPath("/api/upload")
.breakPointUpload(filePath: "/path/to/file.jpg", onSendProgress: (len, total) {});
Web examples
// Preferred: trigger a browser download
Downloader.downloadFile(url: "https://example.com/file.zip");
// Upload: read bytes first, then build MultipartFile (do not use fromFile / paths)
final bytes = selectedFileBytes; // e.g. from file_picker
final file = MultipartFile.fromBytes(bytes, filename: "file.jpg");
await RxNet.post()
.setPath("/api/upload")
.setBodyParam("file", file)
.asFormData()
.uploadFile();
certificate verification
Using DioAdapter (dio package method)
adapter.dio.httpClientAdapter = IOHttpClientAdapter(
createHttpClient: () {
final client = HttpClient();
client.badCertificateCallback = (cert, host, port) {
// final der = cert.der;
// final sha256 = sha256Convert(der);
// const trustedFingerprint = "YOUR_SHA256_FINGERPRINT";
// return sha256 == trustedFingerprint;
return true;
};
return client;
},
);
Using HttpAdapter (http package method)
import 'dart:io';
import 'package:http/io_client.dart';
// Create a custom HTTP client with certificate verification
IOClient createPinnedClient() {
final HttpClient httpClient = HttpClient();
httpClient.badCertificateCallback = (X509Certificate cert, String host, int port) {
// Add your certificate verification logic here
// For example: verifying certificate fingerprint
// final der = cert.der;
// final sha256 = sha256Convert(der);
// const trustedFingerprint = "YOUR_SHA256_FINGERPRINT";
// return sha256 == trustedFingerprint;
return true; // For testing only, please verify the production environment correctly
};
return IOClient(httpClient);
}
// Create an HttpAdapter using a custom client
final httpAdapter = HttpAdapter(client: createPinnedClient());
// Initialize RxNet using a configured adapter
await RxNet.init(config:RxNetConfig
baseUrl: "https://your-api.com",
adapter: httpAdapter,
));
Migration from 0.6.x to 0.7.0
Be sure to configure RxNetConfig
await RxNet.init(config: RxNetConfig(baseUrl: "...", cacheMode: CacheMode.ONLY_REQUEST));
Debug Window
RxNet.showDebugWindow(context);

HarmonyOS Support

관련 프로젝트 살펴보기
freeCodeCamp/freeCodeCamp
freeCodeCamp.org의 오픈 소스 코드베이스와 커리큘럼입니다. 수학, 프로그래밍, 컴퓨터 과학을 무료로 배울 수 있습니다.
459,018 스타donnemartin/system-design-primer
대규모 시스템 설계 방법을 배우고 시스템 설계 면접을 준비할 수 있습니다. Anki 플래시카드가 포함되어 있습니다.
377,640 스타react/react
웹과 네이티브 사용자 인터페이스를 위한 라이브러리.
252,535 스타vuejs/vue
Vue 2 저장소입니다. Vue 3은 https://github.com/vuejs/core를 방문하세요.
212,530 스타