Flutterのhttpパッケージを使った非同期通信の基本をやさしく解説

モバイル開発

はじめに

Flutterでアプリを開発する際、サーバーとデータをやり取りすることはよくあります。 例えば、ニュースアプリで記事情報を取得したり、SNSアプリで投稿データを送信したりといった場面です。 こうした操作を行うときに、Flutterのhttpパッケージは便利な手段になります。

httpパッケージを使えば、外部のAPIと連携する処理をスマートに実装できます。 とはいえ、プログラミング初心者の方にとっては「非同期処理」や「JSONデータの扱い」が難しく感じられるのではないでしょうか。

ここでは、Flutterアプリでhttpパッケージを使う方法や、実務で役立つポイントをやさしく解説します。 あわせて具体的なコード例も示すので、イメージしやすくなるはずです。

この記事を読むとわかること

  • Flutterのhttpパッケージを使った基本的なGETリクエストの流れ
  • 非同期処理エラーハンドリングの考え方
  • よく使われるPOSTリクエストの例
  • 実務での活用シーンや注意点

Flutter httpとは

Flutter httpは、DartのプログラムからHTTPリクエストを手軽に送るためのパッケージです。 サードパーティーが提供しているライブラリですが、Flutterを扱う現場でもよく使われています。 公式リポジトリなどでメンテナンスが続けられているため、信頼度が高いのが特徴です。

Flutterのプロジェクトを作成し、pubspec.yamlhttp:を追加すると利用できます。 追加後はコードの先頭でimport 'package:http/http.dart' as http;のように読み込むだけでOKです。

一度セットアップが完了すれば、http.get()でデータの取得、http.post()でデータの送信といった形で使えるようになります。 このとき、FlutterではUI部分がメインのコードとして走ることが多いですが、httpリクエストは非同期で処理される点に注意が必要です。

HTTP通信はネットワークの状態によって成功したり、失敗したりします。 画面の表示と通信処理を同じ場所で管理するとコードが複雑になりがちなので、処理を分離することで可読性を保ちやすくなります。

基本的なGETリクエストの流れ

Flutterで外部APIからデータを取得するときは、GETリクエストを使うケースが一般的です。 例えばユーザー一覧やニュース記事一覧を取得するときに役立ちます。 ここではシンプルな例として、JSONデータを受け取る流れを見てみましょう。

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

class MyHomePage extends StatefulWidget {
  
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<dynamic> dataList = [];

  Future<void> fetchData() async {
    final url = Uri.parse('https://example.com/data.json');
    final response = await http.get(url);

    if (response.statusCode == 200) {
      final jsonData = json.decode(response.body);
      setState(() {
        dataList = jsonData;
      });
    } else {
      // 失敗時の対応
      print('サーバーから正しいレスポンスが得られませんでした');
    }
  }

  
  void initState() {
    super.initState();
    fetchData();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('データ一覧'),
      ),
      body: ListView.builder(
        itemCount: dataList.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(dataList[index]['title']),
            subtitle: Text(dataList[index]['description']),
          );
        },
      ),
    );
  }
}

このコード例では、fetchData()関数内でhttp.get()を使い、受け取ったレスポンスをjson.decode()でパースしています。 HTTP通信部分は非同期処理になり、awaitで通信完了を待ったあとに結果を処理します。 画面にデータを表示するため、setState()dataListを更新し、ListView.builderでリスト表示を行う形です。

APIから返されるJSONデータは配列になっている場合が多いので、List<dynamic>として受け取ることがよくあります。 もし、オブジェクト単体で受け取る場合はMap<String, dynamic>を使うと便利です。

非同期処理とエラーハンドリング

Flutterではasyncawaitを活用することで、読みやすい非同期コードを書くことができます。 しかし、ネットワークが不安定な場合やサーバー側でエラーが起きた場合など、通信が失敗する可能性があります。

