Handling HTTP and JSON
Contents
Core Guidelines
Workflow: Executing HTTP Operations
Workflow: Implementing JSON Serialization
Workflow: Parsing Large JSON in the Background
Examples
Core Guidelines
Enforce HTTPS:
iOS and Android disable cleartext (HTTP) connections by default. Always use HTTPS endpoints. If HTTP is strictly required for debugging, configure
network_security_config.xml
(Android) and
NSAppTransportSecurity
(iOS).
Construct URIs Safely:
Always use
Uri.https(authority, unencodedPath, [queryParameters])
to safely build URLs. This handles encoding and formatting reliably, preventing string concatenation errors.
Handle Status Codes:
Always validate the
http.Response.statusCode
. Treat
200
(OK) and
201
(Created) as success. Throw explicit exceptions for other codes (do not return
null
).
Prevent UI Jank:
Move expensive JSON parsing operations (taking >16ms) to a background isolate using the
compute()
function.
Structured AI Output:
When integrating LLMs, enforce reliable JSON output by specifying a strict JSON schema in the system prompt and setting the response MIME type to
application/json
.
Workflow: Executing HTTP Operations
Use this workflow to implement network requests using the
http
package.
Task Progress:
Add the
http
package to
pubspec.yaml
.
Configure platform permissions (Internet permission in
AndroidManifest.xml
and macOS
.entitlements
).
Construct the target
Uri
.
Execute the HTTP method.
Validate the response and parse the JSON payload.
Conditional Implementation:
If fetching data (GET):
Use
http.get(uri)
.
If sending new data (POST):
Use
http.post(uri, headers: {...}, body: jsonEncode(data))
. Ensure
Content-Type
is
application/json; charset=UTF-8
.
If updating data (PUT):
Use
http.put(uri, headers: {...}, body: jsonEncode(data))
.
If deleting data (DELETE):
Use
http.delete(uri, headers: {...})
.
Feedback Loop: Validation & Error Handling
Run the HTTP request.
Check
response.statusCode
.
If
200
or
201
, call
jsonDecode(response.body)
and map to a Dart object.
If any other code, throw an
Exception('Failed to load/update/delete resource')
.
Review errors -> fix endpoint, headers, or payload structure.
Workflow: Implementing JSON Serialization
Choose the serialization strategy based on project complexity.
Conditional Implementation:
If building a small prototype or simple models:
Use manual serialization with
dart:convert
.
If building a production app with complex/nested models:
Use code generation with
json_serializable
.
Manual Serialization Setup
Task Progress:
Import
dart:convert
.
Define the Model class with
final
properties.
Implement a
factory Model.fromJson(Map
json ) { return Album ( id : json [ 'id' ] as int , title : json [ 'title' ] as String , ) ; } } Future < Album
fetchAlbum ( ) async { final uri = Uri . https ( 'jsonplaceholder.typicode.com' , '/albums/1' ) ; final response = await http . get ( uri ) ; if ( response . statusCode == 200 ) { return Album . fromJson ( jsonDecode ( response . body ) as Map < String , dynamic
) ; } else { throw Exception ( 'Failed to load album' ) ; } } Example 2: HTTP POST Request Future < Album
createAlbum ( String title ) async { final uri = Uri . https ( 'jsonplaceholder.typicode.com' , '/albums' ) ; final response = await http . post ( uri , headers : < String , String
{ 'Content-Type' : 'application/json; charset=UTF-8' , } , body : jsonEncode ( < String , String
{ 'title' : title } ) , ) ; if ( response . statusCode == 201 ) { return Album . fromJson ( jsonDecode ( response . body ) as Map < String , dynamic
) ; } else { throw Exception ( 'Failed to create album.' ) ; } } Example 3: Background Parsing with compute import 'dart:convert' ; import 'package:flutter/foundation.dart' ; import 'package:http/http.dart' as http ; // 1. Top-level function for parsing List < Photo
parsePhotos ( String responseBody ) { final parsed = ( jsonDecode ( responseBody ) as List < Object ?
) . cast < Map < String , Object ?
( ) ; return parsed . map < Photo
( Photo . fromJson ) . toList ( ) ; } // 2. Fetch function using compute Future < List < Photo
fetchPhotos ( http . Client client ) async { final uri = Uri . https ( 'jsonplaceholder.typicode.com' , '/photos' ) ; final response = await client . get ( uri ) ; if ( response . statusCode == 200 ) { // Run parsePhotos in a separate isolate return compute ( parsePhotos , response . body ) ; } else { throw Exception ( 'Failed to load photos' ) ; } } Example 4: Code Generation ( json_serializable ) import 'package:json_annotation/json_annotation.dart' ; part 'user.g.dart' ; @JsonSerializable ( explicitToJson : true ) class User { final String name ; @JsonKey ( name : 'registration_date_millis' ) final int registrationDateMillis ; User ( this . name , this . registrationDateMillis ) ; factory User . fromJson ( Map < String , dynamic
json ) =
_$ UserFromJson ( json ) ; Map < String , dynamic
toJson ( ) =
_$ UserToJson ( this ) ; }