NCMBのFlutter SDKを使ってTodoアプリを作る（その2：タスクの追加）

NCMBでは公式SDKとしてSwift/Objective-C/Java/Unity/JavaScript SDKを用意しています。また、それ以外にもコミュニティSDKとして、非公式ながらFlutter/React Native/Google Apps Script/C#/Ruby/Python/PHPなど幅広い言語向けにSDKが開発されています。

今回はコミュニティSDKの一つ、Flutter SDKを使ってTodoアプリを作ってみます。[前回は画面の仕様とSDKの初期化について解説](https://qiita.com/goofmint/items/64b32f5abc3f6d32dc72)しました。今回はタスクの追加を行います。

## 完成版のコード

作成したデモアプリのコードは[NCMBMania/Flutter_Todo_App: Flutter SDKを使ったTodoアプリのデモです](https://github.com/NCMBMania/Flutter_Todo_App)にアップロードしてあります。

## タスク追加画面への遷移

タスクの追加は次のフローで処理を行います。

1. 一覧画面上にあるプラスアイコンをタップしてタスク追加画面に遷移
2. タスク名を入力して保存ボタンをタップ
3. タスクをNCMBのデータストアに保存
4. 一覧画面に戻る

### 一覧画面上にあるプラスアイコンをタップしてタスク追加画面に遷移

![FireShot Capture 230 - タスクアプリ - localhost 20211117111623 - 230のコピー.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/faa212a3-91a0-7615-4d0c-1ab8d94f24d8.jpeg)


必要な部分だけを抜き出しますが、AppBarにあるプラスアイコンをタップした際に、TodoPageへ遷移します。その際、新規でNCMBObjectを渡しています。NCMBObjectはNCMBのデータストアに保存する際に利用するクラスです。引数で与えているTodoはクラス名で、DBでいうテーブル名相当になります。

```dart
// タスク一覧画面用ステート
class _TodoListPageState extends State<TodoListPage> {
  // 省略
  // 画面構築
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // 画面上部に表示するAppBar
      appBar: AppBar(
        title: Text('タスク一覧'),
        // タスク追加用のアイコンを設置
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.add),
            onPressed: () async {
              // 新しいTodoを取得
              final NCMBObject item = await Navigator.of(context).push(
                MaterialPageRoute(builder: (context) {
                  // タスクの追加、編集を行う画面への遷移
                  return TodoPage(
                    // 初期値としてNCMBObjectを渡す
                    todo: NCMBObject('Todo'),
                  );
                }),
              );
              // 省略
            }
          )
        ]
      )
    )
  }
}
```

TodoPageでは `_TodoPageState` を呼び出しています。_TodoPageStateでは `widget.todo` にてNCMBObjectを利用できます。

```dart
// Todoを受け取るステートフルウィジェット
class TodoPage extends StatefulWidget {
  TodoPage({
    Key key,
    this.todo,
  }) : super(key: key);

  final NCMBObject todo;

  @override
  _TodoPageState createState() => _TodoPageState();
}
```

### タスク名を入力して保存ボタンをタップ

![FireShot Capture 230 - タスクアプリ - localhost 20211117111624 - 230.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/3ea3dc31-7a2b-b568-620a-a5eb64ee068a.jpeg)


`_TodoPageState` の内容です。NCMBObjectではgetメソッドを使ってデータを取得できます。この時、ユニークキーであるobjectIdがnullか否かによって、データの新規作成と更新とを判別できます。

TextFormFieldの初期値は `(_text = widget.todo.get('body') as String)` としています。NCMBObjectのbodyに入っている値（新規作成時はnull）をStringにキャストし、 `_text` に適用しています。 `_text` はタスクの入力値が変化した場合にも適用しています。

```dart
// Todoの追加、または更新を行うウィジェット
class _TodoPageState extends State<TodoPage> {
  // テキスト入力用
  String _text;
  @override
  Widget build(BuildContext context) {
    // 画面表示用のラベル
    final label = widget.todo.get('objectId') != null ? 'リスト更新' : 'リスト追加';
    return Scaffold(
      // 画面上部に表示するAppBar
      appBar: AppBar(
        title: Text(label),
        actions: <Widget>[
          // 新規保存、更新用のボタン
          IconButton(
            icon: Icon(Icons.save),
            onPressed: () async {
              // 保存処理
              widget.todo.set('body', _text);
              await widget.todo.save();
              // 前の画面に戻る
              Navigator.of(context).pop(widget.todo);
            },
          ),
        ],
      ),
      body: Container(
        // 余白を付ける
        padding: EdgeInsets.all(64),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const SizedBox(height: 8),
            // テキスト入力
            TextFormField(
              initialValue: (_text = widget.todo.get('body') as String),
              onChanged: (String value) {
                _text = value;
              },
            ),
          ],
        ),
      ),
    );
  }
}
```

### タスクをNCMBのデータストアに保存

AppBarにある保存ボタンをタップした際のイベントでは、NCMBObjectのsetメソッドを使って、入力値をbodyに適用しています。bodyは一般的なDBでいうカラム名に相当するもので、自由に決められます。そして save メソッドで保存されます。saveはデータの新規作成、更新どちらでも使えるメソッドです。

```dart
// 保存処理
widget.todo.set('body', _text);
await widget.todo.save();
```

### 一覧画面に戻る

保存が終わったら一覧画面に戻ります。この時、更新されたNCMBObjectを渡しています。保存せずに前の画面に戻った場合、nullが返ります。

```dart
Navigator.of(context).pop(widget.todo);
```

## 一覧画面への反映

タスク追加画面で新しくタスクが作成された場合、NCMBObjectが返ってきます。それを一覧画面の `todoList` へ setState関数内で追加します。戻るボタンを押して画面を戻った場合には item が null になっているので注意してください。

```dart
final NCMBObject item = await Navigator.of(context).push(
  MaterialPageRoute(builder: (context) {
    // タスクの追加、編集を行う画面への遷移
    return TodoPage(
      // 初期値としてNCMBObjectを渡す
      todo: NCMBObject('Todo'),
    );
  }),
);
// レスポンスがあれば、リストに追加
// キャンセルされた場合は null が来る
if (item != null) {
  setState(() {
    todoList.add(item);
  });
}
```

## まとめ

NCMBObjectはデータを柔軟に適用して、簡単にクラウド上のデータベースに保存できます。今回はテキストだけですが、他にも数値や真偽値、オブジェクト、配列、位置情報、日付などが扱えます。ぜひお試しください。

[次回は保存したタスクの取得と編集と削除処理を解説](https://qiita.com/goofmint/items/bf8df0eb64bc7747506d)します。