この記事は kintone アドベントカレンダー 2024 12 日目の記事です。
はじめに
こんにちは!この記事では、2024年のTechSheeker ハッカソンで開発した「できたかなチェック」の開発プロセスを共有します。
対象読者: kintone APIやFlutter、Bluetoothを活用したアプリ開発に興味のある方。
アプリの概要
できたかなチェックとは?
「動くレゴで遊びたい!」という子ども時代の夢が、このプロジェクトの出発点でした。しかし、単にレゴを動かすだけでは飽きてしまう可能性があると考え、実用性を兼ね備えたアプリの構想を練りました。そこで、時間管理という身近な課題を解決しつつ、小学生が楽しく「時間を守る」習慣を身につけられる仕組みを目指しました。
その結果、スマホでタスクを登録し、期限が来るとBluetooth接続したmicro:bitがレゴキャラクター「いぬごまちゃん」を動かす「できたかなチェック」の開発に至りました。
アプリの基本設計
動作の概要
「できたかなチェック」は、以下の流れで動作します:
1.スマホでタスクを登録し、データをkintoneに保存します。
2.登録されたタスクの期限になると、Bluetoothを通じてmicro:bitが動作し、「いぬごまちゃん」が通知を送ります。
3.タスクの進捗状況は、カレンダー上で視覚的に確認できます。
主な機能
1.タスク登録・管理
スマホからタスクを簡単に登録、更新、削除できるようにkintone REST APIを活用。データの保存や管理を効率化しています。
2.Bluetooth接続
Flutterを活用してスマホとmicro:bitをBluetoothで連携。タスクの期限が来ると「いぬごまちゃん」が動き、楽しく時間を知らせます。
3.直感的で親しみやすいUI
子どもたちが操作しやすいカレンダー機能を搭載。親しみやすいキャラクター「いぬごまちゃん」と、視覚的なデザインで、タスク管理を楽しい体験にします。
主な技術スタック
技術 | 用途 |
---|---|
Flutter | クロスプラットフォームのフロントエンド構築 |
kintone REST API | タスクの管理(登録・更新・削除) |
micro:bit | レゴの制御 |
MakeCode | micro:bitのプログラミング |
Dart | アプリ全体のロジック実装 |
Flutterを選んだ理由
今回のプロジェクトでは、以下の理由からFlutterを採用しました:
1.クロスプラットフォーム開発
1つのコードベースで複数のプラットフォームに対応できるため、効率的にアプリを開発可能。
2.UIが作りやすい
Flutterは豊富なUIウィジェットを提供しており、直感的でカスタマイズ性の高いデザインを構築できます。
3.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への応用も非常にスムーズに進めることができました。
実装の詳細
タスクの登録
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('タスク登録失敗: ${response.statusCode}');
}
}
ポイント
- kintone APIのシンプルなJSON形式を活用し、登録データを直感的に記述できます。
- app と record など、必要なパラメータをシンプルに設定する方法を示しています。
- APIのリクエストボディは、kintone公式ドキュメントのサンプルJSON形式をそのまま活用しています。
タスク一覧の取得
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で表示します。
タスクの更新
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('タスク更新失敗: ${response.statusCode}');
}
}
ポイント
レコードIDを指定して更新することで、特定のタスクを修正可能にしました。
タスクの削除
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('タスク削除失敗: ${response.statusCode}');
}
}
学び
削除処理では、レコードIDを指定して削除する。
Bluetoothでmicro:bitを制御
micro:bitを操作する際のBluetooth接続コードを紹介します。
デバイスのスキャンと接続
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;
}
}
});
});
}
イベントの送信
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に送信するところ
まとめ
「できたかなチェック」は、小学生が楽しく時間管理を学べるアプリとして完成しました。「いぬごまちゃん」の感情豊かな動きで、タスク完了の達成感を親子で共有することができます。ハードウェアとアプリの連携が初めての試みでしたが、チームで課題を解決しながら進めたプロセスは貴重な経験となりました。
今後の展望
1.通知機能: 子どもや保護者向けに多彩な通知方法を追加する。
2.Bluetooth機能の改良:
「いぬごまちゃん」以外のBluetoothデバイスも検出し、よりスムーズに選択できる仕組みを整備する。
最後までお読みいただきありがとうございます!
kintone REST API × Flutter × micro:bitを組み合わせた開発が、少しでも参考になれば幸いです。
あとづけ
開発秘話:いぬごまちゃん誕生の裏話
いぬごまちゃんは、実は当初アザラシをイメージして設計していました。しかし、目の部分に基盤が収まりきらず、「どうしよう」と悩んでいたところ、頭の形を少しずらしてみると…なんと犬耳のように見えてきました!こうして、犬とアザラシの融合体である「いぬごまちゃん」が誕生したのです。
また、レゴブロックの自由に組み立てられる特性のおかげで、設計上のトラブルもなんとか対処することができました。尻尾にはハートマークをつけるなど、細部にも遊び心を加えました。
こうした工夫を通じて、柔軟なものづくりの大切さを改めて実感しました。