こんにちは、ゲームソリューション部のsoraです。
今回は、FlutterでローカルにKey-Valueでデータ管理するshared_preferencesを試してみたことについて書いていきます。
実装した画面
・ローカルに保存しているデータを取得して、現在の名前として表示する。(初期値は「あなた」に設定)
・入力欄に名前を入力して、ボタンを押下すると、ローカルに保存しているデータを書き換えて表示する。
・アプリを再起動してみても、ローカルに保存しているデータを取得して表示しているため、変更後の名前で表示される。
利用する主要なパッケージ
shared_preferences
shared_preferences | Flutter package
使用したバージョン:shared_preferences: ^2.2.2
Key-Value形式でローカルにデータを保存するパッケージ
Providerなどの状態管理はアプリを落とすとリセットされるが、ローカルにデータを保存することで再起動時もデータが保持される。
暗号化されずに保存されるため、暗号化が必要なデータはflutter_secure_storage
を使用するか、サーバ側で保存してください。
shared_preferences
はアプリを削除するとデータも削除されますが、flutter_secure_storage
はiOSとAndroidで挙動が異なるらしいです。
Androidはアプリは削除するとデータも削除されますが、iOSはアプリを削除してもデータは残るらしいです。
flutter_riverpod
flutter_riverpod | Flutter package
使用したバージョン:flutter_riverpod: ^2.5.1
状態管理パッケージ
多くの情報が落ちている人気なパッケージのため、詳しい説明は割愛します。
コードの解説
コードは以下です。
今回はテストのため、ファイル分けせずに全てmain.dart
に書いています。
main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
// shared_preferencesインスタンス
final sharedPreferencesProvider = Provider<SharedPreferences>(
(_) => throw UnimplementedError()
);
// NotifierProviderでデータの取得・登録
class SharedPrefTest extends Notifier<String> {
@override
String build(){
// Notifierクラスのbuild内でrefを使用可能
final prefs = ref.watch(sharedPreferencesProvider);
String prefName = prefs.getString('my_name') ?? 'あなた';
return prefName;
}
void setName(WidgetRef ref, String name) {
final prefs = ref.watch(sharedPreferencesProvider);
prefs.setString('my_name', name);
}
void getName(WidgetRef ref) {
final prefs = ref.watch(sharedPreferencesProvider);
// my_nameキーの値を取得(空だった場合は空文字を返す)
String prefName = prefs.getString('my_name') ?? '';
state = prefName;
}
}
final sharedPrefTestProvider = NotifierProvider<SharedPrefTest, String>(() {
return SharedPrefTest();
});
// main
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(
ProviderScope(
// 初期起動時にSharedPreferencesでインスタンスの取得
overrides:[
sharedPreferencesProvider.overrideWithValue(
await SharedPreferences.getInstance()
),
],
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(),
home: MyHomePage(),
);
}
}
class MyHomePage extends ConsumerWidget {
MyHomePage({super.key});
final _messageController = TextEditingController();
@override
Widget build(BuildContext context, WidgetRef ref) {
final providerValue = ref.watch(sharedPrefTestProvider);
final providerNotifier = ref.watch(sharedPrefTestProvider.notifier);
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('shared_preferences test'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
padding: const EdgeInsets.only(
left: 25,
right: 25,
),
child: Text(
'現在の名前:${providerValue}',
style: const TextStyle(
fontSize: 18,
),
),
),
const SizedBox(height: 10),
Container(
padding: const EdgeInsets.only(
left: 25,
right: 25,
),
child: TextField(
controller: _messageController,
maxLines: 1,
decoration: const InputDecoration(
hintText: '名前を入力',
hintStyle: TextStyle(color: Colors.black54),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: const Text('名前変更'),
onPressed: (){
var msg = _messageController.text.trim();
if(msg.isEmpty){
return;
}
// shared_preferences周りの処理
// 入力した名前の登録
providerNotifier.setName(ref, msg);
// 入力した名前で状態を更新
providerNotifier.getName(ref);
_messageController.clear();
},
),
]
),
],
),
),
);
}
}
SharedPreferencesのインスタンス取得
まずSharedPreferencesのインスタンスを取得する必要があって、本来であれば以下のように記述します。
// SharedPreferencesインスタンスの取得
final pref = await SharedPreferences.getInstance();
// 値の登録
pref.setString('key', value);
このインスタンス取得の際に、awaitが入っていて非同期処理で記述しなければならず、毎回記載するのは面倒で使いづらいため、最初に1度だけインスタンスを取得するように記述しています。
final sharedPreferencesProvider = Provider<SharedPreferences>(
(_) => throw UnimplementedError()
);
// ...
// main
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(
ProviderScope(
// 初期起動時にSharedPreferencesのインスタンスを取得
overrides:[
sharedPreferencesProvider.overrideWithValue(
await SharedPreferences.getInstance()
),
],
child: const MyApp(),
),
);
}
shared_preferencesでのデータの設定・取得
shared_preferencesでローカルのデータを取得したり、設定したりします。
Notifierクラス内のメソッドとして、データの取得・登録を定義しています。
今回はStirngのため、setString, getString
です。
intであればsetInt, getInt
など、型に応じて変わります。
// NotifierProviderでデータの取得・登録
class SharedPrefTest extends Notifier<String> {
@override
String build(){
// Notifierクラスのbuild内でrefを使用可能
final prefs = ref.watch(sharedPreferencesProvider);
String prefName = prefs.getString('my_name') ?? 'あなた';
return prefName;
}
void setName(WidgetRef ref, String name) {
final prefs = ref.watch(sharedPreferencesProvider);
// my_nameキーに値を設定
prefs.setString('my_name', name);
}
void getName(WidgetRef ref) {
final prefs = ref.watch(sharedPreferencesProvider);
// my_nameキーの値を取得(空だった場合は空文字を返す)
String prefName = prefs.getString('my_name') ?? '';
state = prefName;
}
}
final sharedPrefTestProvider = NotifierProvider<SharedPrefTest, String>(() {
return SharedPrefTest();
});
// ...
ElevatedButton(
child: const Text('名前変更'),
onPressed: (){
var msg = _messageController.text.trim();
if(msg.isEmpty){
return;
}
// shared_preferences周りの処理
// 入力した名前の登録
providerNotifier.setName(ref, msg);
// 入力した名前で状態を更新
providerNotifier.getName(ref);
_messageController.clear();
},
),
最後に
今回は、FlutterでローカルにKey-Valueでデータ管理するshared_preferencesを試してみたことを記事にしました。
どなたかの参考になると幸いです。