main
md 174 lines 6.3 KB
Rendered Raw
1 ---
2 name: flutter-use-http-package
3 description: Use the `http` package to execute GET, POST, PUT, or DELETE requests. Use when you need to fetch from or send data to a REST API.
4 metadata:
5 model: models/gemini-3.1-pro-preview
6 last_modified: Tue, 21 Apr 2026 21:36:42 GMT
7 ---
8 # Implementing Flutter Networking
9
10 ## Contents
11 - [Configuration & Permissions](#configuration--permissions)
12 - [Request Execution & Response Handling](#request-execution--response-handling)
13 - [Background Parsing](#background-parsing)
14 - [Workflow: Executing Network Operations](#workflow-executing-network-operations)
15 - [Examples](#examples)
16
17 ## Configuration & Permissions
18
19 Configure the environment and platform-specific permissions required for network access.
20
21 1. Add the `http` package dependency via the terminal:
22 ```bash
23 flutter pub add http
24 ```
25 2. Import the package in your Dart files:
26 ```dart
27 import 'package:http/http.dart' as http;
28 ```
29 3. Configure Android permissions by adding the Internet permission to `android/app/src/main/AndroidManifest.xml`:
30 ```xml
31 <uses-permission android:name="android.permission.INTERNET" />
32 ```
33 4. Configure macOS entitlements by adding the network client key to both `macos/Runner/DebugProfile.entitlements` and `macos/Runner/Release.entitlements`:
34 ```xml
35 <key>com.apple.security.network.client</key>
36 <true/>
37 ```
38
39 ## Request Execution & Response Handling
40
41 Execute HTTP operations and map responses to strongly typed Dart objects.
42
43 * **URIs:** Always parse URL strings using `Uri.parse('your_url')`.
44 * **Headers:** Inject authorization and content-type headers via the `headers` parameter map. Use `HttpHeaders.authorizationHeader` for auth tokens.
45 * **Payloads:** For POST and PUT requests, encode the body using `jsonEncode()` from `dart:convert`.
46 * **Status Validation:** Evaluate `response.statusCode`. Treat `200 OK` (GET/PUT/DELETE) and `201 CREATED` (POST) as success.
47 * **Error Handling:** Throw explicit exceptions for non-success status codes. Never return `null` on failure, as this prevents `FutureBuilder` from triggering its error state and causes infinite loading indicators.
48 * **Deserialization:** Parse the raw string using `jsonDecode(response.body)` and map it to a custom Dart object using a factory constructor (e.g., `fromJson`).
49
50 ## Background Parsing
51
52 Offload expensive JSON parsing to a separate Isolate to prevent UI jank (frame drops).
53
54 * Import `package:flutter/foundation.dart`.
55 * Use the `compute()` function to run the parsing logic in a background isolate.
56 * Ensure the parsing function passed to `compute()` is a top-level function or a static method, as closures or instance methods cannot be passed across isolates.
57
58 ## Workflow: Executing Network Operations
59
60 Use the following checklist to implement and validate network operations.
61
62 **Task Progress:**
63 - [ ] 1. Define the strongly typed Dart model with a `fromJson` factory constructor.
64 - [ ] 2. Implement the network request method returning a `Future<Model>`.
65 - [ ] 3. Apply conditional logic based on the operation type:
66 - **If fetching data (GET):** Append query parameters to the URI.
67 - **If mutating data (POST/PUT):** Set `'Content-Type': 'application/json; charset=UTF-8'` and attach the `jsonEncode` body.
68 - **If deleting data (DELETE):** Return an empty model instance on success (`200 OK`).
69 - [ ] 4. Validate the `statusCode` and throw an `Exception` on failure.
70 - [ ] 5. Integrate the `Future` into the UI using `FutureBuilder`.
71 - [ ] 6. Handle `snapshot.hasData`, `snapshot.hasError`, and default to a `CircularProgressIndicator`.
72 - [ ] 7. **Feedback Loop:** Run the app -> trigger the network request -> review console for unhandled exceptions -> fix parsing or permission errors.
73
74 ## Examples
75
76 ### High-Fidelity Implementation: Fetching and Parsing in the Background
77
78 ```dart
79 import 'dart:async';
80 import 'dart:convert';
81 import 'dart:io';
82 import 'package:flutter/foundation.dart';
83 import 'package:flutter/material.dart';
84 import 'package:http/http.dart' as http;
85
86 // 1. Top-level parsing function for Isolate
87 List<Photo> parsePhotos(String responseBody) {
88 final parsed = (jsonDecode(responseBody) as List<Object?>)
89 .cast<Map<String, Object?>>();
90 return parsed.map<Photo>(Photo.fromJson).toList();
91 }
92
93 // 2. Network execution with background parsing
94 Future<List<Photo>> fetchPhotos() async {
95 final response = await http.get(
96 Uri.parse('https://jsonplaceholder.typicode.com/photos'),
97 headers: {
98 HttpHeaders.authorizationHeader: 'Bearer your_token_here',
99 HttpHeaders.acceptHeader: 'application/json',
100 },
101 );
102
103 if (response.statusCode == 200) {
104 // Offload heavy parsing to a background isolate
105 return compute(parsePhotos, response.body);
106 } else {
107 throw Exception('Failed to load photos. Status: ${response.statusCode}');
108 }
109 }
110
111 // 3. Strongly typed model
112 class Photo {
113 final int id;
114 final String title;
115 final String thumbnailUrl;
116
117 const Photo({
118 required this.id,
119 required this.title,
120 required this.thumbnailUrl,
121 });
122
123 factory Photo.fromJson(Map<String, dynamic> json) {
124 return Photo(
125 id: json['id'] as int,
126 title: json['title'] as String,
127 thumbnailUrl: json['thumbnailUrl'] as String,
128 );
129 }
130 }
131
132 // 4. UI Integration
133 class PhotoGallery extends StatefulWidget {
134 const PhotoGallery({super.key});
135
136 @override
137 State<PhotoGallery> createState() => _PhotoGalleryState();
138 }
139
140 class _PhotoGalleryState extends State<PhotoGallery> {
141 late Future<List<Photo>> _futurePhotos;
142
143 @override
144 void initState() {
145 super.initState();
146 // Initialize Future once to prevent re-fetching on rebuilds
147 _futurePhotos = fetchPhotos();
148 }
149
150 @override
151 Widget build(BuildContext context) {
152 return FutureBuilder<List<Photo>>(
153 future: _futurePhotos,
154 builder: (context, snapshot) {
155 if (snapshot.hasData) {
156 final photos = snapshot.data!;
157 return ListView.builder(
158 itemCount: photos.length,
159 itemBuilder: (context, index) => ListTile(
160 leading: Image.network(photos[index].thumbnailUrl),
161 title: Text(photos[index].title),
162 ),
163 );
164 } else if (snapshot.hasError) {
165 return Center(child: Text('Error: ${snapshot.error}'));
166 }
167
168 // Default loading state
169 return const Center(child: CircularProgressIndicator());
170 },
171 );
172 }
173 }
174 ```