NCMBのFlutter SDKを使ってカレンダーアプリを作る（その1：画面の説明とSDKの導入）

NCMBのFlutter SDKを使ってカレンダーアプリを作ります。予定を登録したり、カレンダーライブラリ（[table_calendar](https://pub.dev/packages/table_calendar)）を使って予定を表示できるというアプリです。

まず最初の記事では画面の説明とSDKの導入までを進めます。

## コードについて

今回のコードは[NCMBMania/flutter\-calendar](https://github.com/NCMBMania/flutter-calendar)にアップロードしてあります。実装時の参考にしてください。

## 利用技術・ライブラリについて

今回は次のような組み合わせになっています。

- NCMB Flutter SDK
- [table_calendar](https://pub.dev/packages/table_calendar)
- [flutter_datetime_picker](https://pub.dev/packages/flutter_datetime_picker)
- [intl](https://pub.dev/packages/intl)

## 仕様について

今回はNCMBのID/パスワード認証を利用しています。カレンダー表示についてはtable_calendarを利用し、アプリはFlutterで作成しています。

## 利用する機能について

カレンダーアプリで利用するNCMBの機能は次の通りです。

- 会員管理
	- ID/パスワード認証
- データストア
  - 予定の登録・更新・削除
  - 予定の検索

## 画面について

今回は以下の5つのステートフルウィジェットがあります。

### MyHomePage

メインのウィジェットです。認証状態によってログイン画面とカレンダー画面を出し分けています。

```dart
@override
Widget build(BuildContext context) {
	return _isLogin ? const CalendarPage() : LoginPage(onLogin: _onLogin);
}
```

### LoginPage

![localhost_56942_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/a293f391-467a-92c5-de3c-fe764f8a7b60.png)


認証を行うウィジェットです。認証状態をMyHomePageにフィードバックしています。

```dart
@override
Widget build(BuildContext context) {
	return Scaffold(
			appBar: AppBar(title: const Text('ログイン&サインアップ')),
			body: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
				Row(mainAxisAlignment: MainAxisAlignment.center, children: const [
					SizedBox(
							height: 100,
							child: Text(
								'ユーザー名とパスワードを入力してください',
							)),
				]),
				SizedBox(
						height: 50,
						width: 250,
						child: TextField(
							decoration: const InputDecoration.collapsed(hintText: 'ユーザー名'),
							onChanged: (value) {
								setState(() {
									_userName = value;
								});
							},
						)),
				SizedBox(
					width: 250,
					height: 100,
					child: TextField(
						obscureText: true,
						decoration: const InputDecoration.collapsed(hintText: 'パスワード'),
						onChanged: (value) {
							setState(() {
								_password = value;
							});
						},
					),
				),
				TextButton(onPressed: _login, child: const Text("ログインする"))
			]));
}
```

### CalendarPage

![localhost_56942_ (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/2066ea46-f7cb-2801-240b-3ff5df03b935.png)


カレンダー表示用のウィジェットです。日付を選択した際に、該当日のイベントを一覧表示します。一覧表示時の1行あたりのデータはScheduleListPageで表示します。

```dart
Widget build(BuildContext context) {
	return Scaffold(
			appBar: AppBar(
				title: const Text("カレンダー"),
				actions: [
					IconButton(
						icon: const Icon(Icons.add),
						onPressed: _addSchedule,
					)
				],
			),
			body: Column(children: [
				TableCalendar(
					locale: 'ja',
					eventLoader: _eventLoader,
					firstDay: DateTime.utc(2010, 10, 16, 10),
					lastDay: DateTime.utc(2030, 3, 14),
					focusedDay: _focusDate,
					onDaySelected: _onDaySelected,
				),
				_selectedSchedules.isEmpty
						? const Text("該当日にイベントはありません")
						: Expanded(
								child: ListView.builder(
										itemCount: _selectedSchedules.length,
										itemBuilder: (BuildContext context, int index) {
											final schedule = _selectedSchedules[index];
											return ScheduleListPage(
												schedule: schedule,
												onEdited: (NCMBObject schedule) {
													setState(() {
														_selectedSchedules[index] = schedule;
													});
												},
											);
										}))
			]));
}
```

### ScheduleListPage

スケジュール一覧表示用のウィジェットです。NCMBObjectを受け取り、画面表示に利用します。

```dart
@override
Widget build(BuildContext context) {
	return GestureDetector(
			onTap: () async {
				var schedule = await Navigator.push(
						context,
						MaterialPageRoute(
								settings: const RouteSettings(name: '/new'),
								builder: (BuildContext context) =>
										ScheduleFormPage(schedule: widget.schedule)));
				if (schedule != null) {
					widget.onEdited(schedule);
				}
			},
			child: Container(
					decoration: const BoxDecoration(
						border: Border(
							bottom: BorderSide(color: Colors.black38),
						),
					),
					child: Column(
						children: [
							Row(
								children: [
									Text(widget.schedule
											.getString("title", defaultValue: "タイトルなし")),
									const Spacer(),
									Text(_viewTime())
								],
							),
							Container(
									alignment: Alignment.centerLeft,
									child: Text(
											widget.schedule.getString("body", defaultValue: "")))
						],
					)));
}
```

### ScheduleFormPage

![localhost_56942_ (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/c803af83-ee1b-b8b5-ea29-cfd355394e0c.png)


予定の入力・更新を行うウィジェットです。

```dart
@override
Widget build(BuildContext context) {
	return Scaffold(
			appBar: AppBar(
				title: const Text("予定の追加・編集"),
			),
			body: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
				Column(mainAxisAlignment: MainAxisAlignment.center, children: [
					SizedBox(
							height: 50,
							width: 250,
							child: TextFormField(
								initialValue: _title,
								decoration: const InputDecoration.collapsed(hintText: 'タイトル'),
								onChanged: (value) {
									setState(() {
										_title = value;
									});
								},
							)),
					SizedBox(
							height: 50,
							width: 250,
							child: TextFormField(
								initialValue: _body,
								decoration: const InputDecoration.collapsed(hintText: '予定詳細'),
								maxLines: 10,
								onChanged: (value) {
									setState(() {
										_body = value;
									});
								},
							)),
					SizedBox(
							height: 50,
							width: 250,
							child: TextFormField(
									controller: _startDateController,
									decoration:
											const InputDecoration.collapsed(hintText: '開始日時'),
									maxLines: 10,
									enableInteractiveSelection: false,
									onTap: () => _showDateTimePicker("startDate"))),
					SizedBox(
							height: 50,
							width: 250,
							child: TextFormField(
									controller: _endDateController,
									decoration:
											const InputDecoration.collapsed(hintText: '終了日時'),
									maxLines: 10,
									enableInteractiveSelection: false,
									onTap: () => _showDateTimePicker("endDate"))),
					TextButton(onPressed: _save, child: const Text("保存する")),
				])
			]));
}
```

### SDKのインストール

flutterコマンドを使って各種ライブラリ・SDKをインストールします。

```
flutter pub add ncmb
flutter pub add table_calendar
flutter pub add flutter_datetime_picker
flutter pub add intl
```

## NCMBのAPIキーを取得

[mBaaSでサーバー開発不要！ | ニフクラ mobile backend](https://mbaas.nifcloud.com/)にてアプリを作成し、アプリケーションキーとクライアントキーを作成します。

## main.dartの修正

main.dartファイルを開いて、NCMB SDKの読み込みと初期化を行います。

```dart
import 'package:ncmb/ncmb.dart';
import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';

void main() async {
  NCMB('YOUR_APPLICATION_KEY',
      'YOUR_CLIENT_KEY');
	// 言語設定
  await initializeDateFormatting("ja");
  runApp(const MyApp());
}
```

これでNCMBの初期化が完了します。

## まとめ

今回はカレンダーアプリの仕様と画面、NCMBの初期化までを解説しました。次は[ログイン処理を実装](https://qiita.com/goofmint/items/8a766ffd96c3e84d598a)します。