【Flutter】SharedPreferencesでListデータを保存する方法【図解付き】


こんな方におすすめの記事です。

- 端末にデータ保存できるSharedPreferencesの使い方はある程度理解している
- List形式のデータを保存したい

SharedPreferencesは**List形式のデータを保存したい場合、String型しか保存できません。**

あらゆる型を持つListデータを保存したい場合は、ひと手間必要です。

## 解決策：json形式を活用

json形式のエンコードとデコードを行うことで実現できます。
僕のアプリ製作実践例よりお伝えします。

Providerの使い方はこちら。
> [【Flutter】アプリ製作から学ぶProviderの使い方【図解付き】](https://qiita.com/mamoru_takami/items/d12cba1e274721cf8848 "【Flutter】アプリ製作から学ぶProviderの使い方【図解付き】")

### （下準備）Map関連処理を作成

下記のClockModelは、

- Map型への変換 ※保存の際に使用
- Mapオブジェクトを代入する処理　※読み出しの際に使用

のための下準備です。
後で使用します。

```dart:clock.dart
class ClockModel {
  final String id;
  String clock;
  int currentSeconds;
  int plusSeconds;
  bool isMyTurn = false;
  Timer timer;

  ClockModel(
      {this.id,
      this.clock,
      this.currentSeconds,
      this.plusSeconds,
      this.isMyTurn,
      this.timer});

  /// Map型に変換
  Map toJson() => {
        'id': id,
        'clock': clock,
        'currentSeconds': currentSeconds,
        'plusSeconds': plusSeconds,
        'isMyTurn': isMyTurn,
        'timer': timer,
      };

  /// JSONオブジェクトを代入
  ClockModel.fromJson(Map json)
      : id = json['id'],
        clock = json['clock'],
        currentSeconds = json['currentSeconds'],
        plusSeconds = json['plusSeconds'],
        isMyTurn = json['isMyTurn'],
        timer = json['timer'];
}
```


### SharedPreferences Listデータ保存方法

1. Map形式に変換
2. Json形式に変換（エンコード）
3. String型のリストに変換
4. setStringListで保存

図で表すとご覧のとおり（正確ではないです）。
![Flutter-SharedPreferences保存 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699790/2962b041-a928-5fc2-565f-08c078ec3c0d.png)
※アイコンは、自ブログ( https://koaramarch.com )で使用しているコアラです。




今回は、ClockModelに定義したデータをリスト化した、settingClocksというオブジェクトが保存したいデータです。

```dart:clock.dart
class ClockProvider with ChangeNotifier {
  // 今回保存したいリスト
  List<ClockModel> settingClocks = [];

  ClockProvider() {
    syncDataWithProvider();
  }

  void initSettingClocks() {
    settingClocks = [
      ClockModel(
          id: '1',
          clock: '00:05:00',
          currentSeconds: 300,
          plusSeconds: 5,
          isMyTurn: false),
      ClockModel(
          id: '2',
          clock: '00:05:00',
          currentSeconds: 300,
          plusSeconds: 5,
          isMyTurn: false),
    ];
  }

  void setInitialSharedPrefrences() {
    // 設定初期化
    initSettingClocks();
    // sharedPreferencesに保存
    updateSharedPrefrences();
  }

  /// SharedPrefrences保存
  Future updateSharedPrefrences() async {
    // ①Map型変換→②Json形式にエンコード→③リスト化
    List<String> myClocks =
        settingClocks.map((f) => json.encode(f.toJson())).toList();

    SharedPreferences prefs = await SharedPreferences.getInstance();
    // ④保存
    await prefs.setStringList('settingClocks', myClocks);

    print('updateSharedPrefrences: $myClocks');

  }
}
```
**async**で非同期処理化して、SharedPreferencesの処理自体は**await**にして待たせることをお忘れなく。


以下は参考までに、settingClocksへの設定とupdateSharedPrefrences()の呼び出し箇所のコードです。
ボタンタップ時の処理で設定および保存しています。

```dart:settingsPage.dart
class SettingsPage extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () {
        Navigator.pop(context, '戻った');
        return Future.value(false);
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text('設定'),
          actions: [
            FlatButton(
              child: Icon(Icons.refresh),
              onPressed: () {
                Provider.of<ClockProvider>(context, listen: false)
                    .setInitialSharedPrefrences();
              },
            ),
          ],
        ),
        body: SettingsPageHome(),
      ),
    );
  }
}


  void initSettingClocks() {
    settingClocks = [
      ClockModel(
          id: '1',
          clock: '00:05:00',
          currentSeconds: 300,
          plusSeconds: 5,
          isMyTurn: false),
      ClockModel(
          id: '2',
          clock: '00:05:00',
          currentSeconds: 300,
          plusSeconds: 5,
          isMyTurn: false),
    ];
  }

  void setInitialSharedPrefrences() {
    // 設定初期化
    initSettingClocks();
    // sharedPreferencesに保存
    updateSharedPrefrences();
  }
```

### SharedPreferences Listデータ読み出し方法
1. getStringListで読み出し
2. Jsonデコード
3. MapオブジェクトをClockModelに代入
4. リストに変換

図で表すとご覧のとおり（こちらもあくまでイメージです）。
![Flutter-SharedPreferences読み出し (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699790/9870d35d-9528-aa96-a3d9-813debe380a0.png)

保存処理の逆からたどる感じですね。
コードは以下のとおり。

```dart:clock.dart
/// SharedPrefrences読み出し
  Future syncDataWithProvider() async {
    print('syncDataWithProvider');

    SharedPreferences prefs = await SharedPreferences.getInstance();
    // ①読み出し
    var result = prefs.getStringList('settingClocks');

    print('result:$result');
    
    // 読み出し確認
    if (result != null) {
      // ②デコード→③MapオブジェクトをClockModelに代入→④リストに変換
      settingClocks =
          result.map((f) => ClockModel.fromJson(json.decode(f))).toList();
    } else {
      // 必要に応じて初期化
    }

  }
```

以下は参考までに、読み出し処理の実行箇所です。
ClockProviderのコンストラクタにて読み出しています。

```dart:clock.dart
class ClockProvider with ChangeNotifier {
  List<ClockModel> settingClocks = [];

  ClockProvider() {
    syncDataWithProvider();
  }
}
```

SharedPreferencesでリストを保存する際にお役に立ちますと幸いです。


## 参考
https://stackoverflow.com/questions/59890044/store-list-of-objects-or-map-in-sharedpreference-using-dart