Media

【Flutter入門講座 第5回】APIからデータを取得し、端末に保存する。httpパッケージとshared_preferences

This page is available in Japanese only.

【Flutter入門講座 第5回】APIからデータを取得し、端末に保存する。httpパッケージとshared_preferences

第 4 回では、メモの追加・完了・削除ができるようになりました。ただし、今のままではアプリを閉じるとメモが消えてしまいます。

第 5 回は、

  • メモを端末に保存して、次に開いたときも残るようにする
  • Web API からデータを取得して、メモに追加する

の 2 つを作ります。

API から取得した候補がメモの一覧に追加される
右上のボタンで、練習用の API から候補を 1 件取得して追加(Web で動かした画面)

パッケージを追加する

通信や保存の機能は、pub.dev で公開されているパッケージを使います。npm で npm install するのと同じように、次のコマンドで追加します。

bash
flutter pub add http shared_preferences

pubspec.yaml に次のように追記されます(バージョンは 2026年11月時点)。

pubspec.yaml(抜粋)yaml
dependencies:
  flutter:
    sdk: flutter
  http: ^1.6.0
  shared_preferences: ^2.5.6

^1.6.0 は「1.6.0 以上、2.0.0 未満」という意味です。パッケージを選ぶときは、pub.dev の「Likes」「Pub Points」や、更新の頻度、発行元(Flutter チームの flutter.dev か、個人か)を確認しましょう。

JSON とクラスを変換する

API や保存先とやり取りするデータは JSON です。メモのクラスに、JSON(Map)との変換を追加します。

lib/memo.dart(追加した部分)dart
// 保存・API 用: Map(JSON)との変換
factory Memo.fromJson(Map<String, dynamic> json) => Memo(
  id: json['id'] as int,
  text: json['text'] as String,
  done: json['done'] as bool? ?? false,
);

Map<String, dynamic> toJson() => {'id': id, 'text': text, 'done': done};

factory は、新しいオブジェクトを作って返す特別なコンストラクタです。as int のように型をはっきりさせておくと、データの形が違ったときにすぐ気づけます。

API からデータを取得する(http)

練習用の公開 API「JSONPlaceholder」から、メモの候補(ToDo のタイトル)を 1 件取得します。

lib/quote_api.dartdart
import 'dart:convert';

import 'package:http/http.dart' as http;

/// 練習用の公開 API(JSONPlaceholder)から、メモの候補を 1 件取ってくる
class SuggestionApi {
  SuggestionApi({http.Client? client}) : _client = client ?? http.Client();

  final http.Client _client;

  Future<String> fetchSuggestion(int id) async {
    final uri = Uri.https('jsonplaceholder.typicode.com', '/todos/$id');
    final response = await _client.get(uri);
    if (response.statusCode != 200) {
      throw Exception('取得に失敗しました(${response.statusCode})');
    }
    // 日本語が文字化けしないよう、バイト列を UTF-8 として読む
    final json =
        jsonDecode(utf8.decode(response.bodyBytes)) as Map<String, dynamic>;
    return json['title'] as String;
  }
}

日本語の文字化けに注意

response.body は、サーバーが文字コード(charset)を返さないと、日本語を正しく読めないことがあります。この講座のテストでも、日本語の JSON を返したときに文字コードの扱いでつまずきました。utf8.decode(response.bodyBytes) で UTF-8 として読むようにしておくと安心です。

http.Client を外から渡せるようにする

コンストラクタで http.Client を受け取れるようにしておくと、テストでは本物の通信の代わりに、決まった結果を返すクライアントを渡せます(第 6 回)。

画面から呼び出す・エラーを表示する

lib/memo_list_page.dart(抜粋)dart
Future<void> _addSuggestion(BuildContext context) async {
  final messenger = ScaffoldMessenger.of(context);
  try {
    final text = await api.fetchSuggestion(store.memos.length + 1);
    store.add(text);
  } catch (e) {
    messenger.showSnackBar(SnackBar(content: Text('$e')));
  }
}
  • 通信は失敗することがある前提で、try/catch でエラーを受け止め、画面の下に SnackBar で知らせます
  • ScaffoldMessenger.of(context) を await の前に取り出しているのは、通信の間に画面が閉じられても安全に使えるようにするためです

画面を開いたときに読み込んで表示するだけなら、FutureBuilder というウィジェットも使えます。Flutter の公式の手順(Fetch data from the internet)では、FutureBuilder を使った例が紹介されています。

端末に保存する(shared_preferences)

shared_preferences は、小さなデータを端末に保存するパッケージです(iOS の UserDefaults、Android の SharedPreferences、Web の localStorage を使います)。新しく使い始めるなら、パッケージの説明で推奨されている `SharedPreferencesAsync` を使います。

lib/memo_repository.dartdart
import 'dart:convert';

import 'package:shared_preferences/shared_preferences.dart';

import 'memo.dart';

/// メモの保存先。テストでは InMemoryMemoRepository に差し替える
abstract interface class MemoRepository {
  Future<List<Memo>> load();
  Future<void> save(List<Memo> memos);
}

/// 端末に保存する(shared_preferences の新しい API: SharedPreferencesAsync)
class LocalMemoRepository implements MemoRepository {
  static const _key = 'memos';
  final _prefs = SharedPreferencesAsync();

  @override
  Future<List<Memo>> load() async {
    final json = await _prefs.getString(_key);
    if (json == null) return [];
    final list = jsonDecode(json) as List<dynamic>;
    return [
      for (final item in list) Memo.fromJson(item as Map<String, dynamic>),
    ];
  }

  @override
  Future<void> save(List<Memo> memos) =>
      _prefs.setString(_key, jsonEncode([for (final m in memos) m.toJson()]));
}
  • メモの一覧を JSON の文字列にして 1 つのキー(`memos`)に保存しています。件数が少ないメモアプリならこれで十分です
  • 保存先をインターフェース(`MemoRepository`)にしておくと、テストではメモリーに保存するクラスに差し替えたり、将来サーバーやデータベース(SQLite など)に保存先を変えたりしやすくなります

起動時に読み込む

lib/main.dartdart
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final store = MemoStore(LocalMemoRepository());
  await store.load();
  runApp(MemoApp(store: store, api: SuggestionApi()));
}

runApp の前に保存先を使うときは、最初に WidgetsFlutterBinding.ensureInitialized() を呼びます。

API の取得と端末への保存の流れ

Android の通信の許可を忘れずに

Android のアプリがインターネットを使うには、android/app/src/main/AndroidManifest.xml に通信の許可が必要です。開発中(デバッグ版)は自動で許可されているため、リリース版で初めて「通信できない」と気づくことがあります。

android/app/src/main/AndroidManifest.xml(抜粋)xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET"/>
    <application ...>
通信と保存で気をつける 5 つのこと

まとめ

  • パッケージは `flutter pub add` で追加します。pub.dev で発行元や更新状況を確認しましょう
  • 通信は http パッケージ。日本語は `utf8.decode(response.bodyBytes)` で読み、エラーは try/catch で受け止めて画面に知らせます
  • データは `fromJson`・`toJson` でクラスと JSON を変換します
  • 端末への保存は shared_preferences の `SharedPreferencesAsync`。保存先をインターフェースにしておくと、差し替えやテストが楽です
  • Android のリリース版では、インターネットの許可を忘れずに

次回(最終回)は、画面遷移、テスト、そして App Store・Google Play へのリリース準備です。

Flutter 入門講座(全 6 回)

  1. Flutter とは?できることと開発環境の作り方
  2. Dart の基本文法
  3. ウィジェットで画面を作る
  4. 状態管理の第一歩
  5. API からデータを取得し、端末に保存する(この記事・11月13日)
  6. 画面遷移・テスト・リリースまで(11月14日公開予定)
Flutter・スマホアプリの開発を相談する

既存の業務システムや Web サービスと連携するアプリのご相談も承ります

※ Flutter は Google LLC の商標です。

参考

Contact

Let's talk about your project.

Web, system and app development, video and more. Get in touch with us here.

Request a quoteOther inquiries
Back to Media
Contact