mcpField
F
flutter-use-http-package

flutter-use-http-package

github.com/flutter開発3.1k36.5k 回インストール2 日前に更新
リポジトリ

flutter-use-http-package について

Flutter アプリで `http` パッケージを使って通信を実装するためのスキルです。プラットフォーム権限、型付き JSON 処理、エラー対応、バックグラウンド解析を扱います。

主な機能

  1. 01HTTP 設定とプラットフォーム権限
  2. 02GET、POST、PUT、DELETE のパターン
  3. 03型付き JSON のデシリアライズ
  4. 04compute() によるバックグラウンド解析
  5. 05FutureBuilder のエラー処理

活用シーン

  1. 01Flutter アプリへの API リクエスト追加
  2. 02UI スレッド外での大きな JSON の解析
  3. 03リクエストや解析の失敗を UI に表示

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/flutter/agent-plugins --skill flutter-use-http-package

インストール後、Claude Code で/flutter-use-http-packageを入力して読み込まれたか確認します。

詳細

Flutter ネットワーキングの実装

目次

設定と権限

ネットワークアクセスに必要な環境とプラットフォーム固有の権限を設定します。

  1. ターミナルから http パッケージの依存関係を追加します。
    flutter pub add http
    
  2. Dart ファイルにパッケージをインポートします。
    import 'package:http/http.dart' as http;
    
  3. android/app/src/main/AndroidManifest.xml にインターネット権限を追加して、Android の権限を設定します。
    <uses-permission android:name="android.permission.INTERNET" />
    
  4. macos/Runner/DebugProfile.entitlements と macos/Runner/Release.entitlements の両方にネットワーククライアントのキーを追加して、macOS のエンタイトルメントを設定します。
    <key>com.apple.security.network.client</key>
    <true/>
    

リクエストの実行とレスポンスの処理

HTTP 操作を実行し、レスポンスを型安全な Dart オブジェクトにマッピングします。

  • URI: URL 文字列は必ず Uri.parse('your_url') で解析します。
  • ヘッダー: headers パラメータのマップを介して、認証ヘッダーとコンテンツタイプのヘッダーを設定します。認証トークンには HttpHeaders.authorizationHeader を使用します。
  • ペイロード: POST と PUT リクエストでは、dart:convert の jsonEncode() を使用してボディをエンコードします。
  • ステータスの検証: response.statusCode を確認します。200 OK(GET/PUT/DELETE)と 201 CREATED(POST)を成功として扱います。
  • エラー処理: 成功以外のステータスコードでは、明示的な例外をスローします。失敗時に null を返さないでください。これにより FutureBuilder がエラー状態に移行できず、読み込みインジケーターが無限に表示されるためです。
  • デシリアライズ: jsonDecode(response.body) を使用して生の文字列を解析し、ファクトリーコンストラクター(例:fromJson)を使用してカスタム Dart オブジェクトにマッピングします。

バックグラウンドでの解析

UI のジャンク(フレーム落ち)を防ぐため、負荷の高い JSON 解析を別の Isolate に委譲します。

  • package:flutter/foundation.dart をインポートします。
  • compute() 関数を使用して、解析ロジックをバックグラウンドの isolate で実行します。
  • compute() に渡す解析関数は、トップレベル関数または静的メソッドにしてください。クロージャーやインスタンスメソッドは isolate 間で渡せません。

ワークフロー:ネットワーク操作の実行

以下のチェックリストを使って、ネットワーク操作を実装し、検証します。

タスクの進捗:

  • 1. fromJson ファクトリーコンストラクターを持つ、型安全な Dart モデルを定義します。
  • 2. Future<Model> を返すネットワークリクエストメソッドを実装します。
  • 3. 操作の種類に応じた条件分岐を適用します。
    • データを取得する場合(GET): URI にクエリパラメーターを追加します。
    • データを変更する場合(POST/PUT): 'Content-Type': 'application/json; charset=UTF-8' を設定し、jsonEncode したボディを指定します。
    • データを削除する場合(DELETE): 成功時(200 OK)に空のモデルインスタンスを返します。
  • 4. statusCode を検証し、失敗時には Exception をスローします。
  • 5. Future を FutureBuilder を使用して UI に組み込みます。
  • 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. Top-level parsing function for 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. Network execution with background parsing
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) {
    // Offload heavy parsing to a background isolate
    return compute(parsePhotos, response.body);
  } else {
    throw Exception('Failed to load photos. Status: ${response.statusCode}');
  }
}

// 3. Strongly typed model
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 Integration
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();
    // Initialize Future once to prevent re-fetching on rebuilds
    _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}'));
        }
        
        // Default loading state
        return const Center(child: CircularProgressIndicator());
      },
    );
  }
}

コメント