こうしたトラブルに対処するために、エラーハンドリングをしっかり行うことが重要です。 先ほどの例でも、response.statusCodeが200でない場合にエラーメッセージを表示していますが、実際のアプリ開発ではユーザーにわかりやすい形で知らせることがよく行われます。

例えばダイアログを使って「通信に失敗しました」とメッセージを表示したり、エラーページを表示したりといった方法です。 下のようにtry-catchを加えると、より細かいエラーに対処しやすくなります。

Future<void> fetchData() async {
  try {
    final url = Uri.parse('https://example.com/data.json');
    final response = await http.get(url);

    if (response.statusCode == 200) {
      final jsonData = json.decode(response.body);
      setState(() {
        dataList = jsonData;
      });
    } else {
      print('ステータスコード: ${response.statusCode}');
    }
  } catch (e) {
    // ネットワーク障害など
    print('通信時にエラーが発生しました: $e');
  }
}

ここではエラー発生時にprint文を使っていますが、実務ではロギングやユーザーへの通知に切り替えることをおすすめします。 Flutterのアプリをチーム開発するときは、どのようにエラーをハンドルするかを先に決めておくと、後々のトラブルを最小化しやすいです。

非同期処理の失敗は、タイムアウトやネットワーク障害が原因になることがあります。 仮にエラーが起きても、何が問題なのかログやUIで明示しておくとトラブルシューティングがスムーズになります。

POSTリクエストの例

Flutter httpでのPOSTリクエストは、フォームデータやJSONデータをサーバーに送信するときに使われます。 何かしらのサービスにユーザーの登録情報を送ったり、アプリ内でコメントを投稿したりする場面がイメージしやすいでしょう。

以下の例では、JSON形式のデータをPOSTで送信しています。 headersContent-Typeを指定することで、サーバー側が正しくデータを受け取れるようにしています。

Future<void> postData() async {
  final url = Uri.parse('https://example.com/submit');
  final headers = {'Content-Type': 'application/json'};
  final body = json.encode({
    'title': '新しい投稿',
    'content': 'Flutterのhttpパッケージを使った投稿テストです'
  });

  try {
    final response = await http.post(url, headers: headers, body: body);

    if (response.statusCode == 200) {
      // 成功時の処理
      print('投稿が完了しました');
    } else {
      print('投稿に失敗しました: ステータスコード ${response.statusCode}');
    }
  } catch (e) {
    print('通信時にエラーが発生しました: $e');
  }
}

GETリクエストと同じく、awaitでレスポンスが来るのを待ち、response.statusCodeをチェックして結果を判定します。 送信したデータをサーバー側が正常に処理できなかった場合などは、ステータスコードが200以外になるのでわかりやすいです。

Flutterのhttpパッケージでは、PUTやDELETEメソッドもほぼ同じ要領で使えます。 実装例としてはPOSTと大きく変わらないので、状況に合わせてメソッドを切り替えてください。

実務での活用例

Flutterアプリは多様なジャンルで使われています。 ここでは実務でよくあるケースを挙げながら、どのようにFlutter httpを組み合わせられるかを見ていきましょう。

リスト表示アプリでの活用

企業の社内向けツールや、ニュース系アプリなどではリスト表示が多用されます。 例えば、在庫リストを確認してリアルタイムに更新したり、最新情報を引っ張ってきたりするときに、httpパッケージを組み込むと便利です。

GETリクエストでデータを取得し、ローカルにキャッシュしておくとオフライン環境でも最低限の情報を表示できます。 この際、途中で通信が途切れたり、データが空だった場合はエラー対策としてユーザーに注意を促すUIを用意しましょう。 実際の現場では「データが取得できませんでした。再読み込みしてください。」といった簡易メッセージを表示することが多いです。

データの送信と更新

ユーザーの投稿や設定の更新、フォームの送信などは、POSTやPUTメソッドを用いてサーバーにデータを送ります。 フォームの入力チェックをフロント側で行い、サーバー側で受け取ったデータに対しバリデーションを行う流れがよくあります。

