ChatGPTでアプリを作ろうとしています、id:kouki_dan です。
前回のPart3では、ログイン、ログアウトの画面を作ってきました。それも、ChatGPTが書いてくれたコードをコピーしてペーストしていくだけでです。
今回は、順番的にいうと、やっと機能を作っていくことになるでしょう。
Part1で決めたように、このアプリは、冷凍庫の中身を管理してくれるアプリで、名前は「FreezStock」です。今回もChatGPTの赴くままに機能を実装していきましょう!
今回はYouTubeでライブをしながらやってみました!
前回の延長線ということで、お礼を言った後に次に進む意思表示をしました。すると、次にやらなければいけないことを教えてもらえました。
Part1では、次にやるべきことは冷凍庫一覧画面のはずなのですが、ChatGPTさんは完全に忘れていますね。冷凍庫一覧画面は正直いらないと思ってたので、このまま先に進みます!
ということで、次にやるべきことを再度聞いてみると、詳細化してくれました。今回は、これを1つ1つ実装していくことになりそうですね。
class FrozenItem { final String id; final String name; final int quantity; final DateTime expirationDate; final DateTime addedDate; FrozenItem({ required this.id, required this.name, required this.quantity, required this.expirationDate, required this.addedDate, }); // JSONデータをFrozenItemオブジェクトに変換するファクトリメソッド factory FrozenItem.fromJson(Map<String, dynamic> json) { return FrozenItem( id: json['id'], name: json['name'], quantity: json['quantity'], expirationDate: DateTime.fromMillisecondsSinceEpoch(json['expirationDate']), addedDate: DateTime.fromMillisecondsSinceEpoch(json['addedDate']), ); } // FrozenItemオブジェクトをJSONデータに変換するメソッド Map<String, dynamic> toJson() { return { 'id': id, 'name': name, 'quantity': quantity, 'expirationDate': expirationDate.millisecondsSinceEpoch, 'addedDate': addedDate.millisecondsSinceEpoch, }; } }
なんと、一発でそれっぽいデータクラスを作ってくれました。これをそのままコピペしていきます。次に、2つ目のFirestoreとの連携を聞いてみます。
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:freezstock/frozen_item.dart'; class FirestoreService { final FirebaseFirestore _firestore = FirebaseFirestore.instance; final String _collectionName = 'frozen_items'; // 冷凍庫の中身を取得するメソッド Stream<List<FrozenItem>> getFrozenItems() { return _firestore .collection(_collectionName) .snapshots() .map((snapshot) => snapshot.docs .map((doc) => FrozenItem.fromJson({...doc.data(), 'id': doc.id})) .toList()); } // 冷凍庫に新しい食品を追加するメソッド Future<void> addFrozenItem(FrozenItem item) { return _firestore.collection(_collectionName).add(item.toJson()); } // 選択した食品の情報を更新するメソッド Future<void> updateFrozenItem(FrozenItem item) { return _firestore .collection(_collectionName) .doc(item.id) .update(item.toJson()); } // 選択した食品を削除するメソッド Future<void> deleteFrozenItem(String itemId) { return _firestore.collection(_collectionName).doc(itemId).delete(); } }
すごい、それっぽいデータを管理するクラスが勝手に作成されました。続いて冷凍庫の中身一覧画面を要求してみましょう。
import 'package:flutter/material.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:freezstock/firestore_service.dart'; import 'package:freezstock/frozen_item.dart'; class MainScreen extends StatefulWidget { @override _MainScreenState createState() => _MainScreenState(); } class _MainScreenState extends State<MainScreen> { final FirestoreService _firestoreService = FirestoreService(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('冷凍庫の中身'), ), body: StreamBuilder<List<FrozenItem>>( stream: _firestoreService.getFrozenItems(), builder: (context, snapshot) { if (snapshot.hasError) { return Center(child: Text('エラーが発生しました')); } if (snapshot.connectionState == ConnectionState.waiting) { return Center(child: CircularProgressIndicator()); } final items = snapshot.data; if (items == null || items.isEmpty) { return Center(child: Text('冷凍庫にアイテムがありません')); } return ListView.builder( itemCount: items.length, itemBuilder: (context, index) { final item = items[index]; return ListTile( title: Text(item.name), subtitle: Text('${item.quantity}個 - 期限: ${item.expirationDate}'), onTap: () { // アイテムの詳細画面に遷移する処理をここに追加する }, ); }, ); }, ), floatingActionButton: FloatingActionButton( onPressed: () { // アイテム追加画面に遷移する処理をここに追加する }, child: Icon(Icons.add), ), ); } }
どんどん勝手に画面が出来上がっていきます。この調子で、冷凍庫の中身追加画面と冷凍庫の中身詳細画面、冷凍庫の中身編集画面も作成してくれました。
画面はどんどん作ってくれたのですが、いざ実行しようとするとエラーになります。エラーも直してもらいましょう。
import 'package:flutter/material.dart'; import 'package:freezstock/frozen_item.dart'; class EditItemScreen extends StatefulWidget { final FrozenItem item; final String? id; EditItemScreen({required this.item, this.id}); @override _EditItemScreenState createState() => _EditItemScreenState(); } class _EditItemScreenState extends State<EditItemScreen> { final _formKey = GlobalKey<FormState>(); late FrozenItem item; @override void initState() { super.initState(); item = widget.item; } // 以下のコードは変更なし ... }
エラーメッセージだけで、的確に、コンパイルエラーが修正されていきます。
忘れていそうなところは、適宜実装を教えてあげると、やらなければいけないことを教えてくれます。
IconButton( icon: Icon(Icons.save), onPressed: () async { if (_formKey.currentState!.validate()) { await _firestoreService.updateFrozenItem( FrozenItem( id: widget.item.id, name: _nameController.text, quantity: int.parse(_quantityController.text), expirationDate: DateTime.parse(_expirationDateController.text), addedDate: widget.item.addedDate, ), ); Navigator.pop(context); } }, ),
最後に、Firestoreのルールがなくエラーが出てしまうのを解消します。(FirestoreのエラーだということをChatGPTに気づいてもらおうと頑張ったのですがうまくできなかったので、代わりに僕がデバッグしました)
さて、これで動く状態になりましたね!ざっと動かすとこんな感じです!!
微妙な部分はめっちゃあるけどChatGPTからコピペするだけである程度動くようになってきた pic.twitter.com/3esjwKKQtW
— こーき@だん (@kouki_dan) 2023年3月29日
ただ、このアプリにはいくつか問題がありますね。せっかくログインしているのに、全員でデータを共通にしていたりします。他にも、日付はDatePickerを使うことだったり、色々改善点は見つかりそうです。
でもそれには一旦目を瞑って、次に作らなければいけない機能をChatGPTに聞いてみると・・・?
次はユーザーテストらしい。本気か・・・? https://t.co/niEJAubsd8 pic.twitter.com/SoGu9mNbzf
— こーき@だん (@kouki_dan) 2023年4月1日
どうやら、ユーザーテストらしい・・・。誰かユーザーテストに付き合ってくれる人はいらっしゃいませんか!?
Part1によると、次は期限切れリマインダーだったんですけどね〜〜
今回ChatGPTに書いてもらったコードはこれです!