SKILL.md
唯讀
名稱
flutter-use-http-package
描述
使用 `http` 套件執行 GET、POST、PUT 或 DELETE 請求。適用於需要從 REST API 取得資料或將資料傳送至 REST API 的情境。
實作 Flutter 網路功能
目錄
環境設定與權限
設定網路存取所需的環境與各平台專屬權限。
- 透過終端機新增
http套件相依性:flutter pub add http - 在 Dart 檔案中匯入該套件:
import 'package:http/http.dart' as http; - 設定 Android 權限:將網路存取權限新增至
android/app/src/main/AndroidManifest.xml:<uses-permission android:name="android.permission.INTERNET" /> - 設定 macOS entitlements:將網路用戶端 key 新增至
macos/Runner/DebugProfile.entitlements與macos/Runner/Release.entitlements:<key>com.apple.security.network.client</key> <true/>
執行請求與回應處理
執行 HTTP 操作,並將回應資料對映至強型別(strongly typed)的 Dart 物件。
- URI: 務必使用
Uri.parse('your_url')解析 URL 字串。 - 標頭(Headers): 透過
headers參數 Map 帶入驗證資訊與 content-type 標頭。驗證 Token 請使用HttpHeaders.authorizationHeader。 - Payload: 針對 POST 與 PUT 請求,請使用
dart:convert中的jsonEncode()對 request body 進行編碼。 - 狀態碼驗證: 檢查
response.statusCode。將200 OK(GET/PUT/DELETE)與201 CREATED(POST)視為成功。 - 錯誤處理: 當狀態碼非成功時,請拋出明確的例外(Exception)。失敗時切勿回傳
null,否則會導致FutureBuilder無法觸發錯誤狀態,進而引發無限載入指示器。 - 反序列化: 使用
jsonDecode(response.body)解析原始字串,並透過具名建構子(例如fromJson)將其對映至自訂的 Dart 物件。
背景解析
將高耗能的 JSON 解析作業移至獨立的 Isolate 處理,以避免 UI 卡頓或掉幀。
- 匯入
package:flutter/foundation.dart。 - 使用
compute()函式在背景 Isolate 中執行解析邏輯。 - 確保傳遞給
compute()的解析函式為頂層函式(top-level function)或靜態方法(static method),因為閉包(closure)或實例方法(instance method)無法跨 Isolate 傳遞。
工作流程:執行網路操作
使用以下檢核表來實作並驗證網路操作。
任務進度:
- [ ] 1. 定義具備
fromJson建構子的強型別 Dart 模型。 - [ ] 2. 實作回傳
Future<Model>的網路請求方法。 - [ ] 3. 根據操作類型套用條件邏輯:
- 若是取得資料 (GET): 將查詢參數(query parameters)附加至 URI。
- 若是修改資料 (POST/PUT): 設定
'Content-Type': 'application/json; charset=UTF-8'並附上經jsonEncode編碼的 body。 - 若是刪除資料 (DELETE): 成功 (
200 OK) 時回傳空模型實例。
- [ ] 4. 驗證
statusCode,若失敗則拋出Exception。 - [ ] 5. 在 UI 中使用
FutureBuilder整合該Future。 - [ ] 6. 處理
snapshot.hasData與snapshot.hasError,預設狀態則顯示CircularProgressIndicator。 - [ ] 7. 回饋循環: 執行應用程式 -> 觸發網路請求 -> 檢查主控台是否有未處理的例外 -> 修復解析或權限錯誤。
範例
高完整度實作:在背景進行資料取得與解析
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
// 1. 供 Isolate 使用的頂層解析函式
List<Photo> parsePhotos(String responseBody) {
final parsed = (jsonDecode(responseBody) as List<Object?>)
.cast<Map<String, Object?>>();
return parsed.map<Photo>(Photo.fromJson).toList();
}
// 2. 包含背景解析的網路執行邏輯
Future<List<Photo>> fetchPhotos() async {
final response = await http.get(
Uri.parse('https://jsonplaceholder.typicode.com/photos'),
headers: {
HttpHeaders.authorizationHeader: 'Bearer your_token_here',
HttpHeaders.acceptHeader: 'application/json',
},
);
if (response.statusCode == 200) {
// 將高耗能解析工作移至背景 isolate
return compute(parsePhotos, response.body);
} else {
throw Exception('Failed to load photos. Status: ${response.statusCode}');
}
}
// 3. 強型別模型
class Photo {
final int id;
final String title;
final String thumbnailUrl;
const Photo({
required this.id,
required this.title,
required this.thumbnailUrl,
});
factory Photo.fromJson(Map<String, dynamic> json) {
return Photo(
id: json['id'] as int,
title: json['title'] as String,
thumbnailUrl: json['thumbnailUrl'] as String,
);
}
}
// 4. UI 整合
class PhotoGallery extends StatefulWidget {
const PhotoGallery({super.key});
@override
State<PhotoGallery> createState() => _PhotoGalleryState();
}
class _PhotoGalleryState extends State<PhotoGallery> {
late Future<List<Photo>> _futurePhotos;
@override
void initState() {
super.initState();
// 僅初始化一次 Future,避免元件重新構建(rebuild)時重複觸發請求
_futurePhotos = fetchPhotos();
}
@override
Widget build(BuildContext context) {
return FutureBuilder<List<Photo>>(
future: _futurePhotos,
builder: (context, snapshot) {
if (snapshot.hasData) {
final photos = snapshot.data!;
return ListView.builder(
itemCount: photos.length,
itemBuilder: (context, index) => ListTile(
leading: Image.network(photos[index].thumbnailUrl),
title: Text(photos[index].title),
),
);
} else if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
}
// 預設載入中狀態
return const Center(child: CircularProgressIndicator());
},
);
}
}






