Flutter云数据库插件cloud_firestore的使用
Flutter云数据库插件cloud_firestore的使用
Cloud Firestore Plugin for Flutter
Flutter 的 cloud_firestore
插件允许开发者在 Flutter 应用中使用 Cloud Firestore API。要了解更多关于 Firebase Cloud Firestore 的信息,请访问 Firebase 官方网站。
Getting Started
要开始使用 Flutter 中的 Cloud Firestore,请参考 官方文档。
Usage
使用此插件的具体方法可以参考 Firestore 使用文档。
Issues and feedback
如果您遇到了与 FlutterFire 相关的问题、错误或有功能请求,请在我们的 issue tracker 上提交问题。如果问题是与 FlutterFire 无关的插件问题,请在 Flutter issue tracker 上提交。
要为这个插件贡献代码,请阅读我们的 贡献指南,并提交一个 pull request。
示例代码
以下是一个完整的示例应用程序,展示了如何使用 cloud_firestore
插件来管理电影数据。该应用包括添加、查询、更新和删除数据的功能,并展示了如何处理实时更新和事务。
main.dart
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
FirebaseFirestore.instance.settings = const Settings(persistenceEnabled: true);
runApp(FirestoreExampleApp());
}
/// A reference to the list of movies.
final moviesRef = FirebaseFirestore.instance
.collection('firestore-example-app')
.withConverter<Movie>(
fromFirestore: (snapshots, _) => Movie.fromJson(snapshots.data()!),
toFirestore: (movie, _) => movie.toJson(),
);
enum MovieQuery {
year,
likesAsc,
likesDesc,
rated,
sciFi,
fantasy,
}
extension on Query<Movie> {
Query<Movie> queryBy(MovieQuery query) {
return switch (query) {
MovieQuery.fantasy => where('genre', arrayContainsAny: ['fantasy']),
MovieQuery.sciFi => where('genre', arrayContainsAny: ['sci-fi']),
MovieQuery.likesAsc || MovieQuery.likesDesc =>
orderBy('likes', descending: query == MovieQuery.likesDesc),
MovieQuery.year => orderBy('year', descending: true),
MovieQuery.rated => orderBy('rated', descending: true)
};
}
}
class FirestoreExampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Firestore Example App',
theme: ThemeData.dark(),
home: Scaffold(
body: Center(child: FilmList()),
),
);
}
}
class FilmList extends StatefulWidget {
const FilmList({Key? key}) : super(key: key);
@override
_FilmListState createState() => _FilmListState();
}
class _FilmListState extends State<FilmList> {
MovieQuery query = MovieQuery.year;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text('Firestore Example: Movies'),
StreamBuilder(
stream: FirebaseFirestore.instance.snapshotsInSync(),
builder: (context, _) {
return Text(
'Latest Snapshot: ${DateTime.now()}',
style: Theme.of(context).textTheme.bodySmall,
);
},
),
],
),
actions: <Widget>[
PopupMenuButton<MovieQuery>(
onSelected: (value) => setState(() => query = value),
icon: const Icon(Icons.sort),
itemBuilder: (BuildContext context) {
return [
const PopupMenuItem(
value: MovieQuery.year,
child: Text('Sort by Year'),
),
const PopupMenuItem(
value: MovieQuery.rated,
child: Text('Sort by Rated'),
),
const PopupMenuItem(
value: MovieQuery.likesAsc,
child: Text('Sort by Likes ascending'),
),
const PopupMenuItem(
value: MovieQuery.likesDesc,
child: Text('Sort by Likes descending'),
),
const PopupMenuItem(
value: MovieQuery.fantasy,
child: Text('Filter genre fantasy'),
),
const PopupMenuItem(
value: MovieQuery.sciFi,
child: Text('Filter genre sci-fi'),
),
];
},
),
],
),
body: StreamBuilder<QuerySnapshot<Movie>>(
stream: moviesRef.queryBy(query).snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
}
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
final data = snapshot.requireData;
return ListView.builder(
itemCount: data.size,
itemBuilder: (context, index) {
return _MovieItem(
data.docs[index].data(),
data.docs[index].reference,
);
},
);
},
),
);
}
Future<void> _resetLikes() async {
final movies = await moviesRef.get(
const GetOptions(serverTimestampBehavior: ServerTimestampBehavior.previous),
);
WriteBatch batch = FirebaseFirestore.instance.batch();
for (final movie in movies.docs) {
batch.update(movie.reference, {'likes': 0});
}
await batch.commit();
}
}
class _MovieItem extends StatelessWidget {
_MovieItem(this.movie, this.reference);
final Movie movie;
final DocumentReference<Movie> reference;
Widget get poster {
return SizedBox(
width: 100,
child: Image.network(movie.poster),
);
}
Widget get details {
return Padding(
padding: const EdgeInsets.only(left: 8, right: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title,
metadata,
genres,
Likes(reference: reference, currentLikes: movie.likes),
],
),
);
}
Widget get title {
return Text(
'${movie.title} (${movie.year})',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
);
}
Widget get metadata {
return Padding(
padding: const EdgeInsets.only(top: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(right: 8),
child: Text('Rated: ${movie.rated}'),
),
Text('Runtime: ${movie.runtime}'),
],
),
);
}
List<Widget> get genreItems {
return [
for (final genre in movie.genre)
Padding(
padding: const EdgeInsets.only(right: 2),
child: Chip(
backgroundColor: Colors.lightBlue,
label: Text(
genre,
style: const TextStyle(color: Colors.white),
),
),
),
];
}
Widget get genres {
return Padding(
padding: const EdgeInsets.only(top: 8),
child: Wrap(
children: genreItems,
),
);
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 4, top: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
poster,
Flexible(child: details),
],
),
);
}
}
class Likes extends StatefulWidget {
Likes({
Key? key,
required this.reference,
required this.currentLikes,
}) : super(key: key);
final DocumentReference<Movie> reference;
final int currentLikes;
@override
_LikesState createState() => _LikesState();
}
class _LikesState extends State<Likes> {
late int _likes = widget.currentLikes;
Future<void> _onLike() async {
final currentLikes = _likes;
setState(() {
_likes = currentLikes + 1;
});
try {
int newLikes = await FirebaseFirestore.instance.runTransaction<int>((transaction) async {
DocumentSnapshot<Movie> movie = await transaction.get<Movie>(widget.reference);
if (!movie.exists) {
throw Exception('Document does not exist!');
}
int updatedLikes = movie.data()!.likes + 1;
transaction.update(widget.reference, {'likes': updatedLikes});
return updatedLikes;
});
setState(() => _likes = newLikes);
} catch (e, s) {
print(s);
print('Failed to update likes for document! $e');
setState(() => _likes = currentLikes);
}
}
@override
void didUpdateWidget(Likes oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.currentLikes != oldWidget.currentLikes) {
_likes = widget.currentLikes;
}
}
@override
Widget build(BuildContext context) {
return Row(
children: [
IconButton(
iconSize: 20,
onPressed: _onLike,
icon: const Icon(Icons.favorite),
),
Text('$_likes likes'),
],
);
}
}
@immutable
class Movie {
Movie({
required this.genre,
required this.likes,
required this.poster,
required this.rated,
required this.runtime,
required this.title,
required this.year,
});
Movie.fromJson(Map<String, Object?> json)
: this(
genre: (json['genre']! as List).cast<String>(),
likes: json['likes']! as int,
poster: json['poster']! as String,
rated: json['rated']! as String,
runtime: json['runtime']! as String,
title: json['title']! as String,
year: json['year']! as int,
);
final String poster;
final int likes;
final String title;
final int year;
final String runtime;
final String rated;
final List<String> genre;
Map<String, Object?> toJson() {
return {
'genre': genre,
'likes': likes,
'poster': poster,
'rated': rated,
'runtime': runtime,
'title': title,
'year': year,
};
}
}
firebase_options.dart
确保你已经在 Firebase 控制台中创建了项目,并下载了 google-services.json
(Android)和 GoogleService-Info.plist
(iOS)。然后将它们添加到项目的相应位置,并创建 firebase_options.dart
文件:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb;
import 'package:flutter/services.dart' show rootBundle;
import 'dart:convert';
class DefaultFirebaseOptions {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
return web;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
throw UnsupportedError('DefaultFirebaseOptions have not been configured for macos - you can reconfigure this by running the FlutterFire CLI again.');
case TargetPlatform.windows:
throw UnsupportedError('DefaultFirebaseOptions have not been configured for windows - you can reconfigure this by running the FlutterFire CLI again.');
case TargetPlatform.linux:
throw UnsupportedError('DefaultFirebaseOptions have not been configured for linux - you can reconfigure this by running the FlutterFire CLI again.');
default:
throw UnsupportedError('DefaultFirebaseOptions are not supported for this platform.');
}
}
static const FirebaseOptions web = FirebaseOptions(
apiKey: 'YOUR_API_KEY',
appId: 'YOUR_APP_ID',
messagingSenderId: 'YOUR_MESSAGING_SENDER_ID',
projectId: 'YOUR_PROJECT_ID',
authDomain: 'YOUR_AUTH_DOMAIN',
storageBucket: 'YOUR_STORAGE_BUCKET',
measurementId: 'YOUR_MEASUREMENT_ID',
);
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'YOUR_API_KEY',
appId: 'YOUR_APP_ID',
messagingSenderId: 'YOUR_MESSAGING_SENDER_ID',
projectId: 'YOUR_PROJECT_ID',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'YOUR_API_KEY',
appId: 'YOUR_APP_ID',
messagingSenderId: 'YOUR_MESSAGING_SENDER_ID',
projectId: 'YOUR_PROJECT_ID',
bundleId: 'YOUR_BUNDLE_ID',
);
}
请替换上述代码中的占位符为你从 Firebase 控制台获取的实际值。
pubspec.yaml
确保你的 pubspec.yaml
文件中包含以下依赖项:
dependencies:
flutter:
sdk: flutter
cloud_firestore: latest_version
firebase_core: latest_version
通过以上步骤,你应该能够成功地在 Flutter 应用中集成和使用 cloud_firestore
插件。
更多关于Flutter云数据库插件cloud_firestore的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter云数据库插件cloud_firestore的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter项目中使用cloud_firestore
插件的示例代码。这个示例展示了如何连接到Firebase Firestore数据库,读取和写入数据。
首先,你需要确保已经在Firebase控制台中为你的应用配置了Firestore数据库,并获取了必要的配置信息(如google-services.json
文件)。
1. 添加依赖
在你的pubspec.yaml
文件中添加cloud_firestore
依赖:
dependencies:
flutter:
sdk: flutter
cloud_firestore: ^3.4.3 # 确保使用最新版本
2. 初始化Firebase
在android/app/google-services.json
和ios/Runner/GoogleService-Info.plist
文件中添加你的Firebase配置。
3. 编写Flutter代码
下面是一个简单的Flutter应用示例,展示了如何连接到Firestore数据库,读取和写入数据。
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Firestore Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
CollectionReference users = FirebaseFirestore.instance.collection('users');
String? userEmail;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Firestore Example'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextField(
decoration: InputDecoration(labelText: 'Email'),
onChanged: (value) {
setState(() {
userEmail = value;
});
},
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () async {
await addUser(userEmail!);
},
child: Text('Add User'),
),
SizedBox(height: 16),
StreamBuilder<QuerySnapshot>(
stream: users.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong!');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text('Loading...');
}
return ListView(
children: snapshot.data!.docs.map((document) {
Map<String, dynamic> data = document.data() as Map<String, dynamic>;
return ListTile(
title: Text(data['email'] as String),
);
}).toList(),
);
},
),
],
),
),
);
}
Future<void> addUser(String email) async {
try {
await users.add({
'email': email,
});
} catch (e) {
print('Error writing document: $e');
}
}
}
解释
-
依赖添加:在
pubspec.yaml
文件中添加cloud_firestore
依赖。 -
初始化Firebase:确保你已经将
google-services.json
和GoogleService-Info.plist
文件分别放置在Android和iOS项目中。 -
UI构建:
- 使用
TextField
来输入用户的Email。 - 使用
ElevatedButton
来触发添加用户操作。 - 使用
StreamBuilder
来监听Firestore集合的变化,并实时更新UI。
- 使用
-
数据操作:
CollectionReference
用于引用Firestore中的集合。addUser
函数用于向Firestore集合中添加新文档。
这个示例展示了基本的读写操作,你可以根据需要扩展这个示例,添加更多的功能和复杂的查询。