[読書会]キー値データをディスクに保存する

(当頁はdocs.flutter.dev(https://docs.flutter.dev/cookbook/persistence/key-value)のリファレンスブログです。)

1.概要

 Flutterアプリで、比較的小規模なキーと値のデータを保存したい場合、shared_preferences プラグインを利用できます。

 通常、データを保存するためには、各プラットフォーム(iOS、Androidなど)ごとにネイティブコードを書く必要があります。しかし、shared_preferences プラグインを使えば、Flutterがサポートするすべてのプラットフォームでキーと値のデータを簡単にディスク上に保存できます。

2.SharedPreferences依存関係の追加

 shared_preferences プラグインをプロジェクトに追加します。

flutter pub add shared_preferences

3.SharedPreferencesでデータを永続化する方法

 SharedPreferencesクラスは、アプリのローカルストレージにデータを永続的に保存するためのメソッドを提供しています。

(保存メソッド)

  • setInt: 整数値を保存する。
  • setBool: 真偽値(true または false)を保存する。
  • setString: 文字列を保存する。
  • 他にも setDouble や setStringList といったメソッドがあります。
  • これらのメソッドは、指定したキー(key)に基づいてデータを保存します。
//アプリ用の SharedPreferences インスタンスを取得
final prefs = await SharedPreferences.getInstance();

//カウンター値を 'counter' というキーで保存
await prefs.setInt('counter', counter);

(保存メソッドの動作)

  1. メモリ上でキーと値を即座に更新
    • 保存操作が呼び出されると、即座にメモリ上でデータが更新されます。
  2. ディスクへの永続化
    • メモリ上の更新後、データを非同期でディスクに保存します。
    • 保存されたデータは、次回アプリ起動時にも利用可能です。

(注意点)

  • 保存されたデータは小規模なデータ向けです。大きなデータや複雑な構造体には適していません。
  • 非同期処理のため、保存処理が完了する前に次のコードを実行しないように注意する必要があります。

4.SharedPreferencesでデータを読み込む方法

 SharedPreferencesクラスは、ローカルストレージから保存されたデータを読み込むためのメソッドを提供しています。

(取得メソッド)

  • getInt: 整数値を取得する。
  • getBool: 真偽値(true または false)を取得する。
  • getString: 文字列を取得する。
  • 他にも getDouble や getStringList といったメソッドがあります。
  • これらのメソッドは、指定したキー(key)を使用してデータを検索します。
// SharedPreferences のインスタンスを取得
final prefs = await SharedPreferences.getInstance();

// 永続ストレージから 'counter' キーに対応する値を取得
// 値が存在しない場合は null が返されるため、デフォルト値として 0 を指定
final counter = prefs.getInt('counter') ?? 0;

(getter メソッドの動作)

  1. 対応する型のメソッドを使用
    • setInt で保存されたデータは、getInt を使用して取得します。
  2. 型の不一致に注意
    • 保存されたデータの型がメソッドで期待される型と異なる場合、例外がスローされます。
    • 例: setString で保存されたデータに対して getInt を使用するとエラーになります。

(注意点)

  1. 型の不一致に注意
    • getInt で文字列を取得しようとすると例外が発生します。保存時と同じ型を指定して下さい。
  2. デフォルト値を設定
    • 値が存在しない場合のために、??演算子を使ってデフォルト値を指定するのが一般的です。

5.SharedPreferencesでデータを削除する

 SharedPreferencesクラスは、保存されたデータを削除するための便利なremove()メソッドを提供しています。
このメソッドを使用することで、特定のキーに対応する値を永続ストレージから削除できます。

(remove() メソッドの概要)

  • remove(String key)
    • 指定したキーに関連付けられたデータを削除します。
    • 削除後、そのキーは存在しない状態になり、次回アクセス時にはnullを返します。
//SharedPreferences のインスタンスを取得
final prefs = await SharedPreferences.getInstance();

//キー 'counter' に関連するデータを削除
await prefs.remove('counter');

(注意点)

  • 特定のキーのみ削除
    • remove()は指定されたキーに対応するデータのみを削除します。他のデータは影響を受けません。
  • すべてのデータを削除する場合
    • すべてのデータを削除したい場合は、clear()メソッドを使用します。

6.サポートされるデータ型

 shared_preferencesは便利なキー・バリュー型のストレージを提供しますが、いくつかの制限があります。
特に、保存できるデータ型やデータの扱い方に関して制限があるため、使用する際にはその特性を理解しておく必要があります。

6.1 保存できるデータ型

 shared_preferencesは以下のようなプリミティブ型をサポートしています。

(1)int:

整数値を保存できます。
例: カウンタやユーザーIDなど。

await prefs.setInt('key', 123);
final value = prefs.getInt('key') ?? 0; // 123

(2)double:

浮動小数点数を保存できます。
例: 設定値や座標など。

await prefs.setDouble('key', 3.14);
final value = prefs.getDouble('key') ?? 0.0; // 3.14

(3)bool:

真偽値を保存できます。
例: 設定のオン/オフ。

await prefs.setBool('key', true);
final value = prefs.getBool('key') ?? false; // true

(4)String:

文字列を保存できます。
例: ユーザー名やトークンなど。

await prefs.setString('key', 'Hello');
final value = prefs.getString('key') ?? ''; // Hello

(5)List:

文字列のリストを保存できます。
例: 履歴や選択項目リスト。

await prefs.setStringList('key', ['One', 'Two', 'Three']);
final value = prefs.getStringList('key') ?? []; // ['One', 'Two', 'Three']

6.2 制限事項

(1)プリミティブ型以外は保存できない

 shared_preferencesはプリミティブ型(int, double, bool, String, List)に限定されており、オブジェクトや複雑な構造は直接保存できません。オブジェクトを保存する場合、JSON形式などに変換して保存する必要があります。

//オブジェクトをJSON形式の文字列に変換して保存
final user = {'name': 'John', 'age': 30};
await prefs.setString('user', jsonEncode(user));

//保存したJSONをデコードして復元
final userString = prefs.getString('user');
final userMap = jsonDecode(userString ?? '{}');
print(userMap['name']); // John

(2)大量のデータの保存には不向き

 shared_preferencesは小規模なデータ(設定値やトークンなど)を保存するためのもので、大量のデータやバイナリデータの保存には適していません。大量のデータを扱う場合は、データベース(sqfliteなど)やファイルシステムを使用することが推奨されます。

(3)アプリ再起動時にデータが確実に保持される保証はない

 データの永続性が保証されない場合があります。
アプリがアンインストールされたり、特定の条件下でデータが消失する可能性があります。

7.テストサポート

8.サンプルコード

Dart
import 'package:flutter/material.dart';
//shared_preferences パッケージ:
//( データをデバイスのローカルストレージに保存し、永続化を実現します。)
import 'package:shared_preferences/shared_preferences.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Shared preferences demo',
      home: MyHomePage(title: 'Shared preferences demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  @override
  void initState() {
    super.initState();
    _loadCounter();
  }

  //カウンター値のロード:
  //( アプリ起動時、SharedPreferencesからカウンター値を取得します。
  //  保存された値が存在しない場合はデフォルト値0を使用します。
  //  setState() を使って、取得した値を画面に反映します。)
  Future<void> _loadCounter() async {
    final prefs = await SharedPreferences.getInstance();
    setState(() {
      _counter = prefs.getInt('counter') ?? 0;
    });
  }

  //カウンター値のインクリメントと保存:
  //( ボタンを押すと、現在のカウンター値をインクリメントします。
  //  更新された値をshared_preferencesに保存します。)
  Future<void> _incrementCounter() async {

    //SharedPreferences.getInstance():
    //( 非同期でストレージにアクセスし、インスタンスを取得します。)
    final prefs = await SharedPreferences.getInstance();

    setState(() {

      //prefs.getInt('counter'):
      //( 保存されたcounterキーの値を取得します。)
      _counter = (prefs.getInt('counter') ?? 0) + 1;

      //prefs.setInt('counter', _counter):
      //( 現在のカウンター値をshared_preferencesに保存します。)
      prefs.setInt('counter', _counter);
    });
  }

  //UIの構築:
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),

      //画面の中央に、現在のカウンター値を表示します。
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'You have pushed the button this many times: ',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),

      //FloatingActionButton を押すと、カウンター値がインクリメントされます。
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

(実行結果の例)

  • アプリ起動時に保存されたカウンター値が復元されます。
  • ボタンを押すとカウンターが増加し、現在の値がローカルストレージに保存されます。
  • アプリを閉じても、次回起動時に前回のカウンター値が画面に表示されます。

 このコードは、shared_preferencesを使った基本的なキーと値の永続化の例であり、小規模な設定やユーザのデータを保存するのに最適です。

コメントを残す