FireStoreを使ってデータを追加、表示、編集、削除できるようにする
開発環境
- macOS Monterey
- flutter2.10.3
雑だけど先ずは作ってみて勉強してみる😅
FireStoreのデータモデリング
- products
- name : ""
- price : ""
スクリーンショット
pubspec.yaml
name: products_crud_sample
description: A new Flutter project.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1
environment:
sdk: ">=2.16.1 <3.0.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
firebase_core: ^1.14.0
cloud_firestore: ^3.1.11
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^1.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
main.dart
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
// Remove the debug banner
debugShowCheckedModeBanner: false,
title: 'FireStore CRUD Sample',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// text fields' controllers
final TextEditingController _nameController = TextEditingController();
final TextEditingController _priceController = TextEditingController();
// FireStoreのドキュメントを取得
final CollectionReference _productss =
FirebaseFirestore.instance.collection('products');
// 追加・編集で使う関数
Future<void> _createOrUpdate([DocumentSnapshot? documentSnapshot]) async {
String action = 'create';
if (documentSnapshot != null) {
action = 'update';
_nameController.text = documentSnapshot['name'];
_priceController.text = documentSnapshot['price'].toString();
}
await showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext ctx) {
return Padding(
padding: EdgeInsets.only(
top: 20,
left: 20,
right: 20,
// prevent the soft keyboard from covering text fields
bottom: MediaQuery.of(ctx).viewInsets.bottom + 20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: _nameController,
decoration: const InputDecoration(labelText: '商品名'),
),
TextField(
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: _priceController,
decoration: const InputDecoration(
labelText: '価格',
),
),
const SizedBox(
height: 20,
),
ElevatedButton(
// 日本語表示のボタンを三項演算子で選ぶ
child: Text(action == 'create' ? '追加' : '更新'),
onPressed: () async {
final String? name = _nameController.text;
final double? price =
double.tryParse(_priceController.text);
// 追加の処理
if (name != null && price != null) {
if (action == 'create') {
// Persist a new product to Firestore
await _productss.add({"name": name, "price": price});
// 追加のスナックバーを表示
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.blueAccent,
content: Text(
'${name}を追加しました!',
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 15, color: Colors.white),
)));
}
// 編集の処理
if (action == 'update') {
// Update the product
await _productss
.doc(documentSnapshot!.id)
.update({"name": name, "price": price});
// 編集のスナックバーを表示
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
backgroundColor: Colors.yellowAccent,
content: Text(
'商品情報を更新しました!',
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 15, color: Colors.black87),
)));
}
// Clear the text fields
_nameController.text = '';
_priceController.text = '';
// Hide the bottom sheet
Navigator.of(context).pop();
}
},
)
],
),
);
});
}
// 削除処理の関数
Future<void> _deleteProduct(String productId) async {
await _productss.doc(productId).delete();
// 削除のスナックバーを表示
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
backgroundColor: Colors.redAccent,
content: Text(
'商品を削除しました!',
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 15, color: Colors.black87),
)));
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: const Text('FireStore CRUD Sample'),
backgroundColor: Colors.orangeAccent,
),
// StreamBuilderでFirestoreの値をListView.builderに渡す
body: StreamBuilder(
stream: _productss.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (streamSnapshot.hasData) {
// FireStoreの値をリスト形式で表示
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: (context, index) {
final DocumentSnapshot documentSnapshot =
streamSnapshot.data!.docs[index];
// Cardウイジェットでドキュメントを表示
return Card(
margin: const EdgeInsets.all(10),
child: ListTile(
title: Text(documentSnapshot['name']), // 商品名
subtitle: Text(documentSnapshot['price'].toString()), // 価格
trailing: SizedBox(
width: 100,
child: Row(
children: [
// 編集ボタン
IconButton(
color: Colors.deepOrange[400],
icon: const Icon(Icons.edit),
onPressed: () =>
_createOrUpdate(documentSnapshot)),
// 削除ボタン
IconButton(
color: Colors.red[700],
icon: const Icon(Icons.delete),
onPressed: () =>
_deleteProduct(documentSnapshot.id)),
],
),
),
),
);
},
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
// 追加ボタン
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.black87,
onPressed: () => _createOrUpdate(),
child: const Icon(Icons.add),
),
),
);
}
}
やってみたこと
スナックバーをif文の中に加えて、追加、編集、削除、全ての場所で表示できるようにしてみました。
Flutter大学のYouTube動画の学習が今になって役に立ってきた...
次はriverpod使ったアプリを作る予定。
うまくできるかな...
つづく