{"id":18069144,"url":"https://github.com/arcticfox1919/gio","last_synced_at":"2025-08-19T11:32:04.074Z","repository":{"id":65685288,"uuid":"597331857","full_name":"arcticfox1919/gio","owner":"arcticfox1919","description":"http network package for dart and flutter","archived":false,"fork":false,"pushed_at":"2023-03-19T16:19:08.000Z","size":72,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-12-18T09:45:42.046Z","etag":null,"topics":["dart","dio","flutter","http"],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arcticfox1919.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2023-02-04T07:51:31.000Z","updated_at":"2024-09-27T08:02:43.000Z","dependencies_parsed_at":"2023-02-18T15:45:25.331Z","dependency_job_id":null,"html_url":"https://github.com/arcticfox1919/gio","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arcticfox1919%2Fgio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arcticfox1919%2Fgio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arcticfox1919%2Fgio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arcticfox1919%2Fgio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arcticfox1919","download_url":"https://codeload.github.com/arcticfox1919/gio/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230351138,"owners_count":18212785,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dart","dio","flutter","http"],"created_at":"2024-10-31T08:09:26.617Z","updated_at":"2025-08-19T11:32:04.026Z","avatar_url":"https://github.com/arcticfox1919.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Gio\r\n\r\n**A powerful HTTP client library for Dart, built on top of `package:http`.**\r\n\r\nGio extends the standard `http` package with advanced features like interceptors, request/response logging, and seamless API mocking capabilities. Designed for modern Dart applications, Gio simplifies HTTP communication while providing enterprise-grade flexibility.\r\n\r\n## Features\r\n\r\n- **Built on `package:http`** - Reliable foundation with familiar APIs\r\n- **Interceptor Chain** - Transform requests/responses with ease\r\n- **Smart Logging** - Stream-safe logging with configurable output\r\n- **API Mocking** - Build UIs without backend dependencies\r\n- **Global Configuration** - Base URLs, headers, and settings\r\n- **Lightweight** - Minimal overhead, maximum functionality\r\n- **Simple API** - Get started in minutes\r\n\r\n## Quick Start\r\n\r\nAdd Gio to your `pubspec.yaml`:\r\n\r\n```yaml\r\ndependencies:\r\n  gio: latest\r\n```\r\n\r\n### Basic Usage\r\n\r\n```dart\r\nimport 'package:gio/gio.dart' as gio;\r\n\r\nvoid main() async {\r\n  // Simple GET request\r\n  var response = await gio.get(\"https://api.example.com/users\");\r\n  print(response.body);\r\n\r\n  // GET with query parameters\r\n  response = await gio.get(\r\n    \"https://api.example.com/search\", \r\n    queryParameters: {\"q\": \"dart\", \"limit\": \"10\"}\r\n  );\r\n\r\n  // POST with form data\r\n  response = await gio.post(\r\n    \"https://api.example.com/login\",\r\n    headers: {\"content-type\": \"application/x-www-form-urlencoded\"},\r\n    body: {\"username\": \"john\", \"password\": \"secret\"}\r\n  );\r\n\r\n  // POST with JSON data\r\n  response = await gio.post(\r\n    \"https://api.example.com/users\",\r\n    headers: {\"content-type\": \"application/json\"},\r\n    body: jsonEncode({\"name\": \"John Doe\", \"age\": 30})\r\n  );\r\n}\r\n```\r\n\r\n### Reusable Client\r\n\r\nFor multiple requests, create a reusable client:\r\n\r\n```dart\r\nimport 'package:gio/gio.dart';\r\n\r\nvoid main() async {\r\n  final client = Gio();\r\n  try {\r\n    final response = await client.get(\"https://api.example.com/data\");\r\n    print(response.body);\r\n  } finally {\r\n    client.close(); // Always clean up resources\r\n  }\r\n}\r\n```\r\n\r\n## Configuration\r\n\r\n### Global Settings\r\n\r\nConfigure base URL, logging, and other global options:\r\n\r\n```dart\r\nimport 'package:gio/gio.dart' as gio;\r\n\r\nvoid main() async {\r\n  // Configure global options\r\n  gio.Gio.option = gio.GioOption(\r\n    basePath: 'https://api.example.com',\r\n    enableLog: true,\r\n    headers: {'User-Agent': 'MyApp/1.0'},\r\n  );\r\n\r\n  // Requests now use the base path\r\n  final response = await gio.get(\"/users/123\"); // → https://api.example.com/users/123\r\n  print(response.body);\r\n}\r\n```\r\n\r\n### Custom Logging\r\n\r\nEnable structured logging with `package:logging`:\r\n\r\n```dart\r\nimport 'package:logging/logging.dart';\r\nimport 'package:gio/gio.dart';\r\n\r\nvoid main() async {\r\n  // Set up logging\r\n  Logger.root.level = Level.INFO;\r\n  Logger.root.onRecord.listen((record) {\r\n    print('${record.level.name}: ${record.time}: ${record.message}');\r\n    // Persist to file, send to analytics, etc.\r\n  });\r\n\r\n  Gio.option = GioOption(\r\n    logInterceptor: GioLogInterceptor(\r\n      useLogging: true,\r\n      loggerName: 'gio',\r\n      maxLogBodyBytes: 1024,\r\n    ),\r\n  );\r\n\r\n  // Set the `baseUrl` of `Gio` to an empty string\r\n  // to override the global configuration\r\n  Gio gio = Gio(baseUrl: '');\r\n  try{\r\n    var resp = await gio.get(\"http://worldtimeapi.org/api/timezone/Asia/Shanghai\");\r\n    print(resp.body);\r\n  }finally{\r\n    gio.close();\r\n  }\r\n}\r\n```\r\n\r\n### Interceptor\r\nAn interceptor type, i.e., a function or closure that matches the following signature:\r\n\r\n```dart\r\ntypedef Interceptor = Future\u003cStreamedResponse\u003e Function(Chain chain);\r\n```\r\n\r\nGio interceptors are divided into three types\r\n\r\n- global interceptor\r\n- local interceptor\r\n- default interceptor\r\n\r\nNote that among these three types of interceptors, the local interceptor is called first, followed by the global interceptor, and finally the default interceptor.\r\n\r\n#### Global Interceptor\r\nGlobal interceptors are valid globally, set with `GioOption`:\r\n\r\n```dart\r\nvoid main() async {\r\n  // declare an interceptor\r\n  checkHeader(gio.Chain chain) {\r\n    var auth = chain.request.headers['Authorization'];\r\n    // When the condition is met, continue to execute the next interceptor, \r\n    // otherwise, interrupt the request\r\n    if (auth != null \u0026\u0026 auth.isNotEmpty) {\r\n      return chain.proceed(chain.request);\r\n    }\r\n    throw Exception('Invalid request, does not contain Authorization!');\r\n  }\r\n\r\n  // the parameter is a list, multiple interceptors can be set\r\n  gio.Gio.option = gio.GioOption(\r\n      basePath: 'http://worldtimeapi.org', \r\n      globalInterceptors: [checkHeader]);\r\n\r\n  try {\r\n    var resp = await gio.get(\"/api/timezone/Asia/Shanghai\");\r\n    print(resp.body);\r\n  } catch (e) {\r\n    print(e);\r\n  }\r\n}\r\n```\r\n\r\n```shell\r\nException: Invalid request, does not contain Authorization!\r\n\r\nProcess finished with exit code 0\r\n```\r\n\r\n#### Local interceptor\r\nLocal interceptors can also add multiple:\r\n```dart\r\nimport 'package:gio/gio.dart';\r\n\r\nvoid main() async {\r\n  Gio gio = Gio();\r\n\r\n  // Intercept the request and modify the request header\r\n  gio.addInterceptor((chain) {\r\n    if (chain.request.method == \"POST\") {\r\n      chain.request.headers[\"content-type\"] = \"application/json\";\r\n    }\r\n    return chain.proceed(chain.request);\r\n  });\r\n\r\n  // Intercept the response and do some business processing here\r\n  gio.addInterceptor((chain) async {\r\n    var res = await chain.proceed(chain.request);\r\n    if (res.statusCode != 200) {\r\n      throw Exception(\r\n          \"The request is unsuccessful, the status code is ${res.statusCode}\");\r\n    }\r\n    return res;\r\n  });\r\n\r\n  try {\r\n    var resp =\r\n    await gio.get(\"http://worldtimeapi.org/api/timezone/Asia/Shanghai\");\r\n    print(resp.body);\r\n  } catch (e) {\r\n    print(e);\r\n  } finally {\r\n    gio.close();\r\n  }\r\n}\r\n```\r\n\r\nWe can also put the interceptor logic into a class instead of a closure, which can better organize our code.\r\n\r\nHere we implement an example of canceling the request, when the user leaves the view, the result of the request may no longer be needed:\r\n\r\n`cancel_interceptor.dart`\r\n\r\n```dart\r\nimport 'package:gio/gio.dart';\r\n\r\nclass CancelInterceptor {\r\n  bool _isCancel = false;\r\n\r\n  void cancel() {\r\n    _isCancel = true;\r\n  }\r\n\r\n  Future\u003cStreamedResponse\u003e call(Chain chain) async {\r\n    if (_isCancel) {\r\n      throw CancelError(\"User initiated cancellation.\");\r\n    }\r\n    var res = await chain.proceed(chain.request);\r\n    if (_isCancel) {\r\n      throw CancelError(\"User initiated cancellation.\");\r\n    }\r\n    return res;\r\n  }\r\n}\r\n```\r\n\r\n`main.dart`\r\n```dart\r\nimport 'package:gio/gio.dart';\r\n\r\nvoid main(){\r\n  var cancelInterceptor = CancelInterceptor();\r\n  testCancel(cancelInterceptor);\r\n  cancelInterceptor.cancel();\r\n}\r\n\r\nFuture\u003cvoid\u003e testCancel(CancelInterceptor cancel) async {\r\n  Gio g = Gio();\r\n  g.addInterceptor(cancel);\r\n  try {\r\n    var res = await g.get(\"http://worldtimeapi.org/api/timezone/Asia/Shanghai\");\r\n    print(res.body);\r\n  } on CancelError {\r\n    print(\"request has been canceled\");\r\n  } finally {\r\n    g.close();\r\n  }\r\n}\r\n```\r\n\r\n##### Group Interceptors\r\n\r\nInterceptors allow us to use a uniform way of handling network requests, but sometimes we may need to customize network requests based on modules. Grouped interceptors are used in just such scenarios.\r\n\r\n```dart\r\nimport 'package:gio/gio.dart' as gio;\r\n\r\nvoid main() async {\r\n  // Setting up group interceptors\r\n  gio.group(\"module1\").addInterceptor(CancelInterceptor());\r\n  gio.group(\"module2\").addInterceptor(ModifyHeaderInterceptor());\r\n\r\n  var module1 = gio.GioGroup(\"module1\");\r\n  try{\r\n    var resp = module1.get(\"http://worldtimeapi.org/api/timezone/Asia/Shanghai\");\r\n    print(resp);\r\n  }catch(e){\r\n    print(e);\r\n  }finally{\r\n    module1.close();\r\n  }\r\n\r\n  var module2 = gio.GioGroup(\"module2\");\r\n  try{\r\n    var resp = module2.get(\"http://worldtimeapi.org/api/timezone/Asia/Shanghai\");\r\n    print(resp);\r\n  }catch(e){\r\n    print(e);\r\n  }finally{\r\n    module2.close();\r\n  }\r\n}\r\n```\r\n\r\n#### Default Interceptor\r\nThe default interceptor refers to several interceptors included in `GioOption`, they are:\r\n\r\n- `GioLogInterceptor`\r\n- `GioConnectInterceptor`\r\n- `GioMockInterceptor`\r\n\r\nWe can customize these interceptors to replace the default interceptors:\r\n\r\n```dart\r\nimport 'package:gio/gio.dart';\r\n\r\nvoid main() async {\r\n  Gio.option = GioOption(\r\n    connectInterceptor: MyConnectInterceptor(),\r\n    logInterceptor: MyLogInterceptor()\r\n  );\r\n\r\n  Gio gio = Gio();\r\n  try{\r\n    var resp = await gio.get(\"http://worldtimeapi.org/api/timezone/Asia/Shanghai\");\r\n    print(resp.body);\r\n  }finally{\r\n    gio.close();\r\n  }\r\n}\r\n\r\nclass MyConnectInterceptor extends GioConnectInterceptor{\r\n\r\n  @override\r\n  Future\u003cbool\u003e checkConnectivity() {\r\n    // TODO: Check here whether the current network is connected\r\n      throw ConnectiveError(101,\"Mobile Network Data disabled !\");\r\n      // or\r\n      // throw ConnectiveError(102,\"Wifi disabled !\");\r\n  }\r\n}\r\n\r\nclass MyLogInterceptor extends GioLogInterceptor{\r\n  @override\r\n  Future\u003cStreamedResponse\u003e call(Chain chain) async {\r\n    final request = chain.request;\r\n    // _logRequest(request);\r\n    try {\r\n      final response = await chain.proceed(request);\r\n      // _logResponse(response);\r\n      return response;\r\n    } catch (e) {\r\n      // _logUnknownError(e, request);\r\n      rethrow;\r\n    }\r\n  }\r\n}\r\n```\r\nNote that default interceptors are also globally available.\r\n\r\nHere, we can implement log tracking in the interceptor and request time-consuming monitoring. You can refer to the source code of [`GioLogInterceptor`](https://github.com/arcticfox1919/gio/blob/main/gio/lib/src/interceptor/log_interceptor.dart).\r\n### Mock Response\r\nMock Response is a very useful feature when you want to develop the UI first or test and debug the UI without a service backend.\r\n\r\n```yaml\r\ndependencies:\r\n  gio: latest\r\n  gio_mock: latest\r\n```\r\n\r\nTo use the feature of mocking the backend response, you need to set the `mockInterceptor` parameter, as in the example:\r\n```dart\r\nvoid main() async{\r\n  gio.Gio.option = gio.GioOption(\r\n      basePath: 'https://giomock.io',\r\n      mockInterceptor: GioMockServer(MyMockChannel()));\r\n\r\n  var resp = await gio.get(\"/greet\", queryParameters: {'name': 'Bob'});\r\n  print(resp.body);\r\n\r\n  var data = {\"username\": \"Bob\", \"passwd\": \"123456\"};\r\n  var header = {\"content-type\":\"application/x-www-form-urlencoded\"};\r\n  resp = await gio.post(\"/login\", headers: header,body: data);\r\n  print(resp.body);\r\n\r\n  var data2 = {\r\n    \"array\":[\r\n      {\r\n        \"name\":\"Go\",\r\n        \"url\":\"https://go.dev/\",\r\n      },\r\n      {\r\n        \"name\":\"Dart\",\r\n        \"url\":\"https://dart.dev/\",\r\n      },\r\n    ]\r\n  };\r\n  header = {\"content-type\":\"application/json\"};\r\n  resp = await gio.post(\"/list\", headers: header,body: jsonEncode(data2));\r\n  print(resp.body);\r\n}\r\n```\r\nNext you need to create the `MyMockChannel`:\r\n\r\n```dart\r\nimport 'package:gio_mock/gio_mock.dart';\r\nimport 'package:gio_mock/src/http/response_x.dart';\r\n\r\nclass MyMockChannel extends MockChannel{\r\n\r\n  @override\r\n  void entryPoint() {\r\n    get(\"/greet\",(MockRequest request){\r\n      return ResponseX.ok(\"hello,${request.query['name']}\");\r\n    });\r\n\r\n    post(\"/login\",(MockRequest request){\r\n      return ResponseX.ok(request.bodyFields);\r\n    });\r\n\r\n    post(\"/list\",(MockRequest request){\r\n      return ResponseX.ok(request.body);\r\n    });\r\n  }\r\n}\r\n```\r\n\r\nWe need to register routes in the `entryPoint` method, you can use methods such as `get`/`post` to register routes for HTTP requests.\r\n\r\nThe second parameter to these methods is a handler for the route response, where you can process the request parameters and return the response.\r\n\r\n## License\r\n\r\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farcticfox1919%2Fgio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farcticfox1919%2Fgio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farcticfox1919%2Fgio/lists"}