【Flutter】shared_preferencesを使って値を保存したい

# やりたいこと
下記のようなフォームにて、データ入力後に保存ボタンを押下します。
その後アプリを再ビルドした際に値を表示させたいです。
![スクリーンショット 2022-07-26 15.26.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2737303/9f6ddfa9-c923-ae27-c82a-eca26b49c2e3.png)

# 環境
+ MacOS Monterey 12.4
+ Android Studio
+ Flutter 3.0.3
+ Dart 2.17.6

# 実装方法

### インストール
pubspec.yamlに下記のように行を追加します。
```php:pubspec.yaml
   dependencies:
      shared_preferences: ^2.0.15
```
### パッケージのインポート
main.dartに追加します。
```php:main.dart
   import 'package:shared_preferences/shared_preferences.dart';
```

### 入力フォームを作成する
保存ボタンも実装します。
```php:main.dart
body: Center(
        child: Column(
          children: [
            Container(
              margin: const EdgeInsets.only(top: 30, left: 30, right: 30),
              child: TextField(
                onChanged: setTeamName,
                style: TextStyle(fontSize: 30),
                decoration: InputDecoration(
                  labelText: "チーム名",
                  hintText: "入力してください",
                  suffixIcon: IconButton(
                    padding: const EdgeInsets.only(top: 40),
                    onPressed: () {
                      valueController.clear(); //リセット処理
                      setTeamName(''); //空文字をセット
                    },
                    icon: Icon(Icons.clear),
                  ),
                ),
                controller: valueController,
              ),
            ),
            Container(
                margin: const EdgeInsets.only(top: 40),
                child: SizedBox(
                  width: 150,
                  height: 50,
                  child: ElevatedButton(
                    child: const Text('保存'),
                    style: ElevatedButton.styleFrom(
                      primary: Colors.orange,
                      onPrimary: Colors.white,
                    ),
                    onPressed: () {
                      ScaffoldMessenger.of(context).showSnackBar(
                        SnackBar(
                          duration: const Duration(seconds: 5),
                          content: Text('保存しました'),
                          action: SnackBarAction(
                            label: '閉じる',
                            onPressed: () {},
                          ),
                        ),
                      );
                      debugPrint(teamName);
                    },
                  ),
                )),        
          ],
        ),
      ),
```

### 保存/読み込みのメソッドを作成する
shared_preferencesではキーと値を紐づけて保存できます。
```php:main.dart
class _MyHomePageState extends State<MyHomePage> {
  final valueController = TextEditingController();
  static const keyTeamName = 'teamName';
  String teamName = '';

  void setTeamName(String a) {
    setState(() {
      teamName = a;
    });
  }

  @override
  void initState() {
    super.initState();
    Future(() async {
      await _getData();
    });
  }

  //保存
  Future<void> _setData() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(keyTeamName, teamName);
  }

  //読み込み
  Future<void> _getData() async {
    final prefs = await SharedPreferences.getInstance();
    setState(() {
      teamName = prefs.getString(keyTeamName) ?? ''; //キーに紐付けた値を変数に代入
      valueController.text = teamName; //保存した値を初期値として表示
      debugPrint(teamName);
    });
  }

```

### 画面構築時に_getDataする
initStateを使います。
```php:main.dart
@override
  void initState() {
    super.initState();
    Future(() async {
      await _getData();
    });
  }
```

# 実際の動作
### shared_preferencesを使わない場合
入力後、再ビルドすると値は消えてしまいます。

http://www.giphy.com/gifs/Dtgg7Jdd0zaRV5c0uw

### shared_preferencesを使った場合
再ビルドした後も、値は表示(=保存)されています。

http://www.giphy.com/gifs/rpPnIbMr3rLdgrK6Cn

※GIFを直接貼れなかったのでリンクから飛んでくださいm(__)m

# おわりに
今回はshared_preferencesを学習しました。アプリを落としても値が保存されることで再入力の手間が省け、使いやすさに繋がると思いました！
ここにページ間での値の受け渡しなどが入ってくると、難易度が上がるなと個人的には感じました。
この記事が参考になると嬉しいです(￣▽￣)

# コード全文
```php:main.dart
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final valueController = TextEditingController();
  static const keyTeamName = 'teamName';
  String teamName = '';

  void setTeamName(String a) {
    setState(() {
      teamName = a;
    });
  }

  @override
  void initState() {
    super.initState();
    Future(() async {
      await _getData();
    });
  }

  //保存
  Future<void> _setData() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(keyTeamName, teamName);
  }

  //読み込み
  Future<void> _getData() async {
    final prefs = await SharedPreferences.getInstance();
    setState(() {
      teamName = prefs.getString(keyTeamName) ?? ''; //キーに紐付けた値を変数に代入
      valueController.text = teamName; //保存した値を初期値として表示
      debugPrint(teamName);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("入力フォーム"),
        centerTitle: true,
      ),
      body: Center(
        child: Column(
          children: [
            Container(
              margin: const EdgeInsets.only(top: 30, left: 30, right: 30),
              child: TextField(
                onChanged: setTeamName,
                style: TextStyle(fontSize: 30),
                decoration: InputDecoration(
                  labelText: "チーム名",
                  hintText: "入力してください",
                  suffixIcon: IconButton(
                    padding: const EdgeInsets.only(top: 40),
                    onPressed: () {
                      valueController.clear(); //リセット処理
                      setTeamName(''); //空文字をセット
                    },
                    icon: Icon(Icons.clear),
                  ),
                ),
                controller: valueController,
              ),
            ),
            Container(
                margin: const EdgeInsets.only(top: 40),
                child: SizedBox(
                  width: 150,
                  height: 50,
                  child: ElevatedButton(
                    child: const Text('保存'),
                    style: ElevatedButton.styleFrom(
                      primary: Colors.orange,
                      onPrimary: Colors.white,
                    ),
                    onPressed: () {
                      _setData();
                      ScaffoldMessenger.of(context).showSnackBar(
                        SnackBar(
                          duration: const Duration(seconds: 5),
                          content: Text('保存しました'),
                          action: SnackBarAction(
                            label: '閉じる',
                            onPressed: () {},
                          ),
                        ),
                      );
                      debugPrint(teamName);
                    },
                  ),
                )),          
          ],
        ),
      ),
    );
  }
}

```