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データ保存方法
- Map形式に変換
- Json形式に変換(エンコード)
- String型のリストに変換
- 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('読み出し失敗');
}
}
}
呼び出し
簡単な流れは次の通りです。
- TestDataControllerをインスタンス化
- 保存ボタンのタップ処理(onPressed)で、保存用のsetInitialSharedPrefrences()を呼ぶ
- 読み出しボタンのタップ処理(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","鬼滅の刃","銀魂","呪術廻戦"]}]
保存、読み出しともに成功しました。
画面は特に変化はありません。味気ないですね。
以上となります。