NEW: initial commit
|
@ -0,0 +1,381 @@
|
||||||
|
## 0.12.5
|
||||||
|
|
||||||
|
* Makes `startAtDocument`, `startAfterDocument`, `endAtDocument` and `endBeforeDocument` work
|
||||||
|
with `Query.collectionGroup` queries.
|
||||||
|
* Fixes `startAtDocument`, `startAfterDocument`, `endAtDocument` and `endBeforeDocument` to
|
||||||
|
also work with a descending order as the last explicit sort order.
|
||||||
|
* Fixed an integration test by increasing the value of `cacheSizeBytes` to a valid value.
|
||||||
|
|
||||||
|
## 0.12.4
|
||||||
|
|
||||||
|
* Added support for `Query.collectionGroup`.
|
||||||
|
|
||||||
|
## 0.12.3
|
||||||
|
|
||||||
|
* Added support for `cacheSizeBytes` to `Firestore.settings`.
|
||||||
|
|
||||||
|
## 0.12.2
|
||||||
|
|
||||||
|
* Ensure that all channel calls to the Dart side from the Java side are done
|
||||||
|
on the UI thread. This change allows Transactions to work with upcoming
|
||||||
|
Engine restrictions, which require channel calls be made on the UI thread.
|
||||||
|
**Note** this is an Android only change, the iOS implementation was not impacted.
|
||||||
|
* Updated the Firebase reporting string to `flutter-fire-fst` to be consistent
|
||||||
|
with other reporting libraries.
|
||||||
|
|
||||||
|
## 0.12.1
|
||||||
|
|
||||||
|
* Added support for `Source` to `Query.getDocuments()` and `DocumentReference.get()`.
|
||||||
|
|
||||||
|
## 0.12.0+2
|
||||||
|
|
||||||
|
* Bump the minimum Flutter version to 1.5.
|
||||||
|
* Replace invokeMethod with invokeMapMethod wherever necessary.
|
||||||
|
|
||||||
|
## 0.12.0+1
|
||||||
|
|
||||||
|
* Send user agent to Firebase.
|
||||||
|
|
||||||
|
## 0.12.0
|
||||||
|
|
||||||
|
* **Breaking change**. Fixed `CollectionReference.parent` to correctly return a `DocumentReference`.
|
||||||
|
If you were using the method previously to obtain the parent
|
||||||
|
document's id via `collectionReference.parent().id`,
|
||||||
|
you will have to use `collectionReference.parent().documentID` now.
|
||||||
|
* Added `DocumentReference.parent`.
|
||||||
|
|
||||||
|
## 0.11.0+2
|
||||||
|
|
||||||
|
* Remove iOS dependency on Firebase/Database and Firebase/Auth CocoaPods.
|
||||||
|
|
||||||
|
## 0.11.0+1
|
||||||
|
|
||||||
|
* Update iOS CocoaPod dependencies to '~> 6.0' to ensure support for `FieldValue.increment`.
|
||||||
|
|
||||||
|
## 0.11.0
|
||||||
|
|
||||||
|
* Update Android dependencies to latest.
|
||||||
|
|
||||||
|
## 0.10.1
|
||||||
|
|
||||||
|
* Support for `startAtDocument`, `startAfterDocument`, `endAtDocument`, `endBeforeDocument`.
|
||||||
|
* Added additional unit and integration tests.
|
||||||
|
|
||||||
|
## 0.10.0
|
||||||
|
|
||||||
|
* Support for `FieldValue.increment`.
|
||||||
|
* Remove `FieldValue.type` and `FieldValue.value` from public API.
|
||||||
|
* Additional integration testing.
|
||||||
|
|
||||||
|
## 0.9.13+1
|
||||||
|
|
||||||
|
* Added an integration test for transactions.
|
||||||
|
|
||||||
|
## 0.9.13
|
||||||
|
|
||||||
|
* Remove Gradle BoM to avoid Gradle version issues.
|
||||||
|
|
||||||
|
## 0.9.12
|
||||||
|
|
||||||
|
* Move Android dependency to Gradle BoM to help maintain compatibility
|
||||||
|
with other FlutterFire plugins.
|
||||||
|
|
||||||
|
## 0.9.11
|
||||||
|
|
||||||
|
* Bump Android dependencies to latest.
|
||||||
|
|
||||||
|
# 0.9.10
|
||||||
|
|
||||||
|
* Support for cloud_firestore running in the background on Android.
|
||||||
|
* Fixed a bug in cleanup for DocumentReference.snapshots().
|
||||||
|
* Additional integration testing.
|
||||||
|
|
||||||
|
## 0.9.9
|
||||||
|
|
||||||
|
* Remove `invokeMapMethod` calls to prevent crash.
|
||||||
|
|
||||||
|
## 0.9.8
|
||||||
|
|
||||||
|
* Add metadata field to DocumentSnapshot.
|
||||||
|
|
||||||
|
## 0.9.7+2
|
||||||
|
|
||||||
|
* Bump the minimum Flutter version to 1.2.0.
|
||||||
|
* Add template type parameter to `invokeMethod` calls.
|
||||||
|
|
||||||
|
## 0.9.7+1
|
||||||
|
|
||||||
|
* Update README with example of getting a document.
|
||||||
|
|
||||||
|
## 0.9.7
|
||||||
|
|
||||||
|
* Fixes a NoSuchMethodError when using getDocuments on iOS (introduced in 0.9.6).
|
||||||
|
* Adds a driver test for getDocuments.
|
||||||
|
|
||||||
|
## 0.9.6
|
||||||
|
|
||||||
|
* On iOS, update null checking to match the recommended pattern usage in the Firebase documentation.
|
||||||
|
* Fixes a case where snapshot errors might result in plugin crash.
|
||||||
|
|
||||||
|
## 0.9.5+2
|
||||||
|
|
||||||
|
* Fixing PlatformException(Error 0, null, null) which happened when a successful operation was performed.
|
||||||
|
|
||||||
|
## 0.9.5+1
|
||||||
|
|
||||||
|
* Log messages about automatic configuration of the default app are now less confusing.
|
||||||
|
|
||||||
|
## 0.9.5
|
||||||
|
|
||||||
|
* Fix an issue on some iOS devices that results in reading incorrect dates.
|
||||||
|
|
||||||
|
## 0.9.4
|
||||||
|
|
||||||
|
* No longer sends empty snapshot events on iOS when encountering errors.
|
||||||
|
|
||||||
|
## 0.9.3
|
||||||
|
|
||||||
|
* Fix transactions on iOS when getting snapshot that doesn't exist.
|
||||||
|
|
||||||
|
## 0.9.2
|
||||||
|
|
||||||
|
* Fix IllegalStateException errors when using transactions on Android.
|
||||||
|
|
||||||
|
## 0.9.1
|
||||||
|
|
||||||
|
* Fixed Firebase multiple app support in transactions and document snapshots.
|
||||||
|
|
||||||
|
## 0.9.0+2
|
||||||
|
|
||||||
|
* Remove categories.
|
||||||
|
|
||||||
|
## 0.9.0+1
|
||||||
|
|
||||||
|
* Log a more detailed warning at build time about the previous AndroidX
|
||||||
|
migration.
|
||||||
|
|
||||||
|
## 0.9.0
|
||||||
|
|
||||||
|
* **Breaking change**. Migrate from the deprecated original Android Support
|
||||||
|
Library to AndroidX. This shouldn't result in any functional changes, but it
|
||||||
|
requires any Android apps using this plugin to [also
|
||||||
|
migrate](https://developer.android.com/jetpack/androidx/migrate) if they're
|
||||||
|
using the original support library.
|
||||||
|
|
||||||
|
## 0.8.2+3
|
||||||
|
|
||||||
|
* Resolved "explicit self reference" and "loses accuracy" compiler warnings.
|
||||||
|
|
||||||
|
## 0.8.2+2
|
||||||
|
|
||||||
|
* Clean up Android build logs. @SuppressWarnings("unchecked")
|
||||||
|
|
||||||
|
## 0.8.2+1
|
||||||
|
|
||||||
|
* Avoid crash in document snapshot callback.
|
||||||
|
|
||||||
|
## 0.8.2
|
||||||
|
|
||||||
|
* Added `Firestore.settings`
|
||||||
|
* Added `Timestamp` class
|
||||||
|
|
||||||
|
## 0.8.1+1
|
||||||
|
|
||||||
|
* Bump Android dependencies to latest.
|
||||||
|
|
||||||
|
## 0.8.1
|
||||||
|
|
||||||
|
* Fixed bug where updating arrays in with `FieldValue` always throws an Exception on Android.
|
||||||
|
|
||||||
|
## 0.8.0
|
||||||
|
|
||||||
|
Note: this version depends on features available in iOS SDK versions 5.5.0 or later.
|
||||||
|
To update iOS SDK in existing projects run `pod update Firebase/Firestore`.
|
||||||
|
|
||||||
|
* Added `Firestore.enablePersistence`
|
||||||
|
* Added `FieldValue` with all currently supported values: `arrayUnion`, `arrayRemove`, `delete` and
|
||||||
|
`serverTimestamp`.
|
||||||
|
* Added `arrayContains` argument in `Query.where` method.
|
||||||
|
|
||||||
|
## 0.7.4
|
||||||
|
|
||||||
|
* Bump Android and Firebase dependency versions.
|
||||||
|
|
||||||
|
## 0.7.3
|
||||||
|
|
||||||
|
* Updated Gradle tooling to match Android Studio 3.1.2.
|
||||||
|
|
||||||
|
## 0.7.2
|
||||||
|
|
||||||
|
* Fixes crash on Android if a FirebaseFirestoreException happened.
|
||||||
|
|
||||||
|
## 0.7.1
|
||||||
|
|
||||||
|
* Updated iOS implementation to reflect Firebase API changes.
|
||||||
|
* Fixed bug in Transaction.get that would fail on no data.
|
||||||
|
* Fixed error in README.md code sample.
|
||||||
|
|
||||||
|
## 0.7.0+2
|
||||||
|
|
||||||
|
* Update transactions example in README to add `await`.
|
||||||
|
|
||||||
|
## 0.7.0+1
|
||||||
|
|
||||||
|
* Add transactions example to README.
|
||||||
|
|
||||||
|
## 0.7.0
|
||||||
|
|
||||||
|
* **Breaking change**. `snapshots` is now a method instead of a getter.
|
||||||
|
* **Breaking change**. `setData` uses named arguments instead of `SetOptions`.
|
||||||
|
|
||||||
|
## 0.6.3
|
||||||
|
|
||||||
|
* Updated Google Play Services dependencies to version 15.0.0.
|
||||||
|
|
||||||
|
## 0.6.2
|
||||||
|
|
||||||
|
* Support for BLOB data type.
|
||||||
|
|
||||||
|
## 0.6.1
|
||||||
|
|
||||||
|
* Simplified podspec for Cocoapods 1.5.0, avoiding link issues in app archives.
|
||||||
|
|
||||||
|
## 0.6.0
|
||||||
|
|
||||||
|
* **Breaking change**. Renamed 'getCollection()' to 'collection().'
|
||||||
|
|
||||||
|
## 0.5.1
|
||||||
|
|
||||||
|
* Expose the Firebase app corresponding to a Firestore
|
||||||
|
* Expose a constructor for a Firestore with a non-default Firebase app
|
||||||
|
|
||||||
|
## 0.5.0
|
||||||
|
|
||||||
|
* **Breaking change**. Move path getter to CollectionReference
|
||||||
|
* Add id getter to CollectionReference
|
||||||
|
|
||||||
|
## 0.4.0
|
||||||
|
|
||||||
|
* **Breaking change**. Hide Firestore codec class from public API.
|
||||||
|
* Adjusted Flutter SDK constraint to match Flutter release with extensible
|
||||||
|
platform message codec, required already by version 0.3.1.
|
||||||
|
* Move each class into separate files
|
||||||
|
|
||||||
|
## 0.3.2
|
||||||
|
|
||||||
|
* Support for batched writes.
|
||||||
|
|
||||||
|
## 0.3.1
|
||||||
|
|
||||||
|
* Add GeoPoint class
|
||||||
|
* Allow for reading and writing DocumentReference, DateTime, and GeoPoint
|
||||||
|
values from and to Documents.
|
||||||
|
|
||||||
|
## 0.3.0
|
||||||
|
|
||||||
|
* **Breaking change**. Set SDK constraints to match the Flutter beta release.
|
||||||
|
|
||||||
|
## 0.2.12
|
||||||
|
|
||||||
|
* Fix handling of `null` document snapshots (document not exists).
|
||||||
|
* Add `DocumentSnapshot.exists`.
|
||||||
|
|
||||||
|
## 0.2.11
|
||||||
|
* Fix Dart 2 type errors.
|
||||||
|
|
||||||
|
## 0.2.10
|
||||||
|
* Fix Dart 2 type errors.
|
||||||
|
|
||||||
|
## 0.2.9
|
||||||
|
* Relax sdk upper bound constraint to '<2.0.0' to allow 'edge' dart sdk use.
|
||||||
|
|
||||||
|
## 0.2.8
|
||||||
|
* Support for Query.getDocuments
|
||||||
|
|
||||||
|
## 0.2.7
|
||||||
|
|
||||||
|
* Add transaction support.
|
||||||
|
|
||||||
|
## 0.2.6
|
||||||
|
|
||||||
|
* Build fixes for iOS
|
||||||
|
* Null checking in newly added Query methods
|
||||||
|
|
||||||
|
## 0.2.5
|
||||||
|
|
||||||
|
* Query can now have more than one orderBy field.
|
||||||
|
* startAt, startAfter, endAt, and endBefore support
|
||||||
|
* limit support
|
||||||
|
|
||||||
|
## 0.2.4
|
||||||
|
|
||||||
|
* Support for DocumentReference.documentID
|
||||||
|
* Support for CollectionReference.add
|
||||||
|
|
||||||
|
## 0.2.3
|
||||||
|
|
||||||
|
* Simplified and upgraded Android project template to Android SDK 27.
|
||||||
|
* Updated package description.
|
||||||
|
|
||||||
|
## 0.2.2
|
||||||
|
|
||||||
|
* Add `get` to DocumentReference.
|
||||||
|
|
||||||
|
## 0.2.1
|
||||||
|
|
||||||
|
* Fix bug on Android where removeListener is sometimes called without a handle
|
||||||
|
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
|
* **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin
|
||||||
|
3.0.1. Older Flutter projects need to upgrade their Gradle setup as well in
|
||||||
|
order to use this version of the plugin. Instructions can be found
|
||||||
|
[here](https://github.com/flutter/flutter/wiki/Updating-Flutter-projects-to-Gradle-4.1-and-Android-Studio-Gradle-plugin-3.0.1).
|
||||||
|
* Relaxed GMS dependency to [11.4.0,12.0[
|
||||||
|
|
||||||
|
## 0.1.2
|
||||||
|
|
||||||
|
* Support for `DocumentReference` update and merge writes
|
||||||
|
* Suppress unchecked warnings and package name warnings on Android
|
||||||
|
|
||||||
|
## 0.1.1
|
||||||
|
|
||||||
|
* Added FLT prefix to iOS types.
|
||||||
|
|
||||||
|
## 0.1.0
|
||||||
|
|
||||||
|
* Added reference to DocumentSnapshot
|
||||||
|
* Breaking: removed path from DocumentSnapshot
|
||||||
|
* Additional test coverage for reading collections and documents
|
||||||
|
* Fixed typo in DocumentChange documentation
|
||||||
|
|
||||||
|
## 0.0.6
|
||||||
|
|
||||||
|
* Support for getCollection
|
||||||
|
|
||||||
|
## 0.0.5
|
||||||
|
|
||||||
|
* Support `isNull` filtering in `Query.where`
|
||||||
|
* Fixed `DocumentChange.oldIndex` and `DocumentChange.newIndex` to be signed
|
||||||
|
integers (iOS)
|
||||||
|
|
||||||
|
## 0.0.4
|
||||||
|
|
||||||
|
* Support for where clauses
|
||||||
|
* Support for deletion
|
||||||
|
|
||||||
|
## 0.0.3
|
||||||
|
|
||||||
|
* Renamed package to cloud_firestore
|
||||||
|
|
||||||
|
## 0.0.2
|
||||||
|
|
||||||
|
* Add path property to DocumentSnapshot
|
||||||
|
|
||||||
|
## 0.0.1+1
|
||||||
|
|
||||||
|
* Update project homepage
|
||||||
|
|
||||||
|
## 0.0.1
|
||||||
|
|
||||||
|
* Initial Release
|
|
@ -0,0 +1,26 @@
|
||||||
|
Copyright 2017, the Chromium project authors. All rights reserved.
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer in the documentation and/or other materials provided
|
||||||
|
with the distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,104 @@
|
||||||
|
# Cloud Firestore Plugin for Flutter
|
||||||
|
|
||||||
|
A Flutter plugin to use the [Cloud Firestore API](https://firebase.google.com/docs/firestore/).
|
||||||
|
|
||||||
|
[](https://pub.dartlang.org/packages/cloud_firestore)
|
||||||
|
|
||||||
|
For Flutter plugins for other Firebase products, see [FlutterFire.md](https://github.com/flutter/plugins/blob/master/FlutterFire.md).
|
||||||
|
|
||||||
|
*Note*: This plugin is still under development, and some APIs might not be available yet. [Feedback](https://github.com/flutter/flutter/issues) and [Pull Requests](https://github.com/flutter/plugins/pulls) are most welcome!
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
To use this plugin:
|
||||||
|
|
||||||
|
1. Using the [Firebase Console](http://console.firebase.google.com/), add an Android app to your project:
|
||||||
|
Follow the assistant, download the generated google-services.json file and place it inside android/app. Next,
|
||||||
|
modify the android/build.gradle file and the android/app/build.gradle file to add the Google services plugin
|
||||||
|
as described by the Firebase assistant. Ensure that your `android/build.gradle` file contains the
|
||||||
|
`maven.google.com` as [described here](https://firebase.google.com/docs/android/setup#add_the_sdk).
|
||||||
|
1. Using the [Firebase Console](http://console.firebase.google.com/), add an iOS app to your project:
|
||||||
|
Follow the assistant, download the generated GoogleService-Info.plist file, open ios/Runner.xcworkspace
|
||||||
|
with Xcode, and within Xcode place the file inside ios/Runner. Don't follow the steps named
|
||||||
|
"Add Firebase SDK" and "Add initialization code" in the Firebase assistant.
|
||||||
|
1. Add `cloud_firestore` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
|
```
|
||||||
|
|
||||||
|
Adding a new `DocumentReference`:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
Firestore.instance.collection('books').document()
|
||||||
|
.setData({ 'title': 'title', 'author': 'author' });
|
||||||
|
```
|
||||||
|
|
||||||
|
Binding a `CollectionReference` to a `ListView`:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
class BookList extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return StreamBuilder<QuerySnapshot>(
|
||||||
|
stream: Firestore.instance.collection('books').snapshots(),
|
||||||
|
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
|
||||||
|
if (snapshot.hasError)
|
||||||
|
return new Text('Error: ${snapshot.error}');
|
||||||
|
switch (snapshot.connectionState) {
|
||||||
|
case ConnectionState.waiting: return new Text('Loading...');
|
||||||
|
default:
|
||||||
|
return new ListView(
|
||||||
|
children: snapshot.data.documents.map((DocumentSnapshot document) {
|
||||||
|
return new ListTile(
|
||||||
|
title: new Text(document['title']),
|
||||||
|
subtitle: new Text(document['author']),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Performing a query:
|
||||||
|
```dart
|
||||||
|
Firestore.instance
|
||||||
|
.collection('talks')
|
||||||
|
.where("topic", isEqualTo: "flutter")
|
||||||
|
.snapshots()
|
||||||
|
.listen((data) =>
|
||||||
|
data.documents.forEach((doc) => print(doc["title"])));
|
||||||
|
```
|
||||||
|
|
||||||
|
Get a specific document:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
Firestore.instance
|
||||||
|
.collection('talks')
|
||||||
|
.document('document-name')
|
||||||
|
.get()
|
||||||
|
.then((DocumentSnapshot ds) {
|
||||||
|
// use ds as a snapshot
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Running a transaction:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
final DocumentReference postRef = Firestore.instance.document('posts/123');
|
||||||
|
Firestore.instance.runTransaction((Transaction tx) async {
|
||||||
|
DocumentSnapshot postSnapshot = await tx.get(postRef);
|
||||||
|
if (postSnapshot.exists) {
|
||||||
|
await tx.update(postRef, <String, dynamic>{'likesCount': postSnapshot.data['likesCount'] + 1});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
See the `example` directory for a complete sample app using Cloud Firestore.
|
|
@ -0,0 +1,54 @@
|
||||||
|
def PLUGIN = "cloud_firestore";
|
||||||
|
def ANDROIDX_WARNING = "flutterPluginsAndroidXWarning";
|
||||||
|
gradle.buildFinished { buildResult ->
|
||||||
|
if (buildResult.failure && !rootProject.ext.has(ANDROIDX_WARNING)) {
|
||||||
|
println ' *********************************************************'
|
||||||
|
println 'WARNING: This version of ' + PLUGIN + ' will break your Android build if it or its dependencies aren\'t compatible with AndroidX.'
|
||||||
|
println ' See https://goo.gl/CP92wY for more information on the problem and how to fix it.'
|
||||||
|
println ' This warning prints for all Android build failures. The real root cause of the error may be unrelated.'
|
||||||
|
println ' *********************************************************'
|
||||||
|
rootProject.ext.set(ANDROIDX_WARNING, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group 'io.flutter.plugins.firebase.firestore'
|
||||||
|
version '1.0-SNAPSHOT'
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
mavenLocal()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:3.3.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
mavenLocal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 28
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 16
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
lintOptions {
|
||||||
|
disable 'InvalidPackage'
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
api 'com.google.firebase:firebase-firestore:19.0.0'
|
||||||
|
implementation 'com.google.firebase:firebase-common:16.1.0'
|
||||||
|
implementation 'androidx.annotation:annotation:1.0.0'
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
rootProject.name = 'cloud_firestore'
|
|
@ -0,0 +1,9 @@
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="io.flutter.plugins.firebase.firestore">
|
||||||
|
<application>
|
||||||
|
<service android:name="com.google.firebase.components.ComponentDiscoveryService">
|
||||||
|
<meta-data android:name="com.google.firebase.components:io.flutter.plugins.firebase.cloudfirestore.FlutterFirebaseAppRegistrar"
|
||||||
|
android:value="com.google.firebase.components.ComponentRegistrar" />
|
||||||
|
</service>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
|
@ -0,0 +1,881 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
package io.flutter.plugins.firebase.cloudfirestore;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import com.google.android.gms.tasks.OnCompleteListener;
|
||||||
|
import com.google.android.gms.tasks.OnFailureListener;
|
||||||
|
import com.google.android.gms.tasks.OnSuccessListener;
|
||||||
|
import com.google.android.gms.tasks.Task;
|
||||||
|
import com.google.android.gms.tasks.TaskCompletionSource;
|
||||||
|
import com.google.android.gms.tasks.Tasks;
|
||||||
|
import com.google.firebase.FirebaseApp;
|
||||||
|
import com.google.firebase.Timestamp;
|
||||||
|
import com.google.firebase.firestore.Blob;
|
||||||
|
import com.google.firebase.firestore.CollectionReference;
|
||||||
|
import com.google.firebase.firestore.DocumentChange;
|
||||||
|
import com.google.firebase.firestore.DocumentReference;
|
||||||
|
import com.google.firebase.firestore.DocumentSnapshot;
|
||||||
|
import com.google.firebase.firestore.EventListener;
|
||||||
|
import com.google.firebase.firestore.FieldPath;
|
||||||
|
import com.google.firebase.firestore.FieldValue;
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestoreException;
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestoreSettings;
|
||||||
|
import com.google.firebase.firestore.GeoPoint;
|
||||||
|
import com.google.firebase.firestore.ListenerRegistration;
|
||||||
|
import com.google.firebase.firestore.Query;
|
||||||
|
import com.google.firebase.firestore.QuerySnapshot;
|
||||||
|
import com.google.firebase.firestore.SetOptions;
|
||||||
|
import com.google.firebase.firestore.Source;
|
||||||
|
import com.google.firebase.firestore.Transaction;
|
||||||
|
import com.google.firebase.firestore.WriteBatch;
|
||||||
|
import io.flutter.plugin.common.MethodCall;
|
||||||
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||||
|
import io.flutter.plugin.common.MethodChannel.Result;
|
||||||
|
import io.flutter.plugin.common.PluginRegistry;
|
||||||
|
import io.flutter.plugin.common.StandardMessageCodec;
|
||||||
|
import io.flutter.plugin.common.StandardMethodCodec;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class CloudFirestorePlugin implements MethodCallHandler {
|
||||||
|
|
||||||
|
private static final String TAG = "CloudFirestorePlugin";
|
||||||
|
private final MethodChannel channel;
|
||||||
|
private final Activity activity;
|
||||||
|
|
||||||
|
// Handles are ints used as indexes into the sparse array of active observers
|
||||||
|
private int nextListenerHandle = 0;
|
||||||
|
private int nextBatchHandle = 0;
|
||||||
|
private final SparseArray<EventObserver> observers = new SparseArray<>();
|
||||||
|
private final SparseArray<DocumentObserver> documentObservers = new SparseArray<>();
|
||||||
|
private final SparseArray<ListenerRegistration> listenerRegistrations = new SparseArray<>();
|
||||||
|
private final SparseArray<WriteBatch> batches = new SparseArray<>();
|
||||||
|
private final SparseArray<Transaction> transactions = new SparseArray<>();
|
||||||
|
private final SparseArray<TaskCompletionSource> completionTasks = new SparseArray<>();
|
||||||
|
|
||||||
|
public static void registerWith(PluginRegistry.Registrar registrar) {
|
||||||
|
final MethodChannel channel =
|
||||||
|
new MethodChannel(
|
||||||
|
registrar.messenger(),
|
||||||
|
"plugins.flutter.io/cloud_firestore",
|
||||||
|
new StandardMethodCodec(FirestoreMessageCodec.INSTANCE));
|
||||||
|
channel.setMethodCallHandler(new CloudFirestorePlugin(channel, registrar.activity()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private CloudFirestorePlugin(MethodChannel channel, Activity activity) {
|
||||||
|
this.channel = channel;
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FirebaseFirestore getFirestore(Map<String, Object> arguments) {
|
||||||
|
String appName = (String) arguments.get("app");
|
||||||
|
return FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Query getReference(Map<String, Object> arguments) {
|
||||||
|
if ((boolean) arguments.get("isCollectionGroup")) return getCollectionGroupReference(arguments);
|
||||||
|
else return getCollectionReference(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Query getCollectionGroupReference(Map<String, Object> arguments) {
|
||||||
|
String path = (String) arguments.get("path");
|
||||||
|
return getFirestore(arguments).collectionGroup(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CollectionReference getCollectionReference(Map<String, Object> arguments) {
|
||||||
|
String path = (String) arguments.get("path");
|
||||||
|
return getFirestore(arguments).collection(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DocumentReference getDocumentReference(Map<String, Object> arguments) {
|
||||||
|
String path = (String) arguments.get("path");
|
||||||
|
return getFirestore(arguments).document(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Source getSource(Map<String, Object> arguments) {
|
||||||
|
String source = (String) arguments.get("source");
|
||||||
|
switch (source) {
|
||||||
|
case "server":
|
||||||
|
return Source.SERVER;
|
||||||
|
case "cache":
|
||||||
|
return Source.CACHE;
|
||||||
|
default:
|
||||||
|
return Source.DEFAULT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Query implicitOrderBy(Query query, List<List<Object>> orderBy) {
|
||||||
|
boolean descending = (boolean) orderBy.get(orderBy.size() - 1).get(1);
|
||||||
|
Query.Direction direction = descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING;
|
||||||
|
return query.orderBy(FieldPath.documentId(), direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object[] getDocumentValues(
|
||||||
|
Map<String, Object> document, List<List<Object>> orderBy, Map<String, Object> arguments) {
|
||||||
|
String documentId = (String) document.get("id");
|
||||||
|
Map<String, Object> documentData = (Map<String, Object>) document.get("data");
|
||||||
|
List<Object> data = new ArrayList<>();
|
||||||
|
if (orderBy != null) {
|
||||||
|
for (List<Object> order : orderBy) {
|
||||||
|
String orderByFieldName = (String) order.get(0);
|
||||||
|
data.add(documentData.get(orderByFieldName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.add((boolean) arguments.get("isCollectionGroup") ? document.get("path") : documentId);
|
||||||
|
return data.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> parseQuerySnapshot(QuerySnapshot querySnapshot) {
|
||||||
|
if (querySnapshot == null) return new HashMap<>();
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
List<String> paths = new ArrayList<>();
|
||||||
|
List<Map<String, Object>> documents = new ArrayList<>();
|
||||||
|
List<Map<String, Object>> metadatas = new ArrayList<>();
|
||||||
|
for (DocumentSnapshot document : querySnapshot.getDocuments()) {
|
||||||
|
paths.add(document.getReference().getPath());
|
||||||
|
documents.add(document.getData());
|
||||||
|
Map<String, Object> metadata = new HashMap<String, Object>();
|
||||||
|
metadata.put("hasPendingWrites", document.getMetadata().hasPendingWrites());
|
||||||
|
metadata.put("isFromCache", document.getMetadata().isFromCache());
|
||||||
|
metadatas.add(metadata);
|
||||||
|
}
|
||||||
|
data.put("paths", paths);
|
||||||
|
data.put("documents", documents);
|
||||||
|
data.put("metadatas", metadatas);
|
||||||
|
|
||||||
|
List<Map<String, Object>> documentChanges = new ArrayList<>();
|
||||||
|
for (DocumentChange documentChange : querySnapshot.getDocumentChanges()) {
|
||||||
|
Map<String, Object> change = new HashMap<>();
|
||||||
|
String type = null;
|
||||||
|
switch (documentChange.getType()) {
|
||||||
|
case ADDED:
|
||||||
|
type = "DocumentChangeType.added";
|
||||||
|
break;
|
||||||
|
case MODIFIED:
|
||||||
|
type = "DocumentChangeType.modified";
|
||||||
|
break;
|
||||||
|
case REMOVED:
|
||||||
|
type = "DocumentChangeType.removed";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
change.put("type", type);
|
||||||
|
change.put("oldIndex", documentChange.getOldIndex());
|
||||||
|
change.put("newIndex", documentChange.getNewIndex());
|
||||||
|
change.put("document", documentChange.getDocument().getData());
|
||||||
|
change.put("path", documentChange.getDocument().getReference().getPath());
|
||||||
|
Map<String, Object> metadata = new HashMap();
|
||||||
|
metadata.put(
|
||||||
|
"hasPendingWrites", documentChange.getDocument().getMetadata().hasPendingWrites());
|
||||||
|
metadata.put("isFromCache", documentChange.getDocument().getMetadata().isFromCache());
|
||||||
|
change.put("metadata", metadata);
|
||||||
|
documentChanges.add(change);
|
||||||
|
}
|
||||||
|
data.put("documentChanges", documentChanges);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Transaction getTransaction(Map<String, Object> arguments) {
|
||||||
|
return transactions.get((Integer) arguments.get("transactionId"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Query getQuery(Map<String, Object> arguments) {
|
||||||
|
Query query = getReference(arguments);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> parameters = (Map<String, Object>) arguments.get("parameters");
|
||||||
|
if (parameters == null) return query;
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<List<Object>> whereConditions = (List<List<Object>>) parameters.get("where");
|
||||||
|
for (List<Object> condition : whereConditions) {
|
||||||
|
String fieldName = (String) condition.get(0);
|
||||||
|
String operator = (String) condition.get(1);
|
||||||
|
Object value = condition.get(2);
|
||||||
|
if ("==".equals(operator)) {
|
||||||
|
query = query.whereEqualTo(fieldName, value);
|
||||||
|
} else if ("<".equals(operator)) {
|
||||||
|
query = query.whereLessThan(fieldName, value);
|
||||||
|
} else if ("<=".equals(operator)) {
|
||||||
|
query = query.whereLessThanOrEqualTo(fieldName, value);
|
||||||
|
} else if (">".equals(operator)) {
|
||||||
|
query = query.whereGreaterThan(fieldName, value);
|
||||||
|
} else if (">=".equals(operator)) {
|
||||||
|
query = query.whereGreaterThanOrEqualTo(fieldName, value);
|
||||||
|
} else if ("array-contains".equals(operator)) {
|
||||||
|
query = query.whereArrayContains(fieldName, value);
|
||||||
|
} else {
|
||||||
|
// Invalid operator.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Number limit = (Number) parameters.get("limit");
|
||||||
|
if (limit != null) query = query.limit(limit.longValue());
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<List<Object>> orderBy = (List<List<Object>>) parameters.get("orderBy");
|
||||||
|
if (orderBy == null) return query;
|
||||||
|
for (List<Object> order : orderBy) {
|
||||||
|
String orderByFieldName = (String) order.get(0);
|
||||||
|
boolean descending = (boolean) order.get(1);
|
||||||
|
Query.Direction direction =
|
||||||
|
descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING;
|
||||||
|
query = query.orderBy(orderByFieldName, direction);
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> startAtDocument = (Map<String, Object>) parameters.get("startAtDocument");
|
||||||
|
if (startAtDocument != null) {
|
||||||
|
query =
|
||||||
|
implicitOrderBy(query, orderBy)
|
||||||
|
.startAt(getDocumentValues(startAtDocument, orderBy, arguments));
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> startAfterDocument =
|
||||||
|
(Map<String, Object>) parameters.get("startAfterDocument");
|
||||||
|
if (startAfterDocument != null) {
|
||||||
|
query =
|
||||||
|
implicitOrderBy(query, orderBy)
|
||||||
|
.startAfter(getDocumentValues(startAfterDocument, orderBy, arguments));
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Object> startAt = (List<Object>) parameters.get("startAt");
|
||||||
|
if (startAt != null) query = query.startAt(startAt.toArray());
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Object> startAfter = (List<Object>) parameters.get("startAfter");
|
||||||
|
if (startAfter != null) query = query.startAfter(startAfter.toArray());
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> endAtDocument = (Map<String, Object>) parameters.get("endAtDocument");
|
||||||
|
if (endAtDocument != null) {
|
||||||
|
query =
|
||||||
|
implicitOrderBy(query, orderBy)
|
||||||
|
.endAt(getDocumentValues(endAtDocument, orderBy, arguments));
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> endBeforeDocument =
|
||||||
|
(Map<String, Object>) parameters.get("endBeforeDocument");
|
||||||
|
if (endBeforeDocument != null) {
|
||||||
|
query =
|
||||||
|
implicitOrderBy(query, orderBy)
|
||||||
|
.endBefore(getDocumentValues(endBeforeDocument, orderBy, arguments));
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Object> endAt = (List<Object>) parameters.get("endAt");
|
||||||
|
if (endAt != null) query = query.endAt(endAt.toArray());
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Object> endBefore = (List<Object>) parameters.get("endBefore");
|
||||||
|
if (endBefore != null) query = query.endBefore(endBefore.toArray());
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DocumentObserver implements EventListener<DocumentSnapshot> {
|
||||||
|
private int handle;
|
||||||
|
|
||||||
|
DocumentObserver(int handle) {
|
||||||
|
this.handle = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException e) {
|
||||||
|
if (e != null) {
|
||||||
|
// TODO: send error
|
||||||
|
System.out.println(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Map<String, Object> arguments = new HashMap<>();
|
||||||
|
Map<String, Object> metadata = new HashMap<>();
|
||||||
|
arguments.put("handle", handle);
|
||||||
|
metadata.put("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites());
|
||||||
|
metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache());
|
||||||
|
arguments.put("metadata", metadata);
|
||||||
|
if (documentSnapshot.exists()) {
|
||||||
|
arguments.put("data", documentSnapshot.getData());
|
||||||
|
arguments.put("path", documentSnapshot.getReference().getPath());
|
||||||
|
} else {
|
||||||
|
arguments.put("data", null);
|
||||||
|
arguments.put("path", documentSnapshot.getReference().getPath());
|
||||||
|
}
|
||||||
|
channel.invokeMethod("DocumentSnapshot", arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class EventObserver implements EventListener<QuerySnapshot> {
|
||||||
|
private int handle;
|
||||||
|
|
||||||
|
EventObserver(int handle) {
|
||||||
|
this.handle = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvent(QuerySnapshot querySnapshot, FirebaseFirestoreException e) {
|
||||||
|
if (e != null) {
|
||||||
|
// TODO: send error
|
||||||
|
System.out.println(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> arguments = parseQuerySnapshot(querySnapshot);
|
||||||
|
arguments.put("handle", handle);
|
||||||
|
|
||||||
|
channel.invokeMethod("QuerySnapshot", arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addDefaultListeners(final String description, Task<Void> task, final Result result) {
|
||||||
|
task.addOnSuccessListener(
|
||||||
|
new OnSuccessListener<Void>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Void ignored) {
|
||||||
|
result.success(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
task.addOnFailureListener(
|
||||||
|
new OnFailureListener() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Exception e) {
|
||||||
|
result.error("Error performing " + description, e.getMessage(), null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMethodCall(MethodCall call, final Result result) {
|
||||||
|
switch (call.method) {
|
||||||
|
case "Firestore#runTransaction":
|
||||||
|
{
|
||||||
|
final TaskCompletionSource<Map<String, Object>> transactionTCS =
|
||||||
|
new TaskCompletionSource<>();
|
||||||
|
final Task<Map<String, Object>> transactionTCSTask = transactionTCS.getTask();
|
||||||
|
|
||||||
|
final Map<String, Object> arguments = call.arguments();
|
||||||
|
getFirestore(arguments)
|
||||||
|
.runTransaction(
|
||||||
|
new Transaction.Function<Map<String, Object>>() {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> apply(@NonNull Transaction transaction) {
|
||||||
|
// Store transaction.
|
||||||
|
int transactionId = (Integer) arguments.get("transactionId");
|
||||||
|
transactions.append(transactionId, transaction);
|
||||||
|
completionTasks.append(transactionId, transactionTCS);
|
||||||
|
|
||||||
|
// Start operations on Dart side.
|
||||||
|
activity.runOnUiThread(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
channel.invokeMethod(
|
||||||
|
"DoTransaction",
|
||||||
|
arguments,
|
||||||
|
new Result() {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void success(Object doTransactionResult) {
|
||||||
|
transactionTCS.trySetResult(
|
||||||
|
(Map<String, Object>) doTransactionResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(
|
||||||
|
String errorCode,
|
||||||
|
String errorMessage,
|
||||||
|
Object errorDetails) {
|
||||||
|
transactionTCS.trySetException(
|
||||||
|
new Exception("Do transaction failed."));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void notImplemented() {
|
||||||
|
transactionTCS.trySetException(
|
||||||
|
new Exception("DoTransaction not implemented"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait till transaction is complete.
|
||||||
|
try {
|
||||||
|
String timeoutKey = "transactionTimeout";
|
||||||
|
long timeout = ((Number) arguments.get(timeoutKey)).longValue();
|
||||||
|
final Map<String, Object> transactionResult =
|
||||||
|
Tasks.await(transactionTCSTask, timeout, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
// Once transaction completes return the result to the Dart side.
|
||||||
|
return transactionResult;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, e.getMessage(), e);
|
||||||
|
result.error("Error performing transaction", e.getMessage(), null);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addOnCompleteListener(
|
||||||
|
new OnCompleteListener<Map<String, Object>>() {
|
||||||
|
@Override
|
||||||
|
public void onComplete(Task<Map<String, Object>> task) {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
result.success(task.getResult());
|
||||||
|
} else {
|
||||||
|
result.error(
|
||||||
|
"Error performing transaction", task.getException().getMessage(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Transaction#get":
|
||||||
|
{
|
||||||
|
final Map<String, Object> arguments = call.arguments();
|
||||||
|
final Transaction transaction = getTransaction(arguments);
|
||||||
|
new AsyncTask<Void, Void, Void>() {
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... voids) {
|
||||||
|
try {
|
||||||
|
DocumentSnapshot documentSnapshot =
|
||||||
|
transaction.get(getDocumentReference(arguments));
|
||||||
|
final Map<String, Object> snapshotMap = new HashMap<>();
|
||||||
|
snapshotMap.put("path", documentSnapshot.getReference().getPath());
|
||||||
|
if (documentSnapshot.exists()) {
|
||||||
|
snapshotMap.put("data", documentSnapshot.getData());
|
||||||
|
} else {
|
||||||
|
snapshotMap.put("data", null);
|
||||||
|
}
|
||||||
|
Map<String, Object> metadata = new HashMap();
|
||||||
|
metadata.put("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites());
|
||||||
|
metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache());
|
||||||
|
snapshotMap.put("metadata", metadata);
|
||||||
|
activity.runOnUiThread(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
result.success(snapshotMap);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (final FirebaseFirestoreException e) {
|
||||||
|
activity.runOnUiThread(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
result.error("Error performing Transaction#get", e.getMessage(), null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Transaction#update":
|
||||||
|
{
|
||||||
|
final Map<String, Object> arguments = call.arguments();
|
||||||
|
final Transaction transaction = getTransaction(arguments);
|
||||||
|
new AsyncTask<Void, Void, Void>() {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... voids) {
|
||||||
|
Map<String, Object> data = (Map<String, Object>) arguments.get("data");
|
||||||
|
try {
|
||||||
|
transaction.update(getDocumentReference(arguments), data);
|
||||||
|
activity.runOnUiThread(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
result.success(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (final IllegalStateException e) {
|
||||||
|
activity.runOnUiThread(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
result.error("Error performing Transaction#update", e.getMessage(), null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Transaction#set":
|
||||||
|
{
|
||||||
|
final Map<String, Object> arguments = call.arguments();
|
||||||
|
final Transaction transaction = getTransaction(arguments);
|
||||||
|
new AsyncTask<Void, Void, Void>() {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... voids) {
|
||||||
|
Map<String, Object> data = (Map<String, Object>) arguments.get("data");
|
||||||
|
transaction.set(getDocumentReference(arguments), data);
|
||||||
|
activity.runOnUiThread(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Log.d(TAG, "sending set success");
|
||||||
|
result.success(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Transaction#delete":
|
||||||
|
{
|
||||||
|
final Map<String, Object> arguments = call.arguments();
|
||||||
|
final Transaction transaction = getTransaction(arguments);
|
||||||
|
new AsyncTask<Void, Void, Void>() {
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... voids) {
|
||||||
|
transaction.delete(getDocumentReference(arguments));
|
||||||
|
activity.runOnUiThread(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
result.success(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "WriteBatch#create":
|
||||||
|
{
|
||||||
|
int handle = nextBatchHandle++;
|
||||||
|
final Map<String, Object> arguments = call.arguments();
|
||||||
|
WriteBatch batch = getFirestore(arguments).batch();
|
||||||
|
batches.put(handle, batch);
|
||||||
|
result.success(handle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "WriteBatch#setData":
|
||||||
|
{
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
int handle = (Integer) arguments.get("handle");
|
||||||
|
DocumentReference reference = getDocumentReference(arguments);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> options = (Map<String, Object>) arguments.get("options");
|
||||||
|
WriteBatch batch = batches.get(handle);
|
||||||
|
if (options != null && (boolean) options.get("merge")) {
|
||||||
|
batch.set(reference, arguments.get("data"), SetOptions.merge());
|
||||||
|
} else {
|
||||||
|
batch.set(reference, arguments.get("data"));
|
||||||
|
}
|
||||||
|
result.success(null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "WriteBatch#updateData":
|
||||||
|
{
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
int handle = (Integer) arguments.get("handle");
|
||||||
|
DocumentReference reference = getDocumentReference(arguments);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> data = (Map<String, Object>) arguments.get("data");
|
||||||
|
WriteBatch batch = batches.get(handle);
|
||||||
|
batch.update(reference, data);
|
||||||
|
result.success(null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "WriteBatch#delete":
|
||||||
|
{
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
int handle = (Integer) arguments.get("handle");
|
||||||
|
DocumentReference reference = getDocumentReference(arguments);
|
||||||
|
WriteBatch batch = batches.get(handle);
|
||||||
|
batch.delete(reference);
|
||||||
|
result.success(null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "WriteBatch#commit":
|
||||||
|
{
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
int handle = (Integer) arguments.get("handle");
|
||||||
|
WriteBatch batch = batches.get(handle);
|
||||||
|
Task<Void> task = batch.commit();
|
||||||
|
batches.delete(handle);
|
||||||
|
addDefaultListeners("commit", task, result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Query#addSnapshotListener":
|
||||||
|
{
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
int handle = nextListenerHandle++;
|
||||||
|
EventObserver observer = new EventObserver(handle);
|
||||||
|
observers.put(handle, observer);
|
||||||
|
listenerRegistrations.put(handle, getQuery(arguments).addSnapshotListener(observer));
|
||||||
|
result.success(handle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Query#addDocumentListener":
|
||||||
|
{
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
int handle = nextListenerHandle++;
|
||||||
|
DocumentObserver observer = new DocumentObserver(handle);
|
||||||
|
documentObservers.put(handle, observer);
|
||||||
|
listenerRegistrations.put(
|
||||||
|
handle, getDocumentReference(arguments).addSnapshotListener(observer));
|
||||||
|
result.success(handle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Query#removeListener":
|
||||||
|
{
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
int handle = (Integer) arguments.get("handle");
|
||||||
|
listenerRegistrations.get(handle).remove();
|
||||||
|
listenerRegistrations.remove(handle);
|
||||||
|
observers.remove(handle);
|
||||||
|
result.success(null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Query#getDocuments":
|
||||||
|
{
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
Query query = getQuery(arguments);
|
||||||
|
Source source = getSource(arguments);
|
||||||
|
Task<QuerySnapshot> task = query.get(source);
|
||||||
|
task.addOnSuccessListener(
|
||||||
|
new OnSuccessListener<QuerySnapshot>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(QuerySnapshot querySnapshot) {
|
||||||
|
result.success(parseQuerySnapshot(querySnapshot));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addOnFailureListener(
|
||||||
|
new OnFailureListener() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Exception e) {
|
||||||
|
result.error("Error performing getDocuments", e.getMessage(), null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "DocumentReference#setData":
|
||||||
|
{
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
DocumentReference documentReference = getDocumentReference(arguments);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> options = (Map<String, Object>) arguments.get("options");
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> data = (Map<String, Object>) arguments.get("data");
|
||||||
|
Task<Void> task;
|
||||||
|
if (options != null && (boolean) options.get("merge")) {
|
||||||
|
task = documentReference.set(data, SetOptions.merge());
|
||||||
|
} else {
|
||||||
|
task = documentReference.set(data);
|
||||||
|
}
|
||||||
|
addDefaultListeners("setData", task, result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "DocumentReference#updateData":
|
||||||
|
{
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
DocumentReference documentReference = getDocumentReference(arguments);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> data = (Map<String, Object>) arguments.get("data");
|
||||||
|
Task<Void> task = documentReference.update(data);
|
||||||
|
addDefaultListeners("updateData", task, result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "DocumentReference#get":
|
||||||
|
{
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
DocumentReference documentReference = getDocumentReference(arguments);
|
||||||
|
Source source = getSource(arguments);
|
||||||
|
Task<DocumentSnapshot> task = documentReference.get(source);
|
||||||
|
task.addOnSuccessListener(
|
||||||
|
new OnSuccessListener<DocumentSnapshot>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(DocumentSnapshot documentSnapshot) {
|
||||||
|
Map<String, Object> snapshotMap = new HashMap<>();
|
||||||
|
Map<String, Object> metadata = new HashMap<>();
|
||||||
|
metadata.put(
|
||||||
|
"hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites());
|
||||||
|
metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache());
|
||||||
|
snapshotMap.put("metadata", metadata);
|
||||||
|
snapshotMap.put("path", documentSnapshot.getReference().getPath());
|
||||||
|
if (documentSnapshot.exists()) {
|
||||||
|
snapshotMap.put("data", documentSnapshot.getData());
|
||||||
|
} else {
|
||||||
|
snapshotMap.put("data", null);
|
||||||
|
}
|
||||||
|
result.success(snapshotMap);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addOnFailureListener(
|
||||||
|
new OnFailureListener() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Exception e) {
|
||||||
|
result.error("Error performing get", e.getMessage(), null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "DocumentReference#delete":
|
||||||
|
{
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
DocumentReference documentReference = getDocumentReference(arguments);
|
||||||
|
Task<Void> task = documentReference.delete();
|
||||||
|
addDefaultListeners("delete", task, result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Firestore#enablePersistence":
|
||||||
|
{
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
boolean enable = (boolean) arguments.get("enable");
|
||||||
|
FirebaseFirestoreSettings.Builder builder = new FirebaseFirestoreSettings.Builder();
|
||||||
|
builder.setPersistenceEnabled(enable);
|
||||||
|
FirebaseFirestoreSettings settings = builder.build();
|
||||||
|
getFirestore(arguments).setFirestoreSettings(settings);
|
||||||
|
result.success(null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Firestore#settings":
|
||||||
|
{
|
||||||
|
final Map<String, Object> arguments = call.arguments();
|
||||||
|
final FirebaseFirestoreSettings.Builder builder = new FirebaseFirestoreSettings.Builder();
|
||||||
|
|
||||||
|
if (arguments.get("persistenceEnabled") != null) {
|
||||||
|
builder.setPersistenceEnabled((boolean) arguments.get("persistenceEnabled"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments.get("host") != null) {
|
||||||
|
builder.setHost((String) arguments.get("host"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments.get("sslEnabled") != null) {
|
||||||
|
builder.setSslEnabled((boolean) arguments.get("sslEnabled"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments.get("timestampsInSnapshotsEnabled") != null) {
|
||||||
|
builder.setTimestampsInSnapshotsEnabled(
|
||||||
|
(boolean) arguments.get("timestampsInSnapshotsEnabled"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments.get("cacheSizeBytes") != null) {
|
||||||
|
builder.setCacheSizeBytes(((Integer) arguments.get("cacheSizeBytes")).longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
FirebaseFirestoreSettings settings = builder.build();
|
||||||
|
getFirestore(arguments).setFirestoreSettings(settings);
|
||||||
|
result.success(null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
result.notImplemented();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class FirestoreMessageCodec extends StandardMessageCodec {
|
||||||
|
public static final FirestoreMessageCodec INSTANCE = new FirestoreMessageCodec();
|
||||||
|
private static final Charset UTF8 = Charset.forName("UTF8");
|
||||||
|
private static final byte DATE_TIME = (byte) 128;
|
||||||
|
private static final byte GEO_POINT = (byte) 129;
|
||||||
|
private static final byte DOCUMENT_REFERENCE = (byte) 130;
|
||||||
|
private static final byte BLOB = (byte) 131;
|
||||||
|
private static final byte ARRAY_UNION = (byte) 132;
|
||||||
|
private static final byte ARRAY_REMOVE = (byte) 133;
|
||||||
|
private static final byte DELETE = (byte) 134;
|
||||||
|
private static final byte SERVER_TIMESTAMP = (byte) 135;
|
||||||
|
private static final byte TIMESTAMP = (byte) 136;
|
||||||
|
private static final byte INCREMENT_DOUBLE = (byte) 137;
|
||||||
|
private static final byte INCREMENT_INTEGER = (byte) 138;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeValue(ByteArrayOutputStream stream, Object value) {
|
||||||
|
if (value instanceof Date) {
|
||||||
|
stream.write(DATE_TIME);
|
||||||
|
writeLong(stream, ((Date) value).getTime());
|
||||||
|
} else if (value instanceof Timestamp) {
|
||||||
|
stream.write(TIMESTAMP);
|
||||||
|
writeLong(stream, ((Timestamp) value).getSeconds());
|
||||||
|
writeInt(stream, ((Timestamp) value).getNanoseconds());
|
||||||
|
} else if (value instanceof GeoPoint) {
|
||||||
|
stream.write(GEO_POINT);
|
||||||
|
writeAlignment(stream, 8);
|
||||||
|
writeDouble(stream, ((GeoPoint) value).getLatitude());
|
||||||
|
writeDouble(stream, ((GeoPoint) value).getLongitude());
|
||||||
|
} else if (value instanceof DocumentReference) {
|
||||||
|
stream.write(DOCUMENT_REFERENCE);
|
||||||
|
writeBytes(
|
||||||
|
stream, ((DocumentReference) value).getFirestore().getApp().getName().getBytes(UTF8));
|
||||||
|
writeBytes(stream, ((DocumentReference) value).getPath().getBytes(UTF8));
|
||||||
|
} else if (value instanceof Blob) {
|
||||||
|
stream.write(BLOB);
|
||||||
|
writeBytes(stream, ((Blob) value).toBytes());
|
||||||
|
} else {
|
||||||
|
super.writeValue(stream, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object readValueOfType(byte type, ByteBuffer buffer) {
|
||||||
|
switch (type) {
|
||||||
|
case DATE_TIME:
|
||||||
|
return new Date(buffer.getLong());
|
||||||
|
case TIMESTAMP:
|
||||||
|
return new Timestamp(buffer.getLong(), buffer.getInt());
|
||||||
|
case GEO_POINT:
|
||||||
|
readAlignment(buffer, 8);
|
||||||
|
return new GeoPoint(buffer.getDouble(), buffer.getDouble());
|
||||||
|
case DOCUMENT_REFERENCE:
|
||||||
|
final byte[] appNameBytes = readBytes(buffer);
|
||||||
|
String appName = new String(appNameBytes, UTF8);
|
||||||
|
final FirebaseFirestore firestore =
|
||||||
|
FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName));
|
||||||
|
final byte[] pathBytes = readBytes(buffer);
|
||||||
|
final String path = new String(pathBytes, UTF8);
|
||||||
|
return firestore.document(path);
|
||||||
|
case BLOB:
|
||||||
|
final byte[] bytes = readBytes(buffer);
|
||||||
|
return Blob.fromBytes(bytes);
|
||||||
|
case ARRAY_UNION:
|
||||||
|
return FieldValue.arrayUnion(toArray(readValue(buffer)));
|
||||||
|
case ARRAY_REMOVE:
|
||||||
|
return FieldValue.arrayRemove(toArray(readValue(buffer)));
|
||||||
|
case DELETE:
|
||||||
|
return FieldValue.delete();
|
||||||
|
case SERVER_TIMESTAMP:
|
||||||
|
return FieldValue.serverTimestamp();
|
||||||
|
case INCREMENT_INTEGER:
|
||||||
|
final Number integerIncrementValue = (Number) readValue(buffer);
|
||||||
|
return FieldValue.increment(integerIncrementValue.intValue());
|
||||||
|
case INCREMENT_DOUBLE:
|
||||||
|
final Number doubleIncrementValue = (Number) readValue(buffer);
|
||||||
|
return FieldValue.increment(doubleIncrementValue.doubleValue());
|
||||||
|
default:
|
||||||
|
return super.readValueOfType(type, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object[] toArray(Object source) {
|
||||||
|
if (source instanceof List) {
|
||||||
|
return ((List) source).toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source == null) {
|
||||||
|
return new Object[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
String sourceType = source.getClass().getCanonicalName();
|
||||||
|
String message = "java.util.List was expected, unable to convert '%s' to an object array";
|
||||||
|
throw new IllegalArgumentException(String.format(message, sourceType));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package io.flutter.plugins.firebase.cloudfirestore;
|
||||||
|
|
||||||
|
import com.google.firebase.components.Component;
|
||||||
|
import com.google.firebase.components.ComponentRegistrar;
|
||||||
|
import com.google.firebase.platforminfo.LibraryVersionComponent;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class FlutterFirebaseAppRegistrar implements ComponentRegistrar {
|
||||||
|
private static final String LIBRARY_NAME = "flutter-fire-fst";
|
||||||
|
private static final String LIBRARY_VERSION = "0.12.5";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Component<?>> getComponents() {
|
||||||
|
return Collections.<Component<?>>singletonList(
|
||||||
|
LibraryVersionComponent.create(LIBRARY_NAME, LIBRARY_VERSION));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
cloud_firestore=/Volumes/D1/dev/flutter/google-oem-plugins/packages/cloud_firestore/
|
||||||
|
firebase_core=/Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core-0.4.0+3/
|
|
@ -0,0 +1,64 @@
|
||||||
|
# Generated by pub on 2019-06-10 21:12:02.134413.
|
||||||
|
analyzer:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/analyzer-0.36.3/lib/
|
||||||
|
args:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/args-1.5.2/lib/
|
||||||
|
async:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.2.0/lib/
|
||||||
|
boolean_selector:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-1.0.4/lib/
|
||||||
|
charcode:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.1.2/lib/
|
||||||
|
cloud_firestore:../lib/
|
||||||
|
collection:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.14.11/lib/
|
||||||
|
convert:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/convert-2.1.1/lib/
|
||||||
|
crypto:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/crypto-2.0.6/lib/
|
||||||
|
csslib:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/csslib-0.16.0/lib/
|
||||||
|
file:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/file-5.0.8/lib/
|
||||||
|
firebase_core:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core-0.4.0+3/lib/
|
||||||
|
flutter:file:///Users/j3g/apps/flutter/packages/flutter/lib/
|
||||||
|
flutter_driver:file:///Users/j3g/apps/flutter/packages/flutter_driver/lib/
|
||||||
|
flutter_test:file:///Users/j3g/apps/flutter/packages/flutter_test/lib/
|
||||||
|
front_end:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/front_end-0.1.18/lib/
|
||||||
|
fuchsia_remote_debug_protocol:file:///Users/j3g/apps/flutter/packages/fuchsia_remote_debug_protocol/lib/
|
||||||
|
glob:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/glob-1.1.7/lib/
|
||||||
|
html:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/html-0.14.0+2/lib/
|
||||||
|
http:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/http-0.12.0+2/lib/
|
||||||
|
http_multi_server:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/http_multi_server-2.1.0/lib/
|
||||||
|
http_parser:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/http_parser-3.1.3/lib/
|
||||||
|
intl:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/intl-0.15.8/lib/
|
||||||
|
io:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/io-0.3.3/lib/
|
||||||
|
js:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/js-0.6.1+1/lib/
|
||||||
|
json_rpc_2:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/json_rpc_2-2.1.0/lib/
|
||||||
|
kernel:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/kernel-0.3.18/lib/
|
||||||
|
matcher:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.5/lib/
|
||||||
|
meta:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.1.6/lib/
|
||||||
|
mime:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/mime-0.9.6+3/lib/
|
||||||
|
multi_server_socket:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/multi_server_socket-1.0.2/lib/
|
||||||
|
node_preamble:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/node_preamble-1.4.4/lib/
|
||||||
|
package_config:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/package_config-1.0.5/lib/
|
||||||
|
package_resolver:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/package_resolver-1.0.10/lib/
|
||||||
|
path:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.6.2/lib/
|
||||||
|
pedantic:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/pedantic-1.7.0/lib/
|
||||||
|
platform:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/platform-2.2.0/lib/
|
||||||
|
pool:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/pool-1.4.0/lib/
|
||||||
|
process:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/process-3.0.9/lib/
|
||||||
|
pub_semver:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/pub_semver-1.4.2/lib/
|
||||||
|
quiver:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/quiver-2.0.3/lib/
|
||||||
|
shelf:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/shelf-0.7.5/lib/
|
||||||
|
shelf_packages_handler:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/shelf_packages_handler-1.0.4/lib/
|
||||||
|
shelf_static:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/shelf_static-0.2.8/lib/
|
||||||
|
shelf_web_socket:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/shelf_web_socket-0.2.3/lib/
|
||||||
|
sky_engine:file:///Users/j3g/apps/flutter/bin/cache/pkg/sky_engine/lib/
|
||||||
|
source_map_stack_trace:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/source_map_stack_trace-1.1.5/lib/
|
||||||
|
source_maps:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/source_maps-0.10.8/lib/
|
||||||
|
source_span:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.5.5/lib/
|
||||||
|
stack_trace:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.9.3/lib/
|
||||||
|
stream_channel:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.0.0/lib/
|
||||||
|
string_scanner:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.0.4/lib/
|
||||||
|
term_glyph:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.1.0/lib/
|
||||||
|
test:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/test-1.6.3/lib/
|
||||||
|
test_api:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.2.5/lib/
|
||||||
|
test_core:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/test_core-0.2.5/lib/
|
||||||
|
typed_data:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.1.6/lib/
|
||||||
|
vector_math:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.0.8/lib/
|
||||||
|
vm_service_client:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/vm_service_client-0.2.6+2/lib/
|
||||||
|
watcher:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/watcher-0.9.7+10/lib/
|
||||||
|
web_socket_channel:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/web_socket_channel-1.0.13/lib/
|
||||||
|
yaml:file:///Users/j3g/apps/flutter/.pub-cache/hosted/pub.dartlang.org/yaml-2.1.15/lib/
|
||||||
|
firestore_example:lib/
|
|
@ -0,0 +1,8 @@
|
||||||
|
# firestore_example
|
||||||
|
|
||||||
|
Demonstrates how to use the firestore plugin.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
For help getting started with Flutter, view our online
|
||||||
|
[documentation](http://flutter.io/).
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$/android">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/android/app/src/main/java" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="Flutter for Android" level="project" />
|
||||||
|
</component>
|
||||||
|
</module>
|
|
@ -0,0 +1,63 @@
|
||||||
|
def localProperties = new Properties()
|
||||||
|
def localPropertiesFile = rootProject.file('local.properties')
|
||||||
|
if (localPropertiesFile.exists()) {
|
||||||
|
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||||
|
localProperties.load(reader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||||
|
if (flutterRoot == null) {
|
||||||
|
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||||
|
if (flutterVersionCode == null) {
|
||||||
|
flutterVersionCode = '1'
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||||
|
if (flutterVersionName == null) {
|
||||||
|
flutterVersionName = '1.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 28
|
||||||
|
|
||||||
|
lintOptions {
|
||||||
|
disable 'InvalidPackage'
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId 'io.flutter.plugins.firebase.firestoreexample'
|
||||||
|
minSdkVersion 16
|
||||||
|
targetSdkVersion 28
|
||||||
|
versionCode flutterVersionCode.toInteger()
|
||||||
|
versionName flutterVersionName
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
multiDexEnabled true
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
// TODO: Add your own signing config for the release build.
|
||||||
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
|
signingConfig signingConfigs.debug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flutter {
|
||||||
|
source '../..'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation 'junit:junit:4.12'
|
||||||
|
androidTestImplementation 'androidx.test:runner:1.1.1'
|
||||||
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.google.gms.google-services'
|
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"project_info": {
|
||||||
|
"project_number": "159623150305",
|
||||||
|
"firebase_url": "https://flutter-firebase-plugins.firebaseio.com",
|
||||||
|
"project_id": "flutter-firebase-plugins",
|
||||||
|
"storage_bucket": "flutter-firebase-plugins.appspot.com"
|
||||||
|
},
|
||||||
|
"client": [
|
||||||
|
{
|
||||||
|
"client_info": {
|
||||||
|
"mobilesdk_app_id": "1:159623150305:android:236f9daea101f77e",
|
||||||
|
"android_client_info": {
|
||||||
|
"package_name": "io.flutter.plugins.firebase.firestoreexample"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oauth_client": [
|
||||||
|
{
|
||||||
|
"client_id": "159623150305-q05bbbtsutr02abhips3suj7hujfk4bg.apps.googleusercontent.com",
|
||||||
|
"client_type": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"api_key": [
|
||||||
|
{
|
||||||
|
"current_key": "AIzaSyChk3KEG7QYrs4kQPLP1tjJNxBTbfCAdgg"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services": {
|
||||||
|
"analytics_service": {
|
||||||
|
"status": 1
|
||||||
|
},
|
||||||
|
"appinvite_service": {
|
||||||
|
"status": 1,
|
||||||
|
"other_platform_oauth_client": []
|
||||||
|
},
|
||||||
|
"ads_service": {
|
||||||
|
"status": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configuration_version": "1"
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
android.enableJetifier=true
|
||||||
|
android.useAndroidX=true
|
5
packages/cloud_firestore/example/android/app/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
|
@ -0,0 +1,19 @@
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="io.flutter.plugins.firebase.firestoreexample">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
||||||
|
<application android:name="io.flutter.app.FlutterApplication" android:label="firestore_example" android:icon="@mipmap/ic_launcher">
|
||||||
|
<activity android:name=".MainActivity"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:theme="@android:style/Theme.Black.NoTitleBar"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
|
@ -0,0 +1,27 @@
|
||||||
|
package io.flutter.plugins;
|
||||||
|
|
||||||
|
import io.flutter.plugin.common.PluginRegistry;
|
||||||
|
import io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin;
|
||||||
|
import io.flutter.plugins.firebase.core.FirebaseCorePlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generated file. Do not edit.
|
||||||
|
*/
|
||||||
|
public final class GeneratedPluginRegistrant {
|
||||||
|
public static void registerWith(PluginRegistry registry) {
|
||||||
|
if (alreadyRegisteredWith(registry)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CloudFirestorePlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin"));
|
||||||
|
FirebaseCorePlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebase.core.FirebaseCorePlugin"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean alreadyRegisteredWith(PluginRegistry registry) {
|
||||||
|
final String key = GeneratedPluginRegistrant.class.getCanonicalName();
|
||||||
|
if (registry.hasPlugin(key)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
registry.registrarFor(key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
package io.flutter.plugins.firebase.firestoreexample;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import io.flutter.app.FlutterActivity;
|
||||||
|
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||||
|
|
||||||
|
public class MainActivity extends FlutterActivity {
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
GeneratedPluginRegistrant.registerWith(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
package io.flutter.plugins.firebasedatabaseexample;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import io.flutter.app.FlutterActivity;
|
||||||
|
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||||
|
|
||||||
|
public class MainActivity extends FlutterActivity {
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
GeneratedPluginRegistrant.registerWith(this);
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 544 B |
After Width: | Height: | Size: 442 B |
After Width: | Height: | Size: 721 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,32 @@
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
mavenLocal()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:3.3.0'
|
||||||
|
classpath 'com.google.gms:google-services:4.2.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
mavenLocal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.buildDir = '../build'
|
||||||
|
subprojects {
|
||||||
|
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||||
|
}
|
||||||
|
subprojects {
|
||||||
|
project.evaluationDependsOn(':app')
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
org.gradle.jvmargs=-Xmx1536M
|
5
packages/cloud_firestore/example/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
|
|
@ -0,0 +1,2 @@
|
||||||
|
sdk.dir=/usr/local/opt/android-sdk
|
||||||
|
flutter.sdk=/Users/j3g/apps/flutter
|
|
@ -0,0 +1,15 @@
|
||||||
|
include ':app'
|
||||||
|
|
||||||
|
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
|
||||||
|
|
||||||
|
def plugins = new Properties()
|
||||||
|
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
|
||||||
|
if (pluginsFile.exists()) {
|
||||||
|
pluginsFile.withInputStream { stream -> plugins.load(stream) }
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins.each { name, path ->
|
||||||
|
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
|
||||||
|
include ":$name"
|
||||||
|
project(":$name").projectDir = pluginDirectory
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="FLUTTER_MODULE_TYPE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.idea" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/packages" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="Dart SDK" level="application" />
|
||||||
|
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||||
|
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||||
|
</component>
|
||||||
|
</module>
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>App</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>io.flutter.flutter.app</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>App</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
|
<array>
|
||||||
|
<string>arm64</string>
|
||||||
|
</array>
|
||||||
|
<key>MinimumOSVersion</key>
|
||||||
|
<string>8.0</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,2 @@
|
||||||
|
#include "Generated.xcconfig"
|
||||||
|
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
|
@ -0,0 +1,7 @@
|
||||||
|
// This is a generated file; do not edit or check into version control.
|
||||||
|
FLUTTER_ROOT=/Users/j3g/apps/flutter
|
||||||
|
FLUTTER_APPLICATION_PATH=/Volumes/D1/dev/flutter/google-oem-plugins/packages/cloud_firestore/example
|
||||||
|
FLUTTER_TARGET=lib/main.dart
|
||||||
|
FLUTTER_BUILD_DIR=build
|
||||||
|
SYMROOT=${SOURCE_ROOT}/../build/ios
|
||||||
|
FLUTTER_FRAMEWORK_DIR=/Users/j3g/apps/flutter/bin/cache/artifacts/engine/ios
|
|
@ -0,0 +1,2 @@
|
||||||
|
#include "Generated.xcconfig"
|
||||||
|
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
|
@ -0,0 +1,72 @@
|
||||||
|
# Uncomment this line to define a global platform for your project
|
||||||
|
# platform :ios, '9.0'
|
||||||
|
|
||||||
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|
||||||
|
project 'Runner', {
|
||||||
|
'Debug' => :debug,
|
||||||
|
'Profile' => :release,
|
||||||
|
'Release' => :release,
|
||||||
|
}
|
||||||
|
|
||||||
|
def parse_KV_file(file, separator='=')
|
||||||
|
file_abs_path = File.expand_path(file)
|
||||||
|
if !File.exists? file_abs_path
|
||||||
|
return [];
|
||||||
|
end
|
||||||
|
pods_ary = []
|
||||||
|
skip_line_start_symbols = ["#", "/"]
|
||||||
|
File.foreach(file_abs_path) { |line|
|
||||||
|
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
|
||||||
|
plugin = line.split(pattern=separator)
|
||||||
|
if plugin.length == 2
|
||||||
|
podname = plugin[0].strip()
|
||||||
|
path = plugin[1].strip()
|
||||||
|
podpath = File.expand_path("#{path}", file_abs_path)
|
||||||
|
pods_ary.push({:name => podname, :path => podpath});
|
||||||
|
else
|
||||||
|
puts "Invalid plugin specification: #{line}"
|
||||||
|
end
|
||||||
|
}
|
||||||
|
return pods_ary
|
||||||
|
end
|
||||||
|
|
||||||
|
target 'Runner' do
|
||||||
|
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
|
||||||
|
# referring to absolute paths on developers' machines.
|
||||||
|
system('rm -rf .symlinks')
|
||||||
|
system('mkdir -p .symlinks/plugins')
|
||||||
|
|
||||||
|
# Flutter Pods
|
||||||
|
generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig')
|
||||||
|
if generated_xcode_build_settings.empty?
|
||||||
|
puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first."
|
||||||
|
end
|
||||||
|
generated_xcode_build_settings.map { |p|
|
||||||
|
if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
|
||||||
|
symlink = File.join('.symlinks', 'flutter')
|
||||||
|
File.symlink(File.dirname(p[:path]), symlink)
|
||||||
|
pod 'Flutter', :path => File.join(symlink, File.basename(p[:path]))
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
# Plugin Pods
|
||||||
|
plugin_pods = parse_KV_file('../.flutter-plugins')
|
||||||
|
plugin_pods.map { |p|
|
||||||
|
symlink = File.join('.symlinks', 'plugins', p[:name])
|
||||||
|
File.symlink(p[:path], symlink)
|
||||||
|
pod p[:name], :path => File.join(symlink, 'ios')
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
|
||||||
|
install! 'cocoapods', :disable_input_output_paths => true
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
target.build_configurations.each do |config|
|
||||||
|
config.build_settings['ENABLE_BITCODE'] = 'NO'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,500 @@
|
||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 46;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
|
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
|
||||||
|
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
|
5C6F5A711EC3CCCC008D64B5 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C6F5A701EC3CCCC008D64B5 /* GeneratedPluginRegistrant.m */; };
|
||||||
|
7A1ECC911E8EDB6900309407 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7A1ECC901E8EDB6900309407 /* GoogleService-Info.plist */; };
|
||||||
|
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
|
||||||
|
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
|
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
|
||||||
|
9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; };
|
||||||
|
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
|
||||||
|
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
|
||||||
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
|
CE57DC9C9240FBD15E358E24 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E13AAF33B0B411D7B2D38642 /* libPods-Runner.a */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 10;
|
||||||
|
files = (
|
||||||
|
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
|
||||||
|
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
|
||||||
|
);
|
||||||
|
name = "Embed Frameworks";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
|
||||||
|
5C6F5A6F1EC3CCCC008D64B5 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
|
5C6F5A701EC3CCCC008D64B5 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
|
7A1ECC901E8EDB6900309407 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||||
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||||
|
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||||
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
|
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
|
||||||
|
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||||
|
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
E13AAF33B0B411D7B2D38642 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
|
||||||
|
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
|
||||||
|
CE57DC9C9240FBD15E358E24 /* libPods-Runner.a in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
840012C8B5EDBCF56B0E4AC1 /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
);
|
||||||
|
name = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
3B80C3931E831B6300D905FE /* App.framework */,
|
||||||
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||||
|
9740EEBA1CF902C7004384FC /* Flutter.framework */,
|
||||||
|
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||||
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||||
|
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Flutter;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146E51CF9000F007C117D = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9740EEB11CF90186004384FC /* Flutter */,
|
||||||
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
|
840012C8B5EDBCF56B0E4AC1 /* Pods */,
|
||||||
|
CF3B75C9A7D2FA2A4C99F110 /* Frameworks */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146EF1CF9000F007C117D /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146F01CF9000F007C117D /* Runner */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
5C6F5A6F1EC3CCCC008D64B5 /* GeneratedPluginRegistrant.h */,
|
||||||
|
5C6F5A701EC3CCCC008D64B5 /* GeneratedPluginRegistrant.m */,
|
||||||
|
7A1ECC901E8EDB6900309407 /* GoogleService-Info.plist */,
|
||||||
|
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
|
||||||
|
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
|
||||||
|
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||||
|
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||||
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||||
|
97C147021CF9000F007C117D /* Info.plist */,
|
||||||
|
97C146F11CF9000F007C117D /* Supporting Files */,
|
||||||
|
);
|
||||||
|
path = Runner;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146F11CF9000F007C117D /* Supporting Files */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
97C146F21CF9000F007C117D /* main.m */,
|
||||||
|
);
|
||||||
|
name = "Supporting Files";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
CF3B75C9A7D2FA2A4C99F110 /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
E13AAF33B0B411D7B2D38642 /* libPods-Runner.a */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
97C146ED1CF9000F007C117D /* Runner */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
|
buildPhases = (
|
||||||
|
AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */,
|
||||||
|
9740EEB61CF901F6004384FC /* Run Script */,
|
||||||
|
97C146EA1CF9000F007C117D /* Sources */,
|
||||||
|
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||||
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
|
95BB15E9E1769C0D146AA592 /* [CP] Embed Pods Frameworks */,
|
||||||
|
532EA9D341340B1DCD08293D /* [CP] Copy Pods Resources */,
|
||||||
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = Runner;
|
||||||
|
productName = Runner;
|
||||||
|
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
97C146E61CF9000F007C117D /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastUpgradeCheck = 0830;
|
||||||
|
ORGANIZATIONNAME = "The Chromium Authors";
|
||||||
|
TargetAttributes = {
|
||||||
|
97C146ED1CF9000F007C117D = {
|
||||||
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||||
|
compatibilityVersion = "Xcode 3.2";
|
||||||
|
developmentRegion = English;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = 97C146E51CF9000F007C117D;
|
||||||
|
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
97C146ED1CF9000F007C117D /* Runner */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
97C146EC1CF9000F007C117D /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
7A1ECC911E8EDB6900309407 /* GoogleService-Info.plist in Resources */,
|
||||||
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||||
|
9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */,
|
||||||
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||||
|
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
|
||||||
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||||
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Thin Binary";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
|
||||||
|
};
|
||||||
|
532EA9D341340B1DCD08293D /* [CP] Copy Pods Resources */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
|
||||||
|
"${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle",
|
||||||
|
);
|
||||||
|
name = "[CP] Copy Pods Resources";
|
||||||
|
outputPaths = (
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
95BB15E9E1769C0D146AA592 /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
|
||||||
|
"${PODS_ROOT}/../.symlinks/flutter/ios-release/Flutter.framework",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputPaths = (
|
||||||
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run Script";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||||
|
};
|
||||||
|
AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
97C146EA1CF9000F007C117D /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
|
||||||
|
97C146F31CF9000F007C117D /* main.m in Sources */,
|
||||||
|
5C6F5A711EC3CCCC008D64B5 /* GeneratedPluginRegistrant.m in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXVariantGroup section */
|
||||||
|
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
97C146FB1CF9000F007C117D /* Base */,
|
||||||
|
);
|
||||||
|
name = Main.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
97C147001CF9000F007C117D /* Base */,
|
||||||
|
);
|
||||||
|
name = LaunchScreen.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
97C147031CF9000F007C117D /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
97C147041CF9000F007C117D /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
97C147061CF9000F007C117D /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ARCHS = arm64;
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.firestoreExample;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
97C147071CF9000F007C117D /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ARCHS = arm64;
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.firestoreExample;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
97C147031CF9000F007C117D /* Debug */,
|
||||||
|
97C147041CF9000F007C117D /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
97C147061CF9000F007C117D /* Debug */,
|
||||||
|
97C147071CF9000F007C117D /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
|
}
|
10
packages/cloud_firestore/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:Runner.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
|
@ -0,0 +1,91 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "0830"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
10
packages/cloud_firestore/example/ios/Runner.xcworkspace/contents.xcworkspacedata
generated
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:Runner.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,10 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#import <Flutter/Flutter.h>
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface AppDelegate : FlutterAppDelegate
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "AppDelegate.h"
|
||||||
|
#include "GeneratedPluginRegistrant.h"
|
||||||
|
|
||||||
|
@implementation AppDelegate
|
||||||
|
|
||||||
|
- (BOOL)application:(UIApplication *)application
|
||||||
|
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||||
|
[GeneratedPluginRegistrant registerWithRegistry:self];
|
||||||
|
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,116 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-20x20@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-20x20@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-29x29@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-29x29@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-29x29@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-40x40@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-40x40@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "60x60",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-60x60@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "60x60",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-60x60@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-20x20@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-20x20@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-29x29@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-29x29@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-40x40@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-40x40@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "76x76",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-76x76@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "76x76",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-76x76@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "83.5x83.5",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 564 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.5 KiB |
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--View Controller-->
|
||||||
|
<scene sceneID="EHf-IW-A2E">
|
||||||
|
<objects>
|
||||||
|
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||||
|
<layoutGuides>
|
||||||
|
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
|
||||||
|
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
|
||||||
|
</layoutGuides>
|
||||||
|
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="53" y="375"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Flutter View Controller-->
|
||||||
|
<scene sceneID="tne-QT-ifu">
|
||||||
|
<objects>
|
||||||
|
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
|
||||||
|
<layoutGuides>
|
||||||
|
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||||
|
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||||
|
</layoutGuides>
|
||||||
|
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
|
@ -0,0 +1,14 @@
|
||||||
|
//
|
||||||
|
// Generated file. Do not edit.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GeneratedPluginRegistrant_h
|
||||||
|
#define GeneratedPluginRegistrant_h
|
||||||
|
|
||||||
|
#import <Flutter/Flutter.h>
|
||||||
|
|
||||||
|
@interface GeneratedPluginRegistrant : NSObject
|
||||||
|
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry;
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif /* GeneratedPluginRegistrant_h */
|
|
@ -0,0 +1,16 @@
|
||||||
|
//
|
||||||
|
// Generated file. Do not edit.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "GeneratedPluginRegistrant.h"
|
||||||
|
#import <cloud_firestore/CloudFirestorePlugin.h>
|
||||||
|
#import <firebase_core/FirebaseCorePlugin.h>
|
||||||
|
|
||||||
|
@implementation GeneratedPluginRegistrant
|
||||||
|
|
||||||
|
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
|
||||||
|
[FLTCloudFirestorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTCloudFirestorePlugin"]];
|
||||||
|
[FLTFirebaseCorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseCorePlugin"]];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>AD_UNIT_ID_FOR_BANNER_TEST</key>
|
||||||
|
<string>ca-app-pub-3940256099942544/2934735716</string>
|
||||||
|
<key>AD_UNIT_ID_FOR_INTERSTITIAL_TEST</key>
|
||||||
|
<string>ca-app-pub-3940256099942544/4411468910</string>
|
||||||
|
<key>CLIENT_ID</key>
|
||||||
|
<string>159623150305-1iiqqggbff817a8bpnalo64nuc3qobid.apps.googleusercontent.com</string>
|
||||||
|
<key>REVERSED_CLIENT_ID</key>
|
||||||
|
<string>com.googleusercontent.apps.159623150305-1iiqqggbff817a8bpnalo64nuc3qobid</string>
|
||||||
|
<key>API_KEY</key>
|
||||||
|
<string>AIzaSyDyzecVw1zXTpBKwfFHxpl7QyYBhimNhUk</string>
|
||||||
|
<key>GCM_SENDER_ID</key>
|
||||||
|
<string>159623150305</string>
|
||||||
|
<key>PLIST_VERSION</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>BUNDLE_ID</key>
|
||||||
|
<string>io.flutter.plugins.firestoreExample</string>
|
||||||
|
<key>PROJECT_ID</key>
|
||||||
|
<string>flutter-firebase-plugins</string>
|
||||||
|
<key>STORAGE_BUCKET</key>
|
||||||
|
<string>flutter-firebase-plugins.appspot.com</string>
|
||||||
|
<key>IS_ADS_ENABLED</key>
|
||||||
|
<true></true>
|
||||||
|
<key>IS_ANALYTICS_ENABLED</key>
|
||||||
|
<false></false>
|
||||||
|
<key>IS_APPINVITE_ENABLED</key>
|
||||||
|
<false></false>
|
||||||
|
<key>IS_GCM_ENABLED</key>
|
||||||
|
<true></true>
|
||||||
|
<key>IS_SIGNIN_ENABLED</key>
|
||||||
|
<true></true>
|
||||||
|
<key>GOOGLE_APP_ID</key>
|
||||||
|
<string>1:159623150305:ios:7e8aafdf0bd8d289</string>
|
||||||
|
<key>DATABASE_URL</key>
|
||||||
|
<string>https://flutter-firebase-plugins.firebaseio.com</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>firestore_example</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
<key>UILaunchStoryboardName</key>
|
||||||
|
<string>LaunchScreen</string>
|
||||||
|
<key>UIMainStoryboardFile</key>
|
||||||
|
<string>Main</string>
|
||||||
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
|
<array>
|
||||||
|
<string>arm64</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#import <Flutter/Flutter.h>
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
@autoreleasepool {
|
||||||
|
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
|
|
||||||
|
Future<void> main() async {
|
||||||
|
final FirebaseApp app = await FirebaseApp.configure(
|
||||||
|
name: 'test',
|
||||||
|
options: const FirebaseOptions(
|
||||||
|
googleAppID: '1:79601577497:ios:5f2bcc6ba8cecddd',
|
||||||
|
gcmSenderID: '79601577497',
|
||||||
|
apiKey: 'AIzaSyArgmRGfB5kiQT6CunAOmKRVKEsxKmy6YI-G72PVU',
|
||||||
|
projectID: 'flutter-firestore',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final Firestore firestore = Firestore(app: app);
|
||||||
|
await firestore.settings(timestampsInSnapshotsEnabled: true);
|
||||||
|
|
||||||
|
runApp(MaterialApp(
|
||||||
|
title: 'Firestore Example', home: MyHomePage(firestore: firestore)));
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessageList extends StatelessWidget {
|
||||||
|
MessageList({this.firestore});
|
||||||
|
|
||||||
|
final Firestore firestore;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return StreamBuilder<QuerySnapshot>(
|
||||||
|
stream: firestore.collection('messages').snapshots(),
|
||||||
|
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
|
||||||
|
if (!snapshot.hasData) return const Text('Loading...');
|
||||||
|
final int messageCount = snapshot.data.documents.length;
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: messageCount,
|
||||||
|
itemBuilder: (_, int index) {
|
||||||
|
final DocumentSnapshot document = snapshot.data.documents[index];
|
||||||
|
return ListTile(
|
||||||
|
title: Text(document['message'] ?? '<No message retrieved>'),
|
||||||
|
subtitle: Text('Message ${index + 1} of $messageCount'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyHomePage extends StatelessWidget {
|
||||||
|
MyHomePage({this.firestore});
|
||||||
|
final Firestore firestore;
|
||||||
|
CollectionReference get messages => firestore.collection('messages');
|
||||||
|
|
||||||
|
Future<void> _addMessage() async {
|
||||||
|
await messages.add(<String, dynamic>{
|
||||||
|
'message': 'Hello world!',
|
||||||
|
'created_at': FieldValue.serverTimestamp(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Firestore Example'),
|
||||||
|
),
|
||||||
|
body: MessageList(firestore: firestore),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
onPressed: _addMessage,
|
||||||
|
tooltip: 'Increment',
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,430 @@
|
||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages:
|
||||||
|
analyzer:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: analyzer
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.36.3"
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.2"
|
||||||
|
async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: async
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
boolean_selector:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: boolean_selector
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
|
charcode:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: charcode
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
cloud_firestore:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: ".."
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "0.12.5"
|
||||||
|
collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: collection
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.14.11"
|
||||||
|
convert:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: convert
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
crypto:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: crypto
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.6"
|
||||||
|
csslib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csslib
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.16.0"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.8"
|
||||||
|
firebase_core:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: firebase_core
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.0+3"
|
||||||
|
flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_driver:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_test:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
front_end:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: front_end
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.18"
|
||||||
|
fuchsia_remote_debug_protocol:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
glob:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: glob
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.7"
|
||||||
|
html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: html
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.14.0+2"
|
||||||
|
http:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.12.0+2"
|
||||||
|
http_multi_server:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_multi_server
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
http_parser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_parser
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.3"
|
||||||
|
intl:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: intl
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.8"
|
||||||
|
io:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: io
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.3"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.1+1"
|
||||||
|
json_rpc_2:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: json_rpc_2
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
kernel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: kernel
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.18"
|
||||||
|
matcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: matcher
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.12.5"
|
||||||
|
meta:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: meta
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.6"
|
||||||
|
mime:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: mime
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.6+3"
|
||||||
|
multi_server_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: multi_server_socket
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
|
node_preamble:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: node_preamble
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.4"
|
||||||
|
package_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_config
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.5"
|
||||||
|
package_resolver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_resolver
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.10"
|
||||||
|
path:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.6.2"
|
||||||
|
pedantic:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pedantic
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.7.0"
|
||||||
|
platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: platform
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
pool:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pool
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
|
process:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: process
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.9"
|
||||||
|
pub_semver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pub_semver
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.2"
|
||||||
|
quiver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: quiver
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.3"
|
||||||
|
shelf:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.5"
|
||||||
|
shelf_packages_handler:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_packages_handler
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
|
shelf_static:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_static
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.8"
|
||||||
|
shelf_web_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_web_socket
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.3"
|
||||||
|
sky_engine:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.99"
|
||||||
|
source_map_stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_map_stack_trace
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.5"
|
||||||
|
source_maps:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_maps
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.10.8"
|
||||||
|
source_span:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_span
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.5"
|
||||||
|
stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stack_trace
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.9.3"
|
||||||
|
stream_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_channel
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
|
string_scanner:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: string_scanner
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
|
term_glyph:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: term_glyph
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
test:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: test
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.6.3"
|
||||||
|
test_api:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_api
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.5"
|
||||||
|
test_core:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_core
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.5"
|
||||||
|
typed_data:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: typed_data
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.6"
|
||||||
|
vector_math:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_math
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.8"
|
||||||
|
vm_service_client:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vm_service_client
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.6+2"
|
||||||
|
watcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: watcher
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.7+10"
|
||||||
|
web_socket_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web_socket_channel
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.13"
|
||||||
|
yaml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: yaml
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.15"
|
||||||
|
sdks:
|
||||||
|
dart: ">=2.2.0 <3.0.0"
|
||||||
|
flutter: ">=1.5.0 <2.0.0"
|
|
@ -0,0 +1,17 @@
|
||||||
|
name: firestore_example
|
||||||
|
description: Demonstrates how to use the firestore plugin.
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
cloud_firestore:
|
||||||
|
path: ../
|
||||||
|
firebase_core: "^0.4.0"
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_driver:
|
||||||
|
sdk: flutter
|
||||||
|
test: any
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
|
@ -0,0 +1,192 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter_driver/driver_extension.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
final Completer<String> completer = Completer<String>();
|
||||||
|
enableFlutterDriverExtension(handler: (_) => completer.future);
|
||||||
|
tearDownAll(() => completer.complete(null));
|
||||||
|
|
||||||
|
group('$Firestore', () {
|
||||||
|
Firestore firestore;
|
||||||
|
Firestore firestoreWithSettings;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
final FirebaseOptions firebaseOptions = const FirebaseOptions(
|
||||||
|
googleAppID: '1:79601577497:ios:5f2bcc6ba8cecddd',
|
||||||
|
gcmSenderID: '79601577497',
|
||||||
|
apiKey: 'AIzaSyArgmRGfB5kiQT6CunAOmKRVKEsxKmy6YI-G72PVU',
|
||||||
|
projectID: 'flutter-firestore',
|
||||||
|
);
|
||||||
|
final FirebaseApp app = await FirebaseApp.configure(
|
||||||
|
name: 'test',
|
||||||
|
options: firebaseOptions,
|
||||||
|
);
|
||||||
|
final FirebaseApp app2 = await FirebaseApp.configure(
|
||||||
|
name: 'test2',
|
||||||
|
options: firebaseOptions,
|
||||||
|
);
|
||||||
|
firestore = Firestore(app: app);
|
||||||
|
firestoreWithSettings = Firestore(app: app2);
|
||||||
|
await firestoreWithSettings.settings(
|
||||||
|
persistenceEnabled: true,
|
||||||
|
host: null,
|
||||||
|
sslEnabled: true,
|
||||||
|
timestampsInSnapshotsEnabled: true,
|
||||||
|
cacheSizeBytes: 1048576,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getDocumentsWithFirestoreSettings', () async {
|
||||||
|
final Query query = firestoreWithSettings.collection('messages').limit(1);
|
||||||
|
final QuerySnapshot querySnapshot = await query.getDocuments();
|
||||||
|
expect(querySnapshot.documents.length, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getDocumentsFromCollection', () async {
|
||||||
|
final Query query = firestore
|
||||||
|
.collection('messages')
|
||||||
|
.where('message', isEqualTo: 'Hello world!')
|
||||||
|
.limit(1);
|
||||||
|
final QuerySnapshot querySnapshot = await query.getDocuments();
|
||||||
|
expect(querySnapshot.documents.first['message'], 'Hello world!');
|
||||||
|
final DocumentReference firstDoc =
|
||||||
|
querySnapshot.documents.first.reference;
|
||||||
|
final DocumentSnapshot documentSnapshot = await firstDoc.get();
|
||||||
|
expect(documentSnapshot.data['message'], 'Hello world!');
|
||||||
|
final DocumentSnapshot cachedSnapshot =
|
||||||
|
await firstDoc.get(source: Source.cache);
|
||||||
|
expect(cachedSnapshot.data['message'], 'Hello world!');
|
||||||
|
final DocumentSnapshot snapshot = await firstDoc.snapshots().first;
|
||||||
|
expect(snapshot.data['message'], 'Hello world!');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getDocumentsFromCollectionGroup', () async {
|
||||||
|
final Query query = firestore
|
||||||
|
.collectionGroup('reviews')
|
||||||
|
.where('stars', isEqualTo: 5)
|
||||||
|
.limit(1);
|
||||||
|
final QuerySnapshot querySnapshot = await query.getDocuments();
|
||||||
|
expect(querySnapshot.documents.first['stars'], 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('increment', () async {
|
||||||
|
final DocumentReference ref = firestore.collection('messages').document();
|
||||||
|
await ref.setData(<String, dynamic>{
|
||||||
|
'message': 1,
|
||||||
|
'created_at': FieldValue.serverTimestamp(),
|
||||||
|
});
|
||||||
|
DocumentSnapshot snapshot = await ref.get();
|
||||||
|
expect(snapshot.data['message'], 1);
|
||||||
|
await ref.updateData(<String, dynamic>{
|
||||||
|
'message': FieldValue.increment(1),
|
||||||
|
});
|
||||||
|
snapshot = await ref.get();
|
||||||
|
expect(snapshot.data['message'], 2);
|
||||||
|
await ref.updateData(<String, dynamic>{
|
||||||
|
'message': FieldValue.increment(40.1),
|
||||||
|
});
|
||||||
|
snapshot = await ref.get();
|
||||||
|
expect(snapshot.data['message'], 42.1);
|
||||||
|
await ref.delete();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('runTransaction', () async {
|
||||||
|
final DocumentReference ref = firestore.collection('messages').document();
|
||||||
|
await ref.setData(<String, dynamic>{
|
||||||
|
'message': 'testing',
|
||||||
|
'created_at': FieldValue.serverTimestamp(),
|
||||||
|
});
|
||||||
|
final DocumentSnapshot initialSnapshot = await ref.get();
|
||||||
|
expect(initialSnapshot.data['message'], 'testing');
|
||||||
|
final dynamic result = await firestore.runTransaction(
|
||||||
|
(Transaction tx) async {
|
||||||
|
final DocumentSnapshot snapshot = await tx.get(ref);
|
||||||
|
final Map<String, dynamic> updatedData =
|
||||||
|
Map<String, dynamic>.from(snapshot.data);
|
||||||
|
updatedData['message'] = 'testing2';
|
||||||
|
await tx.update(ref, updatedData);
|
||||||
|
return updatedData;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(result['message'], 'testing2');
|
||||||
|
await ref.delete();
|
||||||
|
final DocumentSnapshot nonexistentSnapshot = await ref.get();
|
||||||
|
expect(nonexistentSnapshot.data, null);
|
||||||
|
expect(nonexistentSnapshot.exists, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('pagination', () async {
|
||||||
|
// Populate the database with two test documents
|
||||||
|
final CollectionReference messages = firestore.collection('messages');
|
||||||
|
final DocumentReference doc1 = messages.document();
|
||||||
|
// Use document ID as a unique identifier to ensure that we don't
|
||||||
|
// collide with other tests running against this database.
|
||||||
|
final String testRun = doc1.documentID;
|
||||||
|
await doc1.setData(<String, dynamic>{
|
||||||
|
'message': 'pagination testing1',
|
||||||
|
'test_run': testRun,
|
||||||
|
'created_at': FieldValue.serverTimestamp(),
|
||||||
|
});
|
||||||
|
final DocumentSnapshot snapshot1 = await doc1.get();
|
||||||
|
final DocumentReference doc2 = messages.document();
|
||||||
|
await doc2.setData(<String, dynamic>{
|
||||||
|
'message': 'pagination testing2',
|
||||||
|
'test_run': testRun,
|
||||||
|
'created_at': FieldValue.serverTimestamp(),
|
||||||
|
});
|
||||||
|
final DocumentSnapshot snapshot2 = await doc2.get();
|
||||||
|
|
||||||
|
QuerySnapshot snapshot;
|
||||||
|
List<DocumentSnapshot> results;
|
||||||
|
|
||||||
|
// startAtDocument
|
||||||
|
snapshot = await messages
|
||||||
|
.orderBy('created_at')
|
||||||
|
.where('test_run', isEqualTo: testRun)
|
||||||
|
.startAtDocument(snapshot1)
|
||||||
|
.getDocuments();
|
||||||
|
results = snapshot.documents;
|
||||||
|
expect(results.length, 2);
|
||||||
|
expect(results[0].data['message'], 'pagination testing1');
|
||||||
|
expect(results[1].data['message'], 'pagination testing2');
|
||||||
|
|
||||||
|
// startAfterDocument
|
||||||
|
snapshot = await messages
|
||||||
|
.orderBy('created_at')
|
||||||
|
.where('test_run', isEqualTo: testRun)
|
||||||
|
.startAfterDocument(snapshot1)
|
||||||
|
.getDocuments();
|
||||||
|
results = snapshot.documents;
|
||||||
|
expect(results.length, 1);
|
||||||
|
expect(results[0].data['message'], 'pagination testing2');
|
||||||
|
|
||||||
|
// endAtDocument
|
||||||
|
snapshot = await messages
|
||||||
|
.orderBy('created_at')
|
||||||
|
.where('test_run', isEqualTo: testRun)
|
||||||
|
.endAtDocument(snapshot2)
|
||||||
|
.getDocuments();
|
||||||
|
results = snapshot.documents;
|
||||||
|
expect(results.length, 2);
|
||||||
|
expect(results[0].data['message'], 'pagination testing1');
|
||||||
|
expect(results[1].data['message'], 'pagination testing2');
|
||||||
|
|
||||||
|
// endBeforeDocument
|
||||||
|
snapshot = await messages
|
||||||
|
.orderBy('created_at')
|
||||||
|
.where('test_run', isEqualTo: testRun)
|
||||||
|
.endBeforeDocument(snapshot2)
|
||||||
|
.getDocuments();
|
||||||
|
results = snapshot.documents;
|
||||||
|
expect(results.length, 1);
|
||||||
|
expect(results[0].data['message'], 'pagination testing1');
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
await doc1.delete();
|
||||||
|
await doc2.delete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import 'package:flutter_driver/flutter_driver.dart';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
final FlutterDriver driver = await FlutterDriver.connect();
|
||||||
|
await driver.requestData(null, timeout: const Duration(minutes: 1));
|
||||||
|
driver.close();
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#import <Flutter/Flutter.h>
|
||||||
|
|
||||||
|
@interface FLTCloudFirestorePlugin : NSObject <FlutterPlugin>
|
||||||
|
@end
|
|
@ -0,0 +1,653 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#import "CloudFirestorePlugin.h"
|
||||||
|
|
||||||
|
#import <Firebase/Firebase.h>
|
||||||
|
|
||||||
|
#define LIBRARY_NAME @"flutter-firebase_cloud_firestore"
|
||||||
|
#define LIBRARY_VERSION @"0.12.5"
|
||||||
|
|
||||||
|
static FlutterError *getFlutterError(NSError *error) {
|
||||||
|
if (error == nil) return nil;
|
||||||
|
|
||||||
|
return [FlutterError errorWithCode:[NSString stringWithFormat:@"Error %ld", error.code]
|
||||||
|
message:error.domain
|
||||||
|
details:error.localizedDescription];
|
||||||
|
}
|
||||||
|
|
||||||
|
static FIRFirestore *getFirestore(NSDictionary *arguments) {
|
||||||
|
FIRApp *app = [FIRApp appNamed:arguments[@"app"]];
|
||||||
|
return [FIRFirestore firestoreForApp:app];
|
||||||
|
}
|
||||||
|
|
||||||
|
static FIRDocumentReference *getDocumentReference(NSDictionary *arguments) {
|
||||||
|
return [getFirestore(arguments) documentWithPath:arguments[@"path"]];
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSArray *getDocumentValues(NSDictionary *document, NSArray *orderBy,
|
||||||
|
BOOL *isCollectionGroup) {
|
||||||
|
NSMutableArray *values = [[NSMutableArray alloc] init];
|
||||||
|
NSDictionary *documentData = document[@"data"];
|
||||||
|
if (orderBy) {
|
||||||
|
for (id item in orderBy) {
|
||||||
|
NSArray *orderByParameters = item;
|
||||||
|
NSString *fieldName = orderByParameters[0];
|
||||||
|
[values addObject:[documentData objectForKey:fieldName]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isCollectionGroup) {
|
||||||
|
NSString *path = document[@"path"];
|
||||||
|
[values addObject:path];
|
||||||
|
} else {
|
||||||
|
NSString *documentId = document[@"id"];
|
||||||
|
[values addObject:documentId];
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FIRQuery *getQuery(NSDictionary *arguments) {
|
||||||
|
NSNumber *data = arguments[@"isCollectionGroup"];
|
||||||
|
BOOL isCollectionGroup = data.boolValue;
|
||||||
|
FIRQuery *query;
|
||||||
|
if (isCollectionGroup) {
|
||||||
|
query = [getFirestore(arguments) collectionGroupWithID:arguments[@"path"]];
|
||||||
|
} else {
|
||||||
|
query = [getFirestore(arguments) collectionWithPath:arguments[@"path"]];
|
||||||
|
}
|
||||||
|
NSDictionary *parameters = arguments[@"parameters"];
|
||||||
|
NSArray *whereConditions = parameters[@"where"];
|
||||||
|
for (id item in whereConditions) {
|
||||||
|
NSArray *condition = item;
|
||||||
|
NSString *fieldName = condition[0];
|
||||||
|
NSString *op = condition[1];
|
||||||
|
id value = condition[2];
|
||||||
|
if ([op isEqualToString:@"=="]) {
|
||||||
|
query = [query queryWhereField:fieldName isEqualTo:value];
|
||||||
|
} else if ([op isEqualToString:@"<"]) {
|
||||||
|
query = [query queryWhereField:fieldName isLessThan:value];
|
||||||
|
} else if ([op isEqualToString:@"<="]) {
|
||||||
|
query = [query queryWhereField:fieldName isLessThanOrEqualTo:value];
|
||||||
|
} else if ([op isEqualToString:@">"]) {
|
||||||
|
query = [query queryWhereField:fieldName isGreaterThan:value];
|
||||||
|
} else if ([op isEqualToString:@">="]) {
|
||||||
|
query = [query queryWhereField:fieldName isGreaterThanOrEqualTo:value];
|
||||||
|
} else if ([op isEqualToString:@"array-contains"]) {
|
||||||
|
query = [query queryWhereField:fieldName arrayContains:value];
|
||||||
|
} else {
|
||||||
|
// Unsupported operator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
id limit = parameters[@"limit"];
|
||||||
|
if (limit) {
|
||||||
|
NSNumber *length = limit;
|
||||||
|
query = [query queryLimitedTo:[length intValue]];
|
||||||
|
}
|
||||||
|
NSArray *orderBy = parameters[@"orderBy"];
|
||||||
|
if (orderBy) {
|
||||||
|
for (NSArray *orderByParameters in orderBy) {
|
||||||
|
NSString *fieldName = orderByParameters[0];
|
||||||
|
NSNumber *descending = orderByParameters[1];
|
||||||
|
query = [query queryOrderedByField:fieldName descending:[descending boolValue]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
id startAt = parameters[@"startAt"];
|
||||||
|
if (startAt) {
|
||||||
|
NSArray *startAtValues = startAt;
|
||||||
|
query = [query queryStartingAtValues:startAtValues];
|
||||||
|
}
|
||||||
|
id startAtDocument = parameters[@"startAtDocument"];
|
||||||
|
if (startAtDocument) {
|
||||||
|
NSArray *orderByParameters = [orderBy lastObject];
|
||||||
|
NSNumber *descending = orderByParameters[1];
|
||||||
|
query = [query queryOrderedByFieldPath:FIRFieldPath.documentID
|
||||||
|
descending:[descending boolValue]];
|
||||||
|
query = [query
|
||||||
|
queryStartingAtValues:getDocumentValues(startAtDocument, orderBy, isCollectionGroup)];
|
||||||
|
}
|
||||||
|
id startAfter = parameters[@"startAfter"];
|
||||||
|
if (startAfter) {
|
||||||
|
NSArray *startAfterValues = startAfter;
|
||||||
|
query = [query queryStartingAfterValues:startAfterValues];
|
||||||
|
}
|
||||||
|
id startAfterDocument = parameters[@"startAfterDocument"];
|
||||||
|
if (startAfterDocument) {
|
||||||
|
NSArray *orderByParameters = [orderBy lastObject];
|
||||||
|
NSNumber *descending = orderByParameters[1];
|
||||||
|
query = [query queryOrderedByFieldPath:FIRFieldPath.documentID
|
||||||
|
descending:[descending boolValue]];
|
||||||
|
query = [query
|
||||||
|
queryStartingAfterValues:getDocumentValues(startAfterDocument, orderBy, isCollectionGroup)];
|
||||||
|
}
|
||||||
|
id endAt = parameters[@"endAt"];
|
||||||
|
if (endAt) {
|
||||||
|
NSArray *endAtValues = endAt;
|
||||||
|
query = [query queryEndingAtValues:endAtValues];
|
||||||
|
}
|
||||||
|
id endAtDocument = parameters[@"endAtDocument"];
|
||||||
|
if (endAtDocument) {
|
||||||
|
NSArray *orderByParameters = [orderBy lastObject];
|
||||||
|
NSNumber *descending = orderByParameters[1];
|
||||||
|
query = [query queryOrderedByFieldPath:FIRFieldPath.documentID
|
||||||
|
descending:[descending boolValue]];
|
||||||
|
query =
|
||||||
|
[query queryEndingAtValues:getDocumentValues(endAtDocument, orderBy, isCollectionGroup)];
|
||||||
|
}
|
||||||
|
id endBefore = parameters[@"endBefore"];
|
||||||
|
if (endBefore) {
|
||||||
|
NSArray *endBeforeValues = endBefore;
|
||||||
|
query = [query queryEndingBeforeValues:endBeforeValues];
|
||||||
|
}
|
||||||
|
id endBeforeDocument = parameters[@"endBeforeDocument"];
|
||||||
|
if (endBeforeDocument) {
|
||||||
|
NSArray *orderByParameters = [orderBy lastObject];
|
||||||
|
NSNumber *descending = orderByParameters[1];
|
||||||
|
query = [query queryOrderedByFieldPath:FIRFieldPath.documentID
|
||||||
|
descending:[descending boolValue]];
|
||||||
|
query = [query
|
||||||
|
queryEndingBeforeValues:getDocumentValues(endBeforeDocument, orderBy, isCollectionGroup)];
|
||||||
|
}
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FIRFirestoreSource getSource(NSDictionary *arguments) {
|
||||||
|
NSString *source = arguments[@"source"];
|
||||||
|
if ([@"server" isEqualToString:source]) {
|
||||||
|
return FIRFirestoreSourceServer;
|
||||||
|
}
|
||||||
|
if ([@"cache" isEqualToString:source]) {
|
||||||
|
return FIRFirestoreSourceCache;
|
||||||
|
}
|
||||||
|
return FIRFirestoreSourceDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSDictionary *parseQuerySnapshot(FIRQuerySnapshot *snapshot) {
|
||||||
|
NSMutableArray *paths = [NSMutableArray array];
|
||||||
|
NSMutableArray *documents = [NSMutableArray array];
|
||||||
|
NSMutableArray *metadatas = [NSMutableArray array];
|
||||||
|
for (FIRDocumentSnapshot *document in snapshot.documents) {
|
||||||
|
[paths addObject:document.reference.path];
|
||||||
|
[documents addObject:document.data];
|
||||||
|
[metadatas addObject:@{
|
||||||
|
@"hasPendingWrites" : @(document.metadata.hasPendingWrites),
|
||||||
|
@"isFromCache" : @(document.metadata.isFromCache),
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
NSMutableArray *documentChanges = [NSMutableArray array];
|
||||||
|
for (FIRDocumentChange *documentChange in snapshot.documentChanges) {
|
||||||
|
NSString *type;
|
||||||
|
switch (documentChange.type) {
|
||||||
|
case FIRDocumentChangeTypeAdded:
|
||||||
|
type = @"DocumentChangeType.added";
|
||||||
|
break;
|
||||||
|
case FIRDocumentChangeTypeModified:
|
||||||
|
type = @"DocumentChangeType.modified";
|
||||||
|
break;
|
||||||
|
case FIRDocumentChangeTypeRemoved:
|
||||||
|
type = @"DocumentChangeType.removed";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
[documentChanges addObject:@{
|
||||||
|
@"type" : type,
|
||||||
|
@"document" : documentChange.document.data,
|
||||||
|
@"path" : documentChange.document.reference.path,
|
||||||
|
@"oldIndex" : [NSNumber numberWithUnsignedInteger:documentChange.oldIndex],
|
||||||
|
@"newIndex" : [NSNumber numberWithUnsignedInteger:documentChange.newIndex],
|
||||||
|
@"metadata" : @{
|
||||||
|
@"hasPendingWrites" : @(documentChange.document.metadata.hasPendingWrites),
|
||||||
|
@"isFromCache" : @(documentChange.document.metadata.isFromCache),
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
return @{
|
||||||
|
@"paths" : paths,
|
||||||
|
@"documentChanges" : documentChanges,
|
||||||
|
@"documents" : documents,
|
||||||
|
@"metadatas" : metadatas,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const UInt8 DATE_TIME = 128;
|
||||||
|
const UInt8 GEO_POINT = 129;
|
||||||
|
const UInt8 DOCUMENT_REFERENCE = 130;
|
||||||
|
const UInt8 BLOB = 131;
|
||||||
|
const UInt8 ARRAY_UNION = 132;
|
||||||
|
const UInt8 ARRAY_REMOVE = 133;
|
||||||
|
const UInt8 DELETE = 134;
|
||||||
|
const UInt8 SERVER_TIMESTAMP = 135;
|
||||||
|
const UInt8 TIMESTAMP = 136;
|
||||||
|
const UInt8 INCREMENT_DOUBLE = 137;
|
||||||
|
const UInt8 INCREMENT_INTEGER = 138;
|
||||||
|
|
||||||
|
@interface FirestoreWriter : FlutterStandardWriter
|
||||||
|
- (void)writeValue:(id)value;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FirestoreWriter : FlutterStandardWriter
|
||||||
|
- (void)writeValue:(id)value {
|
||||||
|
if ([value isKindOfClass:[NSDate class]]) {
|
||||||
|
[self writeByte:DATE_TIME];
|
||||||
|
NSDate *date = value;
|
||||||
|
NSTimeInterval time = date.timeIntervalSince1970;
|
||||||
|
SInt64 ms = (SInt64)(time * 1000.0);
|
||||||
|
[self writeBytes:&ms length:8];
|
||||||
|
} else if ([value isKindOfClass:[FIRTimestamp class]]) {
|
||||||
|
FIRTimestamp *timestamp = value;
|
||||||
|
SInt64 seconds = timestamp.seconds;
|
||||||
|
int nanoseconds = timestamp.nanoseconds;
|
||||||
|
[self writeByte:TIMESTAMP];
|
||||||
|
[self writeBytes:(UInt8 *)&seconds length:8];
|
||||||
|
[self writeBytes:(UInt8 *)&nanoseconds length:4];
|
||||||
|
} else if ([value isKindOfClass:[FIRGeoPoint class]]) {
|
||||||
|
FIRGeoPoint *geoPoint = value;
|
||||||
|
Float64 latitude = geoPoint.latitude;
|
||||||
|
Float64 longitude = geoPoint.longitude;
|
||||||
|
[self writeByte:GEO_POINT];
|
||||||
|
[self writeAlignment:8];
|
||||||
|
[self writeBytes:(UInt8 *)&latitude length:8];
|
||||||
|
[self writeBytes:(UInt8 *)&longitude length:8];
|
||||||
|
} else if ([value isKindOfClass:[FIRDocumentReference class]]) {
|
||||||
|
FIRDocumentReference *document = value;
|
||||||
|
NSString *documentPath = [document path];
|
||||||
|
[self writeByte:DOCUMENT_REFERENCE];
|
||||||
|
[self writeUTF8:document.firestore.app.name];
|
||||||
|
[self writeUTF8:documentPath];
|
||||||
|
} else if ([value isKindOfClass:[NSData class]]) {
|
||||||
|
NSData *blob = value;
|
||||||
|
[self writeByte:BLOB];
|
||||||
|
[self writeSize:(UInt32)blob.length];
|
||||||
|
[self writeData:blob];
|
||||||
|
} else {
|
||||||
|
[super writeValue:value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface FirestoreReader : FlutterStandardReader
|
||||||
|
- (id)readValueOfType:(UInt8)type;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FirestoreReader
|
||||||
|
- (id)readValueOfType:(UInt8)type {
|
||||||
|
switch (type) {
|
||||||
|
case DATE_TIME: {
|
||||||
|
SInt64 value;
|
||||||
|
[self readBytes:&value length:8];
|
||||||
|
return [NSDate dateWithTimeIntervalSince1970:(value / 1000.0)];
|
||||||
|
}
|
||||||
|
case TIMESTAMP: {
|
||||||
|
SInt64 seconds;
|
||||||
|
int nanoseconds;
|
||||||
|
[self readBytes:&seconds length:8];
|
||||||
|
[self readBytes:&nanoseconds length:4];
|
||||||
|
return [[FIRTimestamp alloc] initWithSeconds:seconds nanoseconds:nanoseconds];
|
||||||
|
}
|
||||||
|
case GEO_POINT: {
|
||||||
|
Float64 latitude;
|
||||||
|
Float64 longitude;
|
||||||
|
[self readAlignment:8];
|
||||||
|
[self readBytes:&latitude length:8];
|
||||||
|
[self readBytes:&longitude length:8];
|
||||||
|
return [[FIRGeoPoint alloc] initWithLatitude:latitude longitude:longitude];
|
||||||
|
}
|
||||||
|
case DOCUMENT_REFERENCE: {
|
||||||
|
NSString *appName = [self readUTF8];
|
||||||
|
FIRFirestore *firestore = [FIRFirestore firestoreForApp:[FIRApp appNamed:appName]];
|
||||||
|
NSString *documentPath = [self readUTF8];
|
||||||
|
return [firestore documentWithPath:documentPath];
|
||||||
|
}
|
||||||
|
case BLOB: {
|
||||||
|
UInt32 elementCount = [self readSize];
|
||||||
|
return [self readData:elementCount];
|
||||||
|
}
|
||||||
|
case ARRAY_UNION: {
|
||||||
|
return [FIRFieldValue fieldValueForArrayUnion:[self readValue]];
|
||||||
|
}
|
||||||
|
case ARRAY_REMOVE: {
|
||||||
|
return [FIRFieldValue fieldValueForArrayRemove:[self readValue]];
|
||||||
|
}
|
||||||
|
case DELETE: {
|
||||||
|
return [FIRFieldValue fieldValueForDelete];
|
||||||
|
}
|
||||||
|
case SERVER_TIMESTAMP: {
|
||||||
|
return [FIRFieldValue fieldValueForServerTimestamp];
|
||||||
|
}
|
||||||
|
case INCREMENT_DOUBLE: {
|
||||||
|
NSNumber *value = [self readValue];
|
||||||
|
return [FIRFieldValue fieldValueForDoubleIncrement:value.doubleValue];
|
||||||
|
}
|
||||||
|
case INCREMENT_INTEGER: {
|
||||||
|
NSNumber *value = [self readValue];
|
||||||
|
return [FIRFieldValue fieldValueForIntegerIncrement:value.intValue];
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return [super readValueOfType:type];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface FirestoreReaderWriter : FlutterStandardReaderWriter
|
||||||
|
- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data;
|
||||||
|
- (FlutterStandardReader *)readerWithData:(NSData *)data;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FirestoreReaderWriter
|
||||||
|
- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data {
|
||||||
|
return [[FirestoreWriter alloc] initWithData:data];
|
||||||
|
}
|
||||||
|
- (FlutterStandardReader *)readerWithData:(NSData *)data {
|
||||||
|
return [[FirestoreReader alloc] initWithData:data];
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface FLTCloudFirestorePlugin ()
|
||||||
|
@property(nonatomic, retain) FlutterMethodChannel *channel;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FLTCloudFirestorePlugin {
|
||||||
|
NSMutableDictionary<NSNumber *, id<FIRListenerRegistration>> *_listeners;
|
||||||
|
int _nextListenerHandle;
|
||||||
|
NSMutableDictionary *transactions;
|
||||||
|
NSMutableDictionary *transactionResults;
|
||||||
|
NSMutableDictionary<NSNumber *, FIRWriteBatch *> *_batches;
|
||||||
|
int _nextBatchHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
|
||||||
|
FirestoreReaderWriter *firestoreReaderWriter = [FirestoreReaderWriter new];
|
||||||
|
FlutterMethodChannel *channel =
|
||||||
|
[FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/cloud_firestore"
|
||||||
|
binaryMessenger:[registrar messenger]
|
||||||
|
codec:[FlutterStandardMethodCodec
|
||||||
|
codecWithReaderWriter:firestoreReaderWriter]];
|
||||||
|
FLTCloudFirestorePlugin *instance = [[FLTCloudFirestorePlugin alloc] init];
|
||||||
|
instance.channel = channel;
|
||||||
|
[registrar addMethodCallDelegate:instance channel:channel];
|
||||||
|
|
||||||
|
SEL sel = NSSelectorFromString(@"registerLibrary:withVersion:");
|
||||||
|
if ([FIRApp respondsToSelector:sel]) {
|
||||||
|
[FIRApp performSelector:sel withObject:LIBRARY_NAME withObject:LIBRARY_VERSION];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
if (![FIRApp appNamed:@"__FIRAPP_DEFAULT"]) {
|
||||||
|
NSLog(@"Configuring the default Firebase app...");
|
||||||
|
[FIRApp configure];
|
||||||
|
NSLog(@"Configured the default Firebase app %@.", [FIRApp defaultApp].name);
|
||||||
|
}
|
||||||
|
_listeners = [NSMutableDictionary<NSNumber *, id<FIRListenerRegistration>> dictionary];
|
||||||
|
_batches = [NSMutableDictionary<NSNumber *, FIRWriteBatch *> dictionary];
|
||||||
|
_nextListenerHandle = 0;
|
||||||
|
_nextBatchHandle = 0;
|
||||||
|
transactions = [NSMutableDictionary<NSNumber *, FIRTransaction *> dictionary];
|
||||||
|
transactionResults = [NSMutableDictionary<NSNumber *, id> dictionary];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
|
||||||
|
__weak __typeof__(self) weakSelf = self;
|
||||||
|
void (^defaultCompletionBlock)(NSError *) = ^(NSError *error) {
|
||||||
|
result(getFlutterError(error));
|
||||||
|
};
|
||||||
|
if ([@"Firestore#runTransaction" isEqualToString:call.method]) {
|
||||||
|
[getFirestore(call.arguments)
|
||||||
|
runTransactionWithBlock:^id(FIRTransaction *transaction, NSError **pError) {
|
||||||
|
NSNumber *transactionId = call.arguments[@"transactionId"];
|
||||||
|
NSNumber *transactionTimeout = call.arguments[@"transactionTimeout"];
|
||||||
|
|
||||||
|
self->transactions[transactionId] = transaction;
|
||||||
|
|
||||||
|
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||||
|
|
||||||
|
[weakSelf.channel invokeMethod:@"DoTransaction"
|
||||||
|
arguments:call.arguments
|
||||||
|
result:^(id doTransactionResult) {
|
||||||
|
FLTCloudFirestorePlugin *currentSelf = weakSelf;
|
||||||
|
currentSelf->transactionResults[transactionId] =
|
||||||
|
doTransactionResult;
|
||||||
|
dispatch_semaphore_signal(semaphore);
|
||||||
|
}];
|
||||||
|
|
||||||
|
dispatch_semaphore_wait(
|
||||||
|
semaphore,
|
||||||
|
dispatch_time(DISPATCH_TIME_NOW, [transactionTimeout integerValue] * 1000000));
|
||||||
|
|
||||||
|
return self->transactionResults[transactionId];
|
||||||
|
}
|
||||||
|
completion:^(id transactionResult, NSError *error) {
|
||||||
|
if (error != nil) {
|
||||||
|
result([FlutterError errorWithCode:[NSString stringWithFormat:@"%ld", error.code]
|
||||||
|
message:error.localizedDescription
|
||||||
|
details:nil]);
|
||||||
|
}
|
||||||
|
result(transactionResult);
|
||||||
|
}];
|
||||||
|
} else if ([@"Transaction#get" isEqualToString:call.method]) {
|
||||||
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
|
NSNumber *transactionId = call.arguments[@"transactionId"];
|
||||||
|
FIRDocumentReference *document = getDocumentReference(call.arguments);
|
||||||
|
FIRTransaction *transaction = self->transactions[transactionId];
|
||||||
|
NSError *error = [[NSError alloc] init];
|
||||||
|
|
||||||
|
FIRDocumentSnapshot *snapshot = [transaction getDocument:document error:&error];
|
||||||
|
|
||||||
|
if (error != nil) {
|
||||||
|
result([FlutterError errorWithCode:[NSString stringWithFormat:@"%tu", [error code]]
|
||||||
|
message:[error localizedDescription]
|
||||||
|
details:nil]);
|
||||||
|
} else if (snapshot != nil) {
|
||||||
|
result(@{
|
||||||
|
@"path" : snapshot.reference.path,
|
||||||
|
@"data" : snapshot.exists ? snapshot.data : [NSNull null],
|
||||||
|
@"metadata" : @{
|
||||||
|
@"hasPendingWrites" : @(snapshot.metadata.hasPendingWrites),
|
||||||
|
@"isFromCache" : @(snapshot.metadata.isFromCache),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result(nil);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if ([@"Transaction#update" isEqualToString:call.method]) {
|
||||||
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
|
NSNumber *transactionId = call.arguments[@"transactionId"];
|
||||||
|
FIRDocumentReference *document = getDocumentReference(call.arguments);
|
||||||
|
FIRTransaction *transaction = self->transactions[transactionId];
|
||||||
|
|
||||||
|
[transaction updateData:call.arguments[@"data"] forDocument:document];
|
||||||
|
result(nil);
|
||||||
|
});
|
||||||
|
} else if ([@"Transaction#set" isEqualToString:call.method]) {
|
||||||
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
|
NSNumber *transactionId = call.arguments[@"transactionId"];
|
||||||
|
FIRDocumentReference *document = getDocumentReference(call.arguments);
|
||||||
|
FIRTransaction *transaction = self->transactions[transactionId];
|
||||||
|
|
||||||
|
[transaction setData:call.arguments[@"data"] forDocument:document];
|
||||||
|
result(nil);
|
||||||
|
});
|
||||||
|
} else if ([@"Transaction#delete" isEqualToString:call.method]) {
|
||||||
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
|
NSNumber *transactionId = call.arguments[@"transactionId"];
|
||||||
|
FIRDocumentReference *document = getDocumentReference(call.arguments);
|
||||||
|
FIRTransaction *transaction = self->transactions[transactionId];
|
||||||
|
|
||||||
|
[transaction deleteDocument:document];
|
||||||
|
result(nil);
|
||||||
|
});
|
||||||
|
} else if ([@"DocumentReference#setData" isEqualToString:call.method]) {
|
||||||
|
NSDictionary *options = call.arguments[@"options"];
|
||||||
|
FIRDocumentReference *document = getDocumentReference(call.arguments);
|
||||||
|
if (![options isEqual:[NSNull null]] &&
|
||||||
|
[options[@"merge"] isEqual:[NSNumber numberWithBool:YES]]) {
|
||||||
|
[document setData:call.arguments[@"data"] merge:YES completion:defaultCompletionBlock];
|
||||||
|
} else {
|
||||||
|
[document setData:call.arguments[@"data"] completion:defaultCompletionBlock];
|
||||||
|
}
|
||||||
|
} else if ([@"DocumentReference#updateData" isEqualToString:call.method]) {
|
||||||
|
FIRDocumentReference *document = getDocumentReference(call.arguments);
|
||||||
|
[document updateData:call.arguments[@"data"] completion:defaultCompletionBlock];
|
||||||
|
} else if ([@"DocumentReference#delete" isEqualToString:call.method]) {
|
||||||
|
FIRDocumentReference *document = getDocumentReference(call.arguments);
|
||||||
|
[document deleteDocumentWithCompletion:defaultCompletionBlock];
|
||||||
|
} else if ([@"DocumentReference#get" isEqualToString:call.method]) {
|
||||||
|
FIRDocumentReference *document = getDocumentReference(call.arguments);
|
||||||
|
FIRFirestoreSource source = getSource(call.arguments);
|
||||||
|
[document
|
||||||
|
getDocumentWithSource:source
|
||||||
|
completion:^(FIRDocumentSnapshot *_Nullable snapshot, NSError *_Nullable error) {
|
||||||
|
if (snapshot == nil) {
|
||||||
|
result(getFlutterError(error));
|
||||||
|
} else {
|
||||||
|
result(@{
|
||||||
|
@"path" : snapshot.reference.path,
|
||||||
|
@"data" : snapshot.exists ? snapshot.data : [NSNull null],
|
||||||
|
@"metadata" : @{
|
||||||
|
@"hasPendingWrites" : @(snapshot.metadata.hasPendingWrites),
|
||||||
|
@"isFromCache" : @(snapshot.metadata.isFromCache),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
} else if ([@"Query#addSnapshotListener" isEqualToString:call.method]) {
|
||||||
|
__block NSNumber *handle = [NSNumber numberWithInt:_nextListenerHandle++];
|
||||||
|
FIRQuery *query;
|
||||||
|
@try {
|
||||||
|
query = getQuery(call.arguments);
|
||||||
|
} @catch (NSException *exception) {
|
||||||
|
result([FlutterError errorWithCode:@"invalid_query"
|
||||||
|
message:[exception name]
|
||||||
|
details:[exception reason]]);
|
||||||
|
}
|
||||||
|
id<FIRListenerRegistration> listener = [query
|
||||||
|
addSnapshotListener:^(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error) {
|
||||||
|
if (snapshot == nil) {
|
||||||
|
result(getFlutterError(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NSMutableDictionary *arguments = [parseQuerySnapshot(snapshot) mutableCopy];
|
||||||
|
[arguments setObject:handle forKey:@"handle"];
|
||||||
|
[weakSelf.channel invokeMethod:@"QuerySnapshot" arguments:arguments];
|
||||||
|
}];
|
||||||
|
_listeners[handle] = listener;
|
||||||
|
result(handle);
|
||||||
|
} else if ([@"Query#addDocumentListener" isEqualToString:call.method]) {
|
||||||
|
__block NSNumber *handle = [NSNumber numberWithInt:_nextListenerHandle++];
|
||||||
|
FIRDocumentReference *document = getDocumentReference(call.arguments);
|
||||||
|
id<FIRListenerRegistration> listener =
|
||||||
|
[document addSnapshotListener:^(FIRDocumentSnapshot *snapshot, NSError *_Nullable error) {
|
||||||
|
if (snapshot == nil) {
|
||||||
|
result(getFlutterError(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[weakSelf.channel invokeMethod:@"DocumentSnapshot"
|
||||||
|
arguments:@{
|
||||||
|
@"handle" : handle,
|
||||||
|
@"path" : snapshot ? snapshot.reference.path : [NSNull null],
|
||||||
|
@"data" : snapshot.exists ? snapshot.data : [NSNull null],
|
||||||
|
@"metadata" : snapshot ? @{
|
||||||
|
@"hasPendingWrites" : @(snapshot.metadata.hasPendingWrites),
|
||||||
|
@"isFromCache" : @(snapshot.metadata.isFromCache),
|
||||||
|
}
|
||||||
|
: [NSNull null],
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
_listeners[handle] = listener;
|
||||||
|
result(handle);
|
||||||
|
} else if ([@"Query#getDocuments" isEqualToString:call.method]) {
|
||||||
|
FIRQuery *query;
|
||||||
|
FIRFirestoreSource source = getSource(call.arguments);
|
||||||
|
@try {
|
||||||
|
query = getQuery(call.arguments);
|
||||||
|
} @catch (NSException *exception) {
|
||||||
|
result([FlutterError errorWithCode:@"invalid_query"
|
||||||
|
message:[exception name]
|
||||||
|
details:[exception reason]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[query
|
||||||
|
getDocumentsWithSource:source
|
||||||
|
completion:^(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error) {
|
||||||
|
if (snapshot == nil) {
|
||||||
|
result(getFlutterError(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result(parseQuerySnapshot(snapshot));
|
||||||
|
}];
|
||||||
|
} else if ([@"Query#removeListener" isEqualToString:call.method]) {
|
||||||
|
NSNumber *handle = call.arguments[@"handle"];
|
||||||
|
[[_listeners objectForKey:handle] remove];
|
||||||
|
[_listeners removeObjectForKey:handle];
|
||||||
|
result(nil);
|
||||||
|
} else if ([@"WriteBatch#create" isEqualToString:call.method]) {
|
||||||
|
__block NSNumber *handle = [NSNumber numberWithInt:_nextBatchHandle++];
|
||||||
|
FIRWriteBatch *batch = [getFirestore(call.arguments) batch];
|
||||||
|
_batches[handle] = batch;
|
||||||
|
result(handle);
|
||||||
|
} else if ([@"WriteBatch#setData" isEqualToString:call.method]) {
|
||||||
|
NSNumber *handle = call.arguments[@"handle"];
|
||||||
|
NSDictionary *options = call.arguments[@"options"];
|
||||||
|
FIRDocumentReference *document = getDocumentReference(call.arguments);
|
||||||
|
FIRWriteBatch *batch = [_batches objectForKey:handle];
|
||||||
|
if (![options isEqual:[NSNull null]] &&
|
||||||
|
[options[@"merge"] isEqual:[NSNumber numberWithBool:YES]]) {
|
||||||
|
[batch setData:call.arguments[@"data"] forDocument:document merge:YES];
|
||||||
|
} else {
|
||||||
|
[batch setData:call.arguments[@"data"] forDocument:document];
|
||||||
|
}
|
||||||
|
result(nil);
|
||||||
|
} else if ([@"WriteBatch#updateData" isEqualToString:call.method]) {
|
||||||
|
NSNumber *handle = call.arguments[@"handle"];
|
||||||
|
FIRDocumentReference *document = getDocumentReference(call.arguments);
|
||||||
|
FIRWriteBatch *batch = [_batches objectForKey:handle];
|
||||||
|
[batch updateData:call.arguments[@"data"] forDocument:document];
|
||||||
|
result(nil);
|
||||||
|
} else if ([@"WriteBatch#delete" isEqualToString:call.method]) {
|
||||||
|
NSNumber *handle = call.arguments[@"handle"];
|
||||||
|
FIRDocumentReference *document = getDocumentReference(call.arguments);
|
||||||
|
FIRWriteBatch *batch = [_batches objectForKey:handle];
|
||||||
|
[batch deleteDocument:document];
|
||||||
|
result(nil);
|
||||||
|
} else if ([@"WriteBatch#commit" isEqualToString:call.method]) {
|
||||||
|
NSNumber *handle = call.arguments[@"handle"];
|
||||||
|
FIRWriteBatch *batch = [_batches objectForKey:handle];
|
||||||
|
[batch commitWithCompletion:defaultCompletionBlock];
|
||||||
|
[_batches removeObjectForKey:handle];
|
||||||
|
} else if ([@"Firestore#enablePersistence" isEqualToString:call.method]) {
|
||||||
|
bool enable = (bool)call.arguments[@"enable"];
|
||||||
|
FIRFirestoreSettings *settings = [[FIRFirestoreSettings alloc] init];
|
||||||
|
settings.persistenceEnabled = enable;
|
||||||
|
FIRFirestore *db = getFirestore(call.arguments);
|
||||||
|
db.settings = settings;
|
||||||
|
result(nil);
|
||||||
|
} else if ([@"Firestore#settings" isEqualToString:call.method]) {
|
||||||
|
FIRFirestoreSettings *settings = [[FIRFirestoreSettings alloc] init];
|
||||||
|
if (![call.arguments[@"persistenceEnabled"] isEqual:[NSNull null]]) {
|
||||||
|
settings.persistenceEnabled = (bool)call.arguments[@"persistenceEnabled"];
|
||||||
|
}
|
||||||
|
if (![call.arguments[@"host"] isEqual:[NSNull null]]) {
|
||||||
|
settings.host = (NSString *)call.arguments[@"host"];
|
||||||
|
}
|
||||||
|
if (![call.arguments[@"sslEnabled"] isEqual:[NSNull null]]) {
|
||||||
|
settings.sslEnabled = (bool)call.arguments[@"sslEnabled"];
|
||||||
|
}
|
||||||
|
if (![call.arguments[@"timestampsInSnapshotsEnabled"] isEqual:[NSNull null]]) {
|
||||||
|
settings.timestampsInSnapshotsEnabled = (bool)call.arguments[@"timestampsInSnapshotsEnabled"];
|
||||||
|
}
|
||||||
|
if (![call.arguments[@"cacheSizeBytes"] isEqual:[NSNull null]]) {
|
||||||
|
settings.cacheSizeBytes = ((NSNumber *)call.arguments[@"cacheSizeBytes"]).intValue;
|
||||||
|
}
|
||||||
|
FIRFirestore *db = getFirestore(call.arguments);
|
||||||
|
db.settings = settings;
|
||||||
|
result(nil);
|
||||||
|
} else {
|
||||||
|
result(FlutterMethodNotImplemented);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,22 @@
|
||||||
|
#
|
||||||
|
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
|
||||||
|
#
|
||||||
|
Pod::Spec.new do |s|
|
||||||
|
s.name = 'cloud_firestore'
|
||||||
|
s.version = '0.0.1'
|
||||||
|
s.summary = 'Firestore plugin for Flutter.'
|
||||||
|
s.description = <<-DESC
|
||||||
|
Firestore plugin for Flutter.
|
||||||
|
DESC
|
||||||
|
s.homepage = 'https://github.com/flutter/firestore'
|
||||||
|
s.license = { :file => '../LICENSE' }
|
||||||
|
s.author = { 'Flutter Team' => 'flutter-dev@googlegroups.com' }
|
||||||
|
s.source = { :path => '.' }
|
||||||
|
s.source_files = 'Classes/**/*'
|
||||||
|
s.public_header_files = 'Classes/**/*.h'
|
||||||
|
s.ios.deployment_target = '8.0'
|
||||||
|
s.dependency 'Flutter'
|
||||||
|
s.dependency 'Firebase/Core'
|
||||||
|
s.dependency 'Firebase/Firestore', '~> 6.0'
|
||||||
|
s.static_framework = true
|
||||||
|
end
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
library cloud_firestore;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'dart:ui' show hashValues, hashList;
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
|
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
|
import 'src/utils/push_id_generator.dart';
|
||||||
|
|
||||||
|
part 'src/blob.dart';
|
||||||
|
part 'src/collection_reference.dart';
|
||||||
|
part 'src/document_change.dart';
|
||||||
|
part 'src/document_reference.dart';
|
||||||
|
part 'src/document_snapshot.dart';
|
||||||
|
part 'src/field_value.dart';
|
||||||
|
part 'src/firestore.dart';
|
||||||
|
part 'src/firestore_message_codec.dart';
|
||||||
|
part 'src/geo_point.dart';
|
||||||
|
part 'src/query.dart';
|
||||||
|
part 'src/query_snapshot.dart';
|
||||||
|
part 'src/snapshot_metadata.dart';
|
||||||
|
part 'src/timestamp.dart';
|
||||||
|
part 'src/transaction.dart';
|
||||||
|
part 'src/write_batch.dart';
|
||||||
|
part 'src/source.dart';
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright 2018, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
class Blob {
|
||||||
|
const Blob(this.bytes);
|
||||||
|
|
||||||
|
final Uint8List bytes;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) =>
|
||||||
|
other is Blob &&
|
||||||
|
const DeepCollectionEquality().equals(other.bytes, bytes);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => hashList(bytes);
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
/// A CollectionReference object can be used for adding documents, getting
|
||||||
|
/// document references, and querying for documents (using the methods
|
||||||
|
/// inherited from [Query]).
|
||||||
|
class CollectionReference extends Query {
|
||||||
|
CollectionReference._(Firestore firestore, List<String> pathComponents)
|
||||||
|
: super._(firestore: firestore, pathComponents: pathComponents);
|
||||||
|
|
||||||
|
/// ID of the referenced collection.
|
||||||
|
String get id => _pathComponents.isEmpty ? null : _pathComponents.last;
|
||||||
|
|
||||||
|
/// For subcollections, parent returns the containing [DocumentReference].
|
||||||
|
///
|
||||||
|
/// For root collections, null is returned.
|
||||||
|
DocumentReference parent() {
|
||||||
|
if (_pathComponents.length < 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DocumentReference._(
|
||||||
|
firestore,
|
||||||
|
(List<String>.from(_pathComponents)..removeLast()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A string containing the slash-separated path to this CollectionReference
|
||||||
|
/// (relative to the root of the database).
|
||||||
|
String get path => _path;
|
||||||
|
|
||||||
|
/// Returns a `DocumentReference` with the provided path.
|
||||||
|
///
|
||||||
|
/// If no [path] is provided, an auto-generated ID is used.
|
||||||
|
///
|
||||||
|
/// The unique key generated is prefixed with a client-generated timestamp
|
||||||
|
/// so that the resulting list will be chronologically-sorted.
|
||||||
|
DocumentReference document([String path]) {
|
||||||
|
List<String> childPath;
|
||||||
|
if (path == null) {
|
||||||
|
final String key = PushIdGenerator.generatePushChildName();
|
||||||
|
childPath = List<String>.from(_pathComponents)..add(key);
|
||||||
|
} else {
|
||||||
|
childPath = List<String>.from(_pathComponents)..addAll(path.split(('/')));
|
||||||
|
}
|
||||||
|
return DocumentReference._(firestore, childPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `DocumentReference` with an auto-generated ID, after
|
||||||
|
/// populating it with provided [data].
|
||||||
|
///
|
||||||
|
/// The unique key generated is prefixed with a client-generated timestamp
|
||||||
|
/// so that the resulting list will be chronologically-sorted.
|
||||||
|
Future<DocumentReference> add(Map<String, dynamic> data) async {
|
||||||
|
final DocumentReference newDocument = document();
|
||||||
|
await newDocument.setData(data);
|
||||||
|
return newDocument;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
/// An enumeration of document change types.
|
||||||
|
enum DocumentChangeType {
|
||||||
|
/// Indicates a new document was added to the set of documents matching the
|
||||||
|
/// query.
|
||||||
|
added,
|
||||||
|
|
||||||
|
/// Indicates a document within the query was modified.
|
||||||
|
modified,
|
||||||
|
|
||||||
|
/// Indicates a document within the query was removed (either deleted or no
|
||||||
|
/// longer matches the query.
|
||||||
|
removed,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A DocumentChange represents a change to the documents matching a query.
|
||||||
|
///
|
||||||
|
/// It contains the document affected and the type of change that occurred
|
||||||
|
/// (added, modified, or removed).
|
||||||
|
class DocumentChange {
|
||||||
|
DocumentChange._(Map<dynamic, dynamic> data, this._firestore)
|
||||||
|
: oldIndex = data['oldIndex'],
|
||||||
|
newIndex = data['newIndex'],
|
||||||
|
document = DocumentSnapshot._(
|
||||||
|
data['path'],
|
||||||
|
_asStringKeyedMap(data['document']),
|
||||||
|
SnapshotMetadata._(data["metadata"]["hasPendingWrites"],
|
||||||
|
data["metadata"]["isFromCache"]),
|
||||||
|
_firestore,
|
||||||
|
),
|
||||||
|
type = DocumentChangeType.values.firstWhere((DocumentChangeType type) {
|
||||||
|
return type.toString() == data['type'];
|
||||||
|
});
|
||||||
|
|
||||||
|
final Firestore _firestore;
|
||||||
|
|
||||||
|
/// The type of change that occurred (added, modified, or removed).
|
||||||
|
final DocumentChangeType type;
|
||||||
|
|
||||||
|
/// The index of the changed document in the result set immediately prior to
|
||||||
|
/// this [DocumentChange] (i.e. supposing that all prior DocumentChange objects
|
||||||
|
/// have been applied).
|
||||||
|
///
|
||||||
|
/// -1 for [DocumentChangeType.added] events.
|
||||||
|
final int oldIndex;
|
||||||
|
|
||||||
|
/// The index of the changed document in the result set immediately after this
|
||||||
|
/// DocumentChange (i.e. supposing that all prior [DocumentChange] objects
|
||||||
|
/// and the current [DocumentChange] object have been applied).
|
||||||
|
///
|
||||||
|
/// -1 for [DocumentChangeType.removed] events.
|
||||||
|
final int newIndex;
|
||||||
|
|
||||||
|
/// The document affected by this change.
|
||||||
|
final DocumentSnapshot document;
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
/// A [DocumentReference] refers to a document location in a Firestore database
|
||||||
|
/// and can be used to write, read, or listen to the location.
|
||||||
|
///
|
||||||
|
/// The document at the referenced location may or may not exist.
|
||||||
|
/// A [DocumentReference] can also be used to create a [CollectionReference]
|
||||||
|
/// to a subcollection.
|
||||||
|
class DocumentReference {
|
||||||
|
DocumentReference._(this.firestore, List<String> pathComponents)
|
||||||
|
: _pathComponents = pathComponents,
|
||||||
|
assert(firestore != null);
|
||||||
|
|
||||||
|
/// The Firestore instance associated with this document reference
|
||||||
|
final Firestore firestore;
|
||||||
|
|
||||||
|
final List<String> _pathComponents;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic o) =>
|
||||||
|
o is DocumentReference && o.firestore == firestore && o.path == path;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => hashList(_pathComponents);
|
||||||
|
|
||||||
|
/// Parent returns the containing [CollectionReference].
|
||||||
|
CollectionReference parent() {
|
||||||
|
return CollectionReference._(
|
||||||
|
firestore,
|
||||||
|
(List<String>.from(_pathComponents)..removeLast()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Slash-delimited path representing the database location of this query.
|
||||||
|
String get path => _pathComponents.join('/');
|
||||||
|
|
||||||
|
/// This document's given or generated ID in the collection.
|
||||||
|
String get documentID => _pathComponents.last;
|
||||||
|
|
||||||
|
/// Writes to the document referred to by this [DocumentReference].
|
||||||
|
///
|
||||||
|
/// If the document does not yet exist, it will be created.
|
||||||
|
///
|
||||||
|
/// If [merge] is true, the provided data will be merged into an
|
||||||
|
/// existing document instead of overwriting.
|
||||||
|
Future<void> setData(Map<String, dynamic> data, {bool merge = false}) {
|
||||||
|
return Firestore.channel.invokeMethod<void>(
|
||||||
|
'DocumentReference#setData',
|
||||||
|
<String, dynamic>{
|
||||||
|
'app': firestore.app.name,
|
||||||
|
'path': path,
|
||||||
|
'data': data,
|
||||||
|
'options': <String, bool>{'merge': merge},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates fields in the document referred to by this [DocumentReference].
|
||||||
|
///
|
||||||
|
/// Values in [data] may be of any supported Firestore type as well as
|
||||||
|
/// special sentinel [FieldValue] type.
|
||||||
|
///
|
||||||
|
/// If no document exists yet, the update will fail.
|
||||||
|
Future<void> updateData(Map<String, dynamic> data) {
|
||||||
|
return Firestore.channel.invokeMethod<void>(
|
||||||
|
'DocumentReference#updateData',
|
||||||
|
<String, dynamic>{
|
||||||
|
'app': firestore.app.name,
|
||||||
|
'path': path,
|
||||||
|
'data': data,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the document referenced by this [DocumentReference].
|
||||||
|
///
|
||||||
|
/// If no document exists, the read will return null.
|
||||||
|
Future<DocumentSnapshot> get({Source source = Source.serverAndCache}) async {
|
||||||
|
final Map<String, dynamic> data =
|
||||||
|
await Firestore.channel.invokeMapMethod<String, dynamic>(
|
||||||
|
'DocumentReference#get',
|
||||||
|
<String, dynamic>{
|
||||||
|
'app': firestore.app.name,
|
||||||
|
'path': path,
|
||||||
|
'source': _getSourceString(source),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return DocumentSnapshot._(
|
||||||
|
data['path'],
|
||||||
|
_asStringKeyedMap(data['data']),
|
||||||
|
SnapshotMetadata._(data['metadata']['hasPendingWrites'],
|
||||||
|
data['metadata']['isFromCache']),
|
||||||
|
firestore,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes the document referred to by this [DocumentReference].
|
||||||
|
Future<void> delete() {
|
||||||
|
return Firestore.channel.invokeMethod<void>(
|
||||||
|
'DocumentReference#delete',
|
||||||
|
<String, dynamic>{'app': firestore.app.name, 'path': path},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the reference of a collection contained inside of this
|
||||||
|
/// document.
|
||||||
|
CollectionReference collection(String collectionPath) {
|
||||||
|
return firestore.collection(
|
||||||
|
<String>[path, collectionPath].join('/'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notifies of documents at this location
|
||||||
|
// TODO(jackson): Reduce code duplication with [Query]
|
||||||
|
Stream<DocumentSnapshot> snapshots() {
|
||||||
|
Future<int> _handle;
|
||||||
|
// It's fine to let the StreamController be garbage collected once all the
|
||||||
|
// subscribers have cancelled; this analyzer warning is safe to ignore.
|
||||||
|
StreamController<DocumentSnapshot> controller; // ignore: close_sinks
|
||||||
|
controller = StreamController<DocumentSnapshot>.broadcast(
|
||||||
|
onListen: () {
|
||||||
|
_handle = Firestore.channel.invokeMethod<int>(
|
||||||
|
'Query#addDocumentListener',
|
||||||
|
<String, dynamic>{
|
||||||
|
'app': firestore.app.name,
|
||||||
|
'path': path,
|
||||||
|
},
|
||||||
|
).then<int>((dynamic result) => result);
|
||||||
|
_handle.then((int handle) {
|
||||||
|
Firestore._documentObservers[handle] = controller;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onCancel: () {
|
||||||
|
_handle.then((int handle) async {
|
||||||
|
await Firestore.channel.invokeMethod<void>(
|
||||||
|
'Query#removeListener',
|
||||||
|
<String, dynamic>{'handle': handle},
|
||||||
|
);
|
||||||
|
Firestore._documentObservers.remove(handle);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return controller.stream;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
/// A DocumentSnapshot contains data read from a document in your Firestore
|
||||||
|
/// database.
|
||||||
|
///
|
||||||
|
/// The data can be extracted with the data property or by using subscript
|
||||||
|
/// syntax to access a specific field.
|
||||||
|
class DocumentSnapshot {
|
||||||
|
DocumentSnapshot._(this._path, this.data, this.metadata, this._firestore);
|
||||||
|
|
||||||
|
final String _path;
|
||||||
|
final Firestore _firestore;
|
||||||
|
|
||||||
|
/// The reference that produced this snapshot
|
||||||
|
DocumentReference get reference => _firestore.document(_path);
|
||||||
|
|
||||||
|
/// Contains all the data of this snapshot
|
||||||
|
final Map<String, dynamic> data;
|
||||||
|
|
||||||
|
/// Metadata about this snapshot concerning its source and if it has local
|
||||||
|
/// modifications.
|
||||||
|
final SnapshotMetadata metadata;
|
||||||
|
|
||||||
|
/// Reads individual values from the snapshot
|
||||||
|
dynamic operator [](String key) => data[key];
|
||||||
|
|
||||||
|
/// Returns the ID of the snapshot's document
|
||||||
|
String get documentID => _path.split('/').last;
|
||||||
|
|
||||||
|
/// Returns `true` if the document exists.
|
||||||
|
bool get exists => data != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _asStringKeyedMap(Map<dynamic, dynamic> map) {
|
||||||
|
if (map == null) return null;
|
||||||
|
if (map is Map<String, dynamic>) {
|
||||||
|
return map;
|
||||||
|
} else {
|
||||||
|
return Map<String, dynamic>.from(map);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
enum FieldValueType {
|
||||||
|
arrayUnion,
|
||||||
|
arrayRemove,
|
||||||
|
delete,
|
||||||
|
serverTimestamp,
|
||||||
|
incrementDouble,
|
||||||
|
incrementInteger,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sentinel values that can be used when writing document fields with set() or
|
||||||
|
/// update().
|
||||||
|
class FieldValue {
|
||||||
|
FieldValue._(this.type, this.value);
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
final FieldValueType type;
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
final dynamic value;
|
||||||
|
|
||||||
|
/// Returns a special value that tells the server to union the given elements
|
||||||
|
/// with any array value that already exists on the server.
|
||||||
|
///
|
||||||
|
/// Each specified element that doesn't already exist in the array will be
|
||||||
|
/// added to the end. If the field being modified is not already an array it
|
||||||
|
/// will be overwritten with an array containing exactly the specified
|
||||||
|
/// elements.
|
||||||
|
static FieldValue arrayUnion(List<dynamic> elements) =>
|
||||||
|
FieldValue._(FieldValueType.arrayUnion, elements);
|
||||||
|
|
||||||
|
/// Returns a special value that tells the server to remove the given
|
||||||
|
/// elements from any array value that already exists on the server.
|
||||||
|
///
|
||||||
|
/// All instances of each element specified will be removed from the array.
|
||||||
|
/// If the field being modified is not already an array it will be overwritten
|
||||||
|
/// with an empty array.
|
||||||
|
static FieldValue arrayRemove(List<dynamic> elements) =>
|
||||||
|
FieldValue._(FieldValueType.arrayRemove, elements);
|
||||||
|
|
||||||
|
/// Returns a sentinel for use with update() to mark a field for deletion.
|
||||||
|
static FieldValue delete() => FieldValue._(FieldValueType.delete, null);
|
||||||
|
|
||||||
|
/// Returns a sentinel for use with set() or update() to include a
|
||||||
|
/// server-generated timestamp in the written data.
|
||||||
|
static FieldValue serverTimestamp() =>
|
||||||
|
FieldValue._(FieldValueType.serverTimestamp, null);
|
||||||
|
|
||||||
|
/// Returns a special value for use with set() or update() that tells the
|
||||||
|
/// server to increment the field’s current value by the given value.
|
||||||
|
static FieldValue increment(num value) {
|
||||||
|
// It is a compile-time error for any type other than int or double to
|
||||||
|
// attempt to extend or implement num.
|
||||||
|
assert(value is int || value is double);
|
||||||
|
if (value is double) {
|
||||||
|
return FieldValue._(FieldValueType.incrementDouble, value);
|
||||||
|
} else if (value is int) {
|
||||||
|
return FieldValue._(FieldValueType.incrementInteger, value);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,161 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
/// The entry point for accessing a Firestore.
|
||||||
|
///
|
||||||
|
/// You can get an instance by calling [Firestore.instance].
|
||||||
|
class Firestore {
|
||||||
|
Firestore({FirebaseApp app}) : app = app ?? FirebaseApp.instance {
|
||||||
|
if (_initialized) return;
|
||||||
|
channel.setMethodCallHandler((MethodCall call) async {
|
||||||
|
if (call.method == 'QuerySnapshot') {
|
||||||
|
final QuerySnapshot snapshot = QuerySnapshot._(call.arguments, this);
|
||||||
|
_queryObservers[call.arguments['handle']].add(snapshot);
|
||||||
|
} else if (call.method == 'DocumentSnapshot') {
|
||||||
|
final DocumentSnapshot snapshot = DocumentSnapshot._(
|
||||||
|
call.arguments['path'],
|
||||||
|
_asStringKeyedMap(call.arguments['data']),
|
||||||
|
SnapshotMetadata._(call.arguments['metadata']['hasPendingWrites'],
|
||||||
|
call.arguments['metadata']['isFromCache']),
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
_documentObservers[call.arguments['handle']].add(snapshot);
|
||||||
|
} else if (call.method == 'DoTransaction') {
|
||||||
|
final int transactionId = call.arguments['transactionId'];
|
||||||
|
return _transactionHandlers[transactionId](
|
||||||
|
Transaction(transactionId, this),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the instance of Firestore for the default Firebase app.
|
||||||
|
static final Firestore instance = Firestore();
|
||||||
|
|
||||||
|
/// The [FirebaseApp] instance to which this [FirebaseDatabase] belongs.
|
||||||
|
///
|
||||||
|
/// If null, the default [FirebaseApp] is used.
|
||||||
|
final FirebaseApp app;
|
||||||
|
|
||||||
|
static bool _initialized = false;
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
static const MethodChannel channel = MethodChannel(
|
||||||
|
'plugins.flutter.io/cloud_firestore',
|
||||||
|
StandardMethodCodec(FirestoreMessageCodec()),
|
||||||
|
);
|
||||||
|
|
||||||
|
static final Map<int, StreamController<QuerySnapshot>> _queryObservers =
|
||||||
|
<int, StreamController<QuerySnapshot>>{};
|
||||||
|
|
||||||
|
static final Map<int, StreamController<DocumentSnapshot>> _documentObservers =
|
||||||
|
<int, StreamController<DocumentSnapshot>>{};
|
||||||
|
|
||||||
|
static final Map<int, TransactionHandler> _transactionHandlers =
|
||||||
|
<int, TransactionHandler>{};
|
||||||
|
static int _transactionHandlerId = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic o) => o is Firestore && o.app == app;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => app.hashCode;
|
||||||
|
|
||||||
|
/// Gets a [CollectionReference] for the specified Firestore path.
|
||||||
|
CollectionReference collection(String path) {
|
||||||
|
assert(path != null);
|
||||||
|
return CollectionReference._(this, path.split('/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a [Query] for the specified collection group.
|
||||||
|
Query collectionGroup(String path) {
|
||||||
|
assert(path != null);
|
||||||
|
assert(!path.contains("/"), "Collection IDs must not contain '/'.");
|
||||||
|
return Query._(
|
||||||
|
firestore: this,
|
||||||
|
isCollectionGroup: true,
|
||||||
|
pathComponents: path.split('/'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a [DocumentReference] for the specified Firestore path.
|
||||||
|
DocumentReference document(String path) {
|
||||||
|
assert(path != null);
|
||||||
|
return DocumentReference._(this, path.split('/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a write batch, used for performing multiple writes as a single
|
||||||
|
/// atomic operation.
|
||||||
|
///
|
||||||
|
/// Unlike transactions, write batches are persisted offline and therefore are
|
||||||
|
/// preferable when you don’t need to condition your writes on read data.
|
||||||
|
WriteBatch batch() => WriteBatch._(this);
|
||||||
|
|
||||||
|
/// Executes the given TransactionHandler and then attempts to commit the
|
||||||
|
/// changes applied within an atomic transaction.
|
||||||
|
///
|
||||||
|
/// In the TransactionHandler, a set of reads and writes can be performed
|
||||||
|
/// atomically using the Transaction object passed to the TransactionHandler.
|
||||||
|
/// After the TransactionHandler is run, Firestore will attempt to apply the
|
||||||
|
/// changes to the server. If any of the data read has been modified outside
|
||||||
|
/// of this transaction since being read, then the transaction will be
|
||||||
|
/// retried by executing the updateBlock again. If the transaction still
|
||||||
|
/// fails after 5 retries, then the transaction will fail.
|
||||||
|
///
|
||||||
|
/// The TransactionHandler may be executed multiple times, it should be able
|
||||||
|
/// to handle multiple executions.
|
||||||
|
///
|
||||||
|
/// Data accessed with the transaction will not reflect local changes that
|
||||||
|
/// have not been committed. For this reason, it is required that all
|
||||||
|
/// reads are performed before any writes. Transactions must be performed
|
||||||
|
/// while online. Otherwise, reads will fail, and the final commit will fail.
|
||||||
|
///
|
||||||
|
/// By default transactions are limited to 5 seconds of execution time. This
|
||||||
|
/// timeout can be adjusted by setting the timeout parameter.
|
||||||
|
Future<Map<String, dynamic>> runTransaction(
|
||||||
|
TransactionHandler transactionHandler,
|
||||||
|
{Duration timeout = const Duration(seconds: 5)}) async {
|
||||||
|
assert(timeout.inMilliseconds > 0,
|
||||||
|
'Transaction timeout must be more than 0 milliseconds');
|
||||||
|
final int transactionId = _transactionHandlerId++;
|
||||||
|
_transactionHandlers[transactionId] = transactionHandler;
|
||||||
|
final Map<String, dynamic> result = await channel
|
||||||
|
.invokeMapMethod<String, dynamic>(
|
||||||
|
'Firestore#runTransaction', <String, dynamic>{
|
||||||
|
'app': app.name,
|
||||||
|
'transactionId': transactionId,
|
||||||
|
'transactionTimeout': timeout.inMilliseconds
|
||||||
|
});
|
||||||
|
return result ?? <String, dynamic>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
@deprecated
|
||||||
|
Future<void> enablePersistence(bool enable) async {
|
||||||
|
assert(enable != null);
|
||||||
|
await channel
|
||||||
|
.invokeMethod<void>('Firestore#enablePersistence', <String, dynamic>{
|
||||||
|
'app': app.name,
|
||||||
|
'enable': enable,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> settings(
|
||||||
|
{bool persistenceEnabled,
|
||||||
|
String host,
|
||||||
|
bool sslEnabled,
|
||||||
|
bool timestampsInSnapshotsEnabled,
|
||||||
|
int cacheSizeBytes}) async {
|
||||||
|
await channel.invokeMethod<void>('Firestore#settings', <String, dynamic>{
|
||||||
|
'app': app.name,
|
||||||
|
'persistenceEnabled': persistenceEnabled,
|
||||||
|
'host': host,
|
||||||
|
'sslEnabled': sslEnabled,
|
||||||
|
'timestampsInSnapshotsEnabled': timestampsInSnapshotsEnabled,
|
||||||
|
'cacheSizeBytes': cacheSizeBytes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
class FirestoreMessageCodec extends StandardMessageCodec {
|
||||||
|
const FirestoreMessageCodec();
|
||||||
|
|
||||||
|
static const int _kDateTime = 128;
|
||||||
|
static const int _kGeoPoint = 129;
|
||||||
|
static const int _kDocumentReference = 130;
|
||||||
|
static const int _kBlob = 131;
|
||||||
|
static const int _kArrayUnion = 132;
|
||||||
|
static const int _kArrayRemove = 133;
|
||||||
|
static const int _kDelete = 134;
|
||||||
|
static const int _kServerTimestamp = 135;
|
||||||
|
static const int _kTimestamp = 136;
|
||||||
|
static const int _kIncrementDouble = 137;
|
||||||
|
static const int _kIncrementInteger = 138;
|
||||||
|
|
||||||
|
static const Map<FieldValueType, int> _kFieldValueCodes =
|
||||||
|
<FieldValueType, int>{
|
||||||
|
FieldValueType.arrayUnion: _kArrayUnion,
|
||||||
|
FieldValueType.arrayRemove: _kArrayRemove,
|
||||||
|
FieldValueType.delete: _kDelete,
|
||||||
|
FieldValueType.serverTimestamp: _kServerTimestamp,
|
||||||
|
FieldValueType.incrementDouble: _kIncrementDouble,
|
||||||
|
FieldValueType.incrementInteger: _kIncrementInteger,
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
void writeValue(WriteBuffer buffer, dynamic value) {
|
||||||
|
if (value is DateTime) {
|
||||||
|
buffer.putUint8(_kDateTime);
|
||||||
|
buffer.putInt64(value.millisecondsSinceEpoch);
|
||||||
|
} else if (value is Timestamp) {
|
||||||
|
buffer.putUint8(_kTimestamp);
|
||||||
|
buffer.putInt64(value.seconds);
|
||||||
|
buffer.putInt32(value.nanoseconds);
|
||||||
|
} else if (value is GeoPoint) {
|
||||||
|
buffer.putUint8(_kGeoPoint);
|
||||||
|
buffer.putFloat64(value.latitude);
|
||||||
|
buffer.putFloat64(value.longitude);
|
||||||
|
} else if (value is DocumentReference) {
|
||||||
|
buffer.putUint8(_kDocumentReference);
|
||||||
|
final List<int> appName = utf8.encoder.convert(value.firestore.app.name);
|
||||||
|
writeSize(buffer, appName.length);
|
||||||
|
buffer.putUint8List(appName);
|
||||||
|
final List<int> bytes = utf8.encoder.convert(value.path);
|
||||||
|
writeSize(buffer, bytes.length);
|
||||||
|
buffer.putUint8List(bytes);
|
||||||
|
} else if (value is Blob) {
|
||||||
|
buffer.putUint8(_kBlob);
|
||||||
|
writeSize(buffer, value.bytes.length);
|
||||||
|
buffer.putUint8List(value.bytes);
|
||||||
|
} else if (value is FieldValue) {
|
||||||
|
final int code = _kFieldValueCodes[value.type];
|
||||||
|
assert(code != null);
|
||||||
|
buffer.putUint8(code);
|
||||||
|
if (value.value != null) writeValue(buffer, value.value);
|
||||||
|
} else {
|
||||||
|
super.writeValue(buffer, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
dynamic readValueOfType(int type, ReadBuffer buffer) {
|
||||||
|
switch (type) {
|
||||||
|
case _kDateTime:
|
||||||
|
return DateTime.fromMillisecondsSinceEpoch(buffer.getInt64());
|
||||||
|
case _kTimestamp:
|
||||||
|
return Timestamp(buffer.getInt64(), buffer.getInt32());
|
||||||
|
case _kGeoPoint:
|
||||||
|
return GeoPoint(buffer.getFloat64(), buffer.getFloat64());
|
||||||
|
case _kDocumentReference:
|
||||||
|
final int appNameLength = readSize(buffer);
|
||||||
|
final String appName =
|
||||||
|
utf8.decoder.convert(buffer.getUint8List(appNameLength));
|
||||||
|
final FirebaseApp app = FirebaseApp(name: appName);
|
||||||
|
final Firestore firestore = Firestore(app: app);
|
||||||
|
final int pathLength = readSize(buffer);
|
||||||
|
final String path =
|
||||||
|
utf8.decoder.convert(buffer.getUint8List(pathLength));
|
||||||
|
return firestore.document(path);
|
||||||
|
case _kBlob:
|
||||||
|
final int length = readSize(buffer);
|
||||||
|
final List<int> bytes = buffer.getUint8List(length);
|
||||||
|
return Blob(bytes);
|
||||||
|
case _kArrayUnion:
|
||||||
|
final List<dynamic> value = readValue(buffer);
|
||||||
|
return FieldValue.arrayUnion(value);
|
||||||
|
case _kArrayRemove:
|
||||||
|
final List<dynamic> value = readValue(buffer);
|
||||||
|
return FieldValue.arrayRemove(value);
|
||||||
|
case _kDelete:
|
||||||
|
return FieldValue.delete();
|
||||||
|
case _kServerTimestamp:
|
||||||
|
return FieldValue.serverTimestamp();
|
||||||
|
case _kIncrementDouble:
|
||||||
|
final double value = readValue(buffer);
|
||||||
|
return FieldValue.increment(value);
|
||||||
|
case _kIncrementInteger:
|
||||||
|
final int value = readValue(buffer);
|
||||||
|
return FieldValue.increment(value);
|
||||||
|
default:
|
||||||
|
return super.readValueOfType(type, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
class GeoPoint {
|
||||||
|
const GeoPoint(this.latitude, this.longitude);
|
||||||
|
|
||||||
|
final double latitude;
|
||||||
|
final double longitude;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic o) =>
|
||||||
|
o is GeoPoint && o.latitude == latitude && o.longitude == longitude;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => hashValues(latitude, longitude);
|
||||||
|
}
|
|
@ -0,0 +1,352 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
/// Represents a query over the data at a particular location.
|
||||||
|
class Query {
|
||||||
|
Query._(
|
||||||
|
{@required this.firestore,
|
||||||
|
@required List<String> pathComponents,
|
||||||
|
bool isCollectionGroup = false,
|
||||||
|
Map<String, dynamic> parameters})
|
||||||
|
: _pathComponents = pathComponents,
|
||||||
|
_isCollectionGroup = isCollectionGroup,
|
||||||
|
_parameters = parameters ??
|
||||||
|
Map<String, dynamic>.unmodifiable(<String, dynamic>{
|
||||||
|
'where': List<List<dynamic>>.unmodifiable(<List<dynamic>>[]),
|
||||||
|
'orderBy': List<List<dynamic>>.unmodifiable(<List<dynamic>>[]),
|
||||||
|
}),
|
||||||
|
assert(firestore != null),
|
||||||
|
assert(pathComponents != null);
|
||||||
|
|
||||||
|
/// The Firestore instance associated with this query
|
||||||
|
final Firestore firestore;
|
||||||
|
|
||||||
|
final List<String> _pathComponents;
|
||||||
|
final Map<String, dynamic> _parameters;
|
||||||
|
final bool _isCollectionGroup;
|
||||||
|
|
||||||
|
String get _path => _pathComponents.join('/');
|
||||||
|
|
||||||
|
Query _copyWithParameters(Map<String, dynamic> parameters) {
|
||||||
|
return Query._(
|
||||||
|
firestore: firestore,
|
||||||
|
isCollectionGroup: _isCollectionGroup,
|
||||||
|
pathComponents: _pathComponents,
|
||||||
|
parameters: Map<String, dynamic>.unmodifiable(
|
||||||
|
Map<String, dynamic>.from(_parameters)..addAll(parameters),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> buildArguments() {
|
||||||
|
return Map<String, dynamic>.from(_parameters)
|
||||||
|
..addAll(<String, dynamic>{
|
||||||
|
'path': _path,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notifies of query results at this location
|
||||||
|
// TODO(jackson): Reduce code duplication with [DocumentReference]
|
||||||
|
Stream<QuerySnapshot> snapshots() {
|
||||||
|
Future<int> _handle;
|
||||||
|
// It's fine to let the StreamController be garbage collected once all the
|
||||||
|
// subscribers have cancelled; this analyzer warning is safe to ignore.
|
||||||
|
StreamController<QuerySnapshot> controller; // ignore: close_sinks
|
||||||
|
controller = StreamController<QuerySnapshot>.broadcast(
|
||||||
|
onListen: () {
|
||||||
|
_handle = Firestore.channel.invokeMethod<int>(
|
||||||
|
'Query#addSnapshotListener',
|
||||||
|
<String, dynamic>{
|
||||||
|
'app': firestore.app.name,
|
||||||
|
'path': _path,
|
||||||
|
'isCollectionGroup': _isCollectionGroup,
|
||||||
|
'parameters': _parameters,
|
||||||
|
},
|
||||||
|
).then<int>((dynamic result) => result);
|
||||||
|
_handle.then((int handle) {
|
||||||
|
Firestore._queryObservers[handle] = controller;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onCancel: () {
|
||||||
|
_handle.then((int handle) async {
|
||||||
|
await Firestore.channel.invokeMethod<void>(
|
||||||
|
'Query#removeListener',
|
||||||
|
<String, dynamic>{'handle': handle},
|
||||||
|
);
|
||||||
|
Firestore._queryObservers.remove(handle);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return controller.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch the documents for this query
|
||||||
|
Future<QuerySnapshot> getDocuments(
|
||||||
|
{Source source = Source.serverAndCache}) async {
|
||||||
|
assert(source != null);
|
||||||
|
final Map<dynamic, dynamic> data =
|
||||||
|
await Firestore.channel.invokeMapMethod<String, dynamic>(
|
||||||
|
'Query#getDocuments',
|
||||||
|
<String, dynamic>{
|
||||||
|
'app': firestore.app.name,
|
||||||
|
'path': _path,
|
||||||
|
'isCollectionGroup': _isCollectionGroup,
|
||||||
|
'parameters': _parameters,
|
||||||
|
'source': _getSourceString(source),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return QuerySnapshot._(data, firestore);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtains a CollectionReference corresponding to this query's location.
|
||||||
|
CollectionReference reference() =>
|
||||||
|
CollectionReference._(firestore, _pathComponents);
|
||||||
|
|
||||||
|
/// Creates and returns a new [Query] with additional filter on specified
|
||||||
|
/// [field]. [field] refers to a field in a document.
|
||||||
|
///
|
||||||
|
/// The [field] may consist of a single field name (referring to a top level
|
||||||
|
/// field in the document), or a series of field names seperated by dots '.'
|
||||||
|
/// (referring to a nested field in the document).
|
||||||
|
///
|
||||||
|
/// Only documents satisfying provided condition are included in the result
|
||||||
|
/// set.
|
||||||
|
Query where(
|
||||||
|
String field, {
|
||||||
|
dynamic isEqualTo,
|
||||||
|
dynamic isLessThan,
|
||||||
|
dynamic isLessThanOrEqualTo,
|
||||||
|
dynamic isGreaterThan,
|
||||||
|
dynamic isGreaterThanOrEqualTo,
|
||||||
|
dynamic arrayContains,
|
||||||
|
bool isNull,
|
||||||
|
}) {
|
||||||
|
final ListEquality<dynamic> equality = const ListEquality<dynamic>();
|
||||||
|
final List<List<dynamic>> conditions =
|
||||||
|
List<List<dynamic>>.from(_parameters['where']);
|
||||||
|
|
||||||
|
void addCondition(String field, String operator, dynamic value) {
|
||||||
|
final List<dynamic> condition = <dynamic>[field, operator, value];
|
||||||
|
assert(
|
||||||
|
conditions
|
||||||
|
.where((List<dynamic> item) => equality.equals(condition, item))
|
||||||
|
.isEmpty,
|
||||||
|
'Condition $condition already exists in this query.');
|
||||||
|
conditions.add(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEqualTo != null) addCondition(field, '==', isEqualTo);
|
||||||
|
if (isLessThan != null) addCondition(field, '<', isLessThan);
|
||||||
|
if (isLessThanOrEqualTo != null)
|
||||||
|
addCondition(field, '<=', isLessThanOrEqualTo);
|
||||||
|
if (isGreaterThan != null) addCondition(field, '>', isGreaterThan);
|
||||||
|
if (isGreaterThanOrEqualTo != null)
|
||||||
|
addCondition(field, '>=', isGreaterThanOrEqualTo);
|
||||||
|
if (arrayContains != null)
|
||||||
|
addCondition(field, 'array-contains', arrayContains);
|
||||||
|
if (isNull != null) {
|
||||||
|
assert(
|
||||||
|
isNull,
|
||||||
|
'isNull can only be set to true. '
|
||||||
|
'Use isEqualTo to filter on non-null values.');
|
||||||
|
addCondition(field, '==', null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _copyWithParameters(<String, dynamic>{'where': conditions});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates and returns a new [Query] that's additionally sorted by the specified
|
||||||
|
/// [field].
|
||||||
|
Query orderBy(String field, {bool descending = false}) {
|
||||||
|
final List<List<dynamic>> orders =
|
||||||
|
List<List<dynamic>>.from(_parameters['orderBy']);
|
||||||
|
|
||||||
|
final List<dynamic> order = <dynamic>[field, descending];
|
||||||
|
assert(orders.where((List<dynamic> item) => field == item[0]).isEmpty,
|
||||||
|
'OrderBy $field already exists in this query');
|
||||||
|
orders.add(order);
|
||||||
|
return _copyWithParameters(<String, dynamic>{'orderBy': orders});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates and returns a new [Query] that starts after the provided document
|
||||||
|
/// (exclusive). The starting position is relative to the order of the query.
|
||||||
|
/// The document must contain all of the fields provided in the orderBy of
|
||||||
|
/// this query.
|
||||||
|
///
|
||||||
|
/// Cannot be used in combination with [startAtDocument], [startAt], or
|
||||||
|
/// [startAfter].
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
/// * [endAfterDocument] for a query that ends after a document.
|
||||||
|
/// * [startAtDocument] for a query that starts at a document.
|
||||||
|
/// * [endAtDocument] for a query that ends at a document.
|
||||||
|
Query startAfterDocument(DocumentSnapshot documentSnapshot) {
|
||||||
|
assert(documentSnapshot != null);
|
||||||
|
assert(!_parameters.containsKey('startAfter'));
|
||||||
|
assert(!_parameters.containsKey('startAt'));
|
||||||
|
assert(!_parameters.containsKey('startAfterDocument'));
|
||||||
|
assert(!_parameters.containsKey('startAtDocument'));
|
||||||
|
return _copyWithParameters(<String, dynamic>{
|
||||||
|
'startAfterDocument': <String, dynamic>{
|
||||||
|
'id': documentSnapshot.documentID,
|
||||||
|
'path': documentSnapshot.reference.path,
|
||||||
|
'data': documentSnapshot.data
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates and returns a new [Query] that starts at the provided document
|
||||||
|
/// (inclusive). The starting position is relative to the order of the query.
|
||||||
|
/// The document must contain all of the fields provided in the orderBy of
|
||||||
|
/// this query.
|
||||||
|
///
|
||||||
|
/// Cannot be used in combination with [startAfterDocument], [startAfter], or
|
||||||
|
/// [startAt].
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
/// * [startAfterDocument] for a query that starts after a document.
|
||||||
|
/// * [endAtDocument] for a query that ends at a document.
|
||||||
|
/// * [endBeforeDocument] for a query that ends before a document.
|
||||||
|
Query startAtDocument(DocumentSnapshot documentSnapshot) {
|
||||||
|
assert(documentSnapshot != null);
|
||||||
|
assert(!_parameters.containsKey('startAfter'));
|
||||||
|
assert(!_parameters.containsKey('startAt'));
|
||||||
|
assert(!_parameters.containsKey('startAfterDocument'));
|
||||||
|
assert(!_parameters.containsKey('startAtDocument'));
|
||||||
|
return _copyWithParameters(<String, dynamic>{
|
||||||
|
'startAtDocument': <String, dynamic>{
|
||||||
|
'id': documentSnapshot.documentID,
|
||||||
|
'path': documentSnapshot.reference.path,
|
||||||
|
'data': documentSnapshot.data
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes a list of [values], creates and returns a new [Query] that starts
|
||||||
|
/// after the provided fields relative to the order of the query.
|
||||||
|
///
|
||||||
|
/// The [values] must be in order of [orderBy] filters.
|
||||||
|
///
|
||||||
|
/// Cannot be used in combination with [startAt], [startAfterDocument], or
|
||||||
|
/// [startAtDocument].
|
||||||
|
Query startAfter(List<dynamic> values) {
|
||||||
|
assert(values != null);
|
||||||
|
assert(!_parameters.containsKey('startAfter'));
|
||||||
|
assert(!_parameters.containsKey('startAt'));
|
||||||
|
assert(!_parameters.containsKey('startAfterDocument'));
|
||||||
|
assert(!_parameters.containsKey('startAtDocument'));
|
||||||
|
return _copyWithParameters(<String, dynamic>{'startAfter': values});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes a list of [values], creates and returns a new [Query] that starts at
|
||||||
|
/// the provided fields relative to the order of the query.
|
||||||
|
///
|
||||||
|
/// The [values] must be in order of [orderBy] filters.
|
||||||
|
///
|
||||||
|
/// Cannot be used in combination with [startAfter], [startAfterDocument],
|
||||||
|
/// or [startAtDocument].
|
||||||
|
Query startAt(List<dynamic> values) {
|
||||||
|
assert(values != null);
|
||||||
|
assert(!_parameters.containsKey('startAfter'));
|
||||||
|
assert(!_parameters.containsKey('startAt'));
|
||||||
|
assert(!_parameters.containsKey('startAfterDocument'));
|
||||||
|
assert(!_parameters.containsKey('startAtDocument'));
|
||||||
|
return _copyWithParameters(<String, dynamic>{'startAt': values});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates and returns a new [Query] that ends at the provided document
|
||||||
|
/// (inclusive). The end position is relative to the order of the query.
|
||||||
|
/// The document must contain all of the fields provided in the orderBy of
|
||||||
|
/// this query.
|
||||||
|
///
|
||||||
|
/// Cannot be used in combination with [endBefore], [endBeforeDocument], or
|
||||||
|
/// [endAt].
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
/// * [startAfterDocument] for a query that starts after a document.
|
||||||
|
/// * [startAtDocument] for a query that starts at a document.
|
||||||
|
/// * [endBeforeDocument] for a query that ends before a document.
|
||||||
|
Query endAtDocument(DocumentSnapshot documentSnapshot) {
|
||||||
|
assert(documentSnapshot != null);
|
||||||
|
assert(!_parameters.containsKey('endBefore'));
|
||||||
|
assert(!_parameters.containsKey('endAt'));
|
||||||
|
assert(!_parameters.containsKey('endBeforeDocument'));
|
||||||
|
assert(!_parameters.containsKey('endAtDocument'));
|
||||||
|
return _copyWithParameters(<String, dynamic>{
|
||||||
|
'endAtDocument': <String, dynamic>{
|
||||||
|
'id': documentSnapshot.documentID,
|
||||||
|
'path': documentSnapshot.reference.path,
|
||||||
|
'data': documentSnapshot.data
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes a list of [values], creates and returns a new [Query] that ends at the
|
||||||
|
/// provided fields relative to the order of the query.
|
||||||
|
///
|
||||||
|
/// The [values] must be in order of [orderBy] filters.
|
||||||
|
///
|
||||||
|
/// Cannot be used in combination with [endBefore], [endBeforeDocument], or
|
||||||
|
/// [endAtDocument].
|
||||||
|
Query endAt(List<dynamic> values) {
|
||||||
|
assert(values != null);
|
||||||
|
assert(!_parameters.containsKey('endBefore'));
|
||||||
|
assert(!_parameters.containsKey('endAt'));
|
||||||
|
assert(!_parameters.containsKey('endBeforeDocument'));
|
||||||
|
assert(!_parameters.containsKey('endAtDocument'));
|
||||||
|
return _copyWithParameters(<String, dynamic>{'endAt': values});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates and returns a new [Query] that ends before the provided document
|
||||||
|
/// (exclusive). The end position is relative to the order of the query.
|
||||||
|
/// The document must contain all of the fields provided in the orderBy of
|
||||||
|
/// this query.
|
||||||
|
///
|
||||||
|
/// Cannot be used in combination with [endAt], [endBefore], or
|
||||||
|
/// [endAtDocument].
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
/// * [startAfterDocument] for a query that starts after document.
|
||||||
|
/// * [startAtDocument] for a query that starts at a document.
|
||||||
|
/// * [endAtDocument] for a query that ends at a document.
|
||||||
|
Query endBeforeDocument(DocumentSnapshot documentSnapshot) {
|
||||||
|
assert(documentSnapshot != null);
|
||||||
|
assert(!_parameters.containsKey('endBefore'));
|
||||||
|
assert(!_parameters.containsKey('endAt'));
|
||||||
|
assert(!_parameters.containsKey('endBeforeDocument'));
|
||||||
|
assert(!_parameters.containsKey('endAtDocument'));
|
||||||
|
return _copyWithParameters(<String, dynamic>{
|
||||||
|
'endBeforeDocument': <String, dynamic>{
|
||||||
|
'id': documentSnapshot.documentID,
|
||||||
|
'path': documentSnapshot.reference.path,
|
||||||
|
'data': documentSnapshot.data,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes a list of [values], creates and returns a new [Query] that ends before
|
||||||
|
/// the provided fields relative to the order of the query.
|
||||||
|
///
|
||||||
|
/// The [values] must be in order of [orderBy] filters.
|
||||||
|
///
|
||||||
|
/// Cannot be used in combination with [endAt], [endBeforeDocument], or
|
||||||
|
/// [endBeforeDocument]
|
||||||
|
Query endBefore(List<dynamic> values) {
|
||||||
|
assert(values != null);
|
||||||
|
assert(!_parameters.containsKey('endBefore'));
|
||||||
|
assert(!_parameters.containsKey('endAt'));
|
||||||
|
assert(!_parameters.containsKey('endBeforeDocument'));
|
||||||
|
assert(!_parameters.containsKey('endAtDocument'));
|
||||||
|
return _copyWithParameters(<String, dynamic>{'endBefore': values});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates and returns a new Query that's additionally limited to only return up
|
||||||
|
/// to the specified number of documents.
|
||||||
|
Query limit(int length) {
|
||||||
|
assert(!_parameters.containsKey('limit'));
|
||||||
|
return _copyWithParameters(<String, dynamic>{'limit': length});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
/// A QuerySnapshot contains zero or more DocumentSnapshot objects.
|
||||||
|
class QuerySnapshot {
|
||||||
|
QuerySnapshot._(Map<dynamic, dynamic> data, this._firestore)
|
||||||
|
: documents = List<DocumentSnapshot>.generate(data['documents'].length,
|
||||||
|
(int index) {
|
||||||
|
return DocumentSnapshot._(
|
||||||
|
data['paths'][index],
|
||||||
|
_asStringKeyedMap(data['documents'][index]),
|
||||||
|
SnapshotMetadata._(
|
||||||
|
data['metadatas'][index]['hasPendingWrites'],
|
||||||
|
data['metadatas'][index]['isFromCache'],
|
||||||
|
),
|
||||||
|
_firestore,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
documentChanges = List<DocumentChange>.generate(
|
||||||
|
data['documentChanges'].length, (int index) {
|
||||||
|
return DocumentChange._(
|
||||||
|
data['documentChanges'][index],
|
||||||
|
_firestore,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Gets a list of all the documents included in this snapshot
|
||||||
|
final List<DocumentSnapshot> documents;
|
||||||
|
|
||||||
|
/// An array of the documents that changed since the last snapshot. If this
|
||||||
|
/// is the first snapshot, all documents will be in the list as Added changes.
|
||||||
|
final List<DocumentChange> documentChanges;
|
||||||
|
|
||||||
|
final Firestore _firestore;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
/// Metadata about a snapshot, describing the state of the snapshot.
|
||||||
|
class SnapshotMetadata {
|
||||||
|
SnapshotMetadata._(this.hasPendingWrites, this.isFromCache);
|
||||||
|
|
||||||
|
/// Whether the snapshot contains the result of local writes that have not yet
|
||||||
|
/// been committed to the backend.
|
||||||
|
///
|
||||||
|
/// If your listener has opted into metadata updates (via
|
||||||
|
/// [DocumentListenOptions] or [QueryListenOptions]) you will receive another
|
||||||
|
/// snapshot with `hasPendingWrites` equal to `false` once the writes have been
|
||||||
|
/// committed to the backend.
|
||||||
|
final bool hasPendingWrites;
|
||||||
|
|
||||||
|
/// Whether the snapshot was created from cached data rather than guaranteed
|
||||||
|
/// up-to-date server data.
|
||||||
|
///
|
||||||
|
/// If your listener has opted into metadata updates (via
|
||||||
|
/// [DocumentListenOptions] or [QueryListenOptions]) you will receive another
|
||||||
|
/// snapshot with `isFomCache` equal to `false` once the client has received
|
||||||
|
/// up-to-date data from the backend.
|
||||||
|
final bool isFromCache;
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
/// An enumeration of firestore source types.
|
||||||
|
enum Source {
|
||||||
|
/// Causes Firestore to try to retrieve an up-to-date (server-retrieved) snapshot, but fall back to
|
||||||
|
/// returning cached data if the server can't be reached.
|
||||||
|
serverAndCache,
|
||||||
|
|
||||||
|
/// Causes Firestore to avoid the cache, generating an error if the server cannot be reached. Note
|
||||||
|
/// that the cache will still be updated if the server request succeeds. Also note that
|
||||||
|
/// latency-compensation still takes effect, so any pending write operations will be visible in the
|
||||||
|
/// returned data (merged into the server-provided data).
|
||||||
|
server,
|
||||||
|
|
||||||
|
/// Causes Firestore to immediately return a value from the cache, ignoring the server completely
|
||||||
|
/// (implying that the returned value may be stale with respect to the value on the server). If
|
||||||
|
/// there is no data in the cache to satisfy the [get()] or [getDocuments()] call,
|
||||||
|
/// [DocumentReference.get()] will return an error and [Query.getDocuments()] will return an empty
|
||||||
|
/// [QuerySnapshot] with no documents.
|
||||||
|
cache,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts [Source] to [String]
|
||||||
|
String _getSourceString(Source source) {
|
||||||
|
assert(source != null);
|
||||||
|
if (source == Source.server) {
|
||||||
|
return 'server';
|
||||||
|
}
|
||||||
|
if (source == Source.cache) {
|
||||||
|
return 'cache';
|
||||||
|
}
|
||||||
|
return 'default';
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
// Copyright 2018, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
const int _kThousand = 1000;
|
||||||
|
const int _kMillion = 1000000;
|
||||||
|
const int _kBillion = 1000000000;
|
||||||
|
|
||||||
|
void _check(bool expr, String name, int value) {
|
||||||
|
if (!expr) {
|
||||||
|
throw ArgumentError("Timestamp $name out of range: $value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Timestamp represents a point in time independent of any time zone or calendar,
|
||||||
|
/// represented as seconds and fractions of seconds at nanosecond resolution in UTC
|
||||||
|
/// Epoch time. It is encoded using the Proleptic Gregorian Calendar which extends
|
||||||
|
/// the Gregorian calendar backwards to year one. It is encoded assuming all minutes
|
||||||
|
/// are 60 seconds long, i.e. leap seconds are "smeared" so that no leap second table
|
||||||
|
/// is needed for interpretation. Range is from 0001-01-01T00:00:00Z to
|
||||||
|
/// 9999-12-31T23:59:59.999999999Z. By restricting to that range, we ensure that we
|
||||||
|
/// can convert to and from RFC 3339 date strings.
|
||||||
|
///
|
||||||
|
/// For more information, see [the reference timestamp definition](https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto)
|
||||||
|
class Timestamp implements Comparable<Timestamp> {
|
||||||
|
Timestamp(this._seconds, this._nanoseconds) {
|
||||||
|
_validateRange(_seconds, _nanoseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Timestamp.fromMillisecondsSinceEpoch(int milliseconds) {
|
||||||
|
final int seconds = (milliseconds / _kThousand).floor();
|
||||||
|
final int nanoseconds = (milliseconds - seconds * _kThousand) * _kMillion;
|
||||||
|
return Timestamp(seconds, nanoseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Timestamp.fromMicrosecondsSinceEpoch(int microseconds) {
|
||||||
|
final int seconds = (microseconds / _kMillion).floor();
|
||||||
|
final int nanoseconds = (microseconds - seconds * _kMillion) * _kThousand;
|
||||||
|
return Timestamp(seconds, nanoseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Timestamp.fromDate(DateTime date) {
|
||||||
|
return Timestamp.fromMicrosecondsSinceEpoch(date.microsecondsSinceEpoch);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Timestamp.now() {
|
||||||
|
return Timestamp.fromMicrosecondsSinceEpoch(
|
||||||
|
DateTime.now().microsecondsSinceEpoch);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int _seconds;
|
||||||
|
final int _nanoseconds;
|
||||||
|
|
||||||
|
static const int _kStartOfTime = -62135596800;
|
||||||
|
static const int _kEndOfTime = 253402300800;
|
||||||
|
|
||||||
|
int get seconds => _seconds;
|
||||||
|
|
||||||
|
int get nanoseconds => _nanoseconds;
|
||||||
|
|
||||||
|
int get millisecondsSinceEpoch =>
|
||||||
|
(seconds * _kThousand + nanoseconds / _kMillion).floor();
|
||||||
|
|
||||||
|
int get microsecondsSinceEpoch =>
|
||||||
|
(seconds * _kMillion + nanoseconds / _kThousand).floor();
|
||||||
|
|
||||||
|
DateTime toDate() {
|
||||||
|
return DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => hashValues(seconds, nanoseconds);
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic o) =>
|
||||||
|
o is Timestamp && o.seconds == seconds && o.nanoseconds == nanoseconds;
|
||||||
|
@override
|
||||||
|
int compareTo(Timestamp other) {
|
||||||
|
if (seconds == other.seconds) {
|
||||||
|
return nanoseconds.compareTo(other.nanoseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return seconds.compareTo(other.seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return "Timestamp(seconds=$seconds, nanoseconds=$nanoseconds)";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _validateRange(int seconds, int nanoseconds) {
|
||||||
|
_check(nanoseconds >= 0, 'nanoseconds', nanoseconds);
|
||||||
|
_check(nanoseconds < _kBillion, 'nanoseconds', nanoseconds);
|
||||||
|
_check(seconds >= _kStartOfTime, 'seconds', seconds);
|
||||||
|
_check(seconds < _kEndOfTime, 'seconds', seconds);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
typedef Future<dynamic> TransactionHandler(Transaction transaction);
|
||||||
|
|
||||||
|
class Transaction {
|
||||||
|
@visibleForTesting
|
||||||
|
Transaction(this._transactionId, this._firestore);
|
||||||
|
|
||||||
|
int _transactionId;
|
||||||
|
Firestore _firestore;
|
||||||
|
|
||||||
|
Future<DocumentSnapshot> get(DocumentReference documentReference) async {
|
||||||
|
final Map<String, dynamic> result = await Firestore.channel
|
||||||
|
.invokeMapMethod<String, dynamic>('Transaction#get', <String, dynamic>{
|
||||||
|
'app': _firestore.app.name,
|
||||||
|
'transactionId': _transactionId,
|
||||||
|
'path': documentReference.path,
|
||||||
|
});
|
||||||
|
if (result != null) {
|
||||||
|
return DocumentSnapshot._(
|
||||||
|
documentReference.path,
|
||||||
|
result['data']?.cast<String, dynamic>(),
|
||||||
|
SnapshotMetadata._(result['metadata']['hasPendingWrites'],
|
||||||
|
result['metadata']['isFromCache']),
|
||||||
|
_firestore);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> delete(DocumentReference documentReference) async {
|
||||||
|
return Firestore.channel
|
||||||
|
.invokeMethod<void>('Transaction#delete', <String, dynamic>{
|
||||||
|
'app': _firestore.app.name,
|
||||||
|
'transactionId': _transactionId,
|
||||||
|
'path': documentReference.path,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> update(
|
||||||
|
DocumentReference documentReference, Map<String, dynamic> data) async {
|
||||||
|
return Firestore.channel
|
||||||
|
.invokeMethod<void>('Transaction#update', <String, dynamic>{
|
||||||
|
'app': _firestore.app.name,
|
||||||
|
'transactionId': _transactionId,
|
||||||
|
'path': documentReference.path,
|
||||||
|
'data': data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> set(
|
||||||
|
DocumentReference documentReference, Map<String, dynamic> data) async {
|
||||||
|
return Firestore.channel
|
||||||
|
.invokeMethod<void>('Transaction#set', <String, dynamic>{
|
||||||
|
'app': _firestore.app.name,
|
||||||
|
'transactionId': _transactionId,
|
||||||
|
'path': documentReference.path,
|
||||||
|
'data': data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
/// Utility class for generating Firebase child node keys.
|
||||||
|
///
|
||||||
|
/// Since the Flutter plugin API is asynchronous, there's no way for us
|
||||||
|
/// to use the native SDK to generate the node key synchronously and we
|
||||||
|
/// have to do it ourselves if we want to be able to reference the
|
||||||
|
/// newly-created node synchronously.
|
||||||
|
///
|
||||||
|
/// This code is based on a Firebase blog post and ported to Dart.
|
||||||
|
/// https://firebase.googleblog.com/2015/02/the-2120-ways-to-ensure-unique_68.html
|
||||||
|
class PushIdGenerator {
|
||||||
|
static const String PUSH_CHARS =
|
||||||
|
'-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
|
||||||
|
|
||||||
|
static final Random _random = Random();
|
||||||
|
|
||||||
|
static int _lastPushTime;
|
||||||
|
|
||||||
|
static final List<int> _lastRandChars = List<int>(12);
|
||||||
|
|
||||||
|
static String generatePushChildName() {
|
||||||
|
int now = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
final bool duplicateTime = (now == _lastPushTime);
|
||||||
|
_lastPushTime = now;
|
||||||
|
|
||||||
|
final List<String> timeStampChars = List<String>(8);
|
||||||
|
for (int i = 7; i >= 0; i--) {
|
||||||
|
timeStampChars[i] = PUSH_CHARS[now % 64];
|
||||||
|
now = (now / 64).floor();
|
||||||
|
}
|
||||||
|
assert(now == 0);
|
||||||
|
|
||||||
|
final StringBuffer result = StringBuffer(timeStampChars.join());
|
||||||
|
|
||||||
|
if (!duplicateTime) {
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
_lastRandChars[i] = _random.nextInt(64);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_incrementArray();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
result.write(PUSH_CHARS[_lastRandChars[i]]);
|
||||||
|
}
|
||||||
|
assert(result.length == 20);
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _incrementArray() {
|
||||||
|
for (int i = 11; i >= 0; i--) {
|
||||||
|
if (_lastRandChars[i] != 63) {
|
||||||
|
_lastRandChars[i] = _lastRandChars[i] + 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_lastRandChars[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
// Copyright 2018, the Chromium project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
part of cloud_firestore;
|
||||||
|
|
||||||
|
/// A [WriteBatch] is a series of write operations to be performed as one unit.
|
||||||
|
///
|
||||||
|
/// Operations done on a [WriteBatch] do not take effect until you [commit].
|
||||||
|
///
|
||||||
|
/// Once committed, no further operations can be performed on the [WriteBatch],
|
||||||
|
/// nor can it be committed again.
|
||||||
|
class WriteBatch {
|
||||||
|
WriteBatch._(this._firestore)
|
||||||
|
: _handle = Firestore.channel.invokeMethod<dynamic>(
|
||||||
|
'WriteBatch#create', <String, dynamic>{'app': _firestore.app.name});
|
||||||
|
|
||||||
|
final Firestore _firestore;
|
||||||
|
Future<dynamic> _handle;
|
||||||
|
final List<Future<dynamic>> _actions = <Future<dynamic>>[];
|
||||||
|
|
||||||
|
/// Indicator to whether or not this [WriteBatch] has been committed.
|
||||||
|
bool _committed = false;
|
||||||
|
|
||||||
|
/// Commits all of the writes in this write batch as a single atomic unit.
|
||||||
|
///
|
||||||
|
/// Calling this method prevents any future operations from being added.
|
||||||
|
Future<void> commit() async {
|
||||||
|
if (!_committed) {
|
||||||
|
_committed = true;
|
||||||
|
await Future.wait<dynamic>(_actions);
|
||||||
|
await Firestore.channel.invokeMethod<void>(
|
||||||
|
'WriteBatch#commit', <String, dynamic>{'handle': await _handle});
|
||||||
|
} else {
|
||||||
|
throw StateError("This batch has already been committed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes the document referred to by [document].
|
||||||
|
void delete(DocumentReference document) {
|
||||||
|
if (!_committed) {
|
||||||
|
_handle.then((dynamic handle) {
|
||||||
|
_actions.add(
|
||||||
|
Firestore.channel.invokeMethod<void>(
|
||||||
|
'WriteBatch#delete',
|
||||||
|
<String, dynamic>{
|
||||||
|
'app': _firestore.app.name,
|
||||||
|
'handle': handle,
|
||||||
|
'path': document.path,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw StateError(
|
||||||
|
"This batch has been committed and can no longer be changed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes to the document referred to by [document].
|
||||||
|
///
|
||||||
|
/// If the document does not yet exist, it will be created.
|
||||||
|
///
|
||||||
|
/// If [merge] is true, the provided data will be merged into an
|
||||||
|
/// existing document instead of overwriting.
|
||||||
|
void setData(DocumentReference document, Map<String, dynamic> data,
|
||||||
|
{bool merge = false}) {
|
||||||
|
if (!_committed) {
|
||||||
|
_handle.then((dynamic handle) {
|
||||||
|
_actions.add(
|
||||||
|
Firestore.channel.invokeMethod<void>(
|
||||||
|
'WriteBatch#setData',
|
||||||
|
<String, dynamic>{
|
||||||
|
'app': _firestore.app.name,
|
||||||
|
'handle': handle,
|
||||||
|
'path': document.path,
|
||||||
|
'data': data,
|
||||||
|
'options': <String, bool>{'merge': merge},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw StateError(
|
||||||
|
"This batch has been committed and can no longer be changed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates fields in the document referred to by [document].
|
||||||
|
///
|
||||||
|
/// If the document does not exist, the operation will fail.
|
||||||
|
void updateData(DocumentReference document, Map<String, dynamic> data) {
|
||||||
|
if (!_committed) {
|
||||||
|
_handle.then((dynamic handle) {
|
||||||
|
_actions.add(
|
||||||
|
Firestore.channel.invokeMethod<void>(
|
||||||
|
'WriteBatch#updateData',
|
||||||
|
<String, dynamic>{
|
||||||
|
'app': _firestore.app.name,
|
||||||
|
'handle': handle,
|
||||||
|
'path': document.path,
|
||||||
|
'data': data,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw StateError(
|
||||||
|
"This batch has been committed and can no longer be changed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
name: cloud_firestore
|
||||||
|
description: Flutter plugin for Cloud Firestore, a cloud-hosted, noSQL database with
|
||||||
|
live synchronization and offline support on Android and iOS.
|
||||||
|
author: Flutter Team <flutter-dev@googlegroups.com>
|
||||||
|
homepage: https://github.com/flutter/plugins/tree/master/packages/cloud_firestore
|
||||||
|
version: 0.12.5
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
plugin:
|
||||||
|
androidPackage: io.flutter.plugins.firebase.cloudfirestore
|
||||||
|
iosPrefix: FLT
|
||||||
|
pluginClass: CloudFirestorePlugin
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
meta: "^1.0.5"
|
||||||
|
collection: "^1.14.3"
|
||||||
|
firebase_core: "^0.4.0"
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_driver:
|
||||||
|
sdk: flutter
|
||||||
|
test: any
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.0.0-dev.28.0 <3.0.0"
|
||||||
|
flutter: ">=1.5.0 <2.0.0"
|
|
@ -0,0 +1,133 @@
|
||||||
|
## 0.4.0+3
|
||||||
|
|
||||||
|
* Add missing template type parameter to `invokeMethod` calls.
|
||||||
|
* Bump minimum Flutter version to 1.5.0.
|
||||||
|
* Replace invokeMethod with invokeMapMethod wherever necessary.
|
||||||
|
|
||||||
|
## 0.4.0+2
|
||||||
|
|
||||||
|
* Update user agent name. Set to `flutter-fire-core` for consistency with other
|
||||||
|
libraries.
|
||||||
|
|
||||||
|
## 0.4.0+1
|
||||||
|
|
||||||
|
* Send user agent to Firebase.
|
||||||
|
|
||||||
|
## 0.4.0
|
||||||
|
|
||||||
|
* Update Android dependencies to latest.
|
||||||
|
|
||||||
|
## 0.3.4
|
||||||
|
|
||||||
|
* Updates Android firebase-core dependency to a version that is compatible with other Flutterfire plugins.
|
||||||
|
|
||||||
|
## 0.3.3
|
||||||
|
|
||||||
|
* Remove Gradle BoM to avoid Gradle version issues.
|
||||||
|
|
||||||
|
## 0.3.2
|
||||||
|
|
||||||
|
* Move Android dependency to Gradle BoM to help maintain compatability
|
||||||
|
with other FlutterFire plugins.
|
||||||
|
|
||||||
|
## 0.3.1+1
|
||||||
|
|
||||||
|
* Add nil check on static functions to prevent crashes or unwanted behaviors.
|
||||||
|
|
||||||
|
## 0.3.1
|
||||||
|
|
||||||
|
* Remove an assertion that can interfere with hot-restart.
|
||||||
|
|
||||||
|
## 0.3.0+2
|
||||||
|
|
||||||
|
* Remove categories.
|
||||||
|
|
||||||
|
## 0.3.0+1
|
||||||
|
|
||||||
|
* Log a more detailed warning at build time about the previous AndroidX
|
||||||
|
migration.
|
||||||
|
|
||||||
|
## 0.3.0
|
||||||
|
|
||||||
|
* **Breaking change**. Migrate from the deprecated original Android Support
|
||||||
|
Library to AndroidX. This shouldn't result in any functional changes, but it
|
||||||
|
requires any Android apps using this plugin to [also
|
||||||
|
migrate](https://developer.android.com/jetpack/androidx/migrate) if they're
|
||||||
|
using the original support library.
|
||||||
|
|
||||||
|
## 0.2.5+1
|
||||||
|
|
||||||
|
* Bump Android dependencies to latest.
|
||||||
|
|
||||||
|
## 0.2.5
|
||||||
|
|
||||||
|
* Bump Android and Firebase dependency versions.
|
||||||
|
|
||||||
|
## 0.2.4
|
||||||
|
|
||||||
|
* Updated Gradle tooling to match Android Studio 3.1.2.
|
||||||
|
|
||||||
|
## 0.2.3
|
||||||
|
|
||||||
|
* Updated Google Play Services dependencies to version 15.0.0.
|
||||||
|
|
||||||
|
## 0.2.2
|
||||||
|
|
||||||
|
* Simplified podspec for Cocoapods 1.5.0, avoiding link issues in app archives.
|
||||||
|
|
||||||
|
## 0.2.1
|
||||||
|
|
||||||
|
* Fix setting project ID on Android.
|
||||||
|
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
|
* **Breaking change**. Options API is now async to interoperate with native code that configures Firebase apps.
|
||||||
|
* Provide a getter for the default app
|
||||||
|
* Fix setting of GCM sender ID on iOS
|
||||||
|
|
||||||
|
## 0.1.2
|
||||||
|
|
||||||
|
* Fix projectID on iOS
|
||||||
|
|
||||||
|
## 0.1.1
|
||||||
|
|
||||||
|
* Fix behavior of constructor for named Firebase apps.
|
||||||
|
|
||||||
|
## 0.1.0
|
||||||
|
|
||||||
|
* **Breaking change**. Set SDK constraints to match the Flutter beta release.
|
||||||
|
|
||||||
|
## 0.0.7
|
||||||
|
|
||||||
|
* Fixed Dart 2 type errors.
|
||||||
|
|
||||||
|
## 0.0.6
|
||||||
|
|
||||||
|
* Enabled use in Swift projects.
|
||||||
|
|
||||||
|
## 0.0.5
|
||||||
|
|
||||||
|
* Moved to the io.flutter.plugins org.
|
||||||
|
|
||||||
|
## 0.0.4
|
||||||
|
|
||||||
|
* Fixed warnings from the Dart 2.0 analyzer.
|
||||||
|
* Simplified and upgraded Android project template to Android SDK 27.
|
||||||
|
* Updated package description.
|
||||||
|
|
||||||
|
# 0.0.3
|
||||||
|
|
||||||
|
* **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin
|
||||||
|
3.0.1. Older Flutter projects need to upgrade their Gradle setup as well in
|
||||||
|
order to use this version of the plugin. Instructions can be found
|
||||||
|
[here](https://github.com/flutter/flutter/wiki/Updating-Flutter-projects-to-Gradle-4.1-and-Android-Studio-Gradle-plugin-3.0.1).
|
||||||
|
|
||||||
|
## 0.0.2
|
||||||
|
|
||||||
|
* Fixes for database URL on Android
|
||||||
|
* Make GCM sender id optional on Android
|
||||||
|
* Relax GMS dependency to 11.+
|
||||||
|
|
||||||
|
## 0.0.1
|
||||||
|
|
||||||
|
* Initial Release
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Firebase Core for Flutter
|
||||||
|
|
||||||
|
[](https://pub.dartlang.org/packages/firebase_core)
|
||||||
|
|
||||||
|
A Flutter plugin to use the Firebase Core API, which enables connecting to multiple Firebase apps.
|
||||||
|
|
||||||
|
For Flutter plugins for other Firebase products, see [FlutterFire.md](https://github.com/flutter/plugins/blob/master/FlutterFire.md).
|
||||||
|
|
||||||
|
*Note*: This plugin is still under development, and some APIs might not be available yet. [Feedback](https://github.com/flutter/flutter/issues) and [Pull Requests](https://github.com/flutter/plugins/pulls) are most welcome!
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
To use this plugin, add `firebase_core` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
See the `example` directory for a complete sample app using Firebase Core.
|
|
@ -0,0 +1,51 @@
|
||||||
|
def PLUGIN = "firebase_core";
|
||||||
|
def ANDROIDX_WARNING = "flutterPluginsAndroidXWarning";
|
||||||
|
gradle.buildFinished { buildResult ->
|
||||||
|
if (buildResult.failure && !rootProject.ext.has(ANDROIDX_WARNING)) {
|
||||||
|
println ' *********************************************************'
|
||||||
|
println 'WARNING: This version of ' + PLUGIN + ' will break your Android build if it or its dependencies aren\'t compatible with AndroidX.'
|
||||||
|
println ' See https://goo.gl/CP92wY for more information on the problem and how to fix it.'
|
||||||
|
println ' This warning prints for all Android build failures. The real root cause of the error may be unrelated.'
|
||||||
|
println ' *********************************************************'
|
||||||
|
rootProject.ext.set(ANDROIDX_WARNING, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group 'io.flutter.plugins.firebase.core'
|
||||||
|
version '1.0-SNAPSHOT'
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:3.3.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 28
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 16
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
lintOptions {
|
||||||
|
disable 'InvalidPackage'
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
api 'com.google.firebase:firebase-core:16.0.9'
|
||||||
|
implementation 'com.google.firebase:firebase-common:16.1.0'
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
org.gradle.jvmargs=-Xmx1536M
|