flutter-networking
Scannednpx machina-cli add skill MADTeacher/mad-agents-skills/flutter-networking --openclawFlutter Networking
Quick Start
Add HTTP dependency to pubspec.yaml:
dependencies:
http: ^1.6.0
Basic GET request:
import 'package:http/http.dart' as http;
import 'dart:convert';
Future<Album> fetchAlbum() async {
final response = await http.get(
Uri.parse('https://api.example.com/albums/1'),
);
if (response.statusCode == 200) {
return Album.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load album');
}
}
Use in UI with FutureBuilder:
FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.title);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
)
HTTP Methods
GET - Fetch Data
Use for retrieving data. See http-basics.md for complete examples.
POST - Create Data
Use for creating new resources. Requires Content-Type: application/json header.
final response = await http.post(
Uri.parse('https://api.example.com/albums'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{'title': title}),
);
See http-basics.md for POST examples.
PUT - Update Data
Use for updating existing resources.
final response = await http.put(
Uri.parse('https://api.example.com/albums/1'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{'title': title}),
);
DELETE - Remove Data
Use for deleting resources.
final response = await http.delete(
Uri.parse('https://api.example.com/albums/1'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
);
WebSocket
Add WebSocket dependency:
dependencies:
web_socket_channel: ^3.0.3
Basic WebSocket connection:
import 'package:web_socket_channel/web_socket_channel.dart';
final _channel = WebSocketChannel.connect(
Uri.parse('wss://echo.websocket.events'),
);
// Listen for messages
StreamBuilder(
stream: _channel.stream,
builder: (context, snapshot) {
return Text(snapshot.hasData ? '${snapshot.data}' : '');
},
)
// Send message
_channel.sink.add('Hello');
// Close connection
_channel.sink.close();
See websockets.md for complete WebSocket implementation.
Authentication
Add authorization headers to requests:
import 'dart:io';
final response = await http.get(
Uri.parse('https://api.example.com/data'),
headers: {HttpHeaders.authorizationHeader: 'Bearer $token'},
);
Common authentication patterns:
- Bearer Token:
Authorization: Bearer <token> - Basic Auth:
Authorization: Basic <base64_credentials> - API Key:
X-API-Key: <key>
See authentication.md for detailed authentication strategies.
Error Handling
Handle HTTP errors appropriately:
if (response.statusCode >= 200 && response.statusCode < 300) {
return Data.fromJson(jsonDecode(response.body));
} else if (response.statusCode == 401) {
throw UnauthorizedException();
} else if (response.statusCode == 404) {
throw NotFoundException();
} else {
throw ServerException();
}
See error-handling.md for comprehensive error handling strategies.
Performance
Background Parsing with Isolates
For large JSON responses, use compute() to parse in background isolate:
import 'package:flutter/foundation.dart';
Future<List<Photo>> fetchPhotos(http.Client client) async {
final response = await client.get(
Uri.parse('https://api.example.com/photos'),
);
return compute(parsePhotos, response.body);
}
List<Photo> parsePhotos(String responseBody) {
final parsed = (jsonDecode(responseBody) as List)
.cast<Map<String, dynamic>>();
return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
}
See performance.md for optimization techniques.
Integration with Architecture
When using MVVM architecture (see flutter-architecture):
- Service Layer: Create HTTP service for API endpoints
- Repository Layer: Aggregate data from services, handle caching
- ViewModel Layer: Transform repository data for UI
Example service:
class AlbumService {
final http.Client _client;
AlbumService(this._client);
Future<Album> fetchAlbum(int id) async {
final response = await _client.get(
Uri.parse('https://api.example.com/albums/$id'),
);
if (response.statusCode == 200) {
return Album.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load album');
}
}
}
Common Patterns
Repository Pattern
Single source of truth for data type:
class AlbumRepository {
final AlbumService _service;
final LocalStorage _cache;
Future<Album> getAlbum(int id) async {
try {
return await _cache.getAlbum(id) ??
await _service.fetchAlbum(id);
} catch (e) {
throw AlbumFetchException();
}
}
}
Retry Logic
Implement exponential backoff for failed requests:
Future<T> fetchWithRetry<T>(
Future<T> Function() fetch, {
int maxRetries = 3,
}) async {
for (int i = 0; i < maxRetries; i++) {
try {
return await fetch();
} catch (e) {
if (i == maxRetries - 1) rethrow;
await Future.delayed(Duration(seconds: 2 << i));
}
}
throw StateError('Unreachable');
}
Best Practices
DO
- Use type-safe model classes with
fromJsonfactories - Handle all HTTP status codes appropriately
- Parse JSON in background isolates for large responses
- Implement retry logic for transient failures
- Cache responses when appropriate
- Use proper timeouts
- Secure tokens and credentials
DON'T
- Parse JSON on main thread for large responses
- Ignore error states in UI
- Store tokens in source code or public repositories
- Make requests without timeout configuration
- Block UI thread with network operations
- Throw generic exceptions without context
Resources
references/
- http-basics.md - Complete HTTP CRUD operations examples
- websockets.md - WebSocket implementation patterns
- authentication.md - Authentication strategies and token management
- error-handling.md - Comprehensive error handling patterns
- performance.md - Optimization techniques and best practices
assets/examples/
fetch_example.dart- Complete GET request with FutureBuilderpost_example.dart- POST request implementationwebsocket_example.dart- WebSocket client with stream handlingauth_example.dart- Authenticated request examplebackground_parsing.dart- compute() for JSON parsing
assets/code-templates/
http_service.dart- Reusable HTTP service templaterepository_template.dart- Repository pattern template
Source
git clone https://github.com/MADTeacher/mad-agents-skills/blob/main/flutter-networking/SKILL.mdView on GitHub Overview
This skill covers Flutter networking essentials, including HTTP CRUD operations, WebSocket real-time communication, and authenticated requests using headers or tokens. It also addresses REST API integration, error handling, and performance optimizations such as background parsing with isolates.
How This Skill Works
Add dependencies for http and web_socket_channel, implement HTTP CRUD calls with proper JSON bodies and Content-Type headers, and parse responses. For real-time data, establish a WebSocket connection using WebSocketChannel, listen to the stream, and send messages via the sink. Authentication is achieved by attaching Authorization headers, and errors are handled by mapping HTTP status codes to specific exceptions; performance notes include using isolates for background parsing when processing large responses.
When to Use It
- Implement HTTP CRUD operations (GET, POST, PUT, DELETE) against a REST API.
- Establish WebSocket real-time communication for live updates.
- Make authenticated requests using headers and tokens (e.g., Bearer tokens).
- Parse large or CPU-intensive responses in the background with isolates to keep the UI responsive.
- Integrate REST APIs with proper error handling and structured exception management.
Quick Start
- Step 1: Add dependencies (http and web_socket_channel) to pubspec.yaml.
- Step 2: Create a simple HTTP GET function to fetch data and parse JSON.
- Step 3: Use FutureBuilder in the UI to display data or handle loading and errors.
Best Practices
- Declare and pin dependencies in pubspec.yaml (http and web_socket_channel).
- Use appropriate Content-Type headers when sending JSON payloads (application/json; charset=UTF-8).
- Check response.statusCode and map common errors to specific exceptions (401, 404, etc.).
- Use FutureBuilder for single HTTP responses and StreamBuilder for WebSocket streams to reflect UI state.
- Store tokens securely and implement refresh/retry strategies for authenticated requests.
Example Use Cases
- GET an album from a REST API and decode JSON into a model.
- POST a new album with a JSON body and Content-Type header.
- PUT to update an existing album with a JSON payload.
- DELETE an album resource by ID.
- Connect to a WebSocket echo server, display messages, and send user input.