例えば、タスク管理アプリで「タスクを追加する」ボタンを押したら、タイトルや期日などをPOSTリクエストでサーバーに送信し、DBに書き込むわけです。 この通信処理はエラーが起きやすい場所でもあるので、try-catchやステータスコードチェックをしっかり組み込み、ユーザーが途中で処理を中断しても問題ないように設計します。

実務ではリクエストごとにトークンを付与し、認証や認可を行うことがよくあります。 httpヘッダーに認証情報を含めるか、セッション管理をどうするかも含めて検討しましょう。

複数APIとの連携を考える場合

実務では、同じアプリ内で複数のAPIを呼び出すケースも多いです。 ユーザープロフィール情報を取得するAPIと、投稿一覧を取得するAPIを別々に呼ぶ、というシーンがあるかもしれません。

複数のGETリクエストを並列で処理するには、Future.wait()を使う方法があります。 例えば次のように書くと、同時に2つのHTTPリクエストを投げて、両方の結果をまとめて受け取れます。

Future<void> fetchMultiple() async {
  final url1 = Uri.parse('https://example.com/profile.json');
  final url2 = Uri.parse('https://example.com/posts.json');

  try {
    final responses = await Future.wait([
      http.get(url1),
      http.get(url2),
    ]);

    // responses[0]がprofile.jsonのレスポンス
    // responses[1]がposts.jsonのレスポンス
    if (responses[0].statusCode == 200 && responses[1].statusCode == 200) {
      final profileData = json.decode(responses[0].body);
      final postsData = json.decode(responses[1].body);
      print(profileData);
      print(postsData);
    } else {
      print('いずれかのAPIでエラーが発生しました');
    }
  } catch (e) {
    print('通信時にエラーが発生しました: $e');
  }
}

こうするとアプリの起動時にまとめてデータを取得し、画面表示に必要な情報を一気にそろえることができます。 ただし、時間のかかるAPI同士を並列で呼び出すと、レスポンスの順番が前後する可能性があるので、処理順序が重要なときは工夫しましょう。

テストやデバッグのポイント

httpパッケージを使うときは、テスト環境と本番環境で異なるURLを使うことがあります。 アプリ開発では、libフォルダとは別にtestフォルダを用意し、テスト時にモックサーバーを使って擬似的なレスポンスを返すこともあります。 そうすることで、サーバーが実際に用意されていなくても、通信処理のロジックを検証しやすいからです。

本番環境に切り替える際は、ベースURLを変数化しておくか、コンフィグを切り替える仕組みを作っておくと便利です。 特に複数のAPIを呼び出すアプリでは、共通の設定ファイルを用いて管理するケースが多いでしょう。

Flutterのdebugビルドでは、通信速度が遅めに感じることがありますが、これはデバッグ用のオーバーヘッドがかかっている場合もあります。 本番ビルドで試したときに処理速度が変わることがあるので、実行時パフォーマンスの計測を行いたいときは注意してください。

まとめ

ここまで、Flutterのhttpパッケージを使った基本的な通信処理の流れを解説しました。 GETリクエストやPOSTリクエストの実装例、そしてエラーハンドリングの方法など、初心者の方がつまずきやすいポイントを取り上げてきました。

非同期処理に慣れていないと最初は少し戸惑うかもしれません。 ですが、一連の流れを理解すると、自分のアプリに様々なサービスやAPIを組み込みやすくなります。

実務ではエラー処理や認証、複数APIとの連携など幅広いシーンに応用できます。 ぜひ今回紹介した内容を参考に、自分のFlutterアプリでのデータ通信を試してみてください。

Flutterをマスターしよう

この記事で学んだFlutterの知識をさらに伸ばしませんか?
Udemyには、現場ですぐ使えるスキルを身につけられる実践的な講座が揃っています。