This page is available in Japanese only.

第 4 回では、メモの追加・完了・削除ができるようになりました。ただし、今のままではアプリを閉じるとメモが消えてしまいます。
第 5 回は、
- メモを端末に保存して、次に開いたときも残るようにする
- Web API からデータを取得して、メモに追加する
の 2 つを作ります。

パッケージを追加する
通信や保存の機能は、pub.dev で公開されているパッケージを使います。npm で npm install するのと同じように、次のコマンドで追加します。
flutter pub add http shared_preferencespubspec.yaml に次のように追記されます(バージョンは 2026年11月時点)。
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)との変換を追加します。
// 保存・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 件取得します。
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 回)。
画面から呼び出す・エラーを表示する
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` を使います。
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 など)に保存先を変えたりしやすくなります
起動時に読み込む
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final store = MemoStore(LocalMemoRepository());
await store.load();
runApp(MemoApp(store: store, api: SuggestionApi()));
}runApp の前に保存先を使うときは、最初に WidgetsFlutterBinding.ensureInitialized() を呼びます。

Android の通信の許可を忘れずに
Android のアプリがインターネットを使うには、android/app/src/main/AndroidManifest.xml に通信の許可が必要です。開発中(デバッグ版)は自動で許可されているため、リリース版で初めて「通信できない」と気づくことがあります。
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application ...>
まとめ
- パッケージは `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 回)
- Flutter とは?できることと開発環境の作り方
- Dart の基本文法
- ウィジェットで画面を作る
- 状態管理の第一歩
- API からデータを取得し、端末に保存する(この記事・11月13日)
- 画面遷移・テスト・リリースまで(11月14日公開予定)
既存の業務システムや Web サービスと連携するアプリのご相談も承ります
※ Flutter は Google LLC の商標です。
参考
- Flutter 公式ドキュメント「Fetch data from the internet」
- pub.dev「http」「shared_preferences」
Contact
Let's talk about your project.
Web, system and app development, video and more. Get in touch with us here.

