flutter_secure_storageを使用してデータを保存する方法

こんにちは、株式会社Pentagonでアプリ開発をしている山崎です!
この記事では、flutter_secure_storageを使用して端末にデータを保存する方法と気を付けるべき点について説明します。
flutter_secure_storageとは、端末内にデータを保存するために使用するパッケージです。
これを使用することで端末内にデータを保存することができます。
iOSとAndroidでは挙動が違うのでその点についても説明します。

【こんな人に読んで欲しい】

  • アプリのデータを端末内に保存する方法を知りたいエンジニア
  • flutter_secure_storageを使用した場合のiOSとAndroidの挙動の違いを知りたい人

【この記事を読むメリット】

  • flutter_secure_storageの使い方が分かる
  • flutter_secure_storageのiOSとAndroidの挙動の違いを知り実装時に注意すべき点が分かる

【結論】
flutter_secure_storageを使用することで安全にデータを端末に保存することができます。
また、iOSの場合は、アプリを削除してもデータが残りますが、Androidの場合は、アプリを削除するとデータも消えてしまう点に考慮が必要となります。

目次

パッケージのインストール

最初に、pubspec.yamlに今回使用するパッケージを入力し、保存します。

dependencies:
  flutter:
    sdk: flutter
  flutter_secure_storage: ^9.0.0

ターミナルに下記のコマンドを入力し、パッケージをインストールします。

flutter pub get

サンプル画面の作成

first_page1.dart

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

class FirstPage1 extends StatefulWidget {
  const FirstPage1({Key? key}) : super(key: key);

  @override
  FirstPage1State createState() => FirstPage1State();
}

class FirstPage1State extends State<FirstPage1> {
  // flutter_secure_storageを使用するためにFlutterSecureStorageを生成する
  final _storage = const FlutterSecureStorage();

  final _keyTextController = TextEditingController();
  final _valueTextController = TextEditingController();

  List<_keyValueItem> _items = [];

  @override
  void initState() {
    super.initState();
    // アプリを起動した時に保存したデータがあれば読み込み表示する
    _readAll();
  }

  Future<void> _readAll() async {
    // データを読み込む
    final all = await _storage.readAll();
    setState(() {
      // 読み込んだデータを、変数_itemsに入れ、再描画して表示させる
      _items = all.entries
          .map((entry) => _keyValueItem(entry.key, entry.value))
          .toList();
    });
  }

// 全てのデータを削除して、再描画する
  Future<void> _deleteAll() async {
    await _storage.deleteAll();
    _keyTextController.text = '';
    _valueTextController.text = '';
    _readAll();
  }

// データを保存する
  Future<void> _addNewItem() async {
    final String key = _keyTextController.text;
    final String value = _valueTextController.text;

    await _storage.write(
      key: key,
      value: value,
    );
    _keyTextController.text = '';
    _valueTextController.text = '';
    _readAll();
  }

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(
          title: const Text('サンプル画面'),
          centerTitle: true,
        ),
        body: Column(
          children: [
            const Padding(
              padding: EdgeInsets.symmetric(vertical: 10),
              child: Text('保存したいキーと値のペアを入力してください!'),
            ),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 16),
              child: TextFormField(
                controller: _keyTextController,
                decoration: const InputDecoration(labelText: 'キー'),
              ),
            ),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 16),
              child: TextFormField(
                controller: _valueTextController,
                decoration: const InputDecoration(labelText: '値'),
              ),
            ),
            ElevatedButton(
              onPressed: _addNewItem,
              child: const Text('保存'),
            ),
            Expanded(
              child: ListView.builder(
                  itemCount: _items.length,
                  itemBuilder: (BuildContext context, int index) => Row(
                        children: [
                          Text(
                            'キー${_items[index].key}:値${_items[index].value}',
                            style: const TextStyle(
                              fontSize: 30,
                            ),
                          )
                        ],
                      )),
            ),
            Padding(
              padding: const EdgeInsets.symmetric(vertical: 20),
              child: TextButton(
                onPressed: _deleteAll,
                child: const Text(
                  'キーと値のペアを全て削除します',
                  style: TextStyle(color: Colors.red, fontSize: 20),
                ),
              ),
            )
          ],
        ),
      );
}

class _keyValueItem {
  _keyValueItem(this.key, this.value);
  final String key;
  final String value;
}

キーと値を入力し、保存ボタンをタップするとデータを端末内に保存できます。

iOSとAndroidの挙動の違い

iOS

iOSでは、キーチェーンを使用してデータを端末内に保存するため、アプリを削除してもデータが残ります。また、端末を移行してもキーチェーンを使用しているため、新しい端末でもデータを引き続き使用することができます。
気をつけなければならない点は、アプリを削除してもデータが残るため、アプリを削除した場合に、削除を検知し、データを削除する処理を実装しなければデータが残る点です。もし最初からアプリを削除した時にデータが削除されるようにしたい場合は、パッケージのshared_preferencesを使用して端末に保存しましょう。

Android

Androidでは、キーストアに保存されるためアプリを削除した場合は、データも一緒に削除されます。

flutter_secure_storageとshared_preferencesの違い

flutter_secure_storage

flutter_secure_storageは安全にデータを保存することができます。
iOSの場合は、キーチェーンを使用してデータを保存し、Androidの場合は、AES暗号化が使用されキーストアに保存されます。
iOSの場合は、キーチェーンを使用しているため、アプリを削除してもデータが残ります。
Androidの場合は、アプリを削除するとデータも削除されるためshared_preferencesと同じ挙動になります。

shared_preferences

shared_preferencesは暗号化されずにデータを保存するため、安全性が低いです。
iOS、Androidともに暗号化せずにデータを保存します。
アプリを削除するとデータも削除されます。

まとめ

flutter_secure_storageを使用することでデータを安全に保存することができますが、iOSとAndroidの挙動が違うため、この違いを理解して実装する必要があります。
単純なデータを保存し、アプリを削除した時にデータを消したい場合はshared_preferencesを使用しましょう!

おまけ

Flutterに関する知識を深めたい方には、『Flutterの特徴・メリット・デメリットを徹底解説』という記事がおすすめです。

この記事では、Flutter アプリ開発の基本から、flutter とは何か、そして実際のflutter アプリ 事例を通じて、その将来性やメリット、デメリットまで詳しく解説しています。
Flutterを使ったアプリ開発に興味がある方、またはその潜在的な可能性を理解したい方にとって、必見の内容となっています。

ぜひ一度ご覧ください。

採用情報はこちら
目次