kintone REST API × Flutter × micro:bit：ハッカソンで作ったレゴが動くタスク管理アプリ

この記事は [kintone アドベントカレンダー 2024](https://qiita.com/advent-calendar/2024/kintone) 12 日目の記事です。

## はじめに
こんにちは！この記事では、2024年の[TechSheeker ハッカソン](https://techseeker.jp/hackathon2024/)で開発した「できたかなチェック」の開発プロセスを共有します。

**対象読者**: kintone APIやFlutter、Bluetoothを活用したアプリ開発に興味のある方。

## アプリの概要

### できたかなチェックとは？
「動くレゴで遊びたい！」という子ども時代の夢が、このプロジェクトの出発点でした。しかし、単にレゴを動かすだけでは飽きてしまう可能性があると考え、実用性を兼ね備えたアプリの構想を練りました。そこで、時間管理という身近な課題を解決しつつ、小学生が楽しく「時間を守る」習慣を身につけられる仕組みを目指しました。

その結果、スマホでタスクを登録し、期限が来るとBluetooth接続したmicro:bitがレゴキャラクター「いぬごまちゃん」を動かす「できたかなチェック」の開発に至りました。

![いぬごまちゃん.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940479/e6686b86-4e0c-8ed3-1016-ccbefcff6249.png)

## アプリの基本設計

### 動作の概要  
「できたかなチェック」は、以下の流れで動作します：

１．スマホでタスクを登録し、データをkintoneに保存します。

２．登録されたタスクの期限になると、Bluetoothを通じてmicro:bitが動作し、「いぬごまちゃん」が通知を送ります。

３．タスクの進捗状況は、カレンダー上で視覚的に確認できます。

### 主な機能
１．**タスク登録・管理**  
   スマホからタスクを簡単に登録、更新、削除できるようにkintone REST APIを活用。データの保存や管理を効率化しています。
   
２．**Bluetooth接続**  
   Flutterを活用してスマホとmicro:bitをBluetoothで連携。タスクの期限が来ると「いぬごまちゃん」が動き、楽しく時間を知らせます。
   
３．**直感的で親しみやすいUI**  
   子どもたちが操作しやすいカレンダー機能を搭載。親しみやすいキャラクター「いぬごまちゃん」と、視覚的なデザインで、タスク管理を楽しい体験にします。

![アプリ画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940479/c1790498-4b52-3234-18df-b16c0e4e1331.png)

## 主な技術スタック
| **技術**         | **用途**                  |
|------------------|--------------------------|
| Flutter          | クロスプラットフォームのフロントエンド構築   |
| kintone REST API | タスクの管理（登録・更新・削除） |
| micro:bit        | レゴの制御               |
| MakeCode         | micro:bitのプログラミング |
| Dart             | アプリ全体のロジック実装 |

---
## Flutterを選んだ理由
今回のプロジェクトでは、以下の理由からFlutterを採用しました：

１．**クロスプラットフォーム開発**  
   1つのコードベースで複数のプラットフォームに対応できるため、効率的にアプリを開発可能。
   
２．**UIが作りやすい**  
   Flutterは豊富なUIウィジェットを提供しており、直感的でカスタマイズ性の高いデザインを構築できます。

３．**Bluetooth連携**  
   Flutterのプラグインエコシステムを活用し、Bluetooth接続を簡単に実装できます。

## Flutterで実現したこと

- **UIの構築**: 子ども向けに直感的で操作しやすいカレンダー機能を作成しました。
- **Bluetooth接続**: `flutter_blue`プラグインを活用し、micro:bitとの通信を実現しました。
- **API連携**: HTTPパッケージを使用してkintone REST APIとのデータ連携を行いました。

## kintone APIを選んだ理由と良かった点

### 1. 使いやすいREST API  
kintoneはシンプルで直感的に扱えるREST APIを提供しており、今回のハッカソンで初めて利用しましたが、データの登録、更新、削除が比較的簡単に実現できました。ハッカソンのような限られた時間内で迅速に開発を進めるには、学習コストが低いことが大きな決め手となりました。

### 2. データ管理に特化  
kintoneはタスク管理や進捗状況の可視化に特化したデータベース機能を備えており、タスクの登録や更新が簡単にできるだけでなく、記録したデータを直感的に管理・操作することができます。そのため、タスク管理のようなユースケースに適していると判断し、利用しました。

### 3. kintone開発者ライセンスの利用  
kintoneの開発者ライセンスを活用することで、コストを気にせずプロトタイプ開発に集中することができました。学生にとって、このような無料で利用できるライセンスは非常にありがたかったです。

### 4. 豊富なドキュメントとサポート  
kintoneの公式ドキュメントやサンプルコードが充実しており、JavaScriptやFlutter向けのサンプルコードを参考にすることで、Dartへの応用も非常にスムーズに進めることができました。

## 実装の詳細

#### タスクの登録

```dart

Future<void> _addTasks() async {
  final response = await http.post(
    Uri.parse('https://[kintoneのURI]/k/v1/record.json'),
    headers: {
      'X-Cybozu-API-Token': '[kintone APIトークン]',
      'Content-Type': 'application/json',
    },
    body: jsonEncode({
      'app': '[kintoneアプリID]',
      'record': {
        'タイトル': {'value': _titleController.text},
        '日付': {'value': _dayController.text},
        '時刻': {'value': _timeController.text},
        '完了': {'value': []}
      },
    }),
  );
  if (response.statusCode != 200) {
    throw Exception('タスク登録失敗');
  }
}

```

**ポイント**
- kintone APIのシンプルなJSON形式を活用し、登録データを直感的に記述できます。
- app と record など、必要なパラメータをシンプルに設定する方法を示しています。
- APIのリクエストボディは、kintone公式ドキュメントのサンプルJSON形式をそのまま活用しています。

---

#### タスク一覧の取得

```dart

Future<List<Task>> fetchTasks(http.Client client) async {
  final response = await client.get(
    Uri.https('[kintoneのURI]', '/k/v1/records.json', {'app': '[kintoneアプリID]'}),
    headers: {'X-Cybozu-API-Token': '[kintone APIトークン]'},
  );
  if (response.statusCode == 200) {
    return compute(parseCustomers, response.body);
  } else {
    throw Exception('タスク取得失敗');
  }
}

```

**学び**
APIからのレスポンスをDartのデータ型にマッピングし、Flutter UIで表示します。

---

#### タスクの更新

```dart

Future<void> _updateTask(String recordId) async {
  final response = await http.put(
    Uri.parse('https://[kintoneのURI]/k/v1/record.json'),
    headers: {
      'X-Cybozu-API-Token': '[kintone APIトークン]',
      'Content-Type': 'application/json',
    },
    body: jsonEncode({
      'app': '[kintoneアプリID]',
      'id': recordId,
      'record': {
        'タイトル': {'value': _updatedTitleController.text},
        '日付': {'value': _updatedDateController.text},
        '時刻': {'value': _updatedTimeController.text},
      },
    }),
  );
  if (response.statusCode != 200) {
    throw Exception('タスク更新失敗');
  }
}

```

**ポイント**
レコードIDを指定して更新することで、特定のタスクを修正可能にしました。

---

#### タスクの削除

```dart

Future<void> _deleteTask(String recordId) async {
  final response = await http.delete(
    Uri.parse('https://[kintoneのURI]/k/v1/records.json'),
    headers: {
      'X-Cybozu-API-Token': '[kintone APIトークン]',
      'Content-Type': 'application/json',
    },
    body: jsonEncode({
      'app': '[kintoneアプリID]',
      'ids': [recordId],
    }),
  );
  if (response.statusCode != 200) {
    throw Exception('タスク削除失敗');
  }
}

```

**学び**
削除処理では、レコードIDを指定して削除する。

## Bluetoothでmicro:bitを制御

micro:bitを操作する際のBluetooth接続コードを紹介します。

#### デバイスのスキャンと接続

```dart

void connectToMicrobit() {
  flutterBlue.startScan(timeout: Duration(seconds: 4)).then((_) {
    flutterBlue.scanResults.listen((results) {
      for (ScanResult result in results) {
        if (result.device.name.startsWith('BBC micro:bit')) {
          targetDevice = result.device;
          _connectToDevice();
          break;
        }
      }
    });
  });
}

```

#### イベントの送信

```dart

Future<void> _sendEventToMicrobit() async {
  List<BluetoothService> services = await targetDevice!.discoverServices();
  for (BluetoothService service in services) {
    if (service.uuid.toString() == ledServiceUUID) {
      for (BluetoothCharacteristic characteristic in service.characteristics) {
        if (characteristic.properties.write) {
          await characteristic.write(utf8.encode('Event Triggered'));
        }
      }
    }
  }
}

```

**工夫点**
スキャン後に自動接続し、特定のイベントが発生したらmicro:bitに送信するところ

## まとめ

「できたかなチェック」は、小学生が楽しく時間管理を学べるアプリとして完成しました。「いぬごまちゃん」の感情豊かな動きで、タスク完了の達成感を親子で共有することができます。ハードウェアとアプリの連携が初めての試みでしたが、チームで課題を解決しながら進めたプロセスは貴重な経験となりました。

## 今後の展望
１．**通知機能**: 子どもや保護者向けに多彩な通知方法を追加する。  

２．**Bluetooth機能の改良**: 「いぬごまちゃん」専用にBluetoothを検出できるようにし、よりスムーズに選択できる仕組みを整備する。  

---

最後までお読みいただきありがとうございます！  
kintone REST API × Flutter × micro:bitを組み合わせた開発が、少しでも参考になれば幸いです。

---

## あとづけ

### 開発秘話：いぬごまちゃん誕生の裏話

「いぬごまちゃん」は、当初アザラシをイメージして設計していましたが、目の部分に基盤が収まらず悩んでいました。そこで、チームでアイデアを出し合いながら、レゴブロックの自由な組み立ての特性を活かし、「別の形にすれば遊び心があり、面白いのではないか」と発想を転換しました。頭の形を調整した結果、犬耳のようなデザインが生まれ、犬とアザラシを融合させたユニークなキャラクター「いぬごまちゃん」が誕生しました。

尻尾にはハート型のレゴをつけ、可愛らしさをさらに引き立てました。
チームで問題を共有し、改良を重ねる過程はとても楽しく、貴重な学びとなりました。