【Flutter】SharedPreferencesでListデータを端末に保存・読み出しする方法

Flutter

SharedPreferencesを使用することで、端末にデータを保存できます。但し、Listデータを保存する場合、そのままではString型のListしか保存できません。そこで一度、Listデータをjsonへ変換することで保存できるようになります。また、読み出しの場合はその逆になります。

本記事は、こちらの記事を参考に、自分用にまとめ直したものになります。<(_ _)>

(Providerを除去したり、微修正してわかりやすくしました。)

詳細

方針

画面は凝らずに、次のようにシンプルな仕様にします。

  • サンプルデータはハードコーディング
  • 画面上はボタン2つ:保存ボタン/読み出しボタン
  • 保存ボタンをタップして保存
  • 読み出しボタンをタップして読み出し
  • 結果はコンソールで確認
データ操作

TestModelで

  • Map型へ変換(保存用)
  • jsonオブジェクトへ代入(読み出し用)

を行います。

/// test_model.dart

class TestModel {
  // 様々な型を用意しました
  final String id;
  String category;
  int number;
  bool isAccepted = false;
  List<dynamic> titles;

  TestModel(
      {required this.id,
        required this.category,
        required this.number,
        required this.isAccepted,
        required this.titles});

  /// Map型に変換
  Map toJson() => {
    'id': id,
    'category': category,
    'number': number,
    'isAccepted': isAccepted,
    'titles': titles,
  };

  /// JSONオブジェクトを代入
  TestModel.fromJson(Map json)
      : id = json['id'],
        category = json['category'],
        number = json['number'],
        isAccepted = json['isAccepted'],
        titles = json['titles'];
}

このTestModelをList化したデータを、端末に保存します。サンプルデータは次のように準備します。

/// test_data_controller.dart

// テストデータの準備
void settingTestData() {
  testData = [
    TestModel(
        id: '1',
        category: 'Business book',
        number: 0,
        isAccepted: false,
        titles: ['ビジネスの教科書', '決算書の読み方', 'ロジカルシンキング']),
    TestModel(
        id: '2',
        category: 'Comics',
        number: 999,
        isAccepted: true,
        titles: ['ONE PIECE', 'SLAM DUNK', 'HUNTER×HUNTER',
                 '鬼滅の刃', '銀魂', '呪術廻戦']),
  ];
}
SharedPreferences Listデータ保存方法
  1. Map形式に変換
  2. Json形式に変換(エンコード)
  3. String型のリストに変換
  4. setStringListで保存
/// test_data_controller.dart

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

    SharedPreferences prefs = await SharedPreferences.getInstance();
    // ④保存
    await prefs.setStringList(testKey, myData);

    print('保存したmyData: $myData');
  }

保存に多少の時間がかかるため、asyncで非同期化して、SharedPreferencesの処理にはawaitを付けます。

SharedPreferences Listデータ読み出し方法
  • getStringListで読み出し
  • Jsonデコード
  • MapオブジェクトをClockModelに代入
  • リストに変換
/// test_data_controller.dart

// SharedPrefrences読み出し
  Future read() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    // ①読み出し
    var result = prefs.getStringList(testKey);

    print('読み出したresult:$result');

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

こちらも読み出しに多少の時間がかかるため、asyncで非同期化して、SharedPreferencesの処理にはawaitを付けます。

とりあえず、test_data_controller.dart 全文です。

/// test_data_controller.dart

import 'dart:async';
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import 'test_model.dart';

class TestDataController {
  List<TestModel> testData = []; // 保存・読み出し用リスト
  String testKey = 'testKey'; // 保存・読み出し用文字列キー

  // テストデータの準備
  void settingTestData() {
    testData = [
      TestModel(
          id: '1',
          category: 'Business book',
          number: 0,
          isAccepted: false,
          titles: ['ビジネスの教科書', '決算書の読み方', 'ロジカルシンキング']),
      TestModel(
          id: '2',
          category: 'Comics',
          number: 999,
          isAccepted: true,
          titles: ['ONE PIECE', 'SLAM DUNK', 'HUNTER×HUNTER',
                   '鬼滅の刃', '銀魂', '呪術廻戦']),
    ];
  }

  // 実際に保存を実行する場合はコレを呼ぶ
  void setInitialSharedPrefrences() {
    // テストデータの準備
    settingTestData();
    // sharedPreferencesに保存
    save();
  }

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

    SharedPreferences prefs = await SharedPreferences.getInstance();
    // ④保存
    await prefs.setStringList(testKey, myData);

    print('保存したmyData: $myData');

  }

  // SharedPrefrences読み出し
  Future read() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    // ①読み出し
    var result = prefs.getStringList(testKey);

    print('読み出したresult:$result');

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

簡単な流れは次の通りです。

  1. TestDataControllerをインスタンス化
  2. 保存ボタンのタップ処理(onPressed)で、保存用のsetInitialSharedPrefrences()を呼ぶ
  3. 読み出しボタンのタップ処理(onPressed)で、保存用のread()を呼ぶ
/// main.dart

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

class _MyHomePageState extends State<MyHomePage> {
  late TestDataController testDataController;

  @override
  void initState() {
    super.initState();
    testDataController = TestDataController();
    print('clockDataController.testData = ${testDataController.testData}');
  }

  @override
  Widget build(BuildContext context) {
    // return SettingsPage();
    return Scaffold(
      appBar: AppBar(
        title: Text('Demo:Save josn to local storage'),
      ),
      body: MyPage(),
    );
  }

  Widget MyPage() {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          OutlinedButton(
            onPressed:
             () {testDataController.setInitialSharedPrefrences();}, // 保存
            child: Text('保存'),),
          OutlinedButton(
            onPressed: (){testDataController.read();}, // 読み出し
            child: Text('読み出し'),),
        ],
      ),
    );
  }
}

余談ですが、StatefulWidgetを採用したので、読み出し成功時にsetStateで画面更新し、読み出したデータを即時表示することもできます。

画面更新の必要がなければStatelessWidgetで構いません。

実行結果

画面は次の通りです。味気ないですね。

保存ボタンをタップすると、コンソールに下記のログが出力されます。

flutter: 保存したmyData: [{"id":"1","category":"Business book","number":0,"isAccepted":false,"titles":["ビジネスの教科書","決算書の読み方","ロジカルシンキング"]}, {"id":"2","category":"Comics","number":999,"isAccepted":true,"titles":["ONE PIECE","SLAM DUNK","HUNTER×HUNTER","鬼滅の刃","銀魂","呪術廻戦"]}]

そのあと、読み出しボタンをタップすると、下記のログが出力されます。

flutter: 読み出したresult:[{"id":"1","category":"Business book","number":0,"isAccepted":false,"titles":["ビジネスの教科書","決算書の読み方","ロジカルシンキング"]}, {"id":"2","category":"Comics","number":999,"isAccepted":true,"titles":["ONE PIECE","SLAM DUNK","HUNTER×HUNTER","鬼滅の刃","銀魂","呪術廻戦"]}]

保存、読み出しともに成功しました。

画面は特に変化はありません。味気ないですね。

以上となります。

タイトルとURLをコピーしました