├── .github ├── workflows │ ├── scripts │ │ ├── remoteconfig.template.json │ │ ├── firestore.indexes.json │ │ ├── .firebaserc │ │ ├── storage.rules │ │ ├── install-flutter.sh │ │ ├── database.rules.json │ │ ├── install-tools.sh │ │ ├── drive-app.sh │ │ ├── firestore.rules │ │ ├── firebase.json │ │ └── start-firebase-emulator.sh │ └── ci.yml └── PULL_REQUEST_TEMPLATE.md ├── packages └── firebase_snippets_app │ ├── remoteconfig.template.json │ ├── firestore.indexes.json │ ├── .firebaserc │ ├── android │ ├── gradle.properties │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── drawable-v21 │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── values │ │ │ │ │ │ └── styles.xml │ │ │ │ │ └── values-night │ │ │ │ │ │ └── styles.xml │ │ │ │ ├── kotlin │ │ │ │ │ └── com │ │ │ │ │ │ └── example │ │ │ │ │ │ └── firebase_snippets_app │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ ├── google-services.json │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── .gitignore │ ├── settings.gradle │ └── build.gradle │ ├── storage.rules │ ├── database.rules.json │ ├── .metadata │ ├── lib │ ├── snippets │ │ ├── crashlytics.dart │ │ ├── firestore_odm │ │ │ ├── address_model.dart │ │ │ └── user_model.dart │ │ ├── snippet_base.dart │ │ ├── firestore_odm.dart │ │ ├── cloud_functions.dart │ │ ├── remote_config.dart │ │ ├── ml_model_downloader.dart │ │ ├── cloud_messaging.dart │ │ ├── firebase_performance.dart │ │ ├── dynamic_links.dart │ │ ├── database.dart │ │ ├── analytics.dart │ │ ├── cloud_storage.dart │ │ └── auth.dart │ ├── model │ │ ├── restaurant.dart │ │ └── firestore_add_data_custom_objects_snippet.dart │ ├── widgets │ │ ├── user.dart │ │ ├── users_list.dart │ │ └── user_info_streambuilder.dart │ ├── firebase_options.dart │ ├── main.dart │ └── app.dart │ ├── firestore.rules │ ├── integration_test │ └── app_test.dart │ ├── README.md │ ├── .gitignore │ ├── firebase.json │ ├── test │ └── widget_test.dart │ ├── pubspec.yaml │ ├── analysis_options.yaml │ └── pubspec.lock ├── analysis_options.yml ├── .gitignore ├── CONTRIBUTING.md ├── README.md └── LICENSE /.github/workflows/scripts/remoteconfig.template.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /packages/firebase_snippets_app/remoteconfig.template.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.github/workflows/scripts/firestore.indexes.json: -------------------------------------------------------------------------------- 1 | { 2 | "indexes": [], 3 | "fieldOverrides": [] 4 | } 5 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/firestore.indexes.json: -------------------------------------------------------------------------------- 1 | { 2 | "indexes": [], 3 | "fieldOverrides": [] 4 | } 5 | -------------------------------------------------------------------------------- /.github/workflows/scripts/.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "flutter-fire-snippets" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "flutter-fire-snippets" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | android.defaults.buildfeatures.buildconfig=true 5 | -------------------------------------------------------------------------------- /.github/workflows/scripts/storage.rules: -------------------------------------------------------------------------------- 1 | rules_version = '2'; 2 | service firebase.storage { 3 | match /b/{bucket}/o { 4 | match /{allPaths=**} { 5 | allow read, write: if true; 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/storage.rules: -------------------------------------------------------------------------------- 1 | rules_version = '2'; 2 | service firebase.storage { 3 | match /b/{bucket}/o { 4 | match /{allPaths=**} { 5 | allow read, write: if true; 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/snippets-flutter/HEAD/packages/firebase_snippets_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/snippets-flutter/HEAD/packages/firebase_snippets_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/snippets-flutter/HEAD/packages/firebase_snippets_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/snippets-flutter/HEAD/packages/firebase_snippets_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/snippets-flutter/HEAD/packages/firebase_snippets_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /.github/workflows/scripts/install-flutter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BRANCH=$1 4 | git clone https://github.com/flutter/flutter.git --depth 1 -b $BRANCH "$GITHUB_WORKSPACE/_flutter" 5 | echo "$GITHUB_WORKSPACE/_flutter/bin" >> $GITHUB_PATH 6 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/app/src/main/kotlin/com/example/firebase_snippets_app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.firebase_snippets_app 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /.github/workflows/scripts/database.rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | ".read": "false", 4 | ".write": "false", 5 | "posts": { 6 | ".read": "true", 7 | ".write": "true" 8 | }, 9 | "users": { 10 | ".read": "true", 11 | ".write": "true" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /packages/firebase_snippets_app/database.rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | ".read": "false", 4 | ".write": "false", 5 | "posts": { 6 | ".read": "true", 7 | ".write": "true" 8 | }, 9 | "users": { 10 | ".read": "true", 11 | ".write": "true" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /.github/workflows/scripts/install-tools.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | flutter config --no-analytics 4 | flutter pub global activate melos 1.1.0 5 | echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH 6 | echo "$GITHUB_WORKSPACE/_flutter/.pub-cache/bin" >> $GITHUB_PATH 7 | echo "$GITHUB_WORKSPACE/_flutter/bin/cache/dart-sdk/bin" >> $GITHUB_PATH 8 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-all.zip 7 | -------------------------------------------------------------------------------- /.github/workflows/scripts/drive-app.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PROJECT_NAME=$1 4 | TARGET_PATH="packages/$PROJECT_NAME/" 5 | 6 | # The && are necessary for some reason when using the Android Emulator action 7 | pushd "$TARGET_PATH" || exit && 8 | flutter clean && 9 | flutter pub get && 10 | dart format . && 11 | flutter test integration_test 12 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: db747aa1331bd95bc9b3874c842261ca2d302cd5 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/snippets/crashlytics.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_snippets_app/snippets/snippet_base.dart'; 2 | 3 | class CrashlyticsSnippets implements DocSnippet { 4 | @override 5 | void runAll() { 6 | // Crashlytics testing isn't yet supported, 7 | // since it requires crashing the app to set up 8 | // and this will require additional CI work 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/workflows/scripts/firestore.rules: -------------------------------------------------------------------------------- 1 | rules_version = '2'; 2 | service cloud.firestore { 3 | match /databases/{database}/documents { 4 | match /{document=**} { 5 | allow read, write: if false; 6 | } 7 | 8 | match /users/{document=**} { 9 | allow read, write; 10 | } 11 | 12 | match /cities/{document=**} { 13 | allow read, write; 14 | } 15 | 16 | match /restaurants/{document=**} { 17 | allow read, write; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /packages/firebase_snippets_app/firestore.rules: -------------------------------------------------------------------------------- 1 | rules_version = '2'; 2 | service cloud.firestore { 3 | match /databases/{database}/documents { 4 | match /{document=**} { 5 | allow read, write: if false; 6 | } 7 | 8 | match /users/{document=**} { 9 | allow read, write; 10 | } 11 | 12 | match /cities/{document=**} { 13 | allow read, write; 14 | } 15 | 16 | match /restaurants/{document=**} { 17 | allow read, write; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | _Description of what this PR is changing or adding:_ 2 | 3 | _Issues fixed by this PR (if any):_ 4 | 5 | ## Risk Level 6 | - [ ] No risk 7 | - [ ] Somewhat Risky 8 | - [ ] High risk 9 | 10 | ## Pre-submit checklist 11 | - [ ] This PR follows the [Google Developer Documentation Style Guidelines](https://developers.google.com/style) 12 | - [ ] This PR uses [semantic line breaks](https://github.com/dart-lang/site-shared/blob/main/doc/writing-for-dart-and-flutter-websites.md#semantic-line-breaks) -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/snippets/firestore_odm/address_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'address_model.g.dart'; 4 | 5 | // [START sub_collections_define] 6 | @JsonSerializable() 7 | class Address { 8 | final String streetName; 9 | 10 | Address({required this.streetName}); 11 | 12 | factory Address.fromJson(Map json) => 13 | _$AddressFromJson(json); 14 | 15 | Map toJson() => _$AddressToJson(this); 16 | } 17 | // [END sub_collections_define] 18 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/integration_test/app_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_snippets_app/main.dart' as app; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:integration_test/integration_test.dart'; 4 | 5 | void main() { 6 | IntegrationTestWidgetsFlutterBinding.ensureInitialized(); 7 | 8 | group('end-to-end test', () { 9 | testWidgets( 10 | 'run the app, which calls all firebase snippet functions on start', 11 | (WidgetTester tester) async { 12 | app.main(); 13 | await tester.pumpAndSettle(); 14 | }); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/README.md: -------------------------------------------------------------------------------- 1 | # firebase_snippets_app 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /analysis_options.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | include: package:flutter_lints/flutter.yaml -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/snippets/snippet_base.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | abstract class DocSnippet { 16 | void runAll(); 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | *.g.dart 34 | 35 | # Web related 36 | lib/generated_plugin_registrant.dart 37 | 38 | # Symbolication related 39 | app.*.symbols 40 | 41 | # Obfuscation related 42 | app.*.map.json 43 | 44 | # Android Studio will place build artifacts here 45 | /android/app/debug 46 | /android/app/profile 47 | /android/app/release 48 | -------------------------------------------------------------------------------- /.github/workflows/scripts/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "database": { 3 | "rules": "database.rules.json" 4 | }, 5 | "firestore": { 6 | "rules": "firestore.rules", 7 | "indexes": "firestore.indexes.json" 8 | }, 9 | "hosting": { 10 | "public": "web", 11 | "ignore": [ 12 | "firebase.json", 13 | "**/.*", 14 | "**/node_modules/**" 15 | ] 16 | }, 17 | "storage": { 18 | "rules": "storage.rules" 19 | }, 20 | "emulators": { 21 | "auth": { 22 | "port": 9099 23 | }, 24 | "firestore": { 25 | "port": 8080 26 | }, 27 | "database": { 28 | "port": 9000 29 | }, 30 | "hosting": { 31 | "port": 3030 32 | }, 33 | "pubsub": { 34 | "port": 8085 35 | }, 36 | "storage": { 37 | "port": 9199 38 | }, 39 | "ui": { 40 | "enabled": true 41 | } 42 | }, 43 | "remoteconfig": { 44 | "template": "remoteconfig.template.json" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/model/restaurant.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | class Restaurant { 16 | final String name; 17 | double avgRating; 18 | int numRatings; 19 | 20 | Restaurant( 21 | this.name, 22 | this.avgRating, 23 | this.numRatings, 24 | ); 25 | 26 | factory Restaurant.fromFirestore(Object? document) { 27 | return Restaurant("fake data", 3.4, 11); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "database": { 3 | "rules": "database.rules.json" 4 | }, 5 | "firestore": { 6 | "rules": "firestore.rules", 7 | "indexes": "firestore.indexes.json" 8 | }, 9 | "hosting": { 10 | "public": "web", 11 | "ignore": [ 12 | "firebase.json", 13 | "**/.*", 14 | "**/node_modules/**" 15 | ] 16 | }, 17 | "storage": { 18 | "rules": "storage.rules" 19 | }, 20 | "emulators": { 21 | "auth": { 22 | "port": 9099 23 | }, 24 | "firestore": { 25 | "port": 8080 26 | }, 27 | "database": { 28 | "port": 9000 29 | }, 30 | "hosting": { 31 | "port": 3030 32 | }, 33 | "pubsub": { 34 | "port": 8085 35 | }, 36 | "storage": { 37 | "port": 9199 38 | }, 39 | "ui": { 40 | "enabled": true 41 | }, 42 | "functions": { 43 | "port": 5001 44 | } 45 | }, 46 | "remoteconfig": { 47 | "template": "remoteconfig.template.json" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/snippets/firestore_odm.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: non_constant_identifier_names 2 | 3 | import 'package:firebase_snippets_app/snippets/firestore_odm/user_model.dart'; 4 | import 'package:firebase_snippets_app/snippets/snippet_base.dart'; 5 | 6 | class ODMSnippets extends DocSnippet { 7 | @override 8 | void runAll() { 9 | /// testing ODM isn't yet supported. 10 | /// It's still in Alpha, and it will require additional CI work 11 | } 12 | 13 | void references_performQueries() async { 14 | // [START references_perform_queries] 15 | usersRef.whereName(isEqualTo: 'John'); 16 | usersRef.whereAge(isGreaterThan: 18); 17 | usersRef.orderByAge(); 18 | // ..etc! 19 | // [END references_perform_queries] 20 | } 21 | 22 | void subCollection_accessSubCollection() async { 23 | // [START sub_collection_access_sub_collection] 24 | AddressCollectionReference addressesRef = 25 | usersRef.doc('myDocumentID').addresses; 26 | // [END sub_collection_access_sub_collection] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/snippets/firestore_odm/user_model.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: non_constant_identifier_names 2 | 3 | import 'package:cloud_firestore/cloud_firestore.dart'; 4 | import 'package:cloud_firestore_odm/cloud_firestore_odm.dart'; 5 | import 'package:firebase_snippets_app/snippets/firestore_odm/address_model.dart'; 6 | import 'package:json_annotation/json_annotation.dart'; 7 | 8 | part 'user_model.g.dart'; 9 | 10 | @JsonSerializable() 11 | class User { 12 | User({ 13 | required this.name, 14 | required this.age, 15 | required this.email, 16 | required this.address, 17 | }) { 18 | _$assertUser(this); 19 | } 20 | 21 | factory User.fromJson(Map json) => _$UserFromJson(json); 22 | 23 | final String name; 24 | final String email; 25 | final Address address; 26 | 27 | @Min(0) 28 | final int age; 29 | 30 | Map toJson() => _$UserToJson(this); 31 | } 32 | // [END defining_models] 33 | 34 | // [START references_collection_ref] 35 | @Collection('users') 36 | @Collection
('users/*/addresses') 37 | final usersRef = UserCollectionReference(); 38 | // [END references_collection_ref] 39 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement (CLA). You (or your employer) retain the copyright to your 10 | contribution; this simply gives us permission to use and redistribute your 11 | contributions as part of the project. Head over to 12 | to see your current agreements on file or 13 | to sign a new one. 14 | 15 | You generally only need to submit a CLA once, so if you've already submitted one 16 | (even if it was for a different project), you probably don't need to do it 17 | again. 18 | 19 | ## Code Reviews 20 | 21 | All submissions, including submissions by project members, require review. We 22 | use GitHub pull requests for this purpose. Consult 23 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 24 | information on using pull requests. 25 | 26 | ## Community Guidelines 27 | 28 | This project follows 29 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). 30 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/widgets/user.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: use_key_in_widget_constructors 2 | 3 | import 'package:cloud_firestore_odm/cloud_firestore_odm.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | import '../snippets/firestore_odm/user_model.dart'; 7 | 8 | class UserLabel extends StatelessWidget { 9 | const UserLabel(this.id); 10 | 11 | final String id; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return FirestoreBuilder( 16 | // Access a specific document 17 | ref: usersRef.doc(id), 18 | builder: (context, AsyncSnapshot snapshot, 19 | Widget? child) { 20 | if (snapshot.hasError) return const Text('Something went wrong!'); 21 | if (!snapshot.hasData) return const Text('Loading user...'); 22 | 23 | // Access the UserDocumentSnapshot 24 | UserDocumentSnapshot documentSnapshot = snapshot.requireData; 25 | 26 | if (!documentSnapshot.exists) { 27 | return const Text('User does not exist.'); 28 | } 29 | 30 | User user = documentSnapshot.data!; 31 | 32 | return Text('User name: ${user.name}, age ${user.age}'); 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.9.0' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:8.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | classpath 'com.google.gms:google-services:4.3.15' 12 | classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1' 13 | 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | } 22 | 23 | subprojects { 24 | afterEvaluate { project -> 25 | if (project.hasProperty('android')) { 26 | project.android { 27 | if (namespace == null) { 28 | namespace project.group 29 | } 30 | } 31 | } 32 | } 33 | } 34 | } 35 | 36 | rootProject.buildDir = '../build' 37 | subprojects { 38 | project.buildDir = "${rootProject.buildDir}/${project.name}" 39 | } 40 | subprojects { 41 | project.evaluationDependsOn(':app') 42 | } 43 | 44 | tasks.register("clean", Delete) { 45 | delete rootProject.buildDir 46 | } 47 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/widgets/users_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore_odm/cloud_firestore_odm.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../snippets/firestore_odm/user_model.dart'; 5 | 6 | // Not currently in the docs, so not currently being tested. 7 | class UsersList extends StatelessWidget { 8 | const UsersList({Key? key}) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return FirestoreBuilder( 13 | ref: usersRef, 14 | builder: (context, AsyncSnapshot snapshot, 15 | Widget? child) { 16 | if (snapshot.hasError) return const Text('Something went wrong!'); 17 | if (!snapshot.hasData) return const Text('Loading users...'); 18 | 19 | // Access the QuerySnapshot 20 | UserQuerySnapshot querySnapshot = snapshot.requireData; 21 | 22 | return ListView.builder( 23 | itemCount: querySnapshot.docs.length, 24 | itemBuilder: (context, index) { 25 | // Access the User instance 26 | User user = querySnapshot.docs[index].data; 27 | 28 | return Text('User name: ${user.name}, age ${user.age}'); 29 | }, 30 | ); 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "220375889386", 4 | "project_id": "flutter-fire-snippets", 5 | "storage_bucket": "flutter-fire-snippets.appspot.com" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:220375889386:android:1a16e85eeab0d5896bae93", 11 | "android_client_info": { 12 | "package_name": "com.example.firebase_snippets_app" 13 | } 14 | }, 15 | "oauth_client": [ 16 | { 17 | "client_id": "220375889386-2k28ra1b5iq3p2qbh61ag1o4rt24asb6.apps.googleusercontent.com", 18 | "client_type": 3 19 | } 20 | ], 21 | "api_key": [ 22 | { 23 | "current_key": "AIzaSyC2bbpFcMWgGpKKEmqNw57Al2vdV4QnNP0" 24 | } 25 | ], 26 | "services": { 27 | "appinvite_service": { 28 | "other_platform_oauth_client": [ 29 | { 30 | "client_id": "220375889386-2k28ra1b5iq3p2qbh61ag1o4rt24asb6.apps.googleusercontent.com", 31 | "client_type": 3 32 | }, 33 | { 34 | "client_id": "220375889386-8v73sv2lia3e7c12h0btp2tfbj0um4it.apps.googleusercontent.com", 35 | "client_type": 2, 36 | "ios_info": { 37 | "bundle_id": "com.fluttersnippets.app" 38 | } 39 | } 40 | ] 41 | } 42 | } 43 | } 44 | ], 45 | "configuration_version": "1" 46 | } -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/widgets/user_info_streambuilder.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | // [START listen_to_realtime_updates_listen_for_updates2] 5 | class UserInformation extends StatefulWidget { 6 | @override 7 | _UserInformationState createState() => _UserInformationState(); 8 | } 9 | 10 | class _UserInformationState extends State { 11 | final Stream _usersStream = 12 | FirebaseFirestore.instance.collection('users').snapshots(); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return StreamBuilder( 17 | stream: _usersStream, 18 | builder: (BuildContext context, AsyncSnapshot snapshot) { 19 | if (snapshot.hasError) { 20 | return const Text('Something went wrong'); 21 | } 22 | 23 | if (snapshot.connectionState == ConnectionState.waiting) { 24 | return const Text("Loading"); 25 | } 26 | 27 | return ListView( 28 | children: snapshot.data!.docs 29 | .map((DocumentSnapshot document) { 30 | Map data = 31 | document.data()! as Map; 32 | return ListTile( 33 | title: Text(data['full_name']), 34 | subtitle: Text(data['company']), 35 | ); 36 | }) 37 | .toList() 38 | .cast(), 39 | ); 40 | }, 41 | ); 42 | } 43 | } 44 | // [END listen_to_realtime_updates_listen_for_updates2] 45 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // This is a basic Flutter widget test. 16 | // 17 | // To perform an interaction with a widget in your test, use the WidgetTester 18 | // utility that Flutter provides. For example, you can send tap and scroll 19 | // gestures. You can also use WidgetTester to find child widgets in the widget 20 | // tree, read text, and verify that the values of widget properties are correct. 21 | 22 | import 'package:flutter_test/flutter_test.dart'; 23 | 24 | void main() { 25 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 26 | // Build our app and trigger a frame. 27 | // await tester.pumpWidget(const MyApp()); 28 | 29 | // Verify that our counter starts at 0. 30 | // expect(find.text('0'), findsOneWidget); 31 | // expect(find.text('1'), findsNothing); 32 | // 33 | // // Tap the '+' icon and trigger a frame. 34 | // await tester.tap(find.byIcon(Icons.add)); 35 | // await tester.pump(); 36 | // 37 | // // Verify that our counter has incremented. 38 | // expect(find.text('0'), findsNothing); 39 | // expect(find.text('1'), findsOneWidget); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /.github/workflows/scripts/start-firebase-emulator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if ! [ -x "$(command -v firebase)" ]; then 3 | echo "❌ Firebase tools CLI is missing." 4 | exit 1 5 | fi 6 | 7 | if ! [ -x "$(command -v node)" ]; then 8 | echo "❌ Node.js is missing." 9 | exit 1 10 | fi 11 | 12 | if ! [ -x "$(command -v npm)" ]; then 13 | echo "❌ NPM is missing." 14 | exit 1 15 | fi 16 | 17 | export STORAGE_EMULATOR_DEBUG=true 18 | EMU_START_COMMAND="firebase emulators:start --only firestore --project flutter-fire-snippets" 19 | 20 | MAX_RETRIES=3 21 | MAX_CHECKATTEMPTS=60 22 | CHECKATTEMPTS_WAIT=1 23 | 24 | RETRIES=1 25 | while [ $RETRIES -le $MAX_RETRIES ]; do 26 | 27 | if [[ -z "${CI}" ]]; then 28 | echo "Starting Firebase Emulator Suite in foreground." 29 | $EMU_START_COMMAND 30 | exit 0 31 | else 32 | echo "Starting Firebase Emulator Suite in background." 33 | $EMU_START_COMMAND & 34 | CHECKATTEMPTS=1 35 | while [ $CHECKATTEMPTS -le $MAX_CHECKATTEMPTS ]; do 36 | sleep $CHECKATTEMPTS_WAIT 37 | if curl --output /dev/null --silent --fail http://localhost:8080; then 38 | # Check again since it can exit before the emulator is ready. 39 | sleep 15 40 | if curl --output /dev/null --silent --fail http://localhost:8080; then 41 | echo "Firebase Emulator Suite is online!" 42 | exit 0 43 | else 44 | echo "❌ Firebase Emulator exited after startup." 45 | exit 1 46 | fi 47 | fi 48 | echo "Waiting for Firebase Emulator Suite to come online, check $CHECKATTEMPTS of $MAX_CHECKATTEMPTS..." 49 | ((CHECKATTEMPTS = CHECKATTEMPTS + 1)) 50 | done 51 | fi 52 | 53 | echo "Firebase Emulator Suite did not come online in $MAX_CHECKATTEMPTS checks. Try $RETRIES of $MAX_RETRIES." 54 | ((RETRIES = RETRIES + 1)) 55 | 56 | done 57 | echo "Firebase Emulator Suite did not come online after $MAX_RETRIES attempts." 58 | exit 1 59 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/pubspec.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: firebase_snippets_app 16 | description: A new Flutter project. 17 | 18 | publish_to: 'none' 19 | version: 1.0.0+1 20 | 21 | environment: 22 | sdk: ">=2.16.1 <3.0.0" 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | firebase_core: ^3.10.0 27 | cloud_firestore: ^5.6.1 28 | cloud_firestore_odm: ^1.0.0-dev.11 29 | cloud_functions: ^5.3.0 30 | firebase_remote_config: ^5.3.0 31 | firebase_analytics: ^11.4.0 32 | firebase_storage: ^12.4.0 33 | firebase_messaging: ^15.2.0 34 | firebase_dynamic_links: ^6.1.0 35 | firebase_ml_model_downloader: ^0.3.2 36 | firebase_auth: ^5.4.0 37 | path_provider: ^2.0.9 38 | firebase_database: ^11.3.0 39 | firebase_performance: ^0.10.1 40 | json_annotation: ^4.4.0 41 | google_sign_in: ^6.1.0 42 | flutter_facebook_auth: ^7.0.0 43 | twitter_login: ^4.4.2 44 | sign_in_with_apple: ^6.1.0 45 | crypto: ^3.0.1 46 | firebase_crashlytics: ^4.3.0 47 | firebase_auth_platform_interface: ^7.3.0 48 | http: ^1.2.2 49 | win32: ^5.5.4 50 | 51 | dev_dependencies: 52 | flutter_test: 53 | sdk: flutter 54 | integration_test: 55 | sdk: flutter 56 | flutter_lints: ^5.0.0 57 | build_runner: ^2.1.8 58 | cloud_firestore_odm_generator: ^1.0.0-dev.11 59 | json_serializable: ^6.1.5 60 | 61 | flutter: 62 | uses-material-design: true 63 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Run CI 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | types: [opened, reopened, synchronize] 7 | paths: 8 | - "packages/**" 9 | - ".github/workflows" 10 | push: 11 | paths-ignore: 12 | - "**.md" 13 | 14 | jobs: 15 | android: 16 | runs-on: ubuntu-latest 17 | timeout-minutes: 30 18 | steps: 19 | 20 | - name: "Git Checkout" 21 | uses: actions/checkout@v2 22 | 23 | - name: Enable KVM 24 | run: | 25 | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules 26 | sudo udevadm control --reload-rules 27 | sudo udevadm trigger --name-match=kvm 28 | 29 | - name: "Install Node" 30 | uses: actions/setup-node@v2 31 | with: 32 | node-version: '20' 33 | 34 | - uses: actions/setup-java@v2 35 | name: "Install Java" 36 | with: 37 | distribution: 'temurin' 38 | java-version: '17' 39 | 40 | - name: "Install Flutter" 41 | run: | 42 | sh .github/workflows/scripts/install-flutter.sh stable 43 | 44 | - name: "Install Tools" 45 | run: | 46 | sh ./.github/workflows/scripts/install-tools.sh 47 | flutter config --no-enable-web --no-enable-ios --no-enable-macos-desktop 48 | sudo npm i -g firebase-tools 49 | # 50 | - name: "Start Firebase Emulator" 51 | run: | 52 | sh ./.github/workflows/scripts/start-firebase-emulator.sh 53 | 54 | - name: "Drive Example" 55 | uses: reactivecircus/android-emulator-runner@v2 56 | with: 57 | api-level: 28 58 | arch: x86_64 59 | disable-animations: true 60 | # Firebase Firestore works without Google Play Services, so we don't use the `googleapis` 61 | # emulator target as it's considerably slower on CI. 62 | target: default 63 | profile: Nexus 5X 64 | script: bash ./.github/workflows/scripts/drive-app.sh firebase_snippets_app 65 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/model/firestore_add_data_custom_objects_snippet.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import 'package:cloud_firestore/cloud_firestore.dart'; 16 | 17 | // [START add_data_custom_objects] 18 | class City { 19 | final String? name; 20 | final String? state; 21 | final String? country; 22 | final bool? capital; 23 | final int? population; 24 | final List? regions; 25 | 26 | City({ 27 | this.name, 28 | this.state, 29 | this.country, 30 | this.capital, 31 | this.population, 32 | this.regions, 33 | }); 34 | 35 | factory City.fromFirestore( 36 | DocumentSnapshot> snapshot, 37 | SnapshotOptions? options, 38 | ) { 39 | final data = snapshot.data(); 40 | return City( 41 | name: data?['name'], 42 | state: data?['state'], 43 | country: data?['country'], 44 | capital: data?['capital'], 45 | population: data?['population'], 46 | regions: 47 | data?['regions'] is Iterable ? List.from(data?['regions']) : null, 48 | ); 49 | } 50 | 51 | Map toFirestore() { 52 | return { 53 | if (name != null) "name": name, 54 | if (state != null) "state": state, 55 | if (country != null) "country": country, 56 | if (capital != null) "capital": capital, 57 | if (population != null) "population": population, 58 | if (regions != null) "regions": regions, 59 | }; 60 | } 61 | } 62 | // [END add_data_custom_objects] 63 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/firebase_options.dart: -------------------------------------------------------------------------------- 1 | // File generated by FlutterFire CLI. 2 | // ignore_for_file: lines_longer_than_80_chars 3 | import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; 4 | import 'package:flutter/foundation.dart' 5 | show defaultTargetPlatform, kIsWeb, TargetPlatform; 6 | 7 | /// Default [FirebaseOptions] for use with your Firebase apps. 8 | /// 9 | /// Example: 10 | /// ```dart 11 | /// import 'firebase_options.dart'; 12 | /// // ... 13 | /// await Firebase.initializeApp( 14 | /// options: DefaultFirebaseOptions.currentPlatform, 15 | /// ); 16 | /// ``` 17 | class DefaultFirebaseOptions { 18 | static FirebaseOptions get currentPlatform { 19 | if (kIsWeb) { 20 | throw UnsupportedError( 21 | 'DefaultFirebaseOptions have not been configured for web - ' 22 | 'you can reconfigure this by running the FlutterFire CLI again.', 23 | ); 24 | } 25 | // ignore: missing_enum_constant_in_switch 26 | switch (defaultTargetPlatform) { 27 | case TargetPlatform.android: 28 | return android; 29 | case TargetPlatform.iOS: 30 | throw UnsupportedError( 31 | 'DefaultFirebaseOptions have not been configured for ios - ' 32 | 'you can reconfigure this by running the FlutterFire CLI again.', 33 | ); 34 | case TargetPlatform.macOS: 35 | throw UnsupportedError( 36 | 'DefaultFirebaseOptions have not been configured for macos - ' 37 | 'you can reconfigure this by running the FlutterFire CLI again.', 38 | ); 39 | } 40 | 41 | throw UnsupportedError( 42 | 'DefaultFirebaseOptions are not supported for this platform.', 43 | ); 44 | } 45 | 46 | static const FirebaseOptions android = FirebaseOptions( 47 | apiKey: 'AIzaSyC2bbpFcMWgGpKKEmqNw57Al2vdV4QnNP0', 48 | appId: '1:220375889386:android:1a16e85eeab0d5896bae93', 49 | messagingSenderId: '220375889386', 50 | projectId: 'flutter-fire-snippets', 51 | databaseURL: 'https://flutter-fire-snippets-default-rtdb.firebaseio.com', 52 | storageBucket: 'flutter-fire-snippets.appspot.com', 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/snippets/cloud_functions.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ignore_for_file: non_constant_identifier_names, avoid_print 16 | 17 | import 'package:cloud_functions/cloud_functions.dart'; 18 | import 'package:firebase_snippets_app/snippets/snippet_base.dart'; 19 | 20 | class CloudFunctionsSnippets implements DocSnippet { 21 | late final FirebaseFunctions functions; 22 | 23 | CloudFunctionsSnippets() { 24 | functions = FirebaseFunctions.instance; 25 | } 26 | 27 | @override 28 | void runAll() { 29 | addMessage('text'); 30 | callFunctionsFromYourApp_handleErrorsOnTheClient(); 31 | } 32 | 33 | // [START call_functions_from_your_app_call_the_function] 34 | Future addMessage(String text) { 35 | final data = { 36 | "text": text, 37 | "push": true, 38 | }; 39 | 40 | final addMessage = functions.httpsCallable("addMessage"); 41 | return addMessage(data); 42 | } 43 | // [END call_functions_from_your_app_call_the_function] 44 | 45 | void callFunctionsFromYourApp_handleErrorsOnTheClient() { 46 | // [START call_functions_from_your_app_handle_errors_on_the_client] 47 | final addMessage = functions.httpsCallable("addMessage"); 48 | addMessage({"text": "message text"}).then( 49 | // Read result of the Cloud Function. 50 | (result) => print(result.data['text']), 51 | onError: (e) => print("Error calling function: $e"), 52 | ); 53 | // [END call_functions_from_your_app_handle_errors_on_the_client] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # This file configures the analyzer, which statically analyzes Dart code to 16 | # check for errors, warnings, and lints. 17 | # 18 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 19 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 20 | # invoked from the command line by running `flutter analyze`. 21 | 22 | # The following line activates a set of recommended lints for Flutter apps, 23 | # packages, and plugins designed to encourage good coding practices. 24 | include: package:flutter_lints/flutter.yaml 25 | 26 | linter: 27 | # The lint rules applied to this project can be customized in the 28 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 29 | # included above or to enable additional rules. A list of all available lints 30 | # and their documentation is published at 31 | # https://dart-lang.github.io/linter/lints/index.html. 32 | # 33 | # Instead of disabling a lint rule for the entire project in the 34 | # section below, it can also be suppressed for a single line of code 35 | # or a specific dart file by using the `// ignore: name_of_lint` and 36 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 37 | # producing the lint. 38 | rules: 39 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 40 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 41 | 42 | # Additional information about this file can be found at 43 | # https://dart.dev/guides/language/analysis-options 44 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'com.google.gms.google-services' 26 | apply plugin: 'kotlin-android' 27 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 28 | 29 | android { 30 | namespace = "com.example.firebase_snippets_app" 31 | compileSdkVersion flutter.compileSdkVersion 32 | 33 | buildFeatures { 34 | buildConfig true 35 | } 36 | 37 | compileOptions { 38 | sourceCompatibility JavaVersion.VERSION_1_8 39 | targetCompatibility JavaVersion.VERSION_1_8 40 | } 41 | 42 | kotlinOptions { 43 | jvmTarget = '1.8' 44 | } 45 | 46 | sourceSets { 47 | main.java.srcDirs += 'src/main/kotlin' 48 | } 49 | 50 | defaultConfig { 51 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 52 | applicationId "com.example.firebase_snippets_app" 53 | minSdkVersion 23 54 | targetSdkVersion flutter.targetSdkVersion 55 | versionCode flutterVersionCode.toInteger() 56 | versionName flutterVersionName 57 | } 58 | 59 | buildTypes { 60 | release { 61 | // TODO: Add your own signing config for the release build. 62 | // Signing with the debug keys for now, so `flutter run --release` works. 63 | signingConfig signingConfigs.debug 64 | } 65 | } 66 | } 67 | 68 | flutter { 69 | source '../..' 70 | } 71 | 72 | dependencies { 73 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 74 | implementation platform('com.google.firebase:firebase-bom:29.3.0') 75 | } 76 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/snippets/remote_config.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ignore_for_file: non_constant_identifier_names, avoid_print 16 | 17 | import 'package:firebase_remote_config/firebase_remote_config.dart'; 18 | import 'package:firebase_snippets_app/snippets/snippet_base.dart'; 19 | 20 | class RemoteConfigSnippets extends DocSnippet { 21 | final FirebaseRemoteConfig firebaseRemoteConfig; 22 | 23 | RemoteConfigSnippets(this.firebaseRemoteConfig); 24 | 25 | @override 26 | void runAll() { 27 | getStarted_setMinimumIntervalFetch(); 28 | getStarted_setDefaultValues(); 29 | getStarted_getDefaultValues(); 30 | getStarted_fetchAndActivateValues(); 31 | } 32 | 33 | void getStarted_setMinimumIntervalFetch() { 34 | // [START get_started_set_minimum_interval_fetch] 35 | firebaseRemoteConfig.settings.minimumFetchInterval = 36 | const Duration(milliseconds: 3600000); 37 | // [END get_started_set_minimum_interval_fetch] 38 | } 39 | 40 | void getStarted_setDefaultValues() { 41 | // [START get_started_set_default_values] 42 | firebaseRemoteConfig.setDefaults({ 43 | 'welcome_message': 'this is the default welcome message', 44 | }); 45 | // [END get_started_set_default_values] 46 | } 47 | 48 | void getStarted_getDefaultValues() { 49 | // [START get_started_get_default_values] 50 | final val = firebaseRemoteConfig.getValue("welcome_messsage"); 51 | // [END get_started_get_default_values] 52 | } 53 | 54 | void getStarted_fetchAndActivateValues() { 55 | // [START get_started_fetch_and_activate_values] 56 | firebaseRemoteConfig.fetchAndActivate().then((bool success) { 57 | if (success) { 58 | final updatedConfig = firebaseRemoteConfig.getAll(); 59 | print("Config params updated: $updatedConfig"); 60 | } 61 | }); 62 | // [END get_started_fetch_and_activate_values] 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/snippets/ml_model_downloader.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: non_constant_identifier_names, avoid_print 2 | 3 | import 'package:firebase_core/firebase_core.dart'; 4 | import 'package:firebase_ml_model_downloader/firebase_ml_model_downloader.dart'; 5 | import 'package:firebase_snippets_app/snippets/snippet_base.dart'; 6 | 7 | class MLModelDownloaderSnippets implements DocSnippet { 8 | @override 9 | void runAll() { 10 | // This plug in is in beta. It won't be tested for now. 11 | } 12 | 13 | void mlDownloader_createReference() async { 14 | // [START ml_downloader_create_reference] 15 | FirebaseModelDownloader downloader = FirebaseModelDownloader.instance; 16 | // [END ml_downloader_create_reference] 17 | 18 | // [START ml_downloader_secondary_app_reference] 19 | FirebaseApp secondaryApp = Firebase.app('SecondaryApp'); 20 | FirebaseModelDownloader secondaryDownloader = 21 | FirebaseModelDownloader.instanceFor(app: secondaryApp); 22 | // [END ml_downloader_secondary_app_reference] 23 | 24 | // [START ml_downloader_list_downloaded_models] 25 | List models = 26 | await FirebaseModelDownloader.instance.listDownloadedModels(); 27 | // [END ml_downloader_list_downloaded_models] 28 | 29 | // [START ml_downloader_custom_model] 30 | List customModels = 31 | await FirebaseModelDownloader.instance.listDownloadedModels(); 32 | 33 | for (var model in customModels) { 34 | print('Name: ${model.name}'); 35 | print('Size: ${model.size}'); 36 | print('Hash: ${model.hash}'); 37 | } 38 | // [END ml_downloader_custom_model] 39 | 40 | // [START ml_downloader_download_model] 41 | FirebaseCustomModel model = await FirebaseModelDownloader.instance 42 | .getModel('myModel', FirebaseModelDownloadType.latestModel); 43 | // [END ml_downloader_download_model] 44 | 45 | // [START ml_downloader_conditions] 46 | // The following are the default conditions: 47 | FirebaseModelDownloadConditions conditions = 48 | FirebaseModelDownloadConditions( 49 | // Download whilst connected to cellular data 50 | iosAllowsCellularAccess: true, 51 | // Allow downloading in the background 52 | iosAllowsBackgroundDownloading: false, 53 | // Only download whilst charging 54 | androidChargingRequired: false, 55 | // Only download whilst on Wifi 56 | androidWifiRequired: false, 57 | // Only download whilst the device is idle 58 | androidDeviceIdleRequired: false, 59 | ); 60 | 61 | FirebaseCustomModel modelWithConditions = await FirebaseModelDownloader 62 | .instance 63 | .getModel('myModel', FirebaseModelDownloadType.latestModel, conditions); 64 | // [END ml_downloader_conditions] 65 | 66 | // [START ml_downloader_delete_a_model] 67 | await FirebaseModelDownloader.instance.deleteDownloadedModel('myModel'); 68 | // [END ml_downloader_delete_a_model] 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Firebase Flutter Snippets 2 | 3 | This repository holds code snippets used in Flutter documentation 4 | on [firebase.google.com](https://firebase.google.com/docs/). 5 | 6 | These snippets are part of our documentation and best read in the context of a 7 | documentation page rather than used directly. 8 | 9 | Each snippet has a "region tag" which is defined by `// [START tag]` 10 | and `// [END tag]` comments. The code between the tags can be included in our 11 | documentation. Keeping the code on GitHub, rather than hard-coded into the HTML 12 | of our documentation, allows us to ensure the code is correct and up to date. 13 | 14 | ## Set up / Running Instructions - WIP 15 | 16 | - installed flutter, intellij, intellij plugins 17 | - created firebase project, downloaded firebase CLI, installed flutterfirecli 18 | - `flutter create firestore-snippets && cd firestore-snippets` 19 | - `flutterfire-configure` 20 | 21 | ## Snippet structure 22 | 23 | **Note**: Most snippets follow this structure, but in some cases multiple 24 | snippets are located in a single function. In this case, the function name 25 | represents which snippet the function name starts on. 26 | 27 | Each Firebase product (that requires Flutter snippets) has its own file located 28 | in the dir `apps/firestore_snippets/lib/snippets`. 29 | 30 | Each of these files defines a class called [Firebase Product Name]Snippets. 31 | These classes have a separate function for each snippet. These functions follow 32 | the naming convention of `firebaseDocsPageName_snippetTitle`. For 33 | example: `FirestoreSnippets.getStarted_addData`. Each snippet is also wrapped by 34 | comments which define the beginning and ending of the snippet, and generally 35 | excludes the function names. The code in this region, between the two comments, 36 | is the code that appears in the Firebase documentation. 37 | 38 | Example: 39 | 40 | See translated snippet in 41 | the [Firebase documentation](https://firebase.google.com/docs/firestore/quickstart#add_data) 42 | 43 | ```dart 44 | void getStarted_addData() async { 45 | // [START get_started_add_data_1] 46 | // Create a new user with a first and last name 47 | final user = { 48 | "first": "Ada", 49 | "last": "Lovelace", 50 | "born": 1815 51 | }; 52 | 53 | // Add a new document with a generated ID 54 | db.collection("users").add(user).then((DocumentReference doc) => 55 | print('DocumentSnapshot added with ID: ${doc.id}')); 56 | // [END get_started_add_data_1] 57 | } 58 | ``` 59 | 60 | [comment]: <> (## Example) 61 | 62 | [comment]: <> (TODO: ewindmill@ fill this in when first snippets are live.) 63 | 64 | [comment]: <> (## Contributing) 65 | 66 | [comment]: <> (TODO: ewindmill@ create the "contributing" docs) 67 | 68 | [comment]: <> (We love contributions! See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.) 69 | 70 | [comment]: <> (## Build Status) 71 | 72 | [comment]: <> ([![Actions Status][gh-actions-badge]][gh-actions]) 73 | 74 | [comment]: <> ([gh-actions]: https://github.com/firebase/snippets-web/actions) 75 | 76 | [comment]: <> ([gh-actions-badge]: https://github.com/firebase/snippets-web/workflows/CI%20Tests/badge.svg) -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/main.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // [START set_up_environment] 16 | import 'package:cloud_firestore/cloud_firestore.dart'; 17 | import 'package:firebase_auth/firebase_auth.dart'; 18 | import 'package:firebase_core/firebase_core.dart'; 19 | import 'package:firebase_database/firebase_database.dart'; 20 | import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; 21 | import 'package:firebase_remote_config/firebase_remote_config.dart'; 22 | // [END set_up_environment] 23 | 24 | import 'package:firebase_snippets_app/app.dart'; 25 | import 'package:firebase_storage/firebase_storage.dart'; 26 | import 'package:flutter/foundation.dart'; 27 | import 'package:flutter/material.dart'; 28 | 29 | import 'firebase_options.dart'; 30 | 31 | void main() async { 32 | WidgetsFlutterBinding.ensureInitialized(); 33 | await Firebase.initializeApp( 34 | name: 'Dev', 35 | options: DefaultFirebaseOptions.currentPlatform, 36 | ); 37 | 38 | final db = FirebaseFirestore.instance; 39 | 40 | // [START access_data_offline_configure_offline_persistence] 41 | final settings = db.settings.copyWith(persistenceEnabled: true); 42 | // [END access_data_offline_configure_offline_persistence] 43 | 44 | // [START access_data_offline_configure_offline_persistence] 45 | final updatedSettings = 46 | db.settings.copyWith(cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED); 47 | db.settings = settings; 48 | // [END access_data_offline_configure_offline_persistence] 49 | 50 | // [START get_started_get_singleton_object] 51 | FirebaseRemoteConfig firebaseRemoteConfig = FirebaseRemoteConfig.instance; 52 | // [END get_started_get_singleton_object] 53 | 54 | // [START dynamic_links_get_initial_links] 55 | final PendingDynamicLinkData? initialLink = 56 | await FirebaseDynamicLinks.instance.getInitialLink(); 57 | // [END dynamic_links_get_initial_links] 58 | 59 | if (kIsWeb) { 60 | // [START auth_persistingAuthState] 61 | await FirebaseAuth.instance.setPersistence(Persistence.NONE); 62 | // [END auth_persistingAuthState] 63 | } 64 | 65 | if (!kReleaseMode) { 66 | FirebaseAuth.instance.useAuthEmulator('localhost', 9099); 67 | FirebaseFirestore.instance.useFirestoreEmulator('localhost', 8080); 68 | FirebaseDatabase.instance.useDatabaseEmulator('localhost', 9000); 69 | FirebaseStorage.instance.useStorageEmulator('localhost', 9199); 70 | } 71 | 72 | runApp( 73 | MyApp( 74 | firestore: db, 75 | firebaseRemoteConfig: firebaseRemoteConfig, 76 | initialLink: null, 77 | ), 78 | ); 79 | } 80 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/snippets/cloud_messaging.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ignore_for_file: non_constant_identifier_names, avoid_print 16 | 17 | import 'package:firebase_messaging/firebase_messaging.dart'; 18 | import 'package:firebase_snippets_app/snippets/snippet_base.dart'; 19 | 20 | class CloudMessagingSnippets implements DocSnippet { 21 | @override 22 | void runAll() { 23 | setUp_retrieveTheCurrentRegistrationToken(); 24 | sendATestMessage_monitorTokenRefresh(); 25 | sendMessagesToMultipleDevices_subscribeClientAppToTopic(); 26 | sendMessagesToMultipleDevices_onMessageReceived(); 27 | sendMessagesToMultipleDevices_onBackgroundMessageReceived(); 28 | } 29 | 30 | void setUp_retrieveTheCurrentRegistrationToken() { 31 | // [START set_up_retrieve_the_current_registration_token] 32 | FirebaseMessaging.instance.getToken().then( 33 | (token) => print("Received token: $token"), 34 | onError: (e) => print("Error completing: $e"), 35 | ); 36 | // [END set_up_retrieve_the_current_registration_token] 37 | } 38 | 39 | void _sendRegistrationToServer(String t) => {}; 40 | 41 | void sendATestMessage_monitorTokenRefresh() { 42 | // [START send_a_test_message_monitor_token_refresh] 43 | FirebaseMessaging.instance.onTokenRefresh.listen((newToken) { 44 | print("Refreshed token: $newToken"); 45 | _sendRegistrationToServer(newToken); 46 | }); 47 | // [END send_a_test_message_monitor_token_refresh] 48 | } 49 | 50 | void sendMessagesToMultipleDevices_subscribeClientAppToTopic() { 51 | // [START send_messages_to_multiple_devices_subscribe_client_app_to_topic] 52 | FirebaseMessaging.instance.subscribeToTopic("weather"); 53 | // [END send_messages_to_multiple_devices_subscribe_client_app_to_topic] 54 | } 55 | 56 | void sendMessagesToMultipleDevices_onMessageReceived() { 57 | // [START send_messages_to_multiple_devices_override_on_message_received] 58 | FirebaseMessaging.onMessage.listen((RemoteMessage event) { 59 | print("Received new message: ${event.data}"); 60 | }); 61 | // [END send_messages_to_multiple_devices_override_on_message_received] 62 | } 63 | 64 | void sendMessagesToMultipleDevices_onBackgroundMessageReceived() { 65 | // [START send_messages_to_multiple_devices_override_on_background_message_received] 66 | FirebaseMessaging.onBackgroundMessage((RemoteMessage event) async { 67 | print("Received new message: ${event.data}"); 68 | }); 69 | // [END send_messages_to_multiple_devices_override_on_background_message_received] 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/snippets/firebase_performance.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: non_constant_identifier_names 2 | 3 | import 'package:firebase_performance/firebase_performance.dart'; 4 | import 'package:firebase_snippets_app/snippets/snippet_base.dart'; 5 | import 'package:http/http.dart' as http; 6 | 7 | class FirebasePerformanceMonitoringSnippets implements DocSnippet { 8 | @override 9 | void runAll() { 10 | performanceMonitoring_usage(); 11 | performanceMonitoring_httpTracking(); 12 | performanceMonitoring_stopAutoCollecting(); 13 | } 14 | 15 | void performanceMonitoring_usage() async { 16 | // [START performance_monitoring_usage] 17 | FirebasePerformance performance = FirebasePerformance.instance; 18 | 19 | Trace trace = performance.newTrace('custom-trace'); 20 | // [END performance_monitoring_usage] 21 | 22 | // [START performance_monitoring_start_trace] 23 | await trace.start(); 24 | 25 | // Set metrics you wish to track 26 | trace.setMetric('sum', 200); 27 | trace.setMetric('time', 342340435); 28 | // [END performance_monitoring_start_trace] 29 | 30 | // [START performance_monitoring_increment_values] 31 | trace.setMetric('sum', 200); 32 | 33 | // `sum` will be incremented to 201 34 | trace.incrementMetric('sum', 1); 35 | // [END performance_monitoring_increment_values] 36 | 37 | // [START performance_monitoring_set_non_metric_data] 38 | trace.putAttribute('userId', '1234'); 39 | // [END performance_monitoring_set_non_metric_data] 40 | 41 | // [START performance_monitoring_stop_trace] 42 | await trace.stop(); 43 | // [END performance_monitoring_stop_trace] 44 | } 45 | 46 | void performanceMonitoring_httpTracking() async { 47 | // [START performance_monitoring_http_tracking] 48 | FirebasePerformance performance = FirebasePerformance.instance; 49 | 50 | // Create a `HttpMetric` instance using the URL you're requesting as well as the type of request 51 | String url = 'https://firebase.flutter.dev'; 52 | HttpMetric metric = performance.newHttpMetric(url, HttpMethod.Get); 53 | 54 | // You may also assign up to 5 attributes for each trace 55 | metric.putAttribute('foo', 'bar'); 56 | 57 | // Start the trace 58 | await metric.start(); 59 | 60 | // Make the request 61 | Uri uri = Uri.parse(url); 62 | var response = await http.get(uri); 63 | 64 | // Set specific headers to be collated 65 | metric.responseContentType = response.headers['Content-Type']; 66 | metric.httpResponseCode = response.statusCode; 67 | metric.responsePayloadSize = response.contentLength; 68 | 69 | // Stops the trace. This is when the data is sent to the Firebase server and it will appear in your Firebase console 70 | await metric.stop(); 71 | // [END performance_monitoring_http_tracking] 72 | } 73 | 74 | void performanceMonitoring_stopAutoCollecting() async { 75 | // [START performance_monitoring_stop_auto_collecting] 76 | FirebasePerformance performance = FirebasePerformance.instance; 77 | // Custom data collection is, by default, enabled 78 | bool isEnabled = await performance.isPerformanceCollectionEnabled(); 79 | // Set data collection to `false` 80 | await performance.setPerformanceCollectionEnabled(false); 81 | // [END performance_monitoring_stop_auto_collecting] 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/snippets/dynamic_links.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: non_constant_identifier_names 2 | 3 | import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; 4 | import 'package:firebase_snippets_app/snippets/snippet_base.dart'; 5 | 6 | class DynamicLinkSnippets implements DocSnippet { 7 | @override 8 | void runAll() { 9 | // TODO: implement runAll 10 | } 11 | 12 | void createDynamicLinks_createParams() async { 13 | // [START create_dynamic_links_create_params] 14 | final dynamicLinkParams = DynamicLinkParameters( 15 | link: Uri.parse("https://www.example.com/"), 16 | uriPrefix: "https://example.page.link", 17 | androidParameters: 18 | const AndroidParameters(packageName: "com.example.app.android"), 19 | iosParameters: const IOSParameters(bundleId: "com.example.app.ios"), 20 | ); 21 | final dynamicLink = 22 | await FirebaseDynamicLinks.instance.buildLink(dynamicLinkParams); 23 | // [END create_dynamic_links_create_params] 24 | } 25 | 26 | void createDynamicLinks_shortLinks() async { 27 | // [START create_dynamic_links_short_links] 28 | final dynamicLinkParams = DynamicLinkParameters( 29 | link: Uri.parse("https://www.example.com/"), 30 | uriPrefix: "https://example.page.link", 31 | androidParameters: 32 | const AndroidParameters(packageName: "com.example.app.android"), 33 | iosParameters: const IOSParameters(bundleId: "com.example.app.ios"), 34 | ); 35 | final dynamicLink = 36 | await FirebaseDynamicLinks.instance.buildShortLink(dynamicLinkParams); 37 | // [END create_dynamic_links_short_links] 38 | 39 | // [START create_dynamic_links_unguessable] 40 | final unguessableDynamicLink = 41 | await FirebaseDynamicLinks.instance.buildShortLink( 42 | dynamicLinkParams, 43 | shortLinkType: ShortDynamicLinkType.unguessable, 44 | ); 45 | // [END create_dynamic_links_unguessable] 46 | } 47 | 48 | void createDynamicLinks_params() async { 49 | // [START create_dynamic_links_params] 50 | final dynamicLinkParams = DynamicLinkParameters( 51 | link: Uri.parse("https://www.example.com/"), 52 | uriPrefix: "https://example.page.link", 53 | androidParameters: const AndroidParameters( 54 | packageName: "com.example.app.android", 55 | minimumVersion: 30, 56 | ), 57 | iosParameters: const IOSParameters( 58 | bundleId: "com.example.app.ios", 59 | appStoreId: "123456789", 60 | minimumVersion: "1.0.1", 61 | ), 62 | googleAnalyticsParameters: const GoogleAnalyticsParameters( 63 | source: "twitter", 64 | medium: "social", 65 | campaign: "example-promo", 66 | ), 67 | socialMetaTagParameters: SocialMetaTagParameters( 68 | title: "Example of a Dynamic Link", 69 | imageUrl: Uri.parse("https://example.com/image.png"), 70 | ), 71 | ); 72 | final dynamicLink = 73 | await FirebaseDynamicLinks.instance.buildShortLink(dynamicLinkParams); 74 | // [END create_dynamic_links_params] 75 | } 76 | 77 | void receiveDynamicLinks_testExactLink() async { 78 | // [START receive_dynamic_links_test_exact_link] 79 | String link = 'https://dynamic-link-domain/ke2Qa'; 80 | 81 | final PendingDynamicLinkData? initialLink = 82 | await FirebaseDynamicLinks.instance.getDynamicLink(Uri.parse(link)); 83 | // [END receive_dynamic_links_test_exact_link] 84 | } 85 | 86 | void receiveDynamicLink_listenInBackground() async {} 87 | } 88 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/app.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import 'package:cloud_firestore/cloud_firestore.dart'; 16 | import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; 17 | import 'package:firebase_remote_config/firebase_remote_config.dart'; 18 | import 'package:firebase_snippets_app/snippets/analytics.dart'; 19 | import 'package:firebase_snippets_app/snippets/auth.dart'; 20 | import 'package:firebase_snippets_app/snippets/cloud_functions.dart'; 21 | import 'package:firebase_snippets_app/snippets/cloud_messaging.dart'; 22 | import 'package:firebase_snippets_app/snippets/cloud_storage.dart'; 23 | import 'package:firebase_snippets_app/snippets/crashlytics.dart'; 24 | import 'package:firebase_snippets_app/snippets/database.dart'; 25 | import 'package:firebase_snippets_app/snippets/dynamic_links.dart'; 26 | import 'package:firebase_snippets_app/snippets/firebase_performance.dart'; 27 | import 'package:firebase_snippets_app/snippets/firestore.dart'; 28 | import 'package:firebase_snippets_app/snippets/ml_model_downloader.dart'; 29 | import 'package:firebase_snippets_app/snippets/remote_config.dart'; 30 | import 'package:firebase_snippets_app/snippets/snippet_base.dart'; 31 | import 'package:flutter/material.dart'; 32 | 33 | class MyApp extends StatefulWidget { 34 | const MyApp({ 35 | Key? key, 36 | required this.firestore, 37 | required this.firebaseRemoteConfig, 38 | this.initialLink, 39 | }) : super(key: key); 40 | 41 | final FirebaseFirestore firestore; 42 | final FirebaseRemoteConfig firebaseRemoteConfig; 43 | final PendingDynamicLinkData? initialLink; 44 | 45 | @override 46 | State createState() => _MyAppState(); 47 | } 48 | 49 | class _MyAppState extends State { 50 | @override 51 | void initState() { 52 | final List _allSnippets = [ 53 | // Build Products 54 | AuthenticationSnippets(), 55 | RealtimeDatabaseSnippets(), 56 | FirestoreSnippets(widget.firestore), 57 | CloudStorageSnippets(), 58 | MLModelDownloaderSnippets(), 59 | CloudFunctionsSnippets(), 60 | 61 | // Release and Monitor Products 62 | CrashlyticsSnippets(), 63 | FirebasePerformanceMonitoringSnippets(), 64 | 65 | // Engage Products 66 | AnalyticsSnippets(), 67 | RemoteConfigSnippets(widget.firebaseRemoteConfig), 68 | CloudMessagingSnippets(), 69 | DynamicLinkSnippets(), 70 | ]; 71 | 72 | for (var snippet in _allSnippets) { 73 | snippet.runAll(); 74 | } 75 | 76 | super.initState(); 77 | } 78 | 79 | @override 80 | Widget build(BuildContext context) { 81 | return MaterialApp( 82 | home: Scaffold( 83 | appBar: AppBar( 84 | title: const Text('Snippet Test'), 85 | ), 86 | body: HomePage( 87 | initialLink: widget.initialLink, 88 | ), 89 | ), 90 | ); 91 | } 92 | } 93 | 94 | class HomePage extends StatelessWidget { 95 | const HomePage({ 96 | Key? key, 97 | this.initialLink, 98 | }) : super(key: key); 99 | 100 | final PendingDynamicLinkData? initialLink; 101 | 102 | @override 103 | Widget build(BuildContext context) { 104 | // [START receive_dynamic_link_handle_initial_link] 105 | if (initialLink != null) { 106 | final Uri? deepLink = initialLink!.link; 107 | // Example of using the dynamic link to push the user to a different screen 108 | Navigator.pushNamed(context, deepLink!.path); 109 | } 110 | // [END receive_dynamic_link_handle_initial_link] 111 | 112 | // [START receive_dynamic_link_listen_in_background] 113 | FirebaseDynamicLinks.instance.onLink.listen((dynamicLinkData) { 114 | Navigator.pushNamed(context, dynamicLinkData.link.path); 115 | }).onError((error) { 116 | // Handle errors 117 | }); 118 | // [END receive_dynamic_link_listen_in_background] 119 | 120 | return Container(); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/snippets/database.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: non_constant_identifier_names, avoid_print 2 | 3 | import 'package:firebase_auth/firebase_auth.dart'; 4 | import 'package:firebase_core/firebase_core.dart'; 5 | import 'package:firebase_database/firebase_database.dart'; 6 | import 'package:firebase_snippets_app/snippets/snippet_base.dart'; 7 | import 'package:flutter/foundation.dart'; 8 | 9 | class RealtimeDatabaseSnippets implements DocSnippet { 10 | @override 11 | void runAll() { 12 | start_initializeDatabase(); 13 | readAndWrite_grabAReference(); 14 | listsOfData_appendToAList(); 15 | offline_keepSynced(); 16 | offline_fullExample(); 17 | } 18 | 19 | void start_initializeDatabase() async { 20 | // [START start_initialize_database] 21 | final FirebaseDatabase database = FirebaseDatabase.instance; 22 | // [END start_initialize_database] 23 | 24 | // [START start_initialize_secondary] 25 | FirebaseApp secondaryApp = Firebase.app('SecondaryApp'); 26 | FirebaseDatabase secondaryDatabase = 27 | FirebaseDatabase.instanceFor(app: secondaryApp); 28 | // [END start_initialize_secondary] 29 | } 30 | 31 | void readAndWrite_grabAReference() async { 32 | // [START read_and_write_grab_a_reference] 33 | final DatabaseReference ref = FirebaseDatabase.instance.ref(); 34 | // [END read_and_write_grab_a_reference] 35 | } 36 | 37 | void readAndWrite_basicWriteOperation() async { 38 | // [START read_and_write_basic_write_operation] 39 | final DatabaseReference ref = FirebaseDatabase.instance.ref(); 40 | await ref.set({ 41 | "name": "John", 42 | "age": 18, 43 | "address": {"line1": "100 Mountain View"} 44 | }); 45 | // [END read_and_write_basic_write_operation] 46 | } 47 | 48 | void readAndWrite_updateRef() async { 49 | // [START read_and_write_update_ref] 50 | final DatabaseReference ref = FirebaseDatabase.instance.ref(); 51 | await ref.update({ 52 | "age": 19, 53 | }); 54 | // [END read_and_write_update_ref] 55 | } 56 | 57 | void readAndWrite_updateSubPaths() async { 58 | // [START read_and_write_update_sub_paths] 59 | final DatabaseReference ref = FirebaseDatabase.instance.ref(); 60 | await ref.update({ 61 | "123/age": 19, 62 | "123/address/line1": "1 Mountain View", 63 | }); 64 | // [END read_and_write_update_sub_paths] 65 | } 66 | 67 | void readAndWrite_readDataByListening() async { 68 | // [START read_and_write_read_data_by_listening] 69 | final DatabaseReference ref = FirebaseDatabase.instance.ref(); 70 | 71 | const postId = '123'; 72 | DatabaseReference starCountRef = 73 | FirebaseDatabase.instance.ref('posts/$postId/starCount'); 74 | starCountRef.onValue.listen((DatabaseEvent event) { 75 | final data = event.snapshot.value; 76 | // ... work with data 77 | }); 78 | // [END read_and_write_read_data_by_listening] 79 | } 80 | 81 | void readAndWrite_readDataOnce() async { 82 | // [START read_and_write_read_data_once] 83 | final DatabaseReference ref = FirebaseDatabase.instance.ref(); 84 | const userId = '123'; 85 | final dbRef = FirebaseDatabase.instance.ref(); 86 | 87 | try { 88 | final snapshot = await dbRef.child('users/$userId').get(); 89 | if (snapshot.exists) { 90 | // ... 91 | } 92 | } on FirebaseException catch (e) { 93 | // ... 94 | } 95 | // [END read_and_write_read_data_once] 96 | } 97 | 98 | void readAndWrite_updateSpecificFields() async { 99 | // [START read_and_write_update_specific_fields] 100 | // A post entry. 101 | final postData = { 102 | 'author': 'example user', 103 | 'uid': '123', 104 | 'body': 'A wonderful post', 105 | 'title': 'Example Post', 106 | 'starCount': 0, 107 | }; 108 | 109 | // Get a key for a new Post. 110 | final newPostKey = 111 | FirebaseDatabase.instance.ref().child('posts').push().key; 112 | 113 | // Write the new post's data simultaneously in the posts list and the 114 | // user's post list. 115 | final Map updates = {}; 116 | updates['/posts/$newPostKey'] = postData; 117 | updates['/user-posts/${postData['uid']}/$newPostKey'] = postData; 118 | 119 | return await FirebaseDatabase.instance.ref().update(updates); 120 | 121 | // [END read_and_write_update_specific_fields] 122 | } 123 | 124 | void readAndWrite_addCompletionCallback() async { 125 | // [START read_and_write_add_completion_callback] 126 | FirebaseDatabase.instance 127 | .ref('users/123/email') 128 | .set('user@example.com') 129 | .then((_) { 130 | // Data saved successfully! 131 | }).catchError((error) { 132 | // The write failed... 133 | }); 134 | // [END read_and_write_add_completion_callback] 135 | } 136 | 137 | void readAndWrite_runATransaction() async { 138 | // [START read_and_write_run_a_transaction] 139 | void toggleStar(String uid) async { 140 | DatabaseReference postRef = 141 | FirebaseDatabase.instance.ref("posts/foo-bar-123"); 142 | 143 | TransactionResult result = await postRef.runTransaction((Object? post) { 144 | // Ensure a post at the ref exists. 145 | if (post == null) { 146 | return Transaction.abort(); 147 | } 148 | 149 | Map _post = Map.from(post as Map); 150 | if (_post["stars"] is Map && _post["stars"][uid] != null) { 151 | _post["starCount"] = (_post["starCount"] ?? 1) - 1; 152 | _post["stars"][uid] = null; 153 | } else { 154 | _post["starCount"] = (_post["starCount"] ?? 0) + 1; 155 | if (!_post.containsKey("stars")) { 156 | _post["stars"] = {}; 157 | } 158 | _post["stars"][uid] = true; 159 | } 160 | 161 | // Return the new data. 162 | return Transaction.success(_post); 163 | }); 164 | // ... 165 | } 166 | // [END read_and_write_run_a_transaction] 167 | } 168 | 169 | void readAndWrite_transactionResult() async { 170 | // [START read_and_write_transaction_result] 171 | void toggleStar(String uid) async { 172 | DatabaseReference postRef = 173 | FirebaseDatabase.instance.ref("posts/foo-bar-123"); 174 | TransactionResult result = await postRef.runTransaction((Object? post) { 175 | // Ensure a post at the ref exists. 176 | if (post == null) { 177 | return Transaction.abort(); 178 | } 179 | 180 | Map _post = Map.from(post as Map); 181 | if (_post["stars"] is Map && _post["stars"][uid] != null) { 182 | _post["starCount"] = (_post["starCount"] ?? 1) - 1; 183 | _post["stars"][uid] = null; 184 | } else { 185 | _post["starCount"] = (_post["starCount"] ?? 0) + 1; 186 | if (!_post.containsKey("stars")) { 187 | _post["stars"] = {}; 188 | } 189 | _post["stars"][uid] = true; 190 | } 191 | 192 | // Return the new data. 193 | return Transaction.success(_post); 194 | }); 195 | 196 | print('Committed? ${result.committed}'); // true / false 197 | print('Snapshot? ${result.snapshot}'); // DataSnapshot 198 | // [END read_and_write_transaction_result] 199 | } 200 | } 201 | 202 | void readAndWrite_cancelTransaction() async { 203 | // [START read_and_write_cancel_transaction] 204 | final DatabaseReference ref = FirebaseDatabase.instance.ref(); 205 | TransactionResult result = await ref.runTransaction((Object? user) { 206 | if (user != null) { 207 | return Transaction.abort(); 208 | } 209 | // ... 210 | return Transaction.success('success!'); 211 | }); 212 | 213 | print(result.committed); // false 214 | // [END read_and_write_cancel_transaction] 215 | } 216 | 217 | void readAndWrite_atomicServersideIncrements() async { 218 | // [START read_and_write_atomic_serverside_increments] 219 | void addStar(uid, key) async { 220 | Map updates = {}; 221 | updates["posts/$key/stars/$uid"] = true; 222 | updates["posts/$key/starCount"] = ServerValue.increment(1); 223 | updates["user-posts/$key/stars/$uid"] = true; 224 | updates["user-posts/$key/starCount"] = ServerValue.increment(1); 225 | return FirebaseDatabase.instance.ref().update(updates); 226 | } 227 | // [END read_and_write_atomic_serverside_increments] 228 | } 229 | 230 | void listsOfData_appendToAList() async { 231 | // [START lists_of_data_append_to_a_list] 232 | DatabaseReference postListRef = FirebaseDatabase.instance.ref("posts"); 233 | DatabaseReference newPostRef = postListRef.push(); 234 | newPostRef.set({ 235 | // ... 236 | }).catchError((e) { 237 | // ... 238 | }); 239 | // [END lists_of_data_append_to_a_list] 240 | } 241 | 242 | void listsOfData_listenForChildEvents() async { 243 | // [START lists_of_data_listen_for_child_events] 244 | final commentsRef = FirebaseDatabase.instance.ref("post-comments/123"); 245 | commentsRef.onChildAdded.listen((event) { 246 | // A new comment has been added, so add it to the displayed list. 247 | }); 248 | commentsRef.onChildChanged.listen((event) { 249 | // A comment has changed; use the key to determine if we are displaying this 250 | // comment and if so displayed the changed comment. 251 | }); 252 | commentsRef.onChildRemoved.listen((event) { 253 | // A comment has been removed; use the key to determine if we are displaying 254 | // this comment and if so remove it. 255 | }); 256 | // [END lists_of_data_listen_for_child_events] 257 | } 258 | 259 | void listsOfData_listenForValueEvents() async { 260 | // [START lists_of_data_listen_for_value_events] 261 | DatabaseReference postListRef = FirebaseDatabase.instance.ref("posts"); 262 | postListRef.onValue.listen((event) { 263 | for (final child in event.snapshot.children) { 264 | // Handle the post. 265 | } 266 | }, onError: (error) { 267 | // Error. 268 | }); 269 | // [END lists_of_data_listen_for_value_events] 270 | } 271 | 272 | void listsOfData_sorting() async { 273 | // [START lists_of_data_sorting] 274 | final myUserId = FirebaseAuth.instance.currentUser?.uid; 275 | final topUserPostsRef = FirebaseDatabase.instance 276 | .ref("user-posts/$myUserId") 277 | .orderByChild("starCount"); 278 | // [END lists_of_data_sorting] 279 | } 280 | 281 | void listsOfData_orderByChild() async { 282 | // [START lists_of_data_order_by_child] 283 | final mostViewedPosts = 284 | FirebaseDatabase.instance.ref('posts').orderByChild('metrics/views'); 285 | // [END lists_of_data_order_by_child] 286 | } 287 | 288 | void listsOfData_limitToLatest() async { 289 | // [START lists_of_data_limit_to_latest] 290 | final recentPostsRef = 291 | FirebaseDatabase.instance.ref('posts').limitToLast(100); 292 | // [END lists_of_data_limit_to_latest] 293 | } 294 | 295 | void offline_setDiskPersistence() async { 296 | // [START offline_set_disk_persistence] 297 | FirebaseDatabase.instance.setPersistenceEnabled(true); 298 | // [END offline_set_disk_persistence] 299 | } 300 | 301 | void offline_keepSynced() async { 302 | // [START offline_keep_synced] 303 | final scoresRef = FirebaseDatabase.instance.ref("scores"); 304 | scoresRef.keepSynced(true); 305 | // [END offline_keep_synced] 306 | } 307 | 308 | void offline_keepSyncedFalse() async { 309 | // [START offline_keep_synced_false] 310 | final scoresRef = FirebaseDatabase.instance.ref("scores"); 311 | scoresRef.keepSynced(false); 312 | // [END offline_keep_synced_false] 313 | } 314 | 315 | void offline_queryDataOffline() async { 316 | // [START offline_query_data_offline] 317 | final scoresRef = FirebaseDatabase.instance.ref("scores"); 318 | scoresRef.orderByValue().limitToLast(4).onChildAdded.listen((event) { 319 | debugPrint( 320 | "The ${event.snapshot.key} dinosaur's score is ${event.snapshot.value}."); 321 | }); 322 | // [END offline_query_data_offline] 323 | } 324 | 325 | void offline_queryDataOffline2() async { 326 | // [START offline_query_data_offline_2] 327 | final scoresRef = FirebaseDatabase.instance.ref("scores"); 328 | scoresRef.orderByValue().limitToLast(2).onChildAdded.listen((event) { 329 | debugPrint( 330 | "The ${event.snapshot.key} dinosaur's score is ${event.snapshot.value}."); 331 | }); 332 | // [END offline_query_data_offline_2] 333 | } 334 | 335 | void offline_onDisconnect() async { 336 | // [START offline_on_disconnect] 337 | final presenceRef = FirebaseDatabase.instance.ref('disconnectmessage'); 338 | // Write a string when this client loses connection 339 | presenceRef.onDisconnect().set('I disconnected!'); 340 | // [END offline_on_disconnect] 341 | } 342 | 343 | void offline_howOnDisconnectWorks() async { 344 | // [START offline_how_on_disconnect_works] 345 | final presenceRef = FirebaseDatabase.instance.ref('disconnectmessage'); 346 | try { 347 | await presenceRef.onDisconnect().remove(); 348 | } catch (error) { 349 | debugPrint("Could not establish onDisconnect event: $error"); 350 | } 351 | // [END offline_how_on_disconnect_works] 352 | } 353 | 354 | void offline_onDisconnectCancel() async { 355 | // [START offline_on_disconnect_cancel] 356 | final presenceRef = FirebaseDatabase.instance.ref('disconnectmessage'); 357 | final onDisconnectRef = presenceRef.onDisconnect(); 358 | onDisconnectRef.set("I disconnected"); 359 | // ... 360 | // some time later when we change our minds 361 | // ... 362 | onDisconnectRef.cancel(); 363 | // [END offline_on_disconnect_cancel] 364 | } 365 | 366 | void offline_detectingConnectionState() async { 367 | // [START offline_detecting_connection_state] 368 | final connectedRef = FirebaseDatabase.instance.ref(".info/connected"); 369 | connectedRef.onValue.listen((event) { 370 | final connected = event.snapshot.value as bool? ?? false; 371 | if (connected) { 372 | debugPrint("Connected."); 373 | } else { 374 | debugPrint("Not connected."); 375 | } 376 | }); 377 | // [END offline_detecting_connection_state] 378 | } 379 | 380 | void offline_serverTimestamps() async { 381 | // [START offline_server_timestamps] 382 | final userLastOnlineRef = 383 | FirebaseDatabase.instance.ref("users/joe/lastOnline"); 384 | userLastOnlineRef.onDisconnect().set(ServerValue.timestamp); 385 | // [END offline_server_timestamps] 386 | } 387 | 388 | void offline_clockSkew() async { 389 | // [START offline_clock_skew] 390 | final offsetRef = FirebaseDatabase.instance.ref(".info/serverTimeOffset"); 391 | offsetRef.onValue.listen((event) { 392 | final offset = event.snapshot.value as num? ?? 0.0; 393 | final estimatedServerTimeMs = 394 | DateTime.now().millisecondsSinceEpoch + offset; 395 | }); 396 | // [END offline_clock_skew] 397 | } 398 | 399 | void offline_fullExample() async { 400 | // [START offline_full_example] 401 | // Since I can connect from multiple devices, we store each connection 402 | // instance separately any time that connectionsRef's value is null (i.e. 403 | // has no children) I am offline. 404 | final myConnectionsRef = 405 | FirebaseDatabase.instance.ref("users/joe/connections"); 406 | 407 | // Stores the timestamp of my last disconnect (the last time I was seen online) 408 | final lastOnlineRef = 409 | FirebaseDatabase.instance.ref("/users/joe/lastOnline"); 410 | 411 | final connectedRef = FirebaseDatabase.instance.ref(".info/connected"); 412 | connectedRef.onValue.listen((event) { 413 | final connected = event.snapshot.value as bool? ?? false; 414 | if (connected) { 415 | final con = myConnectionsRef.push(); 416 | 417 | // When this device disconnects, remove it. 418 | con.onDisconnect().remove(); 419 | 420 | // When I disconnect, update the last time I was seen online. 421 | lastOnlineRef.onDisconnect().set(ServerValue.timestamp); 422 | 423 | // Add this device to my connections list. 424 | // This value could contain info about the device or a timestamp too. 425 | con.set(true); 426 | } 427 | }); 428 | // [END offline_full_example] 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/snippets/analytics.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ignore_for_file: non_constant_identifier_names, avoid_print, unused_local_variable 16 | 17 | import 'package:firebase_analytics/firebase_analytics.dart'; 18 | import 'package:firebase_snippets_app/snippets/snippet_base.dart'; 19 | 20 | class AnalyticsSnippets extends DocSnippet { 21 | @override 22 | void runAll() { 23 | analytics_startLoggingEvents(); 24 | analytics_predefinedEvents(); 25 | analytics_setUserProperties(); 26 | analytics_trackScreens(); 27 | setUserId_setUserId(); 28 | measureAdRevenue_moPub(); 29 | measureEcommerce_implementation(); 30 | measureECommerce_selectProductFromList(); 31 | measureAdRevenue_moPub(); 32 | measureECommerce_logSelectItem(); 33 | measureECommerce_logViewItem(); 34 | measureECommerce_logAddToCart(); 35 | measureECommerce_logViewCart(); 36 | measureECommerce_logRemoveFromCart(); 37 | measureECommerce_logCheckoutBegin(); 38 | measureECommerce_logShippingInfo(); 39 | measureECommerce_logPaymentInfo(); 40 | measureECommerce_logPurchase(); 41 | measureECommerce_logRefund(); 42 | measureECommerce_logPromo(); 43 | } 44 | 45 | void analytics_startLoggingEvents() async { 46 | // [START analytics_start_logging_events] 47 | await FirebaseAnalytics.instance.logBeginCheckout( 48 | value: 10.0, 49 | currency: 'USD', 50 | items: [ 51 | AnalyticsEventItem( 52 | itemName: 'Socks', 53 | itemId: 'xjw73ndnw', 54 | price: 10.0, 55 | ), 56 | ], 57 | coupon: '10PERCENTOFF'); 58 | // [END analytics_start_logging_events] 59 | } 60 | 61 | void analytics_predefinedEvents() async { 62 | // [START analytics_predefined_events] 63 | await FirebaseAnalytics.instance.logSelectContent( 64 | contentType: "image", 65 | itemId: '1234', 66 | ); 67 | // [END analytics_predefined_events] 68 | 69 | // [START analytics_predefined_events2] 70 | await FirebaseAnalytics.instance.logEvent( 71 | name: "select_content", 72 | parameters: { 73 | "content_type": "image", 74 | "item_id": '1234', 75 | }, 76 | ); 77 | // [END analytics_predefined_events2] 78 | 79 | // [START analytics_custom_events] 80 | await FirebaseAnalytics.instance.logEvent( 81 | name: "share_image", 82 | parameters: { 83 | "image_name": 'rivers.jpg', 84 | "full_text": 'This is the full text.', 85 | }, 86 | ); 87 | // [END analytics_custom_events] 88 | 89 | // [START analytics_set_default_parameters] 90 | // Not supported on web 91 | await FirebaseAnalytics.instance.setDefaultEventParameters( 92 | {'version': '1.2.3'}, 93 | ); 94 | // [END analytics_set_default_parameters] 95 | } 96 | 97 | void analytics_setUserProperties() async { 98 | // [START analytics_set_user_properties] 99 | await FirebaseAnalytics.instance.setUserProperty( 100 | name: 'favorite_food', 101 | value: 'banana', 102 | ); 103 | // [END analytics_set_user_properties] 104 | } 105 | 106 | void analytics_trackScreens() async { 107 | // [START analytics_track_screens] 108 | await FirebaseAnalytics.instance.setCurrentScreen( 109 | screenName: 'Products', 110 | ); 111 | // [END analytics_track_screens] 112 | } 113 | 114 | void setUserId_setUserId() async { 115 | // [START set_user_id_set_user_id] 116 | FirebaseAnalytics.instance.setUserId(id: "123456"); 117 | // [END set_user_id_set_user_id] 118 | } 119 | 120 | void measureEcommerce_implementation() async { 121 | // [START measure_ecommerce_implementation] 122 | // A pair of jeggings 123 | final jeggings = AnalyticsEventItem( 124 | itemId: "SKU_123", 125 | itemName: "jeggings", 126 | itemCategory: "pants", 127 | itemVariant: "black", 128 | itemBrand: "Google", 129 | price: 9.99, 130 | ); 131 | // A pair of boots 132 | final boots = AnalyticsEventItem( 133 | itemId: "SKU_456", 134 | itemName: "boots", 135 | itemCategory: "shoes", 136 | itemVariant: "brown", 137 | itemBrand: "Google", 138 | price: 24.99, 139 | ); 140 | // A pair of socks 141 | final socks = AnalyticsEventItem( 142 | itemId: "SKU_789", 143 | itemName: "ankle_socks", 144 | itemCategory: "socks", 145 | itemVariant: "red", 146 | itemBrand: "Google", 147 | price: 5.99, 148 | ); 149 | // [END measure_ecommerce_implementation] 150 | } 151 | 152 | void measureECommerce_selectProductFromList() async { 153 | // [START measure_e_commerce_select_product_from_list] 154 | // A pair of jeggings 155 | final jeggings = AnalyticsEventItem( 156 | itemId: "SKU_123", 157 | itemName: "jeggings", 158 | itemCategory: "pants", 159 | itemVariant: "black", 160 | itemBrand: "Google", 161 | price: 9.99, 162 | ); 163 | // A pair of boots 164 | final boots = AnalyticsEventItem( 165 | itemId: "SKU_456", 166 | itemName: "boots", 167 | itemCategory: "shoes", 168 | itemVariant: "brown", 169 | itemBrand: "Google", 170 | price: 24.99, 171 | ); 172 | // A pair of socks 173 | final socks = AnalyticsEventItem( 174 | itemId: "SKU_789", 175 | itemName: "ankle_socks", 176 | itemCategory: "socks", 177 | itemVariant: "red", 178 | itemBrand: "Google", 179 | price: 5.99, 180 | ); 181 | 182 | await FirebaseAnalytics.instance.logViewItemList( 183 | itemListId: "L001", 184 | itemListName: "Related products", 185 | items: [jeggings, boots, socks], 186 | ); 187 | // [END measure_e_commerce_select_product_from_list] 188 | } 189 | 190 | void measureAdRevenue_moPub() async { 191 | // [START measure_ad_revenue_mo_pub] 192 | FirebaseAnalytics.instance.logAdImpression( 193 | adPlatform: "MoPub", 194 | adSource: "", 195 | adFormat: "", 196 | adUnitName: "", 197 | value: 10.0, 198 | currency: "", 199 | ); 200 | // [END measure_ad_revenue_mo_pub] 201 | } 202 | 203 | void measureECommerce_logSelectItem() async { 204 | // [START measure_e_commerce_log_select_item] 205 | // A pair of jeggings 206 | final jeggings = AnalyticsEventItem( 207 | itemId: "SKU_123", 208 | itemName: "jeggings", 209 | itemCategory: "pants", 210 | itemVariant: "black", 211 | itemBrand: "Google", 212 | price: 9.99, 213 | ); 214 | 215 | await FirebaseAnalytics.instance.logSelectItem( 216 | itemListId: "L001", 217 | itemListName: "Related products", 218 | items: [jeggings], 219 | ); 220 | // [END measure_e_commerce_log_select_item] 221 | } 222 | 223 | void measureECommerce_logViewItem() async { 224 | // [START measure_e_commerce_log_view_item] 225 | final jeggings = AnalyticsEventItem( 226 | itemId: "SKU_123", 227 | itemName: "jeggings", 228 | itemCategory: "pants", 229 | itemVariant: "black", 230 | itemBrand: "Google", 231 | price: 9.99, 232 | ); 233 | 234 | await FirebaseAnalytics.instance.logViewItem( 235 | currency: 'USD', 236 | value: 9.99, 237 | items: [jeggings], 238 | ); 239 | // [END measure_e_commerce_log_view_item] 240 | } 241 | 242 | void measureECommerce_logAddToCart() async { 243 | // [START measure_e_commerce_log_add_to_cart] 244 | final jeggings = AnalyticsEventItem( 245 | itemId: "SKU_123", 246 | itemName: "jeggings", 247 | itemCategory: "pants", 248 | itemVariant: "black", 249 | itemBrand: "Google", 250 | price: 9.99, 251 | ); 252 | 253 | final jeggingsWithQuantity = AnalyticsEventItem( 254 | itemId: jeggings.itemId, 255 | itemName: jeggings.itemName, 256 | itemCategory: jeggings.itemCategory, 257 | itemVariant: jeggings.itemVariant, 258 | itemBrand: jeggings.itemBrand, 259 | price: jeggings.price, 260 | quantity: 2, 261 | ); 262 | await FirebaseAnalytics.instance.logAddToWishlist( 263 | currency: 'USD', 264 | value: 19.98, 265 | items: [jeggingsWithQuantity], 266 | ); 267 | await FirebaseAnalytics.instance.logAddToCart( 268 | currency: 'USD', 269 | value: 19.98, 270 | items: [jeggingsWithQuantity], 271 | ); 272 | // [END measure_e_commerce_log_add_to_cart] 273 | } 274 | 275 | void measureECommerce_logViewCart() async { 276 | // [START measure_e_commerce_log_view_cart] 277 | final jeggings = AnalyticsEventItem( 278 | itemId: "SKU_123", 279 | itemName: "jeggings", 280 | itemCategory: "pants", 281 | itemVariant: "black", 282 | itemBrand: "Google", 283 | price: 9.99, 284 | ); 285 | 286 | final jeggingsWithQuantity = AnalyticsEventItem( 287 | itemId: jeggings.itemId, 288 | itemName: jeggings.itemName, 289 | itemCategory: jeggings.itemCategory, 290 | itemVariant: jeggings.itemVariant, 291 | itemBrand: jeggings.itemBrand, 292 | price: jeggings.price, 293 | quantity: 2, 294 | ); 295 | await FirebaseAnalytics.instance.logViewCart( 296 | currency: 'USD', 297 | value: 19.98, 298 | items: [jeggingsWithQuantity], 299 | ); 300 | // [END measure_e_commerce_log_view_cart] 301 | } 302 | 303 | void measureECommerce_logRemoveFromCart() async { 304 | // [START measure_e_commerce_log_remove_from_cart] 305 | final jeggings = AnalyticsEventItem( 306 | itemId: "SKU_123", 307 | itemName: "jeggings", 308 | itemCategory: "pants", 309 | itemVariant: "black", 310 | itemBrand: "Google", 311 | price: 9.99, 312 | ); 313 | 314 | final jeggingsWithQuantity = AnalyticsEventItem( 315 | itemId: jeggings.itemId, 316 | itemName: jeggings.itemName, 317 | itemCategory: jeggings.itemCategory, 318 | itemVariant: jeggings.itemVariant, 319 | itemBrand: jeggings.itemBrand, 320 | price: jeggings.price, 321 | quantity: 2, 322 | ); 323 | await FirebaseAnalytics.instance.logRemoveFromCart( 324 | currency: 'USD', 325 | value: 9.99, 326 | items: [jeggingsWithQuantity], 327 | ); 328 | // [END measure_e_commerce_log_remove_from_cart] 329 | } 330 | 331 | void measureECommerce_logCheckoutBegin() async { 332 | // [START measure_e_commerce_log_checkout_begin] 333 | final jeggings = AnalyticsEventItem( 334 | itemId: "SKU_123", 335 | itemName: "jeggings", 336 | itemCategory: "pants", 337 | itemVariant: "black", 338 | itemBrand: "Google", 339 | price: 9.99, 340 | ); 341 | 342 | final jeggingsWithQuantity = AnalyticsEventItem( 343 | itemId: jeggings.itemId, 344 | itemName: jeggings.itemName, 345 | itemCategory: jeggings.itemCategory, 346 | itemVariant: jeggings.itemVariant, 347 | itemBrand: jeggings.itemBrand, 348 | price: jeggings.price, 349 | quantity: 2, 350 | ); 351 | await FirebaseAnalytics.instance.logBeginCheckout( 352 | currency: 'USD', 353 | value: 15.98, // Discount applied. 354 | coupon: "SUMMER_FUN", 355 | items: [jeggingsWithQuantity], 356 | ); 357 | // [END measure_e_commerce_log_checkout_begin] 358 | } 359 | 360 | void measureECommerce_logShippingInfo() async { 361 | // [START measure_e_commerce_log_shipping_info] 362 | final jeggings = AnalyticsEventItem( 363 | itemId: "SKU_123", 364 | itemName: "jeggings", 365 | itemCategory: "pants", 366 | itemVariant: "black", 367 | itemBrand: "Google", 368 | price: 9.99, 369 | ); 370 | 371 | final jeggingsWithQuantity = AnalyticsEventItem( 372 | itemId: jeggings.itemId, 373 | itemName: jeggings.itemName, 374 | itemCategory: jeggings.itemCategory, 375 | itemVariant: jeggings.itemVariant, 376 | itemBrand: jeggings.itemBrand, 377 | price: jeggings.price, 378 | quantity: 2, 379 | ); 380 | await FirebaseAnalytics.instance.logAddShippingInfo( 381 | currency: 'USD', 382 | value: 15.98, 383 | coupon: "SUMMER_FUN", 384 | shippingTier: "Ground", 385 | items: [jeggingsWithQuantity], 386 | ); 387 | // [END measure_e_commerce_log_shipping_info] 388 | } 389 | 390 | void measureECommerce_logPaymentInfo() async { 391 | // [START measure_e_commerce_log_payment_info] 392 | final jeggings = AnalyticsEventItem( 393 | itemId: "SKU_123", 394 | itemName: "jeggings", 395 | itemCategory: "pants", 396 | itemVariant: "black", 397 | itemBrand: "Google", 398 | price: 9.99, 399 | ); 400 | 401 | final jeggingsWithQuantity = AnalyticsEventItem( 402 | itemId: jeggings.itemId, 403 | itemName: jeggings.itemName, 404 | itemCategory: jeggings.itemCategory, 405 | itemVariant: jeggings.itemVariant, 406 | itemBrand: jeggings.itemBrand, 407 | price: jeggings.price, 408 | quantity: 2, 409 | ); 410 | await FirebaseAnalytics.instance.logAddPaymentInfo( 411 | currency: 'USD', 412 | value: 15.98, 413 | coupon: "SUMMER_FUN", 414 | paymentType: "Visa", 415 | items: [jeggingsWithQuantity], 416 | ); 417 | // [END measure_e_commerce_log_payment_info] 418 | } 419 | 420 | void measureECommerce_logPurchase() async { 421 | // [START measure_e_commerce_log_purchase] 422 | final jeggings = AnalyticsEventItem( 423 | itemId: "SKU_123", 424 | itemName: "jeggings", 425 | itemCategory: "pants", 426 | itemVariant: "black", 427 | itemBrand: "Google", 428 | price: 9.99, 429 | ); 430 | 431 | final jeggingsWithQuantity = AnalyticsEventItem( 432 | itemId: jeggings.itemId, 433 | itemName: jeggings.itemName, 434 | itemCategory: jeggings.itemCategory, 435 | itemVariant: jeggings.itemVariant, 436 | itemBrand: jeggings.itemBrand, 437 | price: jeggings.price, 438 | quantity: 2, 439 | ); 440 | await FirebaseAnalytics.instance.logPurchase( 441 | transactionId: "12345", 442 | affiliation: "Google Store", 443 | currency: 'USD', 444 | value: 15.98, 445 | shipping: 2.00, 446 | tax: 1.66, 447 | coupon: "SUMMER_FUN", 448 | items: [jeggingsWithQuantity], 449 | ); 450 | // [END measure_e_commerce_log_purchase] 451 | } 452 | 453 | void measureECommerce_logRefund() async { 454 | // [START measure_e_commerce_log_refund] 455 | final jeggings = AnalyticsEventItem( 456 | itemId: "SKU_123", 457 | itemName: "jeggings", 458 | itemCategory: "pants", 459 | itemVariant: "black", 460 | itemBrand: "Google", 461 | price: 9.99, 462 | ); 463 | 464 | final jeggingsWithQuantity = AnalyticsEventItem( 465 | itemId: jeggings.itemId, 466 | itemName: jeggings.itemName, 467 | itemCategory: jeggings.itemCategory, 468 | itemVariant: jeggings.itemVariant, 469 | itemBrand: jeggings.itemBrand, 470 | price: jeggings.price, 471 | quantity: 2, 472 | ); 473 | await FirebaseAnalytics.instance.logRefund( 474 | transactionId: "12345", 475 | affiliation: "Google Store", 476 | currency: 'USD', 477 | value: 15.98, 478 | items: [jeggingsWithQuantity], 479 | ); 480 | // [END measure_e_commerce_log_refund] 481 | } 482 | 483 | void measureECommerce_logPromo() async { 484 | // [START measure_e_commerce_log_promo] 485 | await FirebaseAnalytics.instance.logViewPromotion( 486 | promotionId: "SUMMER_FUN", 487 | promotionName: "Summer Sale", 488 | creativeName: "summer2020_promo.jpg", 489 | creativeSlot: "featured_app_1", 490 | locationId: "HERO_BANNER", 491 | ); 492 | await FirebaseAnalytics.instance.logSelectPromotion( 493 | promotionId: "SUMMER_FUN", 494 | promotionName: "Summer Sale", 495 | creativeName: "summer2020_promo.jpg", 496 | creativeSlot: "featured_app_1", 497 | locationId: "HERO_BANNER", 498 | ); 499 | // [END measure_e_commerce_log_promo] 500 | } 501 | } 502 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/snippets/cloud_storage.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: non_constant_identifier_names, unused_local_variable, avoid_print 2 | 3 | import 'dart:io'; 4 | import 'dart:typed_data'; 5 | 6 | import 'package:firebase_core/firebase_core.dart'; 7 | import 'package:firebase_snippets_app/snippets/snippet_base.dart'; 8 | import 'package:firebase_storage/firebase_storage.dart'; 9 | import 'package:path_provider/path_provider.dart'; 10 | 11 | class CloudStorageSnippets implements DocSnippet { 12 | @override 13 | void runAll() { 14 | uploadFiles_fullExample(); 15 | downloadFiles_fullExample(); 16 | listFiles_listAllFiles(); 17 | deleteFiles_deleteAFile(); 18 | } 19 | 20 | void getStarted_setUpCloudStorage() { 21 | // [START get_started_set_up_cloud_storage] 22 | final storage = FirebaseStorage.instance; 23 | // [END get_started_set_up_cloud_storage] 24 | } 25 | 26 | void getStarted_useMultipleCloudStorageBuckets() { 27 | // [START get_started_use_multiple_cloud_storage_buckets] 28 | // Get a non-default Storage bucket 29 | final storage = 30 | FirebaseStorage.instanceFor(bucket: "gs://my-custom-bucket"); 31 | // [END get_started_use_multiple_cloud_storage_buckets] 32 | } 33 | 34 | void getStarted_useACustomerFirebaseApp() { 35 | // [START get_started_use_a_customer_firebase_app] 36 | final secondApp = Firebase.app('SecondaryApp'); 37 | final storage = FirebaseStorage.instanceFor(app: secondApp); 38 | 39 | final customStorage = FirebaseStorage.instanceFor( 40 | app: secondApp, 41 | bucket: 'gs://my-custom-bucket', 42 | ); 43 | // [END get_started_use_a_customer_firebase_app] 44 | } 45 | 46 | void createAReference_createAReference() { 47 | // [START create_a_reference_create_a_reference] 48 | final storage = FirebaseStorage.instance; 49 | final storageRef = storage.ref(); 50 | // [END create_a_reference_create_a_reference] 51 | 52 | // [START create_a_reference_create_a_reference2] 53 | // Create a child reference 54 | // imagesRef now points to "images" 55 | final imagesRef = storageRef.child('images'); 56 | 57 | // Child references can also take paths 58 | // spaceRef now points to "images/space.jpg 59 | // imagesRef still points to "images" 60 | final spaceRef = storageRef.child("images/space.jpg"); 61 | // [END create_a_reference_create_a_reference2] 62 | 63 | // [START create_a_reference_navigate_with_references] 64 | // parent allows us to move our reference to a parent node 65 | // parentRef now points to 'images' 66 | final parentRef = spaceRef.parent; 67 | 68 | // root allows us to move all the way back to the top of our bucket 69 | // rootRef now points to the root 70 | final rootRef = spaceRef.root; 71 | // [END create_a_reference_navigate_with_references] 72 | 73 | // [START create_a_reference_navigate_with_references_2] 74 | // References can be chained together multiple times 75 | // earthRef points to 'images/earth.jpg' 76 | final earthRef = spaceRef.parent?.child("earth.jpg"); 77 | 78 | // nullRef is null, since the parent of root is null 79 | final nullRef = spaceRef.root.parent; 80 | // [END create_a_reference_navigate_with_references_2] 81 | } 82 | 83 | // Not run in CI. All code is covered in function [uploadFiles_fullExample]. 84 | void uploadFiles_createReference() async { 85 | // [START upload_files_create_reference] 86 | // Create a storage reference from our app 87 | final storageRef = FirebaseStorage.instance.ref(); 88 | // Create a reference to "mountains.jpg" 89 | final mountainsRef = storageRef.child("mountains.jpg"); 90 | // Create a reference to 'images/mountains.jpg' 91 | final mountainImagesRef = storageRef.child("images/mountains.jpg"); 92 | // While the file names are the same, the references point to different files 93 | assert(mountainsRef.name == mountainImagesRef.name); 94 | assert(mountainsRef.fullPath != mountainImagesRef.fullPath); 95 | // [END upload_files_create_reference] 96 | 97 | // [START upload_files_upload_from_files] 98 | final appDocDir = await getApplicationDocumentsDirectory(); 99 | final filePath = '${appDocDir.absolute}/mountains.jpg'; 100 | final file = File(filePath); 101 | 102 | try { 103 | storageRef.child('images').putFile(file); 104 | } on FirebaseException catch (e) { 105 | // handle error 106 | } 107 | // [END upload_files_upload_from_files] 108 | 109 | // [START upload_files_upload_from_a_string] 110 | String dataUrl = 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=='; 111 | try { 112 | await mountainsRef.putString(dataUrl, format: PutStringFormat.dataUrl); 113 | } on FirebaseException catch (e) { 114 | // ... 115 | } 116 | // [END upload_files_upload_from_a_string] 117 | 118 | // [START upload_files_add_file_metadata] 119 | try { 120 | storageRef.child('images').putFile( 121 | file, 122 | SettableMetadata( 123 | contentType: "image/jpeg", 124 | )); 125 | } on FirebaseException catch (e) { 126 | // handle error 127 | } 128 | // [END upload_files_add_file_metadata] 129 | } 130 | 131 | void uploadFiles_getADownloadURL() async { 132 | // [START upload_files_get_a_download_url] 133 | final storageRef = FirebaseStorage.instance.ref(); 134 | final mountainsRef = storageRef.child("mountains.jpg"); 135 | await mountainsRef.getDownloadURL(); 136 | // [END upload_files_get_a_download_url] 137 | } 138 | 139 | // Not run in CI. All code is covered in function [uploadFiles_fullExample]. 140 | void uploadFiles_manageUploads() async { 141 | final storageRef = FirebaseStorage.instance.ref(); 142 | final mountainsRef = storageRef.child("mountains.jpg"); 143 | final appDocDir = await getApplicationDocumentsDirectory(); 144 | final filePath = '${appDocDir.absolute}/mountains.jpg'; 145 | final largeFile = File(filePath); 146 | // [START upload_files_manage_uploads] 147 | final task = mountainsRef.putFile(largeFile); 148 | // Pause the upload. 149 | bool paused = await task.pause(); 150 | print('paused, $paused'); 151 | // Resume the upload. 152 | bool resumed = await task.resume(); 153 | print('resumed, $resumed'); 154 | // Cancel the upload. 155 | bool canceled = await task.cancel(); 156 | print('canceled, $canceled'); 157 | // [END upload_files_manage_uploads] 158 | } 159 | 160 | // Not run in CI. All code is covered in function [uploadFiles_fullExample]. 161 | void uploadFiles_monitorStatus() async { 162 | final appDocDir = await getApplicationDocumentsDirectory(); 163 | final filePath = '${appDocDir.absolute}/mountains.jpg'; 164 | final file = File(filePath); 165 | final storageRef = FirebaseStorage.instance.ref(); 166 | final mountainsRef = storageRef.child("mountains.jpg"); 167 | 168 | // [START upload_files_monitor_status] 169 | mountainsRef.putFile(file).snapshotEvents.listen((taskSnapshot) { 170 | switch (taskSnapshot.state) { 171 | case TaskState.running: 172 | // ... 173 | break; 174 | case TaskState.paused: 175 | // ... 176 | break; 177 | case TaskState.success: 178 | // ... 179 | break; 180 | case TaskState.canceled: 181 | // ... 182 | break; 183 | case TaskState.error: 184 | // ... 185 | break; 186 | } 187 | }); 188 | // [END upload_files_monitor_status] 189 | } 190 | 191 | void uploadFiles_fullExample() async { 192 | // [START upload_files_full_example] 193 | final appDocDir = await getApplicationDocumentsDirectory(); 194 | final filePath = "${appDocDir.absolute}/path/to/mountains.jpg"; 195 | final file = File(filePath); 196 | final storageRef = FirebaseStorage.instance.ref(); 197 | final mountainsRef = storageRef.child("mountains.jpg"); 198 | 199 | // Create the file metadata 200 | final metadata = SettableMetadata(contentType: "image/jpeg"); 201 | // Upload file and metadata to the path 'images/mountains.jpg' 202 | final uploadTask = storageRef 203 | .child("images/path/to/mountains.jpg") 204 | .putFile(file, metadata); 205 | 206 | // Listen for state changes, errors, and completion of the upload. 207 | uploadTask.snapshotEvents.listen((TaskSnapshot taskSnapshot) { 208 | switch (taskSnapshot.state) { 209 | case TaskState.running: 210 | final progress = 211 | 100.0 * (taskSnapshot.bytesTransferred / taskSnapshot.totalBytes); 212 | print("Upload is $progress% complete."); 213 | break; 214 | case TaskState.paused: 215 | print("Upload is paused."); 216 | break; 217 | case TaskState.canceled: 218 | print("Upload was canceled"); 219 | break; 220 | case TaskState.error: 221 | // Handle unsuccessful uploads 222 | break; 223 | case TaskState.success: 224 | // Handle successful uploads on complete 225 | // ... 226 | break; 227 | } 228 | }); 229 | // [END upload_files_full_example] 230 | } 231 | 232 | void downloadFiles_createAReference() { 233 | // [START download_files_create_a_reference] 234 | // Create a storage reference from our app 235 | final storageRef = FirebaseStorage.instance.ref(); 236 | // Create a reference with an initial file path and name 237 | final pathReference = storageRef.child("images/stars.jpg"); 238 | // Create a reference to a file from a Google Cloud Storage URI 239 | final gsReference = FirebaseStorage.instance 240 | .refFromURL("gs://YOUR_BUCKET/images/stars.jpg"); 241 | // Create a reference from an HTTPS URL 242 | // Note that in the URL, characters are URL escaped! 243 | final httpsReference = FirebaseStorage.instance.refFromURL( 244 | "https://firebasestorage.googleapis.com/b/YOUR_BUCKET/o/images%20stars.jpg"); 245 | // [END download_files_create_a_reference] 246 | } 247 | 248 | void downloadFiles_downloadInMemory() async { 249 | final storageRef = FirebaseStorage.instance.ref(); 250 | // [START download_files_download_in_memory] 251 | final islandRef = storageRef.child("images/island.jpg"); 252 | try { 253 | const oneMegabyte = 1024 * 1024; 254 | final Uint8List? data = await islandRef.getData(oneMegabyte); 255 | // Data for "images/island.jpg" is returned, use this as needed. 256 | } on FirebaseException catch (e) { 257 | // Handle any errors. 258 | } 259 | // [END download_files_download_in_memory] 260 | } 261 | 262 | void downloadFiles_downloadToALocalFile() async { 263 | final storageRef = FirebaseStorage.instance.ref(); 264 | // [START download_files_download_to_a_local_file] 265 | final islandRef = storageRef.child("images/island.jpg"); 266 | final appDocDir = await getApplicationDocumentsDirectory(); 267 | final filePath = "${appDocDir.absolute}/images/island.jpg"; 268 | final file = File(filePath); 269 | final downloadTask = islandRef.writeToFile(file); 270 | downloadTask.snapshotEvents.listen((taskSnapshot) { 271 | switch (taskSnapshot.state) { 272 | case TaskState.running: 273 | // TODO: Handle this case. 274 | break; 275 | case TaskState.paused: 276 | // TODO: Handle this case. 277 | break; 278 | case TaskState.success: 279 | // TODO: Handle this case. 280 | break; 281 | case TaskState.canceled: 282 | // TODO: Handle this case. 283 | break; 284 | case TaskState.error: 285 | // TODO: Handle this case. 286 | break; 287 | } 288 | }); 289 | // [END download_files_download_to_a_local_file] 290 | } 291 | 292 | void downloadFiles_downloadViaUrl() async { 293 | final storageRef = FirebaseStorage.instance.ref(); 294 | // [START download_files_download_via_url] 295 | final imageUrl = 296 | await storageRef.child("users/me/profile.png").getDownloadURL(); 297 | // [END download_files_download_via_url] 298 | } 299 | 300 | void downloadFiles_fullExample() async { 301 | final storageRef = FirebaseStorage.instance.ref(); 302 | // [START download_files_full_example] 303 | final islandRef = storageRef.child("images/island.jpg"); 304 | final appDocDir = await getApplicationDocumentsDirectory(); 305 | final filePath = "${appDocDir.absolute}/images/island.jpg"; 306 | final file = File(filePath); 307 | final downloadTask = islandRef.writeToFile(file); 308 | downloadTask.snapshotEvents.listen((taskSnapshot) { 309 | switch (taskSnapshot.state) { 310 | case TaskState.running: 311 | // TODO: Handle this case. 312 | break; 313 | case TaskState.paused: 314 | // TODO: Handle this case. 315 | break; 316 | case TaskState.success: 317 | // TODO: Handle this case. 318 | break; 319 | case TaskState.canceled: 320 | // TODO: Handle this case. 321 | break; 322 | case TaskState.error: 323 | // TODO: Handle this case. 324 | break; 325 | } 326 | }); 327 | // [END download_files_full_example] 328 | } 329 | 330 | void useFileMetadata_getMetadata() async { 331 | // [START use_file_metadata_get_metadata] 332 | final storageRef = FirebaseStorage.instance.ref(); 333 | // Create reference to the file whose metadata we want to retrieve 334 | final forestRef = storageRef.child("images/forest.jpg"); 335 | // Get metadata properties 336 | final metadata = await forestRef.getMetadata(); 337 | // Metadata now contains the metadata for 'images/forest.jpg' 338 | // [END use_file_metadata_get_metadata] 339 | } 340 | 341 | void useFileMetadata_updateMetadata() async { 342 | final storageRef = FirebaseStorage.instance.ref(); 343 | // [START use_file_metadata_update_metadata] 344 | // Create reference to the file whose metadata we want to change 345 | final forestRef = storageRef.child("images/forest.jpg"); 346 | // Create file metadata to update 347 | final newMetadata = SettableMetadata( 348 | cacheControl: "public,max-age=300", 349 | contentType: "image/jpeg", 350 | ); 351 | // Update metadata properties 352 | final metadata = await forestRef.updateMetadata(newMetadata); 353 | // Updated metadata for 'images/forest.jpg' is returned 354 | // [END use_file_metadata_update_metadata] 355 | } 356 | 357 | void useFileMetadata_deleteMetadata() async { 358 | final storageRef = FirebaseStorage.instance.ref(); 359 | final forestRef = storageRef.child("images/forest.jpg"); 360 | // [START use_file_metadata_delete_metadata] 361 | // Delete the cacheControl property 362 | final newMetadata = SettableMetadata(cacheControl: null); 363 | final metadata = await forestRef.updateMetadata(newMetadata); 364 | // [END use_file_metadata_delete_metadata] 365 | } 366 | 367 | void useFileMetadata_customMetadata() async { 368 | final storageRef = FirebaseStorage.instance.ref(); 369 | // [START use_file_metadata_custom_metadata] 370 | // Create reference to the file whose metadata we want to change 371 | final forestRef = storageRef.child("images/forest.jpg"); 372 | // Create file metadata to update 373 | final newCustomMetadata = SettableMetadata( 374 | customMetadata: { 375 | "location": "Yosemite, CA, USA", 376 | "activity": "Hiking", 377 | }, 378 | ); 379 | // Update metadata properties 380 | final metadata = await forestRef.updateMetadata(newCustomMetadata); 381 | // Updated metadata for 'images/forest.jpg' is returned 382 | // [END use_file_metadata_custom_metadata] 383 | } 384 | 385 | void deleteFiles_deleteAFile() async { 386 | final storageRef = FirebaseStorage.instance.ref(); 387 | // [START delete_files_delete_a_file] 388 | // Create a reference to the file to delete 389 | final desertRef = storageRef.child("images/desert.jpg"); 390 | // Delete the file 391 | await desertRef.delete(); 392 | // [END delete_files_delete_a_file] 393 | } 394 | 395 | void listFiles_listAllFiles() async { 396 | // [START list_files_list_all_files] 397 | final storageRef = FirebaseStorage.instance.ref().child("files/uid"); 398 | final listResult = await storageRef.listAll(); 399 | for (var prefix in listResult.prefixes) { 400 | // The prefixes under storageRef. 401 | // You can call listAll() recursively on them. 402 | } 403 | for (var item in listResult.items) { 404 | // The items under storageRef. 405 | } 406 | // [END list_files_list_all_files] 407 | } 408 | 409 | void listFiles_PaginateList() async { 410 | final storageRef = FirebaseStorage.instance.ref().child("files/uid"); 411 | // [START list_files_paginate_list] 412 | Stream listAllPaginated(Reference storageRef) async* { 413 | String? pageToken; 414 | do { 415 | final listResult = await storageRef.list(ListOptions( 416 | maxResults: 100, 417 | pageToken: pageToken, 418 | )); 419 | yield listResult; 420 | pageToken = listResult.nextPageToken; 421 | } while (pageToken != null); 422 | } 423 | // [END list_files_paginate_list] 424 | 425 | listAllPaginated(storageRef); 426 | } 427 | 428 | void handleErrors_handleErrors() async { 429 | // [START handle_errors_handle_errors] 430 | final storageRef = FirebaseStorage.instance.ref().child("files/uid"); 431 | try { 432 | final listResult = await storageRef.listAll(); 433 | } on FirebaseException catch (e) { 434 | // Caught an exception from Firebase. 435 | print("Failed with error '${e.code}': ${e.message}"); 436 | } 437 | // [END handle_errors_handle_errors] 438 | } 439 | } 440 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/lib/snippets/auth.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: non_constant_identifier_names, avoid_print 2 | 3 | import 'dart:convert'; 4 | import 'dart:math'; 5 | 6 | import 'package:crypto/crypto.dart'; 7 | import 'package:firebase_auth/firebase_auth.dart'; 8 | import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart'; 9 | import 'package:firebase_snippets_app/snippets/snippet_base.dart'; 10 | import 'package:flutter/foundation.dart'; 11 | import 'package:google_sign_in/google_sign_in.dart'; 12 | import 'package:sign_in_with_apple/sign_in_with_apple.dart'; 13 | import 'package:twitter_login/twitter_login.dart'; 14 | 15 | class AuthenticationSnippets implements DocSnippet { 16 | @override 17 | void runAll() { 18 | getStarted_authStateChanges(); 19 | getStarted_idTokenChanges(); 20 | getStarted_userChanges(); 21 | manageUsers_getProfile(); 22 | passwordAuth_createPasswordBasedAccount(); 23 | emailLinkAuth_sendAuthLinkEmail(); 24 | social_google_sign_in(); 25 | social_twitterAuth(); 26 | social_facebookAuth(); 27 | phoneAuth_verifyPhoneNumber(); 28 | customAuth_withFirebase(); 29 | anonymousAuth_withFirebase(); 30 | } 31 | 32 | void getStarted_authStateChanges() async { 33 | // [START get_started_auth_state_changes] 34 | FirebaseAuth.instance.authStateChanges().listen((User? user) { 35 | if (user == null) { 36 | // User is signed in, see docs for a list of available properties 37 | // https://firebase.google.com/docs/reference/js/firebase.User 38 | final uid = user!.uid; 39 | } else { 40 | // User is signed out 41 | // ... 42 | } 43 | }); 44 | // [END get_started_auth_state_changes] 45 | } 46 | 47 | void getStarted_authGetCurrentUser() async { 48 | // [START get_started_auth_get_current_user] 49 | final user = FirebaseAuth.instance.currentUser; 50 | if (user == null) { 51 | // User is signed in, see docs for a list of available properties 52 | // https://firebase.google.com/docs/reference/js/firebase.User 53 | final uid = user!.uid; 54 | } else { 55 | // User is signed out 56 | // ... 57 | } 58 | // [END get_started_auth_get_current_user] 59 | } 60 | 61 | void getStarted_idTokenChanges() async { 62 | // [START get_started_id_token_changes] 63 | FirebaseAuth.instance.idTokenChanges().listen((User? user) { 64 | if (user == null) { 65 | // User is signed in, see docs for a list of available properties 66 | // ... 67 | } else { 68 | // User is signed out 69 | // ... 70 | } 71 | }); 72 | // [END get_started_id_token_changes] 73 | } 74 | 75 | void getStarted_userChanges() async { 76 | // [START get_started_user_changes] 77 | FirebaseAuth.instance.userChanges().listen((User? user) { 78 | if (user == null) { 79 | print('User is currently signed out!'); 80 | } else { 81 | print('User is signed in!'); 82 | } 83 | }); 84 | // [END get_started_user_changes] 85 | } 86 | 87 | void manageUsers_getProfile() async { 88 | // [START manage_users_get_profile] 89 | final user = FirebaseAuth.instance.currentUser; 90 | if (user != null) { 91 | // Name, email address, and profile photo URL 92 | final name = user.displayName; 93 | final email = user.email; 94 | final photoUrl = user.photoURL; 95 | 96 | // Check if user's email is verified 97 | final emailVerified = user.emailVerified; 98 | 99 | // The user's ID, unique to the Firebase project. Do NOT use this value to 100 | // authenticate with your backend server, if you have one. Use 101 | // User.getIdToken() instead. 102 | final uid = user.uid; 103 | } 104 | // [END manage_users_get_profile] 105 | } 106 | 107 | void manageUsers_getProviderSpecificProfile() async { 108 | // [START manage_users_get_provider_specific_profile] 109 | final user = FirebaseAuth.instance.currentUser; 110 | if (user != null) { 111 | for (final providerProfile in user.providerData) { 112 | // ID of the provider (google.com, apple.cpm, etc.) 113 | final provider = providerProfile.providerId; 114 | 115 | // UID specific to the provider 116 | final uid = providerProfile.uid; 117 | 118 | // Name, email address, and profile photo URL 119 | final name = providerProfile.displayName; 120 | final emailAddress = providerProfile.email; 121 | final profilePhoto = providerProfile.photoURL; 122 | } 123 | } 124 | // [END manage_users_get_provider_specific_profile] 125 | } 126 | 127 | void manageUsers_updateProfile() async { 128 | // [START manage_users_update_profile] 129 | final user = FirebaseAuth.instance.currentUser; 130 | try { 131 | await user?.updateDisplayName("Jane Q. User"); 132 | await user?.updatePhotoURL("https://example.com/jane-q-user/profile.jpg"); 133 | } on FirebaseAuthException catch (e) { 134 | // An error occurred 135 | // .. 136 | } 137 | // [END manage_users_update_profile] 138 | } 139 | 140 | void manageUsers_setEmail() async { 141 | // [START manage_users_set_email] 142 | final user = FirebaseAuth.instance.currentUser; 143 | await user?.updateEmail("janeq@example.com").then((result) { 144 | // Email updated! 145 | // ... 146 | }, onError: (error) { 147 | // An error occurred 148 | // .. 149 | }); 150 | // [END manage_users_set_email] 151 | } 152 | 153 | void manageUsers_sendVerificationEmail() async { 154 | // [START manage_users_send_verification_email] 155 | final user = FirebaseAuth.instance.currentUser; 156 | await user?.sendEmailVerification().then((result) { 157 | // Email sent! 158 | // ... 159 | }, onError: (error) { 160 | // An error occurred 161 | // .. 162 | }); 163 | 164 | // [END manage_users_send_verification_email] 165 | } 166 | 167 | void manageUsers_setLocaleThenSendEmail() async { 168 | // [START manage_users_set_locale_then_send_email] 169 | final user = FirebaseAuth.instance.currentUser; 170 | await FirebaseAuth.instance.setLanguageCode("fr"); 171 | await user?.sendEmailVerification().then((result) { 172 | // Email sent! 173 | // ... 174 | }, onError: (error) { 175 | // An error occurred 176 | // .. 177 | }); 178 | 179 | // [END manage_users_set_locale_then_send_email] 180 | } 181 | 182 | void manageUsers_setNewPassword() async { 183 | // [START manage_users_set_new_password] 184 | final user = FirebaseAuth.instance.currentUser; 185 | await user?.updatePassword('top.secret%PASSWORD').then((result) { 186 | // Password updated! 187 | // ... 188 | }, onError: (error) { 189 | // An error occurred 190 | // .. 191 | }); 192 | 193 | // [END manage_users_set_new_password] 194 | } 195 | 196 | void manageUsers_sendResetEmail() async { 197 | // [START manage_users_send_reset_email] 198 | await FirebaseAuth.instance 199 | .sendPasswordResetEmail(email: 'user@example.com') 200 | .then((result) { 201 | // Email sent! 202 | // ... 203 | }, onError: (error) { 204 | // An error occurred 205 | // .. 206 | }); 207 | 208 | // [END manage_users_send_reset_email] 209 | } 210 | 211 | void manageUsers_setLanguageCode() async { 212 | // [START manage_users_set_language_code] 213 | // All emails, SMS, and reCAPTCHA sent after this line is executed will have this language code. 214 | await FirebaseAuth.instance.setLanguageCode("fr").then((result) { 215 | // Language code updated! 216 | // ... 217 | }, onError: (error) { 218 | // An error occurred 219 | // .. 220 | }); 221 | // [END manage_users_set_language_code] 222 | } 223 | 224 | void manageUsers_reAuthenticate() async { 225 | // [START manage_users_re_authenticate] 226 | try { 227 | final userCredential = 228 | await FirebaseAuth.instance.signInWithEmailAndPassword( 229 | email: 'user@example.com', 230 | password: 'top.secret.PASSWORD', 231 | ); 232 | final credential = userCredential.credential!; 233 | 234 | // Prompt the user to re-provide their sign-in credentials. 235 | // Then, use the credentials to reauthenticate: 236 | await FirebaseAuth.instance.currentUser! 237 | .reauthenticateWithCredential(credential); 238 | } on FirebaseAuthException catch (e) { 239 | // an error occurred 240 | // ... 241 | } 242 | // [END manage_users_re_authenticate] 243 | } 244 | 245 | void manageUsers_deleteUser() async { 246 | // [START manage_users_delete_user] 247 | final user = FirebaseAuth.instance.currentUser; 248 | await user?.delete().then((result) { 249 | // User deleted! 250 | // ... 251 | }, onError: (error) { 252 | // An error occurred 253 | // .. 254 | }); 255 | ; 256 | // [END manage_users_delete_user] 257 | } 258 | 259 | void passwordAuth_createPasswordBasedAccount() async { 260 | const String emailAddress = 'user@example.com'; 261 | const String password = 'sdf8798324jf673qf8h!'; 262 | 263 | // [START password_auth_create_password_based_account] 264 | try { 265 | final credential = 266 | await FirebaseAuth.instance.createUserWithEmailAndPassword( 267 | email: emailAddress, 268 | password: password, 269 | ); 270 | } on FirebaseAuthException catch (e) { 271 | if (e.code == 'weak-password') { 272 | print('The password provided is too weak.'); 273 | } else if (e.code == 'email-already-in-use') { 274 | print('The account already exists for that email.'); 275 | } 276 | } catch (e) { 277 | print(e); 278 | } 279 | // [END password_auth_create_password_based_account] 280 | 281 | await FirebaseAuth.instance.signOut(); 282 | 283 | // [START password_auth_sign_in] 284 | try { 285 | final credential = await FirebaseAuth.instance.signInWithEmailAndPassword( 286 | email: emailAddress, 287 | password: password, 288 | ); 289 | } on FirebaseAuthException catch (e) { 290 | if (e.code == 'user-not-found') { 291 | print('No user found for that email.'); 292 | } else if (e.code == 'wrong-password') { 293 | print('Wrong password provided for that user.'); 294 | } 295 | } 296 | // [END password_auth_sign_in] 297 | 298 | // [START password_auth_sign_out] 299 | await FirebaseAuth.instance.signOut(); 300 | // [END password_auth_sign_out] 301 | } 302 | 303 | void emailLinkAuth_sendAuthLinkEmail() async { 304 | // [START email_link_auth_action_code_settings] 305 | var acs = ActionCodeSettings( 306 | // URL you want to redirect back to. The domain (www.example.com) for this 307 | // URL must be whitelisted in the Firebase Console. 308 | url: 'https://www.example.com/finishSignUp?cartId=1234', 309 | // This must be true 310 | handleCodeInApp: true, 311 | iOSBundleId: 'com.example.ios', 312 | androidPackageName: 'com.example.android', 313 | // installIfNotAvailable 314 | androidInstallApp: true, 315 | // minimumVersion 316 | androidMinimumVersion: '12', 317 | ); 318 | // [END email_link_auth_action_code_settings] 319 | 320 | // [START email_link_auth_send_auth_link_email] 321 | var emailAuth = 'someemail@domain.com'; 322 | FirebaseAuth.instance 323 | .sendSignInLinkToEmail(email: emailAuth, actionCodeSettings: acs) 324 | .catchError((error) => print('Error sending email verification $error')) 325 | .then((value) => print('Successfully sent email verification')); 326 | // [END email_link_auth_send_auth_link_email] 327 | 328 | await FirebaseAuth.instance.signOut(); 329 | } 330 | 331 | // TODO: ewindmill@ : I'm missing a couple snippets for 'email link' that also require DynamicLinks 332 | 333 | void social_google_sign_in() async { 334 | // [START social_google_sign_in] 335 | Future signInWithGoogle() async { 336 | // Trigger the authentication flow 337 | final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn(); 338 | 339 | // Obtain the auth details from the request 340 | final GoogleSignInAuthentication? googleAuth = 341 | await googleUser?.authentication; 342 | 343 | // Create a new credential 344 | final credential = GoogleAuthProvider.credential( 345 | accessToken: googleAuth?.accessToken, 346 | idToken: googleAuth?.idToken, 347 | ); 348 | 349 | // Once signed in, return the UserCredential 350 | return await FirebaseAuth.instance.signInWithCredential(credential); 351 | } 352 | // [END social_google_sign_in] 353 | 354 | // [START social_google_web_auth] 355 | GoogleAuthProvider googleProvider = GoogleAuthProvider(); 356 | 357 | googleProvider 358 | .addScope('https://www.googleapis.com/auth/contacts.readonly'); 359 | googleProvider.setCustomParameters({'login_hint': 'user@example.com'}); 360 | // [END social_google_web_auth] 361 | } 362 | 363 | void social_googleOnWeb() async { 364 | // [START social_google_on_web] 365 | Future signInWithGoogle() async { 366 | // Create a new provider 367 | GoogleAuthProvider googleProvider = GoogleAuthProvider(); 368 | 369 | googleProvider 370 | .addScope('https://www.googleapis.com/auth/contacts.readonly'); 371 | googleProvider.setCustomParameters({'login_hint': 'user@example.com'}); 372 | 373 | // Once signed in, return the UserCredential 374 | return await FirebaseAuth.instance.signInWithPopup(googleProvider); 375 | 376 | // Or use signInWithRedirect 377 | // return await FirebaseAuth.instance.signInWithRedirect(googleProvider); 378 | } 379 | // [END social_google_on_web] 380 | } 381 | 382 | void social_facebookAuth() async { 383 | // [START social_facebook_auth] 384 | Future signInWithFacebook() async { 385 | // Create a new provider 386 | FacebookAuthProvider facebookProvider = FacebookAuthProvider(); 387 | 388 | facebookProvider.addScope('email'); 389 | facebookProvider.setCustomParameters({ 390 | 'display': 'popup', 391 | }); 392 | 393 | // Once signed in, return the UserCredential 394 | return await FirebaseAuth.instance.signInWithPopup(facebookProvider); 395 | 396 | // Or use signInWithRedirect 397 | // return await FirebaseAuth.instance.signInWithRedirect(facebookProvider); 398 | } 399 | // [END social_facebook_auth] 400 | } 401 | 402 | void social_appleAuth() async { 403 | // [START social_apple_auth] 404 | /// Generates a cryptographically secure random nonce, to be included in a 405 | /// credential request. 406 | String generateNonce([int length = 32]) { 407 | const charset = 408 | '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._'; 409 | final random = Random.secure(); 410 | return List.generate( 411 | length, (_) => charset[random.nextInt(charset.length)]).join(); 412 | } 413 | 414 | /// Returns the sha256 hash of [input] in hex notation. 415 | String sha256ofString(String input) { 416 | final bytes = utf8.encode(input); 417 | final digest = sha256.convert(bytes); 418 | return digest.toString(); 419 | } 420 | 421 | Future signInWithApple() async { 422 | // To prevent replay attacks with the credential returned from Apple, we 423 | // include a nonce in the credential request. When signing in with 424 | // Firebase, the nonce in the id token returned by Apple, is expected to 425 | // match the sha256 hash of `rawNonce`. 426 | final rawNonce = generateNonce(); 427 | final nonce = sha256ofString(rawNonce); 428 | 429 | // Request credential for the currently signed in Apple account. 430 | final appleCredential = await SignInWithApple.getAppleIDCredential( 431 | scopes: [ 432 | AppleIDAuthorizationScopes.email, 433 | AppleIDAuthorizationScopes.fullName, 434 | ], 435 | nonce: nonce, 436 | ); 437 | 438 | // Create an `OAuthCredential` from the credential returned by Apple. 439 | final oauthCredential = OAuthProvider("apple.com").credential( 440 | idToken: appleCredential.identityToken, 441 | rawNonce: rawNonce, 442 | ); 443 | 444 | // Sign in the user with Firebase. If the nonce we generated earlier does 445 | // not match the nonce in `appleCredential.identityToken`, sign in will fail. 446 | return await FirebaseAuth.instance.signInWithCredential(oauthCredential); 447 | } 448 | // [END social_apple_auth] 449 | } 450 | 451 | void social_applePreferableAuth() async { 452 | // [START social_apple_preferable_auth] 453 | Future signInWithApple() async { 454 | // Create and configure an OAuthProvider for Sign In with Apple. 455 | final provider = OAuthProvider("apple.com") 456 | ..addScope('email') 457 | ..addScope('name'); 458 | 459 | // Sign in the user with Firebase. 460 | return await FirebaseAuth.instance.signInWithPopup(provider); 461 | } 462 | // [END social_apple_preferable_auth] 463 | } 464 | 465 | void social_twitterAuth() async { 466 | // [START social_twitter_auth] 467 | Future signInWithTwitter() async { 468 | // Create a TwitterLogin instance 469 | final twitterLogin = TwitterLogin( 470 | apiKey: '', 471 | apiSecretKey: ' ', 472 | redirectURI: '://'); 473 | 474 | // Trigger the sign-in flow 475 | final authResult = await twitterLogin.login(); 476 | 477 | // Create a credential from the access token 478 | final twitterAuthCredential = TwitterAuthProvider.credential( 479 | accessToken: authResult.authToken!, 480 | secret: authResult.authTokenSecret!, 481 | ); 482 | 483 | // Once signed in, return the UserCredential 484 | return await FirebaseAuth.instance 485 | .signInWithCredential(twitterAuthCredential); 486 | } 487 | // [END social_twitter_auth] 488 | } 489 | 490 | void social_twitterWebAuth() async { 491 | // [START social_twitter_web_auth] 492 | Future signInWithTwitter() async { 493 | // Create a new provider 494 | TwitterAuthProvider twitterProvider = TwitterAuthProvider(); 495 | 496 | // Once signed in, return the UserCredential 497 | return await FirebaseAuth.instance.signInWithPopup(twitterProvider); 498 | 499 | // Or use signInWithRedirect 500 | // return await FirebaseAuth.instance.signInWithRedirect(twitterProvider); 501 | } 502 | // [END social_twitter_web_auth] 503 | } 504 | 505 | void phoneAuth_verifyPhoneNumber() async { 506 | // [START phone_auth_verify_phone_number] 507 | await FirebaseAuth.instance.verifyPhoneNumber( 508 | phoneNumber: '+44 7123 123 456', 509 | verificationCompleted: (PhoneAuthCredential credential) {}, 510 | verificationFailed: (FirebaseAuthException e) {}, 511 | codeSent: (String verificationId, int? resendToken) {}, 512 | codeAutoRetrievalTimeout: (String verificationId) {}, 513 | ); 514 | // [END phone_auth_verify_phone_number] 515 | 516 | final FirebaseAuth auth = FirebaseAuth.instance; 517 | await auth.verifyPhoneNumber( 518 | phoneNumber: '+44 7123 123 456', 519 | // [START phone_auth_verification_complete] 520 | verificationCompleted: (PhoneAuthCredential credential) async { 521 | // ANDROID ONLY! 522 | 523 | // Sign the user in (or link) with the auto-generated credential 524 | await auth.signInWithCredential(credential); 525 | }, 526 | // [END phone_auth_verification_complete] 527 | // [START phone_auth_verification_fail] 528 | verificationFailed: (FirebaseAuthException e) { 529 | if (e.code == 'invalid-phone-number') { 530 | print('The provided phone number is not valid.'); 531 | } 532 | 533 | // Handle other errors 534 | }, 535 | // [END phone_auth_verification_fail] 536 | 537 | // [START phone_auth_code_sent] 538 | codeSent: (String verificationId, int? resendToken) async { 539 | // Update the UI - wait for the user to enter the SMS code 540 | String smsCode = 'xxxx'; 541 | 542 | // Create a PhoneAuthCredential with the code 543 | PhoneAuthCredential credential = PhoneAuthProvider.credential( 544 | verificationId: verificationId, smsCode: smsCode); 545 | 546 | // Sign the user in (or link) with the credential 547 | await auth.signInWithCredential(credential); 548 | }, 549 | // [END phone_auth_code_sent] 550 | 551 | // [START phone_auth_code_time_out] 552 | timeout: const Duration(seconds: 60), 553 | codeAutoRetrievalTimeout: (String verificationId) { 554 | // auth resolution timed out... 555 | }, 556 | // [END phone_auth_code_time_out] 557 | ); 558 | } 559 | 560 | void phoneAuth_webAuth() async { 561 | if (kIsWeb) { 562 | // [START phone_auth_web_auth] 563 | FirebaseAuth auth = FirebaseAuth.instance; 564 | 565 | // Wait for the user to complete the reCAPTCHA & for an SMS code to be sent. 566 | ConfirmationResult confirmationResult = 567 | await auth.signInWithPhoneNumber('+44 7123 123 456'); 568 | // [END phone_auth_web_auth] 569 | 570 | // [START phone_auth_web_confirmation] 571 | UserCredential userCredential = 572 | await confirmationResult.confirm('123456'); 573 | // [END phone_auth_web_confirmation] 574 | 575 | // [START phone_auth_web_with_recaptcha] 576 | ConfirmationResult confirmationResultWithRecaptcha = 577 | await auth.signInWithPhoneNumber( 578 | '+44 7123 123 456', 579 | RecaptchaVerifier( 580 | auth: FirebaseAuthPlatform.instance, 581 | container: 'recaptcha', 582 | size: RecaptchaVerifierSize.compact, 583 | theme: RecaptchaVerifierTheme.dark, 584 | )); 585 | // [END phone_auth_web_with_recaptcha] 586 | 587 | // [START phone_auth_verify_recaptcha] 588 | RecaptchaVerifier( 589 | auth: FirebaseAuthPlatform.instance, 590 | onSuccess: () => print('reCAPTCHA Completed!'), 591 | onError: (FirebaseAuthException error) => print(error), 592 | onExpired: () => print('reCAPTCHA Expired!'), 593 | ); 594 | // [END phone_auth_verify_recaptcha] 595 | } 596 | } 597 | 598 | void customAuth_withFirebase() async { 599 | const token = '1234superGoodToken'; 600 | 601 | // [START custom_auth_with_firebase] 602 | try { 603 | final userCredential = 604 | await FirebaseAuth.instance.signInWithCustomToken(token); 605 | print("Sign-in successful."); 606 | } on FirebaseAuthException catch (e) { 607 | switch (e.code) { 608 | case "invalid-custom-token": 609 | print("The supplied token is not a Firebase custom auth token."); 610 | break; 611 | case "custom-token-mismatch": 612 | print("The supplied token is for a different Firebase project."); 613 | break; 614 | default: 615 | print("Unknown error."); 616 | } 617 | } 618 | // [END custom_auth_with_firebase] 619 | await FirebaseAuth.instance.signOut(); 620 | } 621 | 622 | void anonymousAuth_withFirebase() async { 623 | // [START anonymous_auth_with_firebase] 624 | try { 625 | final userCredential = await FirebaseAuth.instance.signInAnonymously(); 626 | print("Signed in with temporary account."); 627 | } on FirebaseAuthException catch (e) { 628 | switch (e.code) { 629 | case "operation-not-allowed": 630 | print("Anonymous auth hasn't been enabled for this project."); 631 | break; 632 | default: 633 | print("Unknown error."); 634 | } 635 | } 636 | // [END anonymous_auth_with_firebase] 637 | 638 | const idToken = 'googleSignInIdToken'; 639 | const emailAddress = 'user@example.com'; 640 | const password = 'jks74l8q238d9sds!'; 641 | 642 | // [START anonymous_auth_get_credential] 643 | // Google Sign-in 644 | final credential = GoogleAuthProvider.credential(idToken: idToken); 645 | 646 | // Email and password sign-in 647 | final emailAndPasswordCredential = 648 | EmailAuthProvider.credential(email: emailAddress, password: password); 649 | 650 | // Etc. 651 | // [END anonymous_auth_get_credential] 652 | 653 | // [START anonymous_auth_link_with_credential] 654 | try { 655 | final userCredential = await FirebaseAuth.instance.currentUser 656 | ?.linkWithCredential(credential); 657 | } on FirebaseAuthException catch (e) { 658 | switch (e.code) { 659 | case "provider-already-linked": 660 | print("The provider has already been linked to the user."); 661 | break; 662 | case "invalid-credential": 663 | print("The provider's credential is not valid."); 664 | break; 665 | case "credential-already-in-use": 666 | print("The account corresponding to the credential already exists, " 667 | "or is already linked to a Firebase User."); 668 | break; 669 | // See the API reference for the full list of error codes. 670 | default: 671 | print("Unknown error."); 672 | } 673 | // [END anonymous_auth_link_with_credential] 674 | } 675 | 676 | await FirebaseAuth.instance.signOut(); 677 | } 678 | 679 | void link_accounts_get_credentials() async { 680 | const idToken = 'googleSignInIdToken'; 681 | const emailAddress = 'user@example.com'; 682 | const password = 'jks74l8q238d9sds!'; 683 | // [START link_accounts_get_credentials] 684 | 685 | // Google Sign-in 686 | final credential = GoogleAuthProvider.credential(idToken: idToken); 687 | 688 | // Email and password sign-in 689 | final emailAndPasswordCredential = 690 | EmailAuthProvider.credential(email: emailAddress, password: password); 691 | 692 | // Etc. 693 | // [END link_accounts_get_credentials] 694 | 695 | // [START link_accounts_link_with_credential] 696 | try { 697 | final userCredential = await FirebaseAuth.instance.currentUser 698 | ?.linkWithCredential(credential); 699 | } on FirebaseAuthException catch (e) { 700 | switch (e.code) { 701 | case "provider-already-linked": 702 | print("The provider has already been linked to the user."); 703 | break; 704 | case "invalid-credential": 705 | print("The provider's credential is not valid."); 706 | break; 707 | case "credential-already-in-use": 708 | print("The account corresponding to the credential already exists, " 709 | "or is already linked to a Firebase User."); 710 | break; 711 | // See the API reference for the full list of error codes. 712 | default: 713 | print("Unknown error."); 714 | } 715 | // [END link_accounts_link_with_credential] 716 | } 717 | 718 | const providerId = 'google'; 719 | // [START link_accounts_unlink_auth_provider] 720 | try { 721 | await FirebaseAuth.instance.currentUser?.unlink(providerId); 722 | } on FirebaseAuthException catch (e) { 723 | switch (e.code) { 724 | case "no-such-provider": 725 | print("The user isn't linked to the provider or the provider " 726 | "doesn't exist."); 727 | break; 728 | default: 729 | print("Unkown error."); 730 | } 731 | } 732 | // [END link_accounts_unlink_auth_provider] 733 | } 734 | } 735 | -------------------------------------------------------------------------------- /packages/firebase_snippets_app/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _fe_analyzer_shared: 5 | dependency: transitive 6 | description: 7 | name: _fe_analyzer_shared 8 | sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "76.0.0" 12 | _flutterfire_internals: 13 | dependency: transitive 14 | description: 15 | name: _flutterfire_internals 16 | sha256: "27899c95f9e7ec06c8310e6e0eac967707714b9f1450c4a58fa00ca011a4a8ae" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "1.3.49" 20 | _macros: 21 | dependency: transitive 22 | description: dart 23 | source: sdk 24 | version: "0.3.3" 25 | analyzer: 26 | dependency: transitive 27 | description: 28 | name: analyzer 29 | sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" 30 | url: "https://pub.dev" 31 | source: hosted 32 | version: "6.11.0" 33 | args: 34 | dependency: transitive 35 | description: 36 | name: args 37 | sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 38 | url: "https://pub.dev" 39 | source: hosted 40 | version: "2.6.0" 41 | async: 42 | dependency: transitive 43 | description: 44 | name: async 45 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 46 | url: "https://pub.dev" 47 | source: hosted 48 | version: "2.11.0" 49 | boolean_selector: 50 | dependency: transitive 51 | description: 52 | name: boolean_selector 53 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 54 | url: "https://pub.dev" 55 | source: hosted 56 | version: "2.1.1" 57 | build: 58 | dependency: transitive 59 | description: 60 | name: build 61 | sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 62 | url: "https://pub.dev" 63 | source: hosted 64 | version: "2.4.2" 65 | build_config: 66 | dependency: transitive 67 | description: 68 | name: build_config 69 | sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" 70 | url: "https://pub.dev" 71 | source: hosted 72 | version: "1.1.2" 73 | build_daemon: 74 | dependency: transitive 75 | description: 76 | name: build_daemon 77 | sha256: "294a2edaf4814a378725bfe6358210196f5ea37af89ecd81bfa32960113d4948" 78 | url: "https://pub.dev" 79 | source: hosted 80 | version: "4.0.3" 81 | build_resolvers: 82 | dependency: transitive 83 | description: 84 | name: build_resolvers 85 | sha256: "99d3980049739a985cf9b21f30881f46db3ebc62c5b8d5e60e27440876b1ba1e" 86 | url: "https://pub.dev" 87 | source: hosted 88 | version: "2.4.3" 89 | build_runner: 90 | dependency: "direct dev" 91 | description: 92 | name: build_runner 93 | sha256: "74691599a5bc750dc96a6b4bfd48f7d9d66453eab04c7f4063134800d6a5c573" 94 | url: "https://pub.dev" 95 | source: hosted 96 | version: "2.4.14" 97 | build_runner_core: 98 | dependency: transitive 99 | description: 100 | name: build_runner_core 101 | sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" 102 | url: "https://pub.dev" 103 | source: hosted 104 | version: "8.0.0" 105 | built_collection: 106 | dependency: transitive 107 | description: 108 | name: built_collection 109 | sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" 110 | url: "https://pub.dev" 111 | source: hosted 112 | version: "5.1.1" 113 | built_value: 114 | dependency: transitive 115 | description: 116 | name: built_value 117 | sha256: "28a712df2576b63c6c005c465989a348604960c0958d28be5303ba9baa841ac2" 118 | url: "https://pub.dev" 119 | source: hosted 120 | version: "8.9.3" 121 | characters: 122 | dependency: transitive 123 | description: 124 | name: characters 125 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 126 | url: "https://pub.dev" 127 | source: hosted 128 | version: "1.3.0" 129 | checked_yaml: 130 | dependency: transitive 131 | description: 132 | name: checked_yaml 133 | sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff 134 | url: "https://pub.dev" 135 | source: hosted 136 | version: "2.0.3" 137 | clock: 138 | dependency: transitive 139 | description: 140 | name: clock 141 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 142 | url: "https://pub.dev" 143 | source: hosted 144 | version: "1.1.1" 145 | cloud_firestore: 146 | dependency: "direct main" 147 | description: 148 | name: cloud_firestore 149 | sha256: a5c28d51ab20ce65db9d9dbeed4a1a825730c2a91a31eb6a6c97dc5d82c6cfd9 150 | url: "https://pub.dev" 151 | source: hosted 152 | version: "5.6.1" 153 | cloud_firestore_odm: 154 | dependency: "direct main" 155 | description: 156 | name: cloud_firestore_odm 157 | sha256: aa1fc8693e064805c0f414037e841e886b52775307308f93bd9895beaa2ba415 158 | url: "https://pub.dev" 159 | source: hosted 160 | version: "1.0.0-dev.88" 161 | cloud_firestore_odm_generator: 162 | dependency: "direct dev" 163 | description: 164 | name: cloud_firestore_odm_generator 165 | sha256: d8b695ac0a3aeb88c1f42b2be8f53d79bd92126a761adc672430c9e2361d71e2 166 | url: "https://pub.dev" 167 | source: hosted 168 | version: "1.0.0-dev.89" 169 | cloud_firestore_platform_interface: 170 | dependency: transitive 171 | description: 172 | name: cloud_firestore_platform_interface 173 | sha256: "6ea302a856373674ce090f97d99a265202a75cb8e3125c6076a782ed586e6f64" 174 | url: "https://pub.dev" 175 | source: hosted 176 | version: "6.6.1" 177 | cloud_firestore_web: 178 | dependency: transitive 179 | description: 180 | name: cloud_firestore_web 181 | sha256: eae5ffd1a96eab6013d1c3bf33280ca254ddc179b30a12f1dbe9f7a7c6ba7af1 182 | url: "https://pub.dev" 183 | source: hosted 184 | version: "4.4.1" 185 | cloud_functions: 186 | dependency: "direct main" 187 | description: 188 | name: cloud_functions 189 | sha256: "119c3816e05b799f26e2d5287ce09af332406cdb74749029e93ac5d423073bc2" 190 | url: "https://pub.dev" 191 | source: hosted 192 | version: "5.3.0" 193 | cloud_functions_platform_interface: 194 | dependency: transitive 195 | description: 196 | name: cloud_functions_platform_interface 197 | sha256: "2a015e2aae4d434329fa3e4d0e652fe9c160587f9bddd635c1ecea56c4dd92b8" 198 | url: "https://pub.dev" 199 | source: hosted 200 | version: "5.6.0" 201 | cloud_functions_web: 202 | dependency: transitive 203 | description: 204 | name: cloud_functions_web 205 | sha256: e9dc5603294b40f53f3c7c8e8c32e005d50f3eadeb1f05390a26a8e48cf16219 206 | url: "https://pub.dev" 207 | source: hosted 208 | version: "4.10.6" 209 | code_builder: 210 | dependency: transitive 211 | description: 212 | name: code_builder 213 | sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" 214 | url: "https://pub.dev" 215 | source: hosted 216 | version: "4.10.1" 217 | collection: 218 | dependency: transitive 219 | description: 220 | name: collection 221 | sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf 222 | url: "https://pub.dev" 223 | source: hosted 224 | version: "1.19.0" 225 | convert: 226 | dependency: transitive 227 | description: 228 | name: convert 229 | sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 230 | url: "https://pub.dev" 231 | source: hosted 232 | version: "3.1.2" 233 | crypto: 234 | dependency: "direct main" 235 | description: 236 | name: crypto 237 | sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" 238 | url: "https://pub.dev" 239 | source: hosted 240 | version: "3.0.6" 241 | dart_style: 242 | dependency: transitive 243 | description: 244 | name: dart_style 245 | sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab" 246 | url: "https://pub.dev" 247 | source: hosted 248 | version: "2.3.7" 249 | facebook_auth_desktop: 250 | dependency: transitive 251 | description: 252 | name: facebook_auth_desktop 253 | sha256: "219d559a33891e937c1913430505eae01fb946cb35729167bbdc747e3ebbd9ff" 254 | url: "https://pub.dev" 255 | source: hosted 256 | version: "2.1.1" 257 | fake_async: 258 | dependency: transitive 259 | description: 260 | name: fake_async 261 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 262 | url: "https://pub.dev" 263 | source: hosted 264 | version: "1.3.1" 265 | ffi: 266 | dependency: transitive 267 | description: 268 | name: ffi 269 | sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" 270 | url: "https://pub.dev" 271 | source: hosted 272 | version: "2.1.3" 273 | file: 274 | dependency: transitive 275 | description: 276 | name: file 277 | sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" 278 | url: "https://pub.dev" 279 | source: hosted 280 | version: "7.0.0" 281 | firebase_analytics: 282 | dependency: "direct main" 283 | description: 284 | name: firebase_analytics 285 | sha256: "498c6cb8468e348a556709c745d92a52173ab3a9b906aa0593393f0787f201ea" 286 | url: "https://pub.dev" 287 | source: hosted 288 | version: "11.4.0" 289 | firebase_analytics_platform_interface: 290 | dependency: transitive 291 | description: 292 | name: firebase_analytics_platform_interface 293 | sha256: ccbb350554e98afdb4b59852689292d194d31232a2647b5012a66622b3711df9 294 | url: "https://pub.dev" 295 | source: hosted 296 | version: "4.3.0" 297 | firebase_analytics_web: 298 | dependency: transitive 299 | description: 300 | name: firebase_analytics_web 301 | sha256: "68e1f18fc16482c211c658e739c25f015b202a260d9ad8249c6d3d7963b8105f" 302 | url: "https://pub.dev" 303 | source: hosted 304 | version: "0.5.10+6" 305 | firebase_auth: 306 | dependency: "direct main" 307 | description: 308 | name: firebase_auth 309 | sha256: "1c2e81c4d4b56f63cb6cefea0560aa5a504457c7a5a601018e6c6d7c07795640" 310 | url: "https://pub.dev" 311 | source: hosted 312 | version: "5.4.0" 313 | firebase_auth_platform_interface: 314 | dependency: "direct main" 315 | description: 316 | name: firebase_auth_platform_interface 317 | sha256: "973899f5be76d37312a07e366033b7f3f262dc51ec70ceae2a3592993289717a" 318 | url: "https://pub.dev" 319 | source: hosted 320 | version: "7.5.0" 321 | firebase_auth_web: 322 | dependency: transitive 323 | description: 324 | name: firebase_auth_web 325 | sha256: "960f7cc50ecb977a721165166aae90e86302f1370549457f78cbcea5be7f2cb2" 326 | url: "https://pub.dev" 327 | source: hosted 328 | version: "5.13.6" 329 | firebase_core: 330 | dependency: "direct main" 331 | description: 332 | name: firebase_core 333 | sha256: "0307c1fde82e2b8b97e0be2dab93612aff9a72f31ebe9bfac66ed8b37ef7c568" 334 | url: "https://pub.dev" 335 | source: hosted 336 | version: "3.10.0" 337 | firebase_core_platform_interface: 338 | dependency: transitive 339 | description: 340 | name: firebase_core_platform_interface 341 | sha256: d7253d255ff10f85cfd2adaba9ac17bae878fa3ba577462451163bd9f1d1f0bf 342 | url: "https://pub.dev" 343 | source: hosted 344 | version: "5.4.0" 345 | firebase_core_web: 346 | dependency: transitive 347 | description: 348 | name: firebase_core_web 349 | sha256: fbc008cf390d909b823763064b63afefe9f02d8afdb13eb3f485b871afee956b 350 | url: "https://pub.dev" 351 | source: hosted 352 | version: "2.19.0" 353 | firebase_crashlytics: 354 | dependency: "direct main" 355 | description: 356 | name: firebase_crashlytics 357 | sha256: f6adb65fa3d6391a79f0e60833bb4cdc468ce0c318831c90057ee11e0909cd29 358 | url: "https://pub.dev" 359 | source: hosted 360 | version: "4.3.0" 361 | firebase_crashlytics_platform_interface: 362 | dependency: transitive 363 | description: 364 | name: firebase_crashlytics_platform_interface 365 | sha256: "6635166c22c6f75f634b8e77b70fcc43b24af4cfee28f975249dbdbd9769a702" 366 | url: "https://pub.dev" 367 | source: hosted 368 | version: "3.8.0" 369 | firebase_database: 370 | dependency: "direct main" 371 | description: 372 | name: firebase_database 373 | sha256: af9e0b2e8a117d2324027a5632d64b003e710283a20c692e12ebcc439d22be5c 374 | url: "https://pub.dev" 375 | source: hosted 376 | version: "11.3.0" 377 | firebase_database_platform_interface: 378 | dependency: transitive 379 | description: 380 | name: firebase_database_platform_interface 381 | sha256: "6f92dfdd773c40d7b04113ad9e9de66826cd8a9f6c939a3693943eba4adce234" 382 | url: "https://pub.dev" 383 | source: hosted 384 | version: "0.2.6" 385 | firebase_database_web: 386 | dependency: transitive 387 | description: 388 | name: firebase_database_web 389 | sha256: "6b863cc62cbd5d006d8e36d5407336115d3737cfe1d8574c6d8a3d035d0ed259" 390 | url: "https://pub.dev" 391 | source: hosted 392 | version: "0.2.6+6" 393 | firebase_dynamic_links: 394 | dependency: "direct main" 395 | description: 396 | name: firebase_dynamic_links 397 | sha256: e4984adcdeaa69f22fb60909c5ff211b9ef14d0d0f9362e4209eeef3e552b5e2 398 | url: "https://pub.dev" 399 | source: hosted 400 | version: "6.1.0" 401 | firebase_dynamic_links_platform_interface: 402 | dependency: transitive 403 | description: 404 | name: firebase_dynamic_links_platform_interface 405 | sha256: f41a911267727d241e6d3453718a4b0a95ac33ee0a6e086923f9cab4acd31806 406 | url: "https://pub.dev" 407 | source: hosted 408 | version: "0.2.7" 409 | firebase_messaging: 410 | dependency: "direct main" 411 | description: 412 | name: firebase_messaging 413 | sha256: "48a8a59197c1c5174060ba9aa1e0036e9b5a0d28a0cc22d19c1fcabc67fafe3c" 414 | url: "https://pub.dev" 415 | source: hosted 416 | version: "15.2.0" 417 | firebase_messaging_platform_interface: 418 | dependency: transitive 419 | description: 420 | name: firebase_messaging_platform_interface 421 | sha256: "9770a8e91f54296829dcaa61ce9b7c2f9ae9abbf99976dd3103a60470d5264dd" 422 | url: "https://pub.dev" 423 | source: hosted 424 | version: "4.6.0" 425 | firebase_messaging_web: 426 | dependency: transitive 427 | description: 428 | name: firebase_messaging_web 429 | sha256: "329ca4ef45ec616abe6f1d5e58feed0934a50840a65aa327052354ad3c64ed77" 430 | url: "https://pub.dev" 431 | source: hosted 432 | version: "3.10.0" 433 | firebase_ml_model_downloader: 434 | dependency: "direct main" 435 | description: 436 | name: firebase_ml_model_downloader 437 | sha256: e56252d337f1b9568d670647eccdea114e8ceef3659e2c6409a479a2d2ea258c 438 | url: "https://pub.dev" 439 | source: hosted 440 | version: "0.3.2" 441 | firebase_ml_model_downloader_platform_interface: 442 | dependency: transitive 443 | description: 444 | name: firebase_ml_model_downloader_platform_interface 445 | sha256: "81f81f2f2ddae9083d847d3f3f455bbe3d83ac52b2a2a6d00d55a4efbf7a6e4e" 446 | url: "https://pub.dev" 447 | source: hosted 448 | version: "0.1.5" 449 | firebase_performance: 450 | dependency: "direct main" 451 | description: 452 | name: firebase_performance 453 | sha256: "0c8c0d9c491d166e95df6db0f8758fc5b5445a56dac6e63f33e8feb9f9021d83" 454 | url: "https://pub.dev" 455 | source: hosted 456 | version: "0.10.1" 457 | firebase_performance_platform_interface: 458 | dependency: transitive 459 | description: 460 | name: firebase_performance_platform_interface 461 | sha256: "53adf58798495c0df223fa7a2e0d36972ce630d8019ed7392a4b5b490c4114da" 462 | url: "https://pub.dev" 463 | source: hosted 464 | version: "0.1.5" 465 | firebase_performance_web: 466 | dependency: transitive 467 | description: 468 | name: firebase_performance_web 469 | sha256: a9db2c82552d853ef121e184229b999e318e03ea54d392ffac9c54eea5aa4216 470 | url: "https://pub.dev" 471 | source: hosted 472 | version: "0.1.7+6" 473 | firebase_remote_config: 474 | dependency: "direct main" 475 | description: 476 | name: firebase_remote_config 477 | sha256: "1ddeee5055a6fd2295429b8cc3019d5f422c60c77fd22156a789aa6446645595" 478 | url: "https://pub.dev" 479 | source: hosted 480 | version: "5.3.0" 481 | firebase_remote_config_platform_interface: 482 | dependency: transitive 483 | description: 484 | name: firebase_remote_config_platform_interface 485 | sha256: a17a43aa27f860a4e13c2cea11f66d71584976c5b9aa59b0e1720b8435d14764 486 | url: "https://pub.dev" 487 | source: hosted 488 | version: "1.4.48" 489 | firebase_remote_config_web: 490 | dependency: transitive 491 | description: 492 | name: firebase_remote_config_web 493 | sha256: a85a95d6cc0692b82663acf3b0c312f219c55fbdf8ce997922ab862e64945415 494 | url: "https://pub.dev" 495 | source: hosted 496 | version: "1.7.6" 497 | firebase_storage: 498 | dependency: "direct main" 499 | description: 500 | name: firebase_storage 501 | sha256: d23097bcbd99951b2d4ac54a9dd2bf807ed900f2ebc242523ccf8d9d65023056 502 | url: "https://pub.dev" 503 | source: hosted 504 | version: "12.4.0" 505 | firebase_storage_platform_interface: 506 | dependency: transitive 507 | description: 508 | name: firebase_storage_platform_interface 509 | sha256: ccdb1ff57517279e0a2b2ad35e5c914f99576744d538602eeff4455d99a9417d 510 | url: "https://pub.dev" 511 | source: hosted 512 | version: "5.2.0" 513 | firebase_storage_web: 514 | dependency: transitive 515 | description: 516 | name: firebase_storage_web 517 | sha256: "04c7194d4a1a0d737073d27d8d6e114eea090a8e575ce50b0592b547b911931f" 518 | url: "https://pub.dev" 519 | source: hosted 520 | version: "3.10.7" 521 | fixnum: 522 | dependency: transitive 523 | description: 524 | name: fixnum 525 | sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be 526 | url: "https://pub.dev" 527 | source: hosted 528 | version: "1.1.1" 529 | flutter: 530 | dependency: "direct main" 531 | description: flutter 532 | source: sdk 533 | version: "0.0.0" 534 | flutter_driver: 535 | dependency: transitive 536 | description: flutter 537 | source: sdk 538 | version: "0.0.0" 539 | flutter_facebook_auth: 540 | dependency: "direct main" 541 | description: 542 | name: flutter_facebook_auth 543 | sha256: d542743aee9571fad59aa4cfb645640dbb49f23b039d07c34b5e21bc3d5857e9 544 | url: "https://pub.dev" 545 | source: hosted 546 | version: "7.1.1" 547 | flutter_facebook_auth_platform_interface: 548 | dependency: transitive 549 | description: 550 | name: flutter_facebook_auth_platform_interface 551 | sha256: e04b8dbfa77702bea45a79993163ad5d20b2c0084109bec591fdc2b9ee505779 552 | url: "https://pub.dev" 553 | source: hosted 554 | version: "6.1.2" 555 | flutter_facebook_auth_web: 556 | dependency: transitive 557 | description: 558 | name: flutter_facebook_auth_web 559 | sha256: f682400d61cf8d52dd8b6458b5ee106ed57e95309a117dc32875d3da129ce47c 560 | url: "https://pub.dev" 561 | source: hosted 562 | version: "6.1.2" 563 | flutter_lints: 564 | dependency: "direct dev" 565 | description: 566 | name: flutter_lints 567 | sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" 568 | url: "https://pub.dev" 569 | source: hosted 570 | version: "5.0.0" 571 | flutter_secure_storage: 572 | dependency: transitive 573 | description: 574 | name: flutter_secure_storage 575 | sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" 576 | url: "https://pub.dev" 577 | source: hosted 578 | version: "9.2.4" 579 | flutter_secure_storage_linux: 580 | dependency: transitive 581 | description: 582 | name: flutter_secure_storage_linux 583 | sha256: bf7404619d7ab5c0a1151d7c4e802edad8f33535abfbeff2f9e1fe1274e2d705 584 | url: "https://pub.dev" 585 | source: hosted 586 | version: "1.2.2" 587 | flutter_secure_storage_macos: 588 | dependency: transitive 589 | description: 590 | name: flutter_secure_storage_macos 591 | sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" 592 | url: "https://pub.dev" 593 | source: hosted 594 | version: "3.1.3" 595 | flutter_secure_storage_platform_interface: 596 | dependency: transitive 597 | description: 598 | name: flutter_secure_storage_platform_interface 599 | sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 600 | url: "https://pub.dev" 601 | source: hosted 602 | version: "1.1.2" 603 | flutter_secure_storage_web: 604 | dependency: transitive 605 | description: 606 | name: flutter_secure_storage_web 607 | sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 608 | url: "https://pub.dev" 609 | source: hosted 610 | version: "1.2.1" 611 | flutter_secure_storage_windows: 612 | dependency: transitive 613 | description: 614 | name: flutter_secure_storage_windows 615 | sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 616 | url: "https://pub.dev" 617 | source: hosted 618 | version: "3.1.2" 619 | flutter_test: 620 | dependency: "direct dev" 621 | description: flutter 622 | source: sdk 623 | version: "0.0.0" 624 | flutter_web_plugins: 625 | dependency: transitive 626 | description: flutter 627 | source: sdk 628 | version: "0.0.0" 629 | freezed_annotation: 630 | dependency: transitive 631 | description: 632 | name: freezed_annotation 633 | sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 634 | url: "https://pub.dev" 635 | source: hosted 636 | version: "2.4.4" 637 | frontend_server_client: 638 | dependency: transitive 639 | description: 640 | name: frontend_server_client 641 | sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 642 | url: "https://pub.dev" 643 | source: hosted 644 | version: "4.0.0" 645 | fuchsia_remote_debug_protocol: 646 | dependency: transitive 647 | description: flutter 648 | source: sdk 649 | version: "0.0.0" 650 | glob: 651 | dependency: transitive 652 | description: 653 | name: glob 654 | sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" 655 | url: "https://pub.dev" 656 | source: hosted 657 | version: "2.1.2" 658 | google_identity_services_web: 659 | dependency: transitive 660 | description: 661 | name: google_identity_services_web 662 | sha256: "55580f436822d64c8ff9a77e37d61f5fb1e6c7ec9d632a43ee324e2a05c3c6c9" 663 | url: "https://pub.dev" 664 | source: hosted 665 | version: "0.3.3" 666 | google_sign_in: 667 | dependency: "direct main" 668 | description: 669 | name: google_sign_in 670 | sha256: fad6ddc80c427b0bba705f2116204ce1173e09cf299f85e053d57a55e5b2dd56 671 | url: "https://pub.dev" 672 | source: hosted 673 | version: "6.2.2" 674 | google_sign_in_android: 675 | dependency: transitive 676 | description: 677 | name: google_sign_in_android 678 | sha256: "3b96f9b6cf61915f73cbe1218a192623e296a9b8b31965702503649477761e36" 679 | url: "https://pub.dev" 680 | source: hosted 681 | version: "6.1.34" 682 | google_sign_in_ios: 683 | dependency: transitive 684 | description: 685 | name: google_sign_in_ios 686 | sha256: "83f015169102df1ab2905cf8abd8934e28f87db9ace7a5fa676998842fed228a" 687 | url: "https://pub.dev" 688 | source: hosted 689 | version: "5.7.8" 690 | google_sign_in_platform_interface: 691 | dependency: transitive 692 | description: 693 | name: google_sign_in_platform_interface 694 | sha256: "1f6e5787d7a120cc0359ddf315c92309069171306242e181c09472d1b00a2971" 695 | url: "https://pub.dev" 696 | source: hosted 697 | version: "2.4.5" 698 | google_sign_in_web: 699 | dependency: transitive 700 | description: 701 | name: google_sign_in_web 702 | sha256: ada595df6c30cead48e66b1f3a050edf0c5cf2ba60c185d69690e08adcc6281b 703 | url: "https://pub.dev" 704 | source: hosted 705 | version: "0.12.4+3" 706 | graphs: 707 | dependency: transitive 708 | description: 709 | name: graphs 710 | sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" 711 | url: "https://pub.dev" 712 | source: hosted 713 | version: "2.3.2" 714 | http: 715 | dependency: "direct main" 716 | description: 717 | name: http 718 | sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 719 | url: "https://pub.dev" 720 | source: hosted 721 | version: "1.2.2" 722 | http_multi_server: 723 | dependency: transitive 724 | description: 725 | name: http_multi_server 726 | sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 727 | url: "https://pub.dev" 728 | source: hosted 729 | version: "3.2.2" 730 | http_parser: 731 | dependency: transitive 732 | description: 733 | name: http_parser 734 | sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" 735 | url: "https://pub.dev" 736 | source: hosted 737 | version: "4.1.2" 738 | integration_test: 739 | dependency: "direct dev" 740 | description: flutter 741 | source: sdk 742 | version: "0.0.0" 743 | io: 744 | dependency: transitive 745 | description: 746 | name: io 747 | sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b 748 | url: "https://pub.dev" 749 | source: hosted 750 | version: "1.0.5" 751 | js: 752 | dependency: transitive 753 | description: 754 | name: js 755 | sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 756 | url: "https://pub.dev" 757 | source: hosted 758 | version: "0.6.7" 759 | json_annotation: 760 | dependency: "direct main" 761 | description: 762 | name: json_annotation 763 | sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" 764 | url: "https://pub.dev" 765 | source: hosted 766 | version: "4.9.0" 767 | json_serializable: 768 | dependency: "direct dev" 769 | description: 770 | name: json_serializable 771 | sha256: c2fcb3920cf2b6ae6845954186420fca40bc0a8abcc84903b7801f17d7050d7c 772 | url: "https://pub.dev" 773 | source: hosted 774 | version: "6.9.0" 775 | leak_tracker: 776 | dependency: transitive 777 | description: 778 | name: leak_tracker 779 | sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" 780 | url: "https://pub.dev" 781 | source: hosted 782 | version: "10.0.7" 783 | leak_tracker_flutter_testing: 784 | dependency: transitive 785 | description: 786 | name: leak_tracker_flutter_testing 787 | sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" 788 | url: "https://pub.dev" 789 | source: hosted 790 | version: "3.0.8" 791 | leak_tracker_testing: 792 | dependency: transitive 793 | description: 794 | name: leak_tracker_testing 795 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" 796 | url: "https://pub.dev" 797 | source: hosted 798 | version: "3.0.1" 799 | lints: 800 | dependency: transitive 801 | description: 802 | name: lints 803 | sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 804 | url: "https://pub.dev" 805 | source: hosted 806 | version: "5.1.1" 807 | logging: 808 | dependency: transitive 809 | description: 810 | name: logging 811 | sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 812 | url: "https://pub.dev" 813 | source: hosted 814 | version: "1.3.0" 815 | macros: 816 | dependency: transitive 817 | description: 818 | name: macros 819 | sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" 820 | url: "https://pub.dev" 821 | source: hosted 822 | version: "0.1.3-main.0" 823 | matcher: 824 | dependency: transitive 825 | description: 826 | name: matcher 827 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb 828 | url: "https://pub.dev" 829 | source: hosted 830 | version: "0.12.16+1" 831 | material_color_utilities: 832 | dependency: transitive 833 | description: 834 | name: material_color_utilities 835 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 836 | url: "https://pub.dev" 837 | source: hosted 838 | version: "0.11.1" 839 | meta: 840 | dependency: transitive 841 | description: 842 | name: meta 843 | sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 844 | url: "https://pub.dev" 845 | source: hosted 846 | version: "1.15.0" 847 | mime: 848 | dependency: transitive 849 | description: 850 | name: mime 851 | sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" 852 | url: "https://pub.dev" 853 | source: hosted 854 | version: "2.0.0" 855 | package_config: 856 | dependency: transitive 857 | description: 858 | name: package_config 859 | sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67" 860 | url: "https://pub.dev" 861 | source: hosted 862 | version: "2.1.1" 863 | path: 864 | dependency: transitive 865 | description: 866 | name: path 867 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 868 | url: "https://pub.dev" 869 | source: hosted 870 | version: "1.9.0" 871 | path_provider: 872 | dependency: "direct main" 873 | description: 874 | name: path_provider 875 | sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" 876 | url: "https://pub.dev" 877 | source: hosted 878 | version: "2.1.5" 879 | path_provider_android: 880 | dependency: transitive 881 | description: 882 | name: path_provider_android 883 | sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" 884 | url: "https://pub.dev" 885 | source: hosted 886 | version: "2.2.15" 887 | path_provider_foundation: 888 | dependency: transitive 889 | description: 890 | name: path_provider_foundation 891 | sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" 892 | url: "https://pub.dev" 893 | source: hosted 894 | version: "2.4.1" 895 | path_provider_linux: 896 | dependency: transitive 897 | description: 898 | name: path_provider_linux 899 | sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 900 | url: "https://pub.dev" 901 | source: hosted 902 | version: "2.2.1" 903 | path_provider_platform_interface: 904 | dependency: transitive 905 | description: 906 | name: path_provider_platform_interface 907 | sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" 908 | url: "https://pub.dev" 909 | source: hosted 910 | version: "2.1.2" 911 | path_provider_windows: 912 | dependency: transitive 913 | description: 914 | name: path_provider_windows 915 | sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 916 | url: "https://pub.dev" 917 | source: hosted 918 | version: "2.3.0" 919 | platform: 920 | dependency: transitive 921 | description: 922 | name: platform 923 | sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" 924 | url: "https://pub.dev" 925 | source: hosted 926 | version: "3.1.5" 927 | plugin_platform_interface: 928 | dependency: transitive 929 | description: 930 | name: plugin_platform_interface 931 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" 932 | url: "https://pub.dev" 933 | source: hosted 934 | version: "2.1.8" 935 | pool: 936 | dependency: transitive 937 | description: 938 | name: pool 939 | sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" 940 | url: "https://pub.dev" 941 | source: hosted 942 | version: "1.5.1" 943 | process: 944 | dependency: transitive 945 | description: 946 | name: process 947 | sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" 948 | url: "https://pub.dev" 949 | source: hosted 950 | version: "5.0.2" 951 | pub_semver: 952 | dependency: transitive 953 | description: 954 | name: pub_semver 955 | sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd" 956 | url: "https://pub.dev" 957 | source: hosted 958 | version: "2.1.5" 959 | pubspec_parse: 960 | dependency: transitive 961 | description: 962 | name: pubspec_parse 963 | sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" 964 | url: "https://pub.dev" 965 | source: hosted 966 | version: "1.5.0" 967 | recase: 968 | dependency: transitive 969 | description: 970 | name: recase 971 | sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 972 | url: "https://pub.dev" 973 | source: hosted 974 | version: "4.1.0" 975 | shelf: 976 | dependency: transitive 977 | description: 978 | name: shelf 979 | sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 980 | url: "https://pub.dev" 981 | source: hosted 982 | version: "1.4.2" 983 | shelf_web_socket: 984 | dependency: transitive 985 | description: 986 | name: shelf_web_socket 987 | sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67 988 | url: "https://pub.dev" 989 | source: hosted 990 | version: "2.0.1" 991 | sign_in_with_apple: 992 | dependency: "direct main" 993 | description: 994 | name: sign_in_with_apple 995 | sha256: e84a62e17b7e463abf0a64ce826c2cd1f0b72dff07b7b275e32d5302d76fb4c5 996 | url: "https://pub.dev" 997 | source: hosted 998 | version: "6.1.4" 999 | sign_in_with_apple_platform_interface: 1000 | dependency: transitive 1001 | description: 1002 | name: sign_in_with_apple_platform_interface 1003 | sha256: c2ef2ce6273fce0c61acd7e9ff5be7181e33d7aa2b66508b39418b786cca2119 1004 | url: "https://pub.dev" 1005 | source: hosted 1006 | version: "1.1.0" 1007 | sign_in_with_apple_web: 1008 | dependency: transitive 1009 | description: 1010 | name: sign_in_with_apple_web 1011 | sha256: "2f7c38368f49e3f2043bca4b46a4a61aaae568c140a79aa0675dc59ad0ca49bc" 1012 | url: "https://pub.dev" 1013 | source: hosted 1014 | version: "2.1.1" 1015 | sky_engine: 1016 | dependency: transitive 1017 | description: flutter 1018 | source: sdk 1019 | version: "0.0.0" 1020 | source_gen: 1021 | dependency: transitive 1022 | description: 1023 | name: source_gen 1024 | sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" 1025 | url: "https://pub.dev" 1026 | source: hosted 1027 | version: "1.5.0" 1028 | source_helper: 1029 | dependency: transitive 1030 | description: 1031 | name: source_helper 1032 | sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c" 1033 | url: "https://pub.dev" 1034 | source: hosted 1035 | version: "1.3.5" 1036 | source_span: 1037 | dependency: transitive 1038 | description: 1039 | name: source_span 1040 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 1041 | url: "https://pub.dev" 1042 | source: hosted 1043 | version: "1.10.0" 1044 | stack_trace: 1045 | dependency: transitive 1046 | description: 1047 | name: stack_trace 1048 | sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" 1049 | url: "https://pub.dev" 1050 | source: hosted 1051 | version: "1.12.0" 1052 | stream_channel: 1053 | dependency: transitive 1054 | description: 1055 | name: stream_channel 1056 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 1057 | url: "https://pub.dev" 1058 | source: hosted 1059 | version: "2.1.2" 1060 | stream_transform: 1061 | dependency: transitive 1062 | description: 1063 | name: stream_transform 1064 | sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 1065 | url: "https://pub.dev" 1066 | source: hosted 1067 | version: "2.1.1" 1068 | string_scanner: 1069 | dependency: transitive 1070 | description: 1071 | name: string_scanner 1072 | sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" 1073 | url: "https://pub.dev" 1074 | source: hosted 1075 | version: "1.3.0" 1076 | sync_http: 1077 | dependency: transitive 1078 | description: 1079 | name: sync_http 1080 | sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" 1081 | url: "https://pub.dev" 1082 | source: hosted 1083 | version: "0.3.1" 1084 | term_glyph: 1085 | dependency: transitive 1086 | description: 1087 | name: term_glyph 1088 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 1089 | url: "https://pub.dev" 1090 | source: hosted 1091 | version: "1.2.1" 1092 | test_api: 1093 | dependency: transitive 1094 | description: 1095 | name: test_api 1096 | sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" 1097 | url: "https://pub.dev" 1098 | source: hosted 1099 | version: "0.7.3" 1100 | timing: 1101 | dependency: transitive 1102 | description: 1103 | name: timing 1104 | sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" 1105 | url: "https://pub.dev" 1106 | source: hosted 1107 | version: "1.0.2" 1108 | twitter_login: 1109 | dependency: "direct main" 1110 | description: 1111 | name: twitter_login 1112 | sha256: "31ff9db2e37eda878b876a4ce6d1525f51d34b6cd9de9aa185b07027a23ab95b" 1113 | url: "https://pub.dev" 1114 | source: hosted 1115 | version: "4.4.2" 1116 | typed_data: 1117 | dependency: transitive 1118 | description: 1119 | name: typed_data 1120 | sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 1121 | url: "https://pub.dev" 1122 | source: hosted 1123 | version: "1.4.0" 1124 | vector_math: 1125 | dependency: transitive 1126 | description: 1127 | name: vector_math 1128 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 1129 | url: "https://pub.dev" 1130 | source: hosted 1131 | version: "2.1.4" 1132 | vm_service: 1133 | dependency: transitive 1134 | description: 1135 | name: vm_service 1136 | sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b 1137 | url: "https://pub.dev" 1138 | source: hosted 1139 | version: "14.3.0" 1140 | watcher: 1141 | dependency: transitive 1142 | description: 1143 | name: watcher 1144 | sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" 1145 | url: "https://pub.dev" 1146 | source: hosted 1147 | version: "1.1.1" 1148 | web: 1149 | dependency: transitive 1150 | description: 1151 | name: web 1152 | sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb 1153 | url: "https://pub.dev" 1154 | source: hosted 1155 | version: "1.1.0" 1156 | web_socket: 1157 | dependency: transitive 1158 | description: 1159 | name: web_socket 1160 | sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" 1161 | url: "https://pub.dev" 1162 | source: hosted 1163 | version: "0.1.6" 1164 | web_socket_channel: 1165 | dependency: transitive 1166 | description: 1167 | name: web_socket_channel 1168 | sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" 1169 | url: "https://pub.dev" 1170 | source: hosted 1171 | version: "3.0.1" 1172 | webdriver: 1173 | dependency: transitive 1174 | description: 1175 | name: webdriver 1176 | sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8" 1177 | url: "https://pub.dev" 1178 | source: hosted 1179 | version: "3.0.4" 1180 | win32: 1181 | dependency: "direct main" 1182 | description: 1183 | name: win32 1184 | sha256: "154360849a56b7b67331c21f09a386562d88903f90a1099c5987afc1912e1f29" 1185 | url: "https://pub.dev" 1186 | source: hosted 1187 | version: "5.10.0" 1188 | xdg_directories: 1189 | dependency: transitive 1190 | description: 1191 | name: xdg_directories 1192 | sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" 1193 | url: "https://pub.dev" 1194 | source: hosted 1195 | version: "1.1.0" 1196 | yaml: 1197 | dependency: transitive 1198 | description: 1199 | name: yaml 1200 | sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce 1201 | url: "https://pub.dev" 1202 | source: hosted 1203 | version: "3.1.3" 1204 | sdks: 1205 | dart: ">=3.6.0 <4.0.0" 1206 | flutter: ">=3.24.0" 1207 | --------------------------------------------------------------------------------