├── .github └── workflows │ ├── pull-and-push-all.yml │ └── pull-map-and-push-repo.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── codelabs ├── codelab-friendlychat-android │ └── steps │ │ ├── .gitignore │ │ ├── img │ │ ├── add-android-app.png │ │ ├── add-message.gif │ │ ├── android_studio_folder.png │ │ ├── emulators-auth-user.png │ │ ├── emulators-home.png │ │ ├── execute.png │ │ ├── import-data.gif │ │ └── screenshot.png │ │ └── index.lab.md ├── codelab-friendlychat-web │ └── steps │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── README.md │ │ ├── angular.json │ │ ├── firebase.json │ │ ├── firestore.indexes.json │ │ ├── firestore.rules │ │ ├── functions │ │ ├── .gitignore │ │ └── package.json │ │ ├── package.json │ │ ├── src │ │ ├── app │ │ │ ├── app-routing.module.ts │ │ │ ├── app.component.css │ │ │ ├── app.component.html │ │ │ ├── app.component.spec.ts │ │ │ ├── app.component.ts │ │ │ ├── app.config.ts │ │ │ ├── app.module.ts │ │ │ ├── app.routes.ts │ │ │ ├── components │ │ │ │ └── header │ │ │ │ │ ├── header.component.css │ │ │ │ │ ├── header.component.html │ │ │ │ │ ├── header.component.spec.ts │ │ │ │ │ └── header.component.ts │ │ │ ├── pages │ │ │ │ ├── chat-page │ │ │ │ │ ├── chat-page.component.css │ │ │ │ │ ├── chat-page.component.html │ │ │ │ │ ├── chat-page.component.spec.ts │ │ │ │ │ └── chat-page.component.ts │ │ │ │ └── login-page │ │ │ │ │ ├── login-page.component.css │ │ │ │ │ ├── login-page.component.html │ │ │ │ │ ├── login-page.component.spec.ts │ │ │ │ │ └── login-page.component.ts │ │ │ └── services │ │ │ │ ├── chat.service.spec.ts │ │ │ │ └── chat.service.ts │ │ ├── assets │ │ │ ├── .gitkeep │ │ │ ├── FriendlyChatLogo.png │ │ │ ├── Google.png │ │ │ ├── WithFirebase.png │ │ │ ├── add.svg │ │ │ ├── menu.svg │ │ │ └── send.svg │ │ ├── environments │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ └── styles.css │ │ ├── storage.rules │ │ ├── tailwind.config.js │ │ ├── test.md │ │ ├── tsconfig.app.json │ │ ├── tsconfig.json │ │ └── tsconfig.spec.json ├── codelab-friendlyeats-android │ └── steps │ │ ├── img │ │ ├── 1129441c6ebb5eaf.png │ │ ├── 4c68c6654f4168ad.png │ │ ├── 67898572a35672a5.png │ │ ├── 73d151ed16016421.png │ │ ├── 78fa16cdf8ef435a.png │ │ ├── 7a67a8a400c80c50.png │ │ ├── 95691e9b71ba55e3.png │ │ ├── 9d2f625aebcab6af.png │ │ ├── 9e45f40faefce5d0.png │ │ ├── a670188398c3c59.png │ │ ├── de06424023ffb4b9.png │ │ ├── emulators-auth.png │ │ ├── emulators-firebase.png │ │ ├── f9e670f40bd615b0.png │ │ └── sign-in-providers.png │ │ └── index.lab.md └── codelab-friendlyeats-web │ └── vanilla-js │ └── steps │ ├── .gitignore │ ├── img │ ├── img1.png │ ├── img2.png │ ├── img3.png │ ├── img4.png │ ├── img5.png │ ├── img6.png │ └── img7.png │ └── index.lab.md └── docs └── flutter ├── README.md ├── analytics ├── _events.md ├── _get-started.md └── _user-properties.md ├── app-check ├── custom-resource.md ├── debug-provider.md └── default-providers.md ├── auth ├── account-linking.md ├── anonymous-auth.md ├── custom-auth.md ├── email-link-auth.md ├── errors.md ├── federated-auth.md ├── manage-users.md ├── multi-factor.md ├── passing-state-in-email-actions.md ├── password-auth.md ├── phone-auth.md └── start.md ├── cloud-messaging ├── client.md ├── first-message.md ├── receive.md └── topic-messaging.md ├── crashlytics ├── _customize-crash-reports.md ├── _deobfuscated.md ├── _force-test-crash.md ├── _get-started.md └── _start-using-analytics.md ├── database ├── _usecase_security_preamble.md ├── lists-of-data.md ├── offline-capabilities.md ├── read-and-write.md ├── start.md └── structure-data.md ├── dynamic-links ├── create.md └── receive.md ├── in-app-messaging ├── _customize-messages.md ├── _get-started.md └── _modify-message-behavior.md ├── ml └── use-custom-models.md ├── perf-mon ├── _custom-code-traces.md ├── _custom-network-traces.md ├── _disable-sdk.md └── get-started.md ├── reference └── _toc.yaml ├── remote-config └── _get-started.md ├── setup ├── _setup_main.md ├── _setup_prereq_android.md ├── _setup_prereq_ios.md └── _setup_prereq_web.md ├── storage ├── create-reference.md ├── delete-files.md ├── download-files.md ├── file-metadata.md ├── handle-errors.md ├── list-files.md ├── start.md └── upload-files.md └── test-lab └── integration-testing-with-flutter.md /.github/workflows/pull-and-push-all.yml: -------------------------------------------------------------------------------- 1 | name: pull-and-push-all 2 | run-name: ${{ github.actor }} is trying pull and push all 3 | on: 4 | schedule: 5 | - cron: '0 8 * * *' # every day at 8 AM UTC (midnight or 1:00 depending on DST) 6 | workflow_dispatch: 7 | 8 | 9 | jobs: 10 | pull-flutter: 11 | uses: firebase/firebase-docs/.github/workflows/pull-map-and-push-repo.yml@main 12 | permissions: 13 | contents: write 14 | with: 15 | target_source_repo: https://github.com/firebase/flutterfire.git 16 | git_commit_user_email: firebase-oss-bot@ossbot.computer 17 | git_commit_user_name: Firebase OSS Robot 18 | git_commit_message_text: Pull and push of flutterfire/docs by ${{github.workflow}} run ${{github.run_number}} 19 | copy_mapping_sources: flutterfire/docs 20 | copy_mapping_destinations: docs/flutter 21 | seconds_between_push_tries: 2 22 | # Web 23 | pull-codelab-web-friendlychat: 24 | uses: firebase/firebase-docs/.github/workflows/pull-map-and-push-repo.yml@main 25 | permissions: 26 | contents: write 27 | with: 28 | target_source_repo: https://github.com/firebase/codelab-friendlychat-web.git 29 | git_commit_user_email: firebase-oss-bot@ossbot.computer 30 | git_commit_user_name: Firebase OSS Robot 31 | git_commit_message_text: Pull and push of codelab-friendlychat-web/angularfire-start by ${{github.workflow}} run ${{github.run_number}} 32 | copy_mapping_sources: codelab-friendlychat-web/angularfire-start 33 | copy_mapping_destinations: codelabs/codelab-friendlychat-web/steps 34 | seconds_between_push_tries: 3 35 | pull-codelab-web-friendlyeats: 36 | uses: firebase/firebase-docs/.github/workflows/pull-map-and-push-repo.yml@main 37 | permissions: 38 | contents: write 39 | with: 40 | target_source_repo: https://github.com/firebase/friendlyeats-web.git 41 | git_commit_user_email: firebase-oss-bot@ossbot.computer 42 | git_commit_user_name: Firebase OSS Robot 43 | git_commit_message_text: Pull and push of friendlyeats-web/vanilla-js/steps by ${{github.workflow}} run ${{github.run_number}} 44 | copy_mapping_sources: friendlyeats-web/vanilla-js/steps 45 | copy_mapping_destinations: codelabs/codelab-friendlyeats-web/vanilla-js/steps 46 | seconds_between_push_tries: 4 47 | # Android 48 | pull-codelab-android-friendlychat: 49 | uses: firebase/firebase-docs/.github/workflows/pull-map-and-push-repo.yml@main 50 | permissions: 51 | contents: write 52 | with: 53 | target_source_repo: https://github.com/firebase/codelab-friendlychat-android.git 54 | git_commit_user_email: firebase-oss-bot@ossbot.computer 55 | git_commit_user_name: Firebase OSS Robot 56 | git_commit_message_text: Pull and push of codelab-friendlychat-android/steps by ${{github.workflow}} run ${{github.run_number}} 57 | copy_mapping_sources: codelab-friendlychat-android/steps 58 | copy_mapping_destinations: codelabs/codelab-friendlychat-android/steps 59 | seconds_between_push_tries: 5 60 | pull-codelab-android-friendlyeats: 61 | uses: firebase/firebase-docs/.github/workflows/pull-map-and-push-repo.yml@main 62 | permissions: 63 | contents: write 64 | with: 65 | target_source_repo: https://github.com/firebase/friendlyeats-android.git 66 | git_commit_user_email: firebase-oss-bot@ossbot.computer 67 | git_commit_user_name: Firebase OSS Robot 68 | git_commit_message_text: Pull and push of friendlyeats-android/steps by ${{github.workflow}} run ${{github.run_number}} 69 | copy_mapping_sources: friendlyeats-android/steps 70 | copy_mapping_destinations: codelabs/codelab-friendlyeats-android/steps 71 | seconds_between_push_tries: 6 72 | # Finalize 73 | send-update-notification: 74 | name: send-update-notification 75 | runs-on: ubuntu-latest 76 | needs: [pull-flutter, pull-codelab-web-friendlychat, pull-codelab-web-friendlyeats, pull-codelab-android-friendlychat, pull-codelab-android-friendlyeats] 77 | permissions: 78 | contents: read 79 | issues: write 80 | steps: 81 | - name: Checkout Latest 82 | uses: actions/checkout@v3 83 | with: 84 | ref: ${{ github.ref }} # Forces getting the latest instead of the default behaviour of the triggering SHA. 85 | - name: Print Diff 86 | shell: bash 87 | run: | 88 | if [ "${{ github.sha }}" = "$(git rev-parse HEAD)" ]; then 89 | echo "No changes." 90 | else 91 | echo "SHAs are not equal. Docs updated." 92 | gh issue create --title "Docs Updated" --body "New changes pulled into monorepo." --assignee "joefspiro" --label "type: process" --label "priority: p3" 93 | fi 94 | echo ${{ github.sha }} $(git rev-parse HEAD) $GITHUB_SHA 95 | env: 96 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/pull-map-and-push-repo.yml: -------------------------------------------------------------------------------- 1 | name: pull-map-and-push-repo 2 | run-name: ${{ github.actor }} is trying to pull, map and push ${{ inputs.target_source_repo }} 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | target_source_repo: 7 | type: string 8 | description: 'Repo to copy in.' 9 | default: 'https://github.com/firebase/flutterfire.git' 10 | required: true 11 | git_commit_user_email: 12 | type: string 13 | description: 'Commit user email.' 14 | default: 'firebase-oss-bot@ossbot.computer' 15 | required: true 16 | git_commit_user_name: 17 | type: string 18 | description: 'Commit user name.' 19 | default: 'Firebase OSS Robot' 20 | required: true 21 | git_commit_message_text: 22 | type: string 23 | description: 'Commit Message Text' 24 | default: 'Pull and push of flutterfire/docs' 25 | required: true 26 | copy_mapping_sources: 27 | type: string 28 | description: 'Source Directories/Files' 29 | default: 'flutterfire/docs' 30 | required: true 31 | copy_mapping_destinations: 32 | type: string 33 | description: 'Destination Directories' 34 | default: 'docs/flutter' 35 | required: true 36 | seconds_between_push_tries: 37 | type: string 38 | description: 'How many seconds to sleep between push attempts.' 39 | default: '2' 40 | required: true 41 | workflow_call: 42 | inputs: 43 | target_source_repo: 44 | type: string 45 | description: 'Repo to copy in.' 46 | default: 'https://github.com/firebase/flutterfire.git' 47 | required: true 48 | git_commit_user_email: 49 | type: string 50 | description: 'Commit user email.' 51 | default: 'firebase-oss-bot@ossbot.computer' 52 | required: true 53 | git_commit_user_name: 54 | type: string 55 | description: 'Commit user name.' 56 | default: 'Firebase OSS Robot' 57 | required: true 58 | git_commit_message_text: 59 | type: string 60 | description: 'Commit Message Text' 61 | default: 'Pull and push of flutterfire/docs' 62 | required: true 63 | copy_mapping_sources: 64 | type: string 65 | description: 'Source Directories/Files' 66 | default: 'flutterfire/docs' 67 | required: true 68 | copy_mapping_destinations: 69 | type: string 70 | description: 'Destination Directories' 71 | default: 'docs/flutter' 72 | required: true 73 | seconds_between_push_tries: 74 | type: string 75 | description: 'How many seconds to sleep between push attempts.' 76 | default: '2' 77 | required: true 78 | 79 | jobs: 80 | checkout-pull-and-push: 81 | runs-on: ubuntu-latest 82 | permissions: 83 | contents: write 84 | steps: 85 | - uses: actions/checkout@v3 86 | - name: pull-external 87 | run: | 88 | mkdir temp-pull && cd temp-pull && 89 | git clone ${{inputs.target_source_repo}} && ls -r 90 | - name: copy-target-to-new-location 91 | run: | 92 | sources_array=(${{inputs.copy_mapping_sources}}) 93 | destinations_array=(${{inputs.copy_mapping_destinations}}) 94 | echo ${#sources_array[@]} ${#destinations_array[@]} 95 | if [ ${#sources_array[@]} != ${#destinations_array[@]} ]; then 96 | echo 'sources_array and destinations_array must be the same length. Please fix.' 97 | exit 1 98 | fi 99 | for (( i=0; i<${#sources_array[@]}; i++ )) 100 | do 101 | echo A:B ${sources_array[$i]}:${destinations_array[$i]} 102 | mkdir -p ${destinations_array[$i]} 103 | # check if its a directoy, if so, do whats listed already, if not then 104 | if [[ -d "temp-pull/${sources_array[$i]}" ]] 105 | then 106 | echo "temp-pull/${sources_array[$i]} is a DIRECTORY." 107 | cp -rf temp-pull/${sources_array[$i]}/. ${destinations_array[$i]} 108 | else 109 | echo "temp-pull/${sources_array[$i]} is a FILE." 110 | cp -rf temp-pull/${sources_array[$i]} ${destinations_array[$i]} 111 | fi 112 | done 113 | - name: remove-temp-pull 114 | run: rm -r temp-pull && git status --untracked-files 115 | - name: git-commit-and-push 116 | run: | 117 | if [ -z "$(git status --porcelain)" ]; then 118 | echo 'No changes detected. Exiting.' 119 | exit 0 120 | fi 121 | echo 'Changes detected. Attempting to add, commit, pull --rebase and push.' 122 | git add . 123 | git config user.email "${{inputs.git_commit_user_email}}" 124 | git config user.name "${{inputs.git_commit_user_name}}" 125 | git commit -m '${{inputs.git_commit_message_text}}' 126 | let retries_remaining=5 127 | let seconds_between_push_tries=${{inputs.seconds_between_push_tries}} 128 | while [ $retries_remaining -gt 0 ] 129 | do 130 | git pull --rebase 131 | git status 132 | set +e # Turns off automatic fail on errored step behaviour. 133 | git push 134 | if [ $? -eq 0 ]; then 135 | echo "Command succeeded" 136 | retries_remaining=0 137 | else 138 | ((retries_remaining=retries_remaining-1)) 139 | echo "Retrying in $seconds_between_push_tries seconds." 140 | sleep $seconds_between_push_tries 141 | fi 142 | set -e 143 | done 144 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Firebase Open Source Docs 2 | Firebase Open Source Docs is a monorepo that contains 3 | all Firebase written documentation that accepts community 4 | contributions. This includes Firebase product documentation 5 | or codelab steps. 6 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-android/steps/.gitignore: -------------------------------------------------------------------------------- 1 | firebase-android 2 | !firebase-android/.firebaserc 3 | !firebase-android/firebase.json 4 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-android/steps/img/add-android-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-android/steps/img/add-android-app.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-android/steps/img/add-message.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-android/steps/img/add-message.gif -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-android/steps/img/android_studio_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-android/steps/img/android_studio_folder.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-android/steps/img/emulators-auth-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-android/steps/img/emulators-auth-user.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-android/steps/img/emulators-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-android/steps/img/emulators-home.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-android/steps/img/execute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-android/steps/img/execute.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-android/steps/img/import-data.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-android/steps/img/import-data.gif -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-android/steps/img/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-android/steps/img/screenshot.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | .angular 4 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/README.md: -------------------------------------------------------------------------------- 1 | # Friendlychat 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.0.4. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 28 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "cli": { 5 | "packageManager": "npm", 6 | "analytics": false 7 | }, 8 | "newProjectRoot": "projects", 9 | "projects": { 10 | "friendlychat": { 11 | "projectType": "application", 12 | "schematics": {}, 13 | "root": "", 14 | "sourceRoot": "src", 15 | "prefix": "app", 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:application", 19 | "options": { 20 | "outputPath": "dist/friendlychat", 21 | "index": "src/index.html", 22 | "browser": "src/main.ts", 23 | "polyfills": ["zone.js"], 24 | "tsConfig": "tsconfig.app.json", 25 | "assets": ["src/favicon.ico", "src/assets"], 26 | "styles": ["src/styles.css"], 27 | "scripts": [] 28 | }, 29 | "configurations": { 30 | "production": { 31 | "budgets": [ 32 | { 33 | "type": "initial", 34 | "maximumWarning": "500kb", 35 | "maximumError": "1mb" 36 | }, 37 | { 38 | "type": "anyComponentStyle", 39 | "maximumWarning": "2kb", 40 | "maximumError": "4kb" 41 | } 42 | ], 43 | "outputHashing": "all" 44 | }, 45 | "development": { 46 | "optimization": false, 47 | "extractLicenses": false, 48 | "sourceMap": true, 49 | "namedChunks": true 50 | } 51 | }, 52 | "defaultConfiguration": "production" 53 | }, 54 | "serve": { 55 | "builder": "@angular-devkit/build-angular:dev-server", 56 | "configurations": { 57 | "production": { 58 | "buildTarget": "friendlychat:build:production" 59 | }, 60 | "development": { 61 | "buildTarget": "friendlychat:build:development" 62 | } 63 | }, 64 | "defaultConfiguration": "development" 65 | }, 66 | "extract-i18n": { 67 | "builder": "@angular-devkit/build-angular:extract-i18n", 68 | "options": { 69 | "buildTarget": "friendlychat:build" 70 | } 71 | }, 72 | "test": { 73 | "builder": "@angular-devkit/build-angular:karma", 74 | "options": { 75 | "polyfills": ["zone.js", "zone.js/testing"], 76 | "tsConfig": "tsconfig.spec.json", 77 | "assets": ["src/favicon.ico", "src/assets"], 78 | "styles": ["src/styles.css"], 79 | "scripts": [] 80 | } 81 | }, 82 | "deploy": { 83 | "builder": "@angular/fire:deploy", 84 | "options": { 85 | "version": 2, 86 | "browserTarget": "friendlychat:build:production" 87 | } 88 | } 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "firestore": { 3 | "rules": "firestore.rules", 4 | "indexes": "firestore.indexes.json" 5 | }, 6 | "hosting": [ 7 | { 8 | "source": ".", 9 | "ignore": [ 10 | "firebase.json", 11 | "**/.*", 12 | "**/node_modules/**" 13 | ], 14 | "frameworksBackend": { 15 | "region": "us-central1" 16 | } 17 | }, 18 | { 19 | "target": "friendlychat", 20 | "source": ".", 21 | "frameworksBackend": {} 22 | } 23 | ], 24 | "storage": { 25 | "rules": "storage.rules" 26 | }, 27 | "emulators": { 28 | "auth": { 29 | "port": 9099 30 | }, 31 | "functions": { 32 | "port": 5001 33 | }, 34 | "firestore": { 35 | "port": 8080 36 | }, 37 | "hosting": { 38 | "port": 5000 39 | }, 40 | "storage": { 41 | "port": 9199 42 | }, 43 | "ui": { 44 | "enabled": true 45 | }, 46 | "singleProjectMode": true 47 | }, 48 | "functions": [ 49 | { 50 | "source": "functions", 51 | "codebase": "default", 52 | "ignore": [ 53 | "node_modules", 54 | ".git", 55 | "firebase-debug.log", 56 | "firebase-debug.*.log" 57 | ], 58 | "predeploy": [ 59 | "npm --prefix \"$RESOURCE_DIR\" run lint" 60 | ] 61 | } 62 | ] 63 | } 64 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/firestore.indexes.json: -------------------------------------------------------------------------------- 1 | { 2 | "indexes": [], 3 | "fieldOverrides": [] 4 | } 5 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/firestore.rules: -------------------------------------------------------------------------------- 1 | rules_version = '2'; 2 | 3 | service cloud.firestore { 4 | match /databases/{database}/documents { 5 | match /{document=**} { 6 | allow read, write; 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/functions/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "scripts": { 5 | "serve": "firebase emulators:start --only functions", 6 | "shell": "firebase functions:shell", 7 | "start": "npm run shell", 8 | "deploy": "firebase deploy --only functions", 9 | "logs": "firebase functions:log" 10 | }, 11 | "engines": { 12 | "node": "18" 13 | }, 14 | "main": "index.js", 15 | "dependencies": { 16 | "firebase-admin": "^11.8.0", 17 | "firebase-functions": "^4.3.1" 18 | }, 19 | "devDependencies": { 20 | "firebase-functions-test": "^3.1.0" 21 | }, 22 | "private": true 23 | } 24 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "friendlychat", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test" 10 | }, 11 | "private": true, 12 | "dependencies": { 13 | "@angular/animations": "^17.2.4", 14 | "@angular/common": "^17.2.4", 15 | "@angular/compiler": "^17.2.4", 16 | "@angular/core": "^17.2.4", 17 | "@angular/fire": "^17.0.1", 18 | "@angular/forms": "^17.2.4", 19 | "@angular/platform-browser": "^17.2.4", 20 | "@angular/platform-browser-dynamic": "^17.2.4", 21 | "@angular/router": "^17.2.4", 22 | "rxjs": "~7.8.0", 23 | "tslib": "^2.3.0", 24 | "zone.js": "~0.14.4" 25 | }, 26 | "devDependencies": { 27 | "@angular-devkit/build-angular": "^17.2.3", 28 | "@angular/cli": "~17.2.3", 29 | "@angular/compiler-cli": "^17.2.4", 30 | "@types/jasmine": "~4.3.0", 31 | "autoprefixer": "^10.4.14", 32 | "jasmine-core": "~4.6.0", 33 | "karma": "~6.4.0", 34 | "karma-chrome-launcher": "~3.2.0", 35 | "karma-coverage": "~2.2.0", 36 | "karma-jasmine": "~5.1.0", 37 | "karma-jasmine-html-reporter": "~2.0.0", 38 | "postcss": "^8.4.24", 39 | "tailwindcss": "^3.3.2", 40 | "typescript": "~5.3.3" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { 4 | AuthGuard, 5 | redirectLoggedInTo, 6 | redirectUnauthorizedTo, 7 | } from '@angular/fire/auth-guard'; 8 | import { LoginPageComponent } from './pages/login-page/login-page.component'; 9 | import { ChatPageComponent } from './pages/chat-page/chat-page.component'; 10 | 11 | const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['login']); 12 | const redirectLoggedInToHome = () => redirectLoggedInTo(['chat']); 13 | 14 | const routes: Routes = [ 15 | { 16 | path: '', 17 | component: LoginPageComponent, 18 | canActivate: [AuthGuard], 19 | data: { authGuardPipe: redirectLoggedInToHome }, 20 | }, 21 | { 22 | path: 'login', 23 | component: LoginPageComponent, 24 | canActivate: [AuthGuard], 25 | data: { authGuardPipe: redirectLoggedInToHome }, 26 | }, 27 | { 28 | path: 'chat', 29 | component: ChatPageComponent, 30 | canActivate: [AuthGuard], 31 | data: { authGuardPipe: redirectUnauthorizedToLogin }, 32 | }, 33 | ]; 34 | 35 | @NgModule({ 36 | imports: [RouterModule.forRoot(routes)], 37 | exports: [RouterModule], 38 | }) 39 | export class AppRoutingModule {} 40 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-web/steps/src/app/app.component.css -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | 6 |
7 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(() => TestBed.configureTestingModule({ 7 | imports: [RouterTestingModule], 8 | declarations: [AppComponent] 9 | })); 10 | 11 | it('should create the app', () => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.componentInstance; 14 | expect(app).toBeTruthy(); 15 | }); 16 | 17 | it(`should have as title 'friendlychat'`, () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app.title).toEqual('friendlychat'); 21 | }); 22 | 23 | it('should render title', () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | fixture.detectChanges(); 26 | const compiled = fixture.nativeElement as HTMLElement; 27 | expect(compiled.querySelector('.content span')?.textContent).toContain('friendlychat app is running!'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { HeaderComponent } from './components/header/header.component'; 3 | import { RouterModule } from '@angular/router'; 4 | 5 | @Component({ 6 | selector: 'app-root', 7 | templateUrl: './app.component.html', 8 | styleUrls: ['./app.component.css'], 9 | standalone: true, 10 | imports: [HeaderComponent, RouterModule], 11 | }) 12 | export class AppComponent { 13 | title = 'friendlychat'; 14 | } 15 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationConfig, importProvidersFrom } from '@angular/core'; 2 | import { initializeApp, provideFirebaseApp } from '@angular/fire/app'; 3 | import { environment } from '../environments/environment'; 4 | import { provideAuth, getAuth, connectAuthEmulator } from '@angular/fire/auth'; 5 | import { provideFirestore, getFirestore, connectFirestoreEmulator } from '@angular/fire/firestore'; 6 | import { provideFunctions, getFunctions, connectFunctionsEmulator} from '@angular/fire/functions'; 7 | import { provideMessaging, getMessaging } from '@angular/fire/messaging'; 8 | import { provideStorage, getStorage, connectStorageEmulator } from '@angular/fire/storage'; 9 | import { routes } from './app.routes'; 10 | import { provideRouter } from '@angular/router'; 11 | 12 | export const appConfig: ApplicationConfig = { 13 | providers: [ 14 | importProvidersFrom( 15 | provideFirebaseApp(() => initializeApp(environment.firebase)), 16 | provideFirestore(() => getFirestore()), 17 | provideAuth(() => getAuth()), 18 | provideFunctions(() => getFunctions()), 19 | provideStorage(() => getStorage()), 20 | provideMessaging(() => getMessaging()) 21 | ), 22 | provideRouter(routes) 23 | ], 24 | }; 25 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { CommonModule } from '@angular/common'; 4 | import { FormsModule } from '@angular/forms'; 5 | 6 | import { AppRoutingModule } from './app-routing.module'; 7 | import { AppComponent } from './app.component'; 8 | import { initializeApp, provideFirebaseApp } from '@angular/fire/app'; 9 | import { environment } from '../environments/environment'; 10 | import { provideAuth, getAuth, connectAuthEmulator } from '@angular/fire/auth'; 11 | import { 12 | provideFirestore, 13 | getFirestore, 14 | connectFirestoreEmulator, 15 | } from '@angular/fire/firestore'; 16 | import { 17 | provideFunctions, 18 | getFunctions, 19 | connectFunctionsEmulator, 20 | } from '@angular/fire/functions'; 21 | import { provideMessaging, getMessaging } from '@angular/fire/messaging'; 22 | import { 23 | provideStorage, 24 | getStorage, 25 | connectStorageEmulator, 26 | } from '@angular/fire/storage'; 27 | 28 | import { LoginPageComponent } from './pages/login-page/login-page.component'; 29 | import { ChatPageComponent } from './pages/chat-page/chat-page.component'; 30 | import { HeaderComponent } from './components/header/header.component'; 31 | 32 | @NgModule({ 33 | declarations: [ 34 | AppComponent, 35 | LoginPageComponent, 36 | ChatPageComponent, 37 | HeaderComponent, 38 | ], 39 | imports: [BrowserModule, AppRoutingModule, CommonModule, FormsModule], 40 | providers: [], 41 | bootstrap: [AppComponent], 42 | }) 43 | export class AppModule {} 44 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { RouterModule, Routes } from '@angular/router'; 2 | import { 3 | AuthGuard, 4 | redirectLoggedInTo, 5 | redirectUnauthorizedTo, 6 | } from '@angular/fire/auth-guard'; 7 | import { LoginPageComponent } from './pages/login-page/login-page.component'; 8 | import { ChatPageComponent } from './pages/chat-page/chat-page.component'; 9 | 10 | const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['login']); 11 | const redirectLoggedInToHome = () => redirectLoggedInTo(['chat']); 12 | 13 | export const routes: Routes = [ 14 | { 15 | path: '', 16 | component: LoginPageComponent, 17 | canActivate: [AuthGuard], 18 | data: { authGuardPipe: redirectLoggedInToHome }, 19 | }, 20 | { 21 | path: 'login', 22 | component: LoginPageComponent, 23 | canActivate: [AuthGuard], 24 | data: { authGuardPipe: redirectLoggedInToHome }, 25 | }, 26 | { 27 | path: 'chat', 28 | component: ChatPageComponent, 29 | canActivate: [AuthGuard], 30 | data: { authGuardPipe: redirectUnauthorizedToLogin }, 31 | }, 32 | ]; 33 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/components/header/header.component.css: -------------------------------------------------------------------------------- 1 | .dropdown:hover .dropdown-menu { 2 | display: block; 3 | } 4 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/components/header/header.component.html: -------------------------------------------------------------------------------- 1 |
2 | 45 |
46 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/components/header/header.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HeaderComponent } from './header.component'; 4 | 5 | describe('HeaderComponent', () => { 6 | let component: HeaderComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [HeaderComponent] 12 | }); 13 | fixture = TestBed.createComponent(HeaderComponent); 14 | component = fixture.componentInstance; 15 | fixture.detectChanges(); 16 | }); 17 | 18 | it('should create', () => { 19 | expect(component).toBeTruthy(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/components/header/header.component.ts: -------------------------------------------------------------------------------- 1 | import { AsyncPipe } from '@angular/common'; 2 | import { Component, inject } from '@angular/core'; 3 | import { ChatService } from 'src/app/services/chat.service'; 4 | 5 | @Component({ 6 | selector: 'app-header', 7 | templateUrl: './header.component.html', 8 | styleUrls: ['./header.component.css'], 9 | standalone: true, 10 | imports: [AsyncPipe], 11 | }) 12 | export class HeaderComponent { 13 | chatService = inject(ChatService); 14 | user$ = this.chatService.user$; 15 | } 16 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/pages/chat-page/chat-page.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-web/steps/src/app/pages/chat-page/chat-page.component.css -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/pages/chat-page/chat-page.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | @for (message of (messages$ | async); track message) { 4 |
5 |
6 | @if (user$ | async; as user) { 7 |
8 | 10 |
11 |
12 | @if (message['text'] && message['text'].length > 0){ 13 |
14 | {{message['text']}} 15 |
16 | } 17 | @if (message['imageUrl'] && message['imageUrl'].length > 0) { 18 |
19 | image 20 |
21 | } 22 |
23 | 24 | {{message['timestamp'].toDate().toLocaleTimeString()}} 25 | 26 |
27 |
28 | 29 | @if (message['response'] && message['response'].length > 0){ 30 |
31 |
32 | {{message['response']}} 33 |
34 |
35 | 36 | ✨ ai generated 37 | 38 |
39 |
40 | } 41 |
42 | 43 |
44 | } 45 |
46 |
47 | } 48 |
49 |
50 |
51 | 52 |
53 | 56 |
57 | 60 | 63 |
64 |
65 |
66 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/pages/chat-page/chat-page.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ChatPageComponent } from './chat-page.component'; 4 | 5 | describe('ChatPageComponent', () => { 6 | let component: ChatPageComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ChatPageComponent] 12 | }); 13 | fixture = TestBed.createComponent(ChatPageComponent); 14 | component = fixture.componentInstance; 15 | fixture.detectChanges(); 16 | }); 17 | 18 | it('should create', () => { 19 | expect(component).toBeTruthy(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/pages/chat-page/chat-page.component.ts: -------------------------------------------------------------------------------- 1 | import { AsyncPipe } from '@angular/common'; 2 | import { Component, inject } from '@angular/core'; 3 | import { DocumentData } from '@angular/fire/firestore'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { Observable } from 'rxjs'; 6 | import { ChatService } from 'src/app/services/chat.service'; 7 | 8 | @Component({ 9 | selector: 'app-chat-page', 10 | templateUrl: './chat-page.component.html', 11 | styleUrls: ['./chat-page.component.css'], 12 | standalone: true, 13 | imports: [AsyncPipe, FormsModule] 14 | }) 15 | export class ChatPageComponent { 16 | chatService = inject(ChatService); 17 | messages$ = this.chatService.loadMessages() as Observable; 18 | user$ = this.chatService.user$; 19 | text = ''; 20 | 21 | sendTextMessage() { 22 | this.chatService.saveTextMessage(this.text); 23 | this.text = ''; 24 | } 25 | 26 | uploadImage(event: any) { 27 | const imgFile: File = event.target.files[0]; 28 | if (!imgFile) { 29 | return; 30 | } 31 | this.chatService.saveImageMessage(imgFile); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/pages/login-page/login-page.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-web/steps/src/app/pages/login-page/login-page.component.css -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/pages/login-page/login-page.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 |
7 | 8 | Friendly Chat 9 |
10 | 14 |
15 |
-------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/pages/login-page/login-page.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LoginPageComponent } from './login-page.component'; 4 | 5 | describe('LoginPageComponent', () => { 6 | let component: LoginPageComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [LoginPageComponent] 12 | }); 13 | fixture = TestBed.createComponent(LoginPageComponent); 14 | component = fixture.componentInstance; 15 | fixture.detectChanges(); 16 | }); 17 | 18 | it('should create', () => { 19 | expect(component).toBeTruthy(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/pages/login-page/login-page.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, inject } from '@angular/core'; 2 | import { ChatService } from 'src/app/services/chat.service'; 3 | 4 | @Component({ 5 | selector: 'app-login-page', 6 | templateUrl: './login-page.component.html', 7 | styleUrls: ['./login-page.component.css'], 8 | standalone: true, 9 | }) 10 | export class LoginPageComponent { 11 | chatService = inject(ChatService); 12 | user$ = this.chatService.user$; 13 | } 14 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/services/chat.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ChatService } from './chat.service'; 4 | 5 | describe('ChatService', () => { 6 | let service: ChatService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(ChatService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/app/services/chat.service.ts: -------------------------------------------------------------------------------- 1 | import { inject, Injectable } from '@angular/core'; 2 | import { 3 | Auth, 4 | authState, 5 | signInWithPopup, 6 | GoogleAuthProvider, 7 | signOut, 8 | user, 9 | getAuth, 10 | User, 11 | } from '@angular/fire/auth'; 12 | import { map, switchMap, firstValueFrom, filter, Observable, Subscription } from 'rxjs'; 13 | import { 14 | doc, 15 | docData, 16 | DocumentReference, 17 | Firestore, 18 | getDoc, 19 | setDoc, 20 | updateDoc, 21 | collection, 22 | addDoc, 23 | deleteDoc, 24 | collectionData, 25 | Timestamp, 26 | serverTimestamp, 27 | query, 28 | orderBy, 29 | limit, 30 | onSnapshot, 31 | DocumentData, 32 | FieldValue, 33 | } from '@angular/fire/firestore'; 34 | import { 35 | Storage, 36 | getDownloadURL, 37 | ref, 38 | uploadBytesResumable, 39 | } from '@angular/fire/storage'; 40 | import { getToken, Messaging, onMessage } from '@angular/fire/messaging'; 41 | import { Router } from '@angular/router'; 42 | 43 | type ChatMessage = { 44 | name: string | null, 45 | profilePicUrl: string | null, 46 | timestamp: FieldValue, 47 | uid: string | null, 48 | text?: string, 49 | imageUrl?: string 50 | }; 51 | 52 | 53 | @Injectable({ 54 | providedIn: 'root', 55 | }) 56 | export class ChatService { 57 | firestore: Firestore = inject(Firestore); 58 | auth: Auth = inject(Auth); 59 | storage: Storage = inject(Storage); 60 | messaging: Messaging = inject(Messaging); 61 | router: Router = inject(Router); 62 | private provider = new GoogleAuthProvider(); 63 | LOADING_IMAGE_URL = 'https://www.google.com/images/spin-32.gif?a'; 64 | 65 | // observable that is updated when the auth state changes 66 | user$ = user(this.auth); 67 | currentUser: User | null = this.auth.currentUser; 68 | userSubscription: Subscription; 69 | 70 | constructor() { 71 | this.userSubscription = this.user$.subscribe((aUser: User | null) => { 72 | this.currentUser = aUser; 73 | }); 74 | } 75 | 76 | // Login Friendly Chat. 77 | login() {} 78 | 79 | // Logout of Friendly Chat. 80 | logout() {} 81 | 82 | // Adds a text or image message to Cloud Firestore. 83 | addMessage = async ( 84 | textMessage: string | null, 85 | imageUrl: string | null 86 | ): Promise> => {}; 87 | 88 | // Saves a new message to Cloud Firestore. 89 | saveTextMessage = async (messageText: string) => { 90 | return this.addMessage(messageText, null); 91 | }; 92 | 93 | // Loads chat messages history and listens for upcoming ones. 94 | loadMessages = () => { 95 | return null as unknown; 96 | }; 97 | 98 | // Saves a new message containing an image in Firebase. 99 | // This first saves the image in Firebase storage. 100 | saveImageMessage = async (file: any) => {}; 101 | 102 | async updateData(path: string, data: any) {} 103 | 104 | async deleteData(path: string) {} 105 | 106 | getDocData(path: string) {} 107 | 108 | getCollectionData(path: string) {} 109 | 110 | async uploadToStorage( 111 | path: string, 112 | input: HTMLInputElement, 113 | contentType: any 114 | ) { 115 | return null; 116 | } 117 | // Requests permissions to show notifications. 118 | requestNotificationsPermissions = async () => {}; 119 | 120 | saveMessagingDeviceToken = async () => {}; 121 | } 122 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-web/steps/src/assets/.gitkeep -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/assets/FriendlyChatLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-web/steps/src/assets/FriendlyChatLogo.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/assets/Google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-web/steps/src/assets/Google.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/assets/WithFirebase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-web/steps/src/assets/WithFirebase.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/assets/add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/assets/menu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/assets/send.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | firebase: { 3 | apiKey: 'API_KEY', 4 | authDomain: 'PROJECT_ID.firebaseapp.com', 5 | databaseURL: 'https://PROJECT_ID.firebaseio.com', 6 | projectId: 'PROJECT_ID', 7 | storageBucket: 'PROJECT_ID.appspot.com', 8 | messagingSenderId: 'SENDER_ID', 9 | appId: 'APP_ID', 10 | measurementId: 'G-MEASUREMENT_ID', 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlychat-web/steps/src/favicon.ico -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Friendlychat 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/main.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { AppComponent } from './app/app.component'; 3 | import { appConfig } from './app/app.config'; 4 | 5 | bootstrapApplication(AppComponent, appConfig).catch((err) => 6 | console.error(err) 7 | ); 8 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/src/styles.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/storage.rules: -------------------------------------------------------------------------------- 1 | rules_version = '2'; 2 | 3 | // Craft rules based on data in your Firestore database 4 | // allow write: if firestore.get( 5 | // /databases/(default)/documents/users/$(request.auth.uid)).data.isAdmin; 6 | service firebase.storage { 7 | match /b/{bucket}/o { 8 | match /{allPaths=**} { 9 | allow read, write: if false; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/app/*.{html,ts}", "./src/app/***/**/*.{html,ts}", "./src/app/**/*.{html,ts}", "./src/*.{html,ts}"], 4 | theme: { 5 | extend: { 6 | colors: { 7 | 'navy': { 8 | 10: '#F6F7F9', 9 | 20: '#E5EAF0', 10 | 30: '#D4DCE7', 11 | 40: '#C3CFDD', 12 | 50: '#B2C1D4', 13 | 100: '#8EA1B9', 14 | 200: '#6B829D', 15 | 300: '#476282', 16 | 400: '#385574', 17 | 500: '#2A4865', 18 | 600: '#1B3A57', 19 | 700: '#0C2D48', 20 | 800: '#051E34', 21 | 900: '#031525', 22 | }, 23 | 'amber': { 24 | 50: '#FFF8E1', 25 | 100: '#FFECB3', 26 | 200: '#FFE082', 27 | 300: '#FFD54F', 28 | 400: '#FFCA28', 29 | 500: '#FFC107', 30 | 600: '#FFB300', 31 | 700: '#FFA000', 32 | 800: '#FF8F00', 33 | 900: '#FF6F00', 34 | A100: '#FFE57F', 35 | A200: '#FFD740', 36 | A400: '#FFC400', 37 | A700: '#FFAB00', 38 | } 39 | }, 40 | }, 41 | }, 42 | plugins: [], 43 | } 44 | 45 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "downlevelIteration": true, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "node", 18 | "importHelpers": true, 19 | "target": "ES2022", 20 | "module": "ES2022", 21 | "useDefineForClassFields": false, 22 | "lib": [ 23 | "ES2022", 24 | "dom" 25 | ] 26 | }, 27 | "angularCompilerOptions": { 28 | "enableI18nLegacyMessageIdFormat": false, 29 | "strictInjectionParameters": true, 30 | "strictInputAccessModifiers": true, 31 | "strictTemplates": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlychat-web/steps/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-android/steps/img/1129441c6ebb5eaf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-android/steps/img/1129441c6ebb5eaf.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-android/steps/img/4c68c6654f4168ad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-android/steps/img/4c68c6654f4168ad.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-android/steps/img/67898572a35672a5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-android/steps/img/67898572a35672a5.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-android/steps/img/73d151ed16016421.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-android/steps/img/73d151ed16016421.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-android/steps/img/78fa16cdf8ef435a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-android/steps/img/78fa16cdf8ef435a.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-android/steps/img/7a67a8a400c80c50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-android/steps/img/7a67a8a400c80c50.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-android/steps/img/95691e9b71ba55e3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-android/steps/img/95691e9b71ba55e3.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-android/steps/img/9d2f625aebcab6af.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-android/steps/img/9d2f625aebcab6af.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-android/steps/img/9e45f40faefce5d0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-android/steps/img/9e45f40faefce5d0.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-android/steps/img/a670188398c3c59.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-android/steps/img/a670188398c3c59.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-android/steps/img/de06424023ffb4b9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-android/steps/img/de06424023ffb4b9.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-android/steps/img/emulators-auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-android/steps/img/emulators-auth.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-android/steps/img/emulators-firebase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-android/steps/img/emulators-firebase.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-android/steps/img/f9e670f40bd615b0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-android/steps/img/f9e670f40bd615b0.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-android/steps/img/sign-in-providers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-android/steps/img/sign-in-providers.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-web/vanilla-js/steps/.gitignore: -------------------------------------------------------------------------------- 1 | firestore-web 2 | !firestore-web/.firebaserc 3 | !firestore-web/firebase.json 4 | -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-web/vanilla-js/steps/img/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-web/vanilla-js/steps/img/img1.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-web/vanilla-js/steps/img/img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-web/vanilla-js/steps/img/img2.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-web/vanilla-js/steps/img/img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-web/vanilla-js/steps/img/img3.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-web/vanilla-js/steps/img/img4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-web/vanilla-js/steps/img/img4.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-web/vanilla-js/steps/img/img5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-web/vanilla-js/steps/img/img5.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-web/vanilla-js/steps/img/img6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-web/vanilla-js/steps/img/img6.png -------------------------------------------------------------------------------- /codelabs/codelab-friendlyeats-web/vanilla-js/steps/img/img7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebase/firebase-docs/1f7c41f79f538686773a23cb869b7ac209575acb/codelabs/codelab-friendlyeats-web/vanilla-js/steps/img/img7.png -------------------------------------------------------------------------------- /docs/flutter/README.md: -------------------------------------------------------------------------------- 1 | # Flutter docs on firebase.google.com 2 | 3 | This directory contains the source of the Flutter documentation on 4 | https://firebase.google.com/docs/. 5 | 6 | We welcome your corrections and improvements! If you're interested in 7 | contributing, see [`CONTRIBUTING.md`](../CONTRIBUTING.md) for general 8 | guidelines. 9 | 10 | This file has some information on how our documentation is organized and some 11 | non-standard extensions we use. 12 | 13 | ## Where are the Firestore docs? 14 | 15 | Only the code snippets are on GitHub. You can find them in the 16 | [`firebase/snippets-flutter`][snippets-repo] repository. 17 | 18 | ## Standalone files vs. page fragments 19 | 20 | There are two kinds of source file for our docs: 21 | 22 | - **Standalone files** map one-to-one to a single page on firebase.google.com. 23 | These files are mostly-standard Markdown with filenames that correspond with 24 | the URL at which they're eventually published. 25 | 26 | For example, the file [`read-and-write.md`](/docs/database/read-and-write.md) 27 | is published to https://firebase.google.com/docs/database/flutter/read-and-write. 28 | 29 | Standalone pages must have filenames that don't begin with an 30 | underscore (`_`). 31 | 32 | - **Page fragments** are included in other pages. We use page fragments either 33 | to include common text in multiple pages or to help organize large pages. 34 | Like standalone files, page fragments are also mostly-standard Markdown, but 35 | their filenames often don't correspond with the URL at which they're 36 | eventually published. 37 | 38 | For example, the file [`_deobfuscated.md`](/docs/crashlytics/_deobfuscated.md) 39 | is published to https://firebase.google.com/docs/crashlytics/get-deobfuscated-reports?platform=flutter. 40 | 41 | Page fragments almost always have filenames that begin with an underscore 42 | (`_`). 43 | 44 | ## Non-standard Markdown 45 | 46 | ### File includes 47 | 48 | > Probably not useful to you as a contributor, but documented FYI. 49 | 50 | We use double angle brackets to include content from another file: 51 | 52 | ``` 53 | <> 54 | ``` 55 | 56 | Note that the path is based on our internal directory structure, and not the 57 | layout on GitHub. Also note that we sometimes use this to include non-Flutter 58 | related content that's not on GitHub. 59 | 60 | ### Page metadata 61 | 62 | > Probably not useful to you as a contributor, but documented FYI. 63 | 64 | Every standalone page begins with the following header: 65 | 66 | ``` 67 | Project: /docs/_project.yaml 68 | Book: /docs/_book.yaml 69 | ``` 70 | 71 | These are non-standard metadata declarations used by our internal publishing 72 | system. There's nothing you can really do with this, but it has to be on every 73 | standalone page. 74 | 75 | ## Non-standard Jinja 76 | 77 | ### includecode 78 | 79 | Code snippets are included from standalone Dart files, which helps facilitate 80 | automated testing. The following custom Jinja tag includes a code snippet into 81 | a file: 82 | 83 | ``` 84 | {% includecode github_path="organization/repository/path/to/file" region_tag="tag_name" %} 85 | ``` 86 | 87 | For example, the file [`firestore.dart`][firestore-snippets] in the 88 | [`firebase/snippets-flutter`][snippets-repo] repository contains a passage like 89 | this: 90 | 91 | ```dart 92 | void addData_addADocument() { 93 | // [START add_data_add_a_document] 94 | db.collection("cities").doc("new-city-id").set({"name": "Chicago"}); 95 | // [END add_data_add_a_document] 96 | } 97 | ``` 98 | 99 | To include the line between the `START` and `END` tags in a page, do the 100 | following: 101 | 102 | ``` 103 | {% includecode 104 | github_path="firebase/snippets-flutter/packages/firebase_snippets_app/lib/snippets/firestore.dart" 105 | region_tag="add_data_add_a_document" 106 | adjust_indentation="auto" %} 107 | ``` 108 | 109 | To update a snippet, submit a PR to the [`snippets-flutter` repo][snippets-repo]. 110 | 111 | [firestore-snippets]: https://github.com/firebase/snippets-flutter/blob/main/packages/firebase_snippets_app/lib/snippets/firestore.dart 112 | [snippets-repo]: https://github.com/firebase/snippets-flutter/ 113 | -------------------------------------------------------------------------------- /docs/flutter/analytics/_events.md: -------------------------------------------------------------------------------- 1 | {# This content gets published to the following location: #} 2 | {# https://firebase.google.com/docs/analytics/events?platform=flutter #} 3 | 4 | Analytics automatically logs some 5 | [events](https://support.google.com/analytics/answer/9234069) for you; you don't 6 | need to add any code to receive them. If your app needs to collect additional 7 | data, you can log up to 500 different Analytics Event *types* in your app. 8 | There is no limit on the total volume of events your app logs. Note that event 9 | names are case-sensitive and that logging two events whose names differ only in 10 | case will result in two distinct events. 11 | 12 | ## Before you begin 13 | 14 | Make sure that you've set up your project and can access Analytics as 15 | described in [Get Started with Analytics](get-started). 16 | 17 | ## Log events 18 | 19 | After you have created a `FirebaseAnalytics` instance, you can use it to log 20 | events with the library's `log`- methods. 21 | 22 | ### Predefined events 23 | 24 | To help you get started, the Analytics SDK defines a number of 25 | recommended events that are common among different types of apps, including 26 | retail and ecommerce, travel, and gaming apps. To learn more 27 | [about these events](https://support.google.com/analytics/answer/9322688) 28 | and when to use them, see 29 | [Recommended events](https://support.google.com/analytics/answer/9267735). 30 | 31 | Note: To get the maximum detail in reports, log the recommended events that make 32 | sense for your app and their prescribed parameters. This also ensures that you 33 | benefit from the latest Google Analytics features as 34 | they become available. 35 | 36 | You can find the log methods for the recommended event types in the 37 | [API reference](https://pub.dev/documentation/firebase_analytics/latest/firebase_analytics/FirebaseAnalytics-class.html). 38 | 39 | The following example demonstrates how to log a `select_content` event: 40 | 41 | ```dart 42 | await FirebaseAnalytics.instance.logSelectContent( 43 | contentType: "image", 44 | itemId: itemId, 45 | ); 46 | ``` 47 | 48 | Alternatively, you can log the same event using `logEvent()`: 49 | 50 | ```dart 51 | await FirebaseAnalytics.instance.logEvent( 52 | name: "select_content", 53 | parameters: { 54 | "content_type": "image", 55 | "item_id": itemId, 56 | }, 57 | ); 58 | ``` 59 | 60 | This can be useful if you want to specify additional parameters other than the 61 | prescribed (required) parameters. You can add the following parameters 62 | to any event: 63 | 64 | * Custom parameters: Custom parameters can be used as 65 | [dimensions or metrics](https://support.google.com/analytics/answer/10075209) 66 | in [Analytics reports](https://support.google.com/analytics/answer/9212670). 67 | You can use custom dimensions for non-numerical event parameter data and 68 | custom metrics for any parameter data better represented numerically. After 69 | you've logged a custom parameter using the SDK, register the dimension or 70 | metric to ensure those custom parameters appear in Analytics 71 | reports. Do this using *Analytics > Events > Manage Custom Definitions > 72 | Create Custom Dimensions*. 73 | 74 | Custom parameters can be used in 75 | [audience](https://support.google.com/firebase/answer/6317509) 76 | definitions that may be applied to every report. 77 | Custom parameters are also included in data 78 | [exported to BigQuery](https://support.google.com/firebase/answer/7030014) 79 | if your app is linked to a BigQuery project. Find sample queries and much more 80 | at [Google Analytics 4 BigQuery Export](https://developers.google.com/analytics/bigquery). 81 | 82 | * `value` parameter: a general purpose parameter 83 | that is useful for accumulating a key metric that pertains to an 84 | event. Examples include revenue, distance, time, and points. 85 | * Parameter names can be up to 40 characters long and must start with an alphabetic 86 | character and contain only alphanumeric characters and underscores. String and num 87 | types are supported. String parameter values can be up to 100 characters long. 88 | The "firebase_", "google_" and "ga_" prefixes are reserved and shouldn't be 89 | used for parameter names. 90 | 91 | ### Custom events 92 | 93 | If your application has specific needs not covered by a recommended 94 | event type, you can log your own custom events as shown in this example: 95 | 96 | ```dart 97 | await FirebaseAnalytics.instance.logEvent( 98 | name: "share_image", 99 | parameters: { 100 | "image_name": name, 101 | "full_text": text, 102 | }, 103 | ); 104 | ``` 105 | 106 | ## Set default event parameters 107 | 108 | You can log parameters across events using `setDefaultEventParameters()`. 109 | Default parameters are associated with all future events that are logged. 110 | 111 | As with custom parameters, register the default event parameters to ensure they 112 | appear in Analytics reports. 113 | 114 | Valid parameter values are String and num. Setting a key's value to `null` 115 | clears that parameter. Passing in a null value clears all parameters. 116 | 117 | ```dart 118 | // Not supported on web 119 | await FirebaseAnalytics.instance 120 | .setDefaultEventParameters({ 121 | version: '1.2.3' 122 | }); 123 | ``` 124 | 125 | If a parameter is specified in the `logEvent()` or `log`- 126 | method, that value is used instead of the default. 127 | 128 | To clear a default parameter, call the `setDefaultEventParameters()` 129 | method with the parameter set to `null`. 130 | -------------------------------------------------------------------------------- /docs/flutter/analytics/_get-started.md: -------------------------------------------------------------------------------- 1 | {# This content gets published to the following location: #} 2 | {# https://firebase.google.com/docs/analytics/get-started?platform=flutter #} 3 | 4 | Google Analytics collects usage and behavior data for your app. The SDK 5 | logs two primary types of information: 6 | 7 | * **Events:** What is happening in your app, such as user actions, system 8 | events, or errors. 9 | * **User properties:** Attributes you define to describe segments of your 10 | user base, such as language preference or geographic location. 11 | 12 | Analytics automatically logs some 13 | [events](https://support.google.com/analytics/answer/9234069) and 14 | [user properties](https://support.google.com/analytics/answer/9268042); 15 | you don't need to add any code to enable them. 16 | 17 | ## Before you begin 18 | 19 | 1. [Install `firebase_core`](/docs/flutter/setup) and add the initialization code 20 | to your app if you haven't already. 21 | 1. Add your app to your Firebase project in the 22 | [Firebase console](https://console.firebase.google.com). 23 | 24 | ## Add the Analytics SDK to your app {:#add-sdk} 25 | 26 | 1. From the root of your Flutter project, run the following command to install the plugin: 27 | 28 | ```bash {5} 29 | flutter pub add firebase_analytics 30 | ``` 31 | 32 | 1. Once complete, rebuild your Flutter application: 33 | 34 | ```bash 35 | flutter run 36 | ``` 37 | 38 | 1. Once installed, you can access the `firebase_analytics` 39 | plugin by importing it in your Dart code: 40 | 41 | ```dart 42 | import 'package:firebase_analytics/firebase_analytics.dart'; 43 | ``` 44 | 45 | 1. Create a new Firebase Analytics instance by accessing the 46 | `instance` property on 47 | `FirebaseAnalytics`: 48 | 49 | ```dart 50 | FirebaseAnalytics analytics = FirebaseAnalytics.instance; 51 | ``` 52 | 53 | 54 | ## Start logging events 55 | 56 | After you have created a `FirebaseAnalytics` instance, you can begin to log 57 | events with the library's `log`- methods. 58 | 59 | Certain events are 60 | [recommended for all apps](https://support.google.com/analytics/answer/9267735); 61 | others are recommended for specific business types or verticals. You should send 62 | recommended events along with their prescribed parameters, to ensure maximum 63 | available detail in your reports and to benefit from future features and 64 | integrations as they become available. This section demonstrates logging a 65 | predefined event, for more information on logging events, see 66 | [Log events](events). 67 | 68 | The following code logs a checkout event: 69 | 70 | ```dart 71 | await FirebaseAnalytics.instance 72 | .logBeginCheckout( 73 | value: 10.0, 74 | currency: 'USD', 75 | items: [ 76 | AnalyticsEventItem( 77 | itemName: 'Socks', 78 | itemId: 'xjw73ndnw', 79 | price: '10.0' 80 | ), 81 | ], 82 | coupon: '10PERCENTOFF' 83 | ); 84 | ``` 85 | 86 | ## Next steps 87 | 88 | * Use the [DebugView](/docs/analytics/debugview) to verify your events. 89 | * Explore your data in the [Firebase console](https://console.firebase.google.com/project/_/analytics/). 90 | * Explore the guides on [events](events) and 91 | [user properties](user-properties). 92 | * Learn how to export your data to [BigQuery](https://support.google.com/firebase/answer/7030014). 93 | -------------------------------------------------------------------------------- /docs/flutter/analytics/_user-properties.md: -------------------------------------------------------------------------------- 1 | {# This content gets published to the following location: #} 2 | {# https://firebase.google.com/docs/analytics/user-properties?platform=flutter #} 3 | 4 | {# TODO(markarndt): Sync all this content up with Analytics content 5 | consolidation plan. #} 6 | 7 | ## Before you begin 8 | 9 | Make sure that you've set up your project and can access {{analytics}} as 10 | described in [Get Started with {{analytics}}](get-started). 11 | 12 | ## Set user properties 13 | 14 | You can set {{ analytics }} user properties to describe the users of your app. 15 | You can make use of user properties by creating custom definitions, then using 16 | them to apply comparisons in your reports or as audience evaluation criteria. 17 | 18 | To set a user property, follow these steps: 19 | 20 | 1. Create a custom definition for the user property in the 21 | [**Custom Definitions** page](https://console.firebase.google.com/project/_/analytics/userproperty){: .external} 22 | of _{{analytics}}_ in the {{name_appmanager}}. For more information, see 23 | [Custom dimensions and metrics](https://support.google.com/analytics/answer/10075209). 24 | 1. Set a user property in your app with the `setUserProperty()` method. 25 | 26 | The following example adds a hypothetical favorite food property, which 27 | assigns the value in `favoriteFood` to the active user: 28 | 29 | ```dart 30 | await FirebaseAnalytics.instance 31 | .setUserProperty({ 32 | name: 'favorite_food', 33 | value: favoriteFood, 34 | }); 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/flutter/app-check/custom-resource.md: -------------------------------------------------------------------------------- 1 | Project: /docs/_project.yaml 2 | Book: /docs/_book.yaml 3 | 4 | 5 | 6 | # Protect non-Firebase resources with App Check 7 | 8 | You can protect your app's non-Firebase resources, such as self-hosted backends, 9 | with App Check. To do so, you will need to do both of the following: 10 | 11 | - Modify your app client to send an App Check token along with each request 12 | to your backend, as described on this page. 13 | - Modify your backend to require a valid App Check token with every request, 14 | as described in [Verify App Check tokens from a custom backend](/docs/app-check/custom-resource-backend). 15 | 16 | ## Before you begin 17 | 18 | Add App Check to your app, using the [default providers](default-providers). 19 | 20 | ## Send App Check tokens with backend requests 21 | 22 | To ensure your backend requests include a valid, unexpired, App Check token, 23 | precede each request with a call to `getToken()`. The App Check library 24 | will refresh the token if necessary. 25 | 26 | Once you have a valid token, send it along with the request to your backend. The 27 | specifics of how you accomplish this are up to you, but _don't send 28 | App Check tokens as part of URLs_, including in query parameters, as this 29 | makes them vulnerable to accidental leakage and interception. The recommended 30 | approach is to send the token in a custom HTTP header. 31 | 32 | For example: 33 | 34 | ```dart 35 | void callApiExample() async { 36 | final appCheckToken = await FirebaseAppCheck.instance.getToken(); 37 | if (appCheckToken != null) { 38 | final response = await http.get( 39 | Uri.parse("https://yourbackend.example.com/yourExampleEndpoint"), 40 | headers: {"X-Firebase-AppCheck": appCheckToken}, 41 | ); 42 | } else { 43 | // Error: couldn't get an App Check token. 44 | } 45 | } 46 | ``` 47 | -------------------------------------------------------------------------------- /docs/flutter/app-check/debug-provider.md: -------------------------------------------------------------------------------- 1 | Project: /docs/app-check/_project.yaml 2 | Book: /docs/_book.yaml 3 | page_type: guide 4 | 5 | {% include "docs/app-check/_local_variables.html" %} 6 | {% include "_shared/firebase/_snippet_include_comment.html" %} 7 | 8 | # Use App Check with the debug provider with Flutter 9 | 10 | After you have registered your app for App Check, your app normally won't run 11 | in an emulator or from a continuous integration (CI) environment, since those 12 | environments don't qualify as valid devices. If you want to run your app in such 13 | an environment during development and testing, you can create a debug build of 14 | your app that uses the App Check debug provider instead of a real attestation 15 | provider. 16 | 17 | Warning: The debug provider allows access to your Firebase resources from 18 | unverified devices. Don't use the debug provider in production builds of your 19 | app, and don't share your debug builds with untrusted parties. 20 | 21 | ## Apple platforms 22 | 23 | To use the debug provider while running your app in a simulator interactively 24 | (during development, for example), do the following: 25 | 26 | 1. Activate App Check with the debug provider right after you have initialized 27 | your Firebase app: 28 | 29 | ```dart 30 | import 'package:flutter/material.dart'; 31 | import 'package:firebase_core/firebase_core.dart'; 32 | 33 | // Import the firebase_app_check plugin 34 | import 'package:firebase_app_check/firebase_app_check.dart'; 35 | 36 | Future main() async { 37 | WidgetsFlutterBinding.ensureInitialized(); 38 | await Firebase.initializeApp(); 39 | await FirebaseAppCheck.instance.activate( 40 | // Set appleProvider to `AppleProvider.debug` 41 | appleProvider: AppleProvider.debug, 42 | ); 43 | runApp(App()); 44 | } 45 | ``` 46 | 47 | 1. Enable debug logging in your Xcode project (v11.0 or newer): 48 | 49 | 1. Open **Product > Scheme > Edit scheme**. 50 | 1. Select **Run** from the left menu, then select the **Arguments** tab. 51 | 1. In the **Arguments Passed on Launch** section, add `-FIRDebugEnabled`. 52 | 53 | 1. Open `ios/Runner.xcworkspace` with Xcode and run your app in the Simulator. 54 | Your app will print a local debug token to the debug output when Firebase 55 | tries to send a request to the backend. For example: 56 | 57 |
Firebase App Check Debug Token:
 58 |     123a4567-b89c-12d3-e456-789012345678
59 | 60 | {# Google-internal common file: #} 61 | <<../_includes/manage-debug-tokens.md>> 62 | 63 | After you register the token, Firebase backend services will accept it as valid. 64 | 65 | Because this token allows access to your Firebase resources without a 66 | valid device, it is crucial that you keep it private. Don't commit it to a 67 | public repository, and if a registered token is ever compromised, revoke it 68 | immediately in the Firebase console. 69 | 70 | ## Android 71 | 72 | To use the debug provider while running your Flutter app in an Android environment, 73 | implement the following code in your Flutter application: 74 | 75 | ```dart 76 | import 'package:flutter/material.dart'; 77 | import 'package:firebase_core/firebase_core.dart'; 78 | 79 | // Import the firebase_app_check plugin 80 | import 'package:firebase_app_check/firebase_app_check.dart'; 81 | 82 | Future main() async { 83 | WidgetsFlutterBinding.ensureInitialized(); 84 | await Firebase.initializeApp(); 85 | await FirebaseAppCheck.instance.activate( 86 | webRecaptchaSiteKey: 'recaptcha-v3-site-key', 87 | // Set androidProvider to `AndroidProvider.debug` 88 | androidProvider: AndroidProvider.debug, 89 | ); 90 | runApp(App()); 91 | } 92 | 93 | ``` 94 | 95 | Your app will print a local debug token to the debug output when Firebase tries 96 | to send a request to the backend. For example: 97 | 98 |
D DebugAppCheckProvider: Enter this debug secret into the allow list in
 99 | the Firebase Console for your project: 123a4567-b89c-12d3-e456-789012345678
100 | 101 | {# Google-internal common file: #} 102 | <<../_includes/manage-debug-tokens.md>> 103 | 104 | After you register the token, Firebase backend services will accept it as valid. 105 | 106 | ## Web 107 | 108 | To use the debug provider while running your app from `localhost` (during 109 | development, for example), do the following: 110 | 111 | Warning: _Do not_ try to enable `localhost` debugging by adding `localhost` to 112 | reCAPTCHA’s allowed domains. Doing so would allow anyone to run your app from 113 | their local machines! 114 | 115 | 1. In the file `web/index.html`, enable debug mode by setting 116 | `self.FIREBASE_APPCHECK_DEBUG_TOKEN` to `true`: 117 | 118 | ```html 119 | 120 | 123 | 124 | ... 125 | 126 | 127 | ``` 128 | 129 | 1. Run your web app locally and open the browser’s developer tool. In the 130 | debug console, you’ll see a debug token: 131 | 132 |
AppCheck debug token: "123a4567-b89c-12d3-e456-789012345678". You will
133 |     need to safelist it in the Firebase console for it to work.
134 | 135 | This token is stored locally in your browser and will be used whenever you 136 | use your app in the same browser on the same machine. If you want to use the 137 | token in another browser or on another machine, set 138 | `self.FIREBASE_APPCHECK_DEBUG_TOKEN` to the token string instead of `true`. 139 | 140 | {# Google-internal common file: #} 141 | <<../_includes/manage-debug-tokens.md>> 142 | 143 | After you register the token, Firebase backend services will accept it as valid. 144 | 145 | Because this token allows access to your Firebase resources without a 146 | valid device, it is crucial that you keep it private. Don't commit it to a 147 | public repository, and if a registered token is ever compromised, revoke it 148 | immediately in the Firebase console. 149 | -------------------------------------------------------------------------------- /docs/flutter/app-check/default-providers.md: -------------------------------------------------------------------------------- 1 | Project: /docs/_project.yaml 2 | Book: /docs/_book.yaml 3 | 4 | 5 | 6 | # Get started using App Check in Flutter apps 7 | 8 | This page shows you how to enable App Check in a Flutter app, using the 9 | default providers: Play Integrity on Android, Device Check on Apple platforms, and 10 | reCAPTCHA v3 on web. When you enable App Check, you help ensure that 11 | only your app can access your project's Firebase resources. See an 12 | [Overview](/docs/app-check) of this feature. 13 | 14 | 15 | ## 1. Set up your Firebase project {:#project-setup} 16 | 17 | 1. [Install and initialize FlutterFire](/docs/flutter/setup) if you haven't 18 | already done so. 19 | 20 | 1. Register your apps to use App Check with the Play Integrity, Device Check, and reCAPTCHA providers in the 21 | [**Project Settings > App Check**](https://console.firebase.google.com/project/_/appcheck) 22 | section of the Firebase console. 23 | 24 | You usually need to register all of your project's apps, because once you 25 | enable enforcement for a Firebase product, only registered apps will be able 26 | to access the product's backend resources. 27 | 28 | 1. **Optional**: In the app registration settings, set a custom time-to-live 29 | (TTL) for App Check tokens issued by the provider. You can set the TTL 30 | to any value between 30 minutes and 7 days. When changing this value, be 31 | aware of the following tradeoffs: 32 | 33 | - Security: Shorter TTLs provide stronger security, because it reduces the 34 | window in which a leaked or intercepted token can be abused by an 35 | attacker. 36 | - Performance: Shorter TTLs mean your app will perform attestation more 37 | frequently. Because the app attestation process adds latency to network 38 | requests every time it's performed, a short TTL can impact the performance 39 | of your app. 40 | - Quota and cost: Shorter TTLs and frequent re-attestation deplete your 41 | quota faster, and for paid services, potentially cost more. 42 | See [Quotas & limits](/docs/app-check#quotas_limits). 43 | 44 | The default TTL 45 | is reasonable for most apps. Note that the App Check library refreshes 46 | tokens at approximately half the TTL duration. 47 | 48 | 49 | ## 2. Add the App Check library to your app {:#install-sdk} 50 | 51 | 1. From the root of your Flutter project, run the following command to install the plugin: 52 | 53 | ```bash 54 | flutter pub add firebase_app_check 55 | ``` 56 | 57 | 1. Once complete, rebuild your Flutter application: 58 | 59 | ```bash 60 | flutter run 61 | ``` 62 | 63 | 64 | ## 3. Initialize App Check {:#initialize} 65 | 66 | Add the following initialization code to your app so that it runs before you 67 | use any Firebase services such as Storage, but after calling 68 | `Firebase.initializeApp()`; 69 | 70 | ```dart 71 | import 'package:flutter/material.dart'; 72 | import 'package:firebase_core/firebase_core.dart'; 73 | 74 | // Import the firebase_app_check plugin 75 | import 'package:firebase_app_check/firebase_app_check.dart'; 76 | 77 | Future main() async { 78 | WidgetsFlutterBinding.ensureInitialized(); 79 | await Firebase.initializeApp(); 80 | await FirebaseAppCheck.instance.activate( 81 | // You can also use a `ReCaptchaEnterpriseProvider` provider instance as an 82 | // argument for `webProvider` 83 | webProvider: ReCaptchaV3Provider('recaptcha-v3-site-key'), 84 | // Default provider for Android is the Play Integrity provider. You can use the "AndroidProvider" enum to choose 85 | // your preferred provider. Choose from: 86 | // 1. Debug provider 87 | // 2. Safety Net provider 88 | // 3. Play Integrity provider 89 | androidProvider: AndroidProvider.debug, 90 | // Default provider for iOS/macOS is the Device Check provider. You can use the "AppleProvider" enum to choose 91 | // your preferred provider. Choose from: 92 | // 1. Debug provider 93 | // 2. Device Check provider 94 | // 3. App Attest provider 95 | // 4. App Attest provider with fallback to Device Check provider (App Attest provider is only available on iOS 14.0+, macOS 14.0+) 96 | appleProvider: AppleProvider.appAttest, 97 | ); 98 | runApp(App()); 99 | } 100 | ``` 101 | 102 | ## Next steps 103 | 104 | Once the App Check library is installed in your app, start distributing the 105 | updated app to your users. 106 | 107 | The updated client app will begin sending App Check tokens along with every 108 | request it makes to Firebase, but Firebase products will not require the tokens 109 | to be valid until you enable enforcement in the App Check section of the 110 | Firebase console. 111 | 112 | ### Monitor metrics and enable enforcement {:#monitor} 113 | 114 | Before you enable enforcement, however, you should make sure that doing so won't 115 | disrupt your existing legitimate users. On the other hand, if you're seeing 116 | suspicious use of your app resources, you might want to enable enforcement 117 | sooner. 118 | 119 | To help make this decision, you can look at App Check metrics for the 120 | services you use: 121 | 122 | - [Monitor App Check request metrics](/docs/app-check/monitor-metrics) for 123 | Realtime Database, Cloud Firestore, Cloud Storage, and Authentication. 124 | - [Monitor App Check request metrics for Cloud Functions](/docs/app-check/monitor-functions-metrics). 125 | 126 | ### Enable App Check enforcement {:#enforce} 127 | 128 | When you understand how App Check will affect your users and you're ready to 129 | proceed, you can enable App Check enforcement: 130 | 131 | - [Enable App Check enforcement](/docs/app-check/enable-enforcement) for 132 | Realtime Database, Cloud Firestore, Cloud Storage, and Authentication. 133 | - [Enable App Check enforcement for Cloud Functions](/docs/app-check/cloud-functions). 134 | 135 | ### Use App Check in debug environments {:#debug} 136 | 137 | If, after you have registered your app for App Check, you want to run your 138 | app in an environment that App Check would normally not classify as valid, 139 | such as an emulator during development, or from a continuous integration (CI) 140 | environment, you can create a debug build of your app that uses the 141 | App Check debug provider instead of a real attestation provider. 142 | 143 | See [Use App Check with the debug provider in Flutter apps](/docs/app-check/flutter/debug-provider). 144 | -------------------------------------------------------------------------------- /docs/flutter/auth/account-linking.md: -------------------------------------------------------------------------------- 1 | Project: /docs/_project.yaml 2 | Book: /docs/_book.yaml 3 | 4 | 5 | 6 | # Link Multiple Auth Providers to an Account 7 | 8 | You can allow users to sign in to your app using multiple authentication 9 | providers by linking auth provider credentials to an existing user account. 10 | Users are identifiable by the same Firebase user ID regardless of the 11 | authentication provider they used to sign in. For example, a user who signed in 12 | with a password can link a Google account and sign in with either method in the 13 | future. Or, an anonymous user can link a Facebook account and then, later, sign 14 | in with Facebook to continue using your app. 15 | 16 | ## Before you begin 17 | 18 | Add support for two or more authentication providers (possibly including 19 | anonymous authentication) to your app. 20 | 21 | ## Link auth provider credentials to a user account 22 | 23 | To link auth provider credentials to an existing user account: 24 | 25 | 1. Sign in the user using any authentication provider or method. 26 | 27 | 1. Complete the sign-in flow for the new authentication provider up to, but not 28 | including, calling one of the `signInWith`- methods. For example, get 29 | the user's Google ID token, Facebook access token, or email and password. 30 | 31 | 1. Get a `Credential` object for the new authentication provider: 32 | 33 | ```dart 34 | // Google Sign-in 35 | final credential = GoogleAuthProvider.credential(idToken: idToken); 36 | 37 | // Email and password sign-in 38 | final credential = 39 | EmailAuthProvider.credential(email: emailAddress, password: password); 40 | 41 | // Etc. 42 | ``` 43 | 44 | 1. Pass the `Credential` object to the sign-in user's `linkWithCredential()` 45 | method: 46 | 47 | ```dart 48 | try { 49 | final userCredential = await FirebaseAuth.instance.currentUser 50 | ?.linkWithCredential(credential); 51 | } on FirebaseAuthException catch (e) { 52 | switch (e.code) { 53 | case "provider-already-linked": 54 | print("The provider has already been linked to the user."); 55 | break; 56 | case "invalid-credential": 57 | print("The provider's credential is not valid."); 58 | break; 59 | case "credential-already-in-use": 60 | print("The account corresponding to the credential already exists, " 61 | "or is already linked to a Firebase User."); 62 | break; 63 | // See the API reference for the full list of error codes. 64 | default: 65 | print("Unknown error."); 66 | } 67 | ``` 68 | 69 | If the call to `linkWithCredential()` succeeds, the user can now sign in using 70 | any linked authentication provider and access the same Firebase data. 71 | 72 | ## Unlink an auth provider from a user account 73 | 74 | You can unlink an auth provider from an account, so that the user can no 75 | longer sign in with that provider. 76 | 77 | To unlink an auth provider from a user account, pass the provider ID to the 78 | `unlink()` method. You can get the provider IDs of the auth providers linked to 79 | a user from the `User` object's `providerData` property. 80 | 81 | ```dart 82 | try { 83 | await FirebaseAuth.instance.currentUser?.unlink(providerId); 84 | } on FirebaseAuthException catch (e) { 85 | switch (e.code) { 86 | case "no-such-provider": 87 | print("The user isn't linked to the provider or the provider " 88 | "doesn't exist."); 89 | break; 90 | default: 91 | print("Unknown error."); 92 | } 93 | } 94 | ``` 95 | -------------------------------------------------------------------------------- /docs/flutter/auth/anonymous-auth.md: -------------------------------------------------------------------------------- 1 | Project: /docs/_project.yaml 2 | Book: /docs/_book.yaml 3 | 4 | 5 | 6 | # Authenticate with Firebase Anonymously 7 | 8 | You can use Firebase Authentication to create and use temporary anonymous accounts 9 | to authenticate with Firebase. These temporary anonymous accounts can be used to 10 | allow users who haven't yet signed up to your app to work with data protected 11 | by security rules. If an anonymous user decides to sign up to your app, you can 12 | [link their sign-in credentials](account-linking) to the anonymous account so 13 | that they can continue to work with their protected data in future sessions. 14 | 15 | ## Before you begin 16 | 17 | 1. If you haven't already, follow the steps in the [Get started](start) guide. 18 | 19 | 1. Enable Anonymous sign-in: 20 | 21 | - In the Firebase console's **Authentication** section, open the 22 | [Sign in method](https://console.firebase.google.com/project/_/authentication/providers) 23 | page. 24 | - From the **Sign in method** page, enable the **Anonymous sign-in** 25 | method and click **Save**. 26 | 27 | ## Authenticate with Firebase anonymously 28 | 29 | When a signed-out user uses an app feature that requires authentication with 30 | Firebase, sign in the user anonymously by calling `signInAnonymously()`: 31 | 32 | ```dart 33 | try { 34 | final userCredential = 35 | await FirebaseAuth.instance.signInAnonymously(); 36 | print("Signed in with temporary account."); 37 | } on FirebaseAuthException catch (e) { 38 | switch (e.code) { 39 | case "operation-not-allowed": 40 | print("Anonymous auth hasn't been enabled for this project."); 41 | break; 42 | default: 43 | print("Unknown error."); 44 | } 45 | } 46 | ``` 47 | 48 | Note: To protect your project from abuse, Firebase limits the number of new 49 | email/password and anonymous sign-ups that your application can have from the 50 | same IP address in a short period of time. You can request and schedule 51 | temporary changes to this quota from the 52 | [Firebase console](https://console.firebase.google.com/project/_/authentication/providers). 53 | 54 | ## Convert an anonymous account to a permanent account 55 | 56 | When an anonymous user signs up to your app, you might want to allow them to 57 | continue their work with their new account—for example, you might want to 58 | make the items the user added to their shopping cart before they signed up 59 | available in their new account's shopping cart. To do so, complete the following 60 | steps: 61 | 62 | 1. When the user signs up, complete the sign-in flow for the user's 63 | authentication provider up to, but not including, calling one of the 64 | `signInWith`- methods. For example, get the user's Google ID token, 65 | Facebook access token, or email address and password. 66 | 67 | 1. Get a `Credential` object for the new authentication provider: 68 | 69 | ```dart 70 | // Google Sign-in 71 | final credential = GoogleAuthProvider.credential(idToken: idToken); 72 | 73 | // Email and password sign-in 74 | final credential = 75 | EmailAuthProvider.credential(email: emailAddress, password: password); 76 | 77 | // Etc. 78 | ``` 79 | 80 | 1. Pass the `Credential` object to the sign-in user's `linkWithCredential()` 81 | method: 82 | 83 | ```dart 84 | try { 85 | final userCredential = await FirebaseAuth.instance.currentUser 86 | ?.linkWithCredential(credential); 87 | } on FirebaseAuthException catch (e) { 88 | switch (e.code) { 89 | case "provider-already-linked": 90 | print("The provider has already been linked to the user."); 91 | break; 92 | case "invalid-credential": 93 | print("The provider's credential is not valid."); 94 | break; 95 | case "credential-already-in-use": 96 | print("The account corresponding to the credential already exists, " 97 | "or is already linked to a Firebase User."); 98 | break; 99 | // See the API reference for the full list of error codes. 100 | default: 101 | print("Unknown error."); 102 | } 103 | ``` 104 | 105 | If the call to `linkWithCredential()` succeeds, the user's new account can 106 | access the anonymous account's Firebase data. 107 | 108 | Note: This technique can also be used to [link any two accounts](account-linking). 109 | 110 | 111 | ## Next steps 112 | 113 | After a user creates a new account, this account is stored as part of your 114 | Firebase project, and can be used to identify a user across every app in your 115 | project, regardless of what sign-in method the user used. 116 | 117 | In your apps, you can get the user's basic profile information from the 118 | `User` object. See [Manage Users](manage-users). 119 | 120 | In your Firebase Realtime Database and Cloud Storage Security Rules, you can 121 | get the signed-in user's unique user ID from the `auth` variable, and use it to 122 | control what data a user can access. 123 | 124 | You can allow users to sign in to your app using multiple authentication 125 | providers by [linking auth provider credentials](account-linking)) to an 126 | existing user account. 127 | 128 | To sign out a user, call `signOut()`: 129 | 130 | ```dart 131 | await FirebaseAuth.instance.signOut(); 132 | ``` 133 | -------------------------------------------------------------------------------- /docs/flutter/auth/custom-auth.md: -------------------------------------------------------------------------------- 1 | Project: /docs/_project.yaml 2 | Book: /docs/_book.yaml 3 | 4 | 5 | 6 | # Authenticate with Firebase Using a Custom Authentication System 7 | 8 | You can integrate Firebase Authentication with a custom authentication system by 9 | modifying your authentication server to produce custom signed tokens when a user 10 | successfully signs in. Your app receives this token and uses it to authenticate 11 | with Firebase. 12 | 13 | ## Before you begin 14 | 15 | 1. If you haven't already, follow the steps in the [Get started](start) guide. 16 | 1. [Install and configure the Firebase Admin SDK](/docs/admin/setup). 17 | Be sure to [initialize the SDK](/docs/admin/setup#initialize-sdk) 18 | with the correct credentials for your Firebase project. 19 | 20 | ## Authenticate with Firebase 21 | 22 | 1. When users sign in to your app, send their sign-in credentials (for 23 | example, their username and password) to your authentication server. Your 24 | server checks the credentials and, if they are valid, 25 | [creates a custom Firebase token](/docs/auth/admin/create-custom-tokens) 26 | and sends the token back to your app. 27 | 28 | 1. After you receive the custom token from your authentication server, pass it 29 | to `signInWithCustomToken()` to sign in the user: 30 | 31 | ```dart 32 | try { 33 | final userCredential = 34 | await FirebaseAuth.instance.signInWithCustomToken(token); 35 | print("Sign-in successful."); 36 | } on FirebaseAuthException catch (e) { 37 | switch (e.code) { 38 | case "invalid-custom-token": 39 | print("The supplied token is not a Firebase custom auth token."); 40 | break; 41 | case "custom-token-mismatch": 42 | print("The supplied token is for a different Firebase project."); 43 | break; 44 | default: 45 | print("Unknown error."); 46 | } 47 | } 48 | ``` 49 | 50 | ## Next steps 51 | 52 | After a user creates a new account, this account is stored as part of your 53 | Firebase project, and can be used to identify a user across every app in your 54 | project, regardless of what sign-in method the user used. 55 | 56 | In your apps, you can get the user's basic profile information from the 57 | `User` object. See [Manage Users](manage-users). 58 | 59 | In your Firebase Realtime Database and Cloud Storage Security Rules, you can 60 | get the signed-in user's unique user ID from the `auth` variable, and use it to 61 | control what data a user can access. 62 | 63 | You can allow users to sign in to your app using multiple authentication 64 | providers by [linking auth provider credentials](account-linking)) to an 65 | existing user account. 66 | 67 | To sign out a user, call `signOut()`: 68 | 69 | ```dart 70 | await FirebaseAuth.instance.signOut(); 71 | ``` 72 | -------------------------------------------------------------------------------- /docs/flutter/auth/errors.md: -------------------------------------------------------------------------------- 1 | Project: /docs/_project.yaml 2 | Book: /docs/_book.yaml 3 | 4 | 5 | 6 | # Error Handling 7 | 8 | The Firebase Authentication SDKs provide a simple way for catching the various errors which may occur which using 9 | authentication methods. The SDKs for Flutter expose these errors via the `FirebaseAuthException` 10 | class. 11 | 12 | At a minimum, a `code` and `message` are provided, however in some cases additional properties such as an email address 13 | and credential are also provided. For example, if the user is attempting to sign in with an email and password, 14 | any errors thrown can be explicitly caught: 15 | 16 | ```dart 17 | try { 18 | await FirebaseAuth.instance.signInWithEmailAndPassword( 19 | email: "barry.allen@example.com", 20 | password: "SuperSecretPassword!" 21 | ); 22 | } on FirebaseAuthException catch (e) { 23 | print('Failed with error code: ${e.code}'); 24 | print(e.message); 25 | } 26 | ``` 27 | 28 | Each method provides various error codes and messages depending on the type of authentication invocation type. The 29 | [Reference API](https://pub.dev/documentation/firebase_auth/latest/) provides up-to-date details on the errors for each method. 30 | 31 | Other errors such as `too-many-requests` or `operation-not-allowed` may be thrown if you reach the Firebase Authentication quota, 32 | or have not enabled a specific auth provider. 33 | 34 | ## Handling `account-exists-with-different-credential` Errors 35 | 36 | If you enabled the One account per email address setting in the [Firebase console](https://console.firebase.google.com/project/_/authentication/providers), 37 | when a user tries to sign in a to a provider (such as Google) with an email that already exists for another Firebase user's provider 38 | (such as Facebook), the error `auth/account-exists-with-different-credential` is thrown along with an `AuthCredential` class (Google ID token). 39 | To complete the sign-in flow to the intended provider, the user has to first sign in to the existing provider (e.g. Facebook) and then link to the former 40 | `AuthCredential` (Google ID token). 41 | 42 | ```dart 43 | FirebaseAuth auth = FirebaseAuth.instance; 44 | 45 | // Create a credential from a Google Sign-in Request 46 | var googleAuthCredential = GoogleAuthProvider.credential(accessToken: 'xxxx'); 47 | 48 | try { 49 | // Attempt to sign in the user in with Google 50 | await auth.signInWithCredential(googleAuthCredential); 51 | } on FirebaseAuthException catch (e) { 52 | if (e.code == 'account-exists-with-different-credential') { 53 | // The account already exists with a different credential 54 | String email = e.email; 55 | AuthCredential pendingCredential = e.credential; 56 | 57 | // Fetch a list of what sign-in methods exist for the conflicting user 58 | List userSignInMethods = await auth.fetchSignInMethodsForEmail(email); 59 | 60 | // If the user has several sign-in methods, 61 | // the first method in the list will be the "recommended" method to use. 62 | if (userSignInMethods.first == 'password') { 63 | // Prompt the user to enter their password 64 | String password = '...'; 65 | 66 | // Sign the user in to their account with the password 67 | UserCredential userCredential = await auth.signInWithEmailAndPassword( 68 | email: email, 69 | password: password, 70 | ); 71 | 72 | // Link the pending credential with the existing account 73 | await userCredential.user.linkWithCredential(pendingCredential); 74 | 75 | // Success! Go back to your application flow 76 | return goToApplication(); 77 | } 78 | 79 | // Since other providers are now external, you must now sign the user in with another 80 | // auth provider, such as Facebook. 81 | if (userSignInMethods.first == 'facebook.com') { 82 | // Create a new Facebook credential 83 | String accessToken = await triggerFacebookAuthentication(); 84 | var facebookAuthCredential = FacebookAuthProvider.credential(accessToken); 85 | 86 | // Sign the user in with the credential 87 | UserCredential userCredential = await auth.signInWithCredential(facebookAuthCredential); 88 | 89 | // Link the pending credential with the existing account 90 | await userCredential.user.linkWithCredential(pendingCredential); 91 | 92 | // Success! Go back to your application flow 93 | return goToApplication(); 94 | } 95 | 96 | // Handle other OAuth providers... 97 | } 98 | } 99 | ``` 100 | -------------------------------------------------------------------------------- /docs/flutter/auth/password-auth.md: -------------------------------------------------------------------------------- 1 | Project: /docs/_project.yaml 2 | Book: /docs/_book.yaml 3 | 4 | 5 | 6 | # Authenticate with Firebase using Password-Based Accounts on Flutter 7 | 8 | You can use Firebase Authentication to let your users authenticate with 9 | Firebase using email addresses and passwords. 10 | 11 | ## Before you begin 12 | 13 | 1. If you haven't already, follow the steps in the [Get started](start) guide. 14 | 15 | 1. Enable Email/Password sign-in: 16 | 17 | - In the Firebase console's **Authentication** section, open the 18 | [Sign in method](https://console.firebase.google.com/project/_/authentication/providers) 19 | page. 20 | - From the **Sign in method** page, enable the **Email/password sign-in** 21 | method and click **Save**. 22 | 23 | ## Create a password-based account 24 | 25 | To create a new user account with a password, call the `createUserWithEmailAndPassword()` 26 | method: 27 | 28 | ```dart 29 | try { 30 | final credential = await FirebaseAuth.instance.createUserWithEmailAndPassword( 31 | email: emailAddress, 32 | password: password, 33 | ); 34 | } on FirebaseAuthException catch (e) { 35 | if (e.code == 'weak-password') { 36 | print('The password provided is too weak.'); 37 | } else if (e.code == 'email-already-in-use') { 38 | print('The account already exists for that email.'); 39 | } 40 | } catch (e) { 41 | print(e); 42 | } 43 | ``` 44 | 45 | Typically, you would do this from your app's sign-up screen. When a new user 46 | signs up using your app's sign-up form, complete any new account validation 47 | steps that your app requires, such as verifying that the new account's password 48 | was correctly typed and meets your complexity requirements. 49 | 50 | If the new account was created successfully, the user is also signed in. If you 51 | are listening to changes in [authentication state](start#auth-state), a new 52 | event will be sent to your listeners. 53 | 54 | As a follow-up to creating a new account, you can 55 | [Verify the user's email address](manage-users#verify-email). 56 | 57 | Note: To protect your project from abuse, Firebase limits the number of new 58 | email/password and anonymous sign-ups that your application can have from the 59 | same IP address in a short period of time. You can request and schedule 60 | temporary changes to this quota from the 61 | [Firebase console](https://console.firebase.google.com/project/_/authentication/providers). 62 | 63 | ## Sign in a user with an email address and password 64 | 65 | The steps for signing in a user with a password are similar to the steps for 66 | creating a new account. From your your app's sign-in screen, call 67 | `signInWithEmailAndPassword()`: 68 | 69 | ```dart 70 | try { 71 | final credential = await FirebaseAuth.instance.signInWithEmailAndPassword( 72 | email: emailAddress, 73 | password: password 74 | ); 75 | } on FirebaseAuthException catch (e) { 76 | if (e.code == 'user-not-found') { 77 | print('No user found for that email.'); 78 | } else if (e.code == 'wrong-password') { 79 | print('Wrong password provided for that user.'); 80 | } 81 | } 82 | ``` 83 | 84 | Caution: When a user uninstalls your app on iOS or macOS, the user's authentication 85 | state can persist between app re-installs, as the Firebase iOS SDK persists 86 | authentication state to the system keychain. 87 | See issue [#4661](https://github.com/firebase/flutterfire/issues/4661) 88 | for more information. 89 | 90 | 91 | ## Next steps 92 | 93 | After a user creates a new account, this account is stored as part of your 94 | Firebase project, and can be used to identify a user across every app in your 95 | project, regardless of what sign-in method the user used. 96 | 97 | In your apps, you can get the user's basic profile information from the 98 | `User` object. See [Manage Users](manage-users). 99 | 100 | In your Firebase Realtime Database and Cloud Storage Security Rules, you can 101 | get the signed-in user's unique user ID from the `auth` variable, and use it to 102 | control what data a user can access. 103 | 104 | You can allow users to sign in to your app using multiple authentication 105 | providers by [linking auth provider credentials](account-linking)) to an 106 | existing user account. 107 | 108 | To sign out a user, call `signOut()`: 109 | 110 | ```dart 111 | await FirebaseAuth.instance.signOut(); 112 | ``` 113 | -------------------------------------------------------------------------------- /docs/flutter/cloud-messaging/first-message.md: -------------------------------------------------------------------------------- 1 | Project: /docs/cloud-messaging/_project.yaml 2 | Book: /docs/_book.yaml 3 | page_type: guide 4 | 5 | {% include "_shared/apis/console/_local_variables.html" %} 6 | {% include "_local_variables.html" %} 7 | {% include "docs/cloud-messaging/_local_variables.html" %} 8 | {% include "docs/android/_local_variables.html" %} 9 | 10 | 11 | 12 | # Send a test message to a backgrounded app 13 | 14 | To get started with FCM, build out the simplest use case: sending a 15 | test notification message from the 16 | 17 | Notifications composer to a development device 18 | when the app is in the background on the device. 19 | This page lists all the steps to achieve this, from setup to verification 20 | — it may cover steps you already completed if you 21 | have [set up a Flutter app](/docs/cloud-messaging/flutter/client) 22 | for FCM. 23 | 24 | Important: This guide focuses on the background case. If you want to receive 25 | messages when your app is in the foreground as well, see also 26 | [Receive Messages in a Flutter App](/docs/cloud-messaging/flutter/receive). 27 | 28 | 29 | ## Install the FCM plugin 30 | 31 | 1. [Install and initialize the Firebase SDKs for Flutter](/docs/flutter/setup) 32 | if you haven't already done so. 33 | 34 | 1. From the root of your Flutter project, run the following command to install 35 | the plugin: 36 | 37 | ```bash 38 | flutter pub add firebase_messaging 39 | ``` 40 | 41 | 1. Once complete, rebuild your Flutter application: 42 | 43 | ```bash 44 | flutter run 45 | ``` 46 | 47 | 48 | ## Access the registration token 49 | 50 | To send a message to a specific device, you need to know that device's 51 | registration token. Because you'll need to enter the token in a field in the 52 | Notifications console to complete this tutorial, make sure to copy the token 53 | or securely store it after you retrieve it. 54 | 55 | To retrieve the current registration token for an app instance, call 56 | `getToken()`. If notification permission has not been granted, this method will 57 | ask the user for notification permissions. Otherwise, it returns a token or 58 | rejects the future due to an error. 59 | 60 | ```dart 61 | final fcmToken = await FirebaseMessaging.instance.getToken(); 62 | ``` 63 | 64 | 65 | ## Send a test notification message 66 | 67 | {# Google-internal include #} 68 | <<../_send-to-device.md>> 69 | 70 | For insight into message delivery to your app, see the 71 | FCM reporting dashboard, 72 | which records the number of messages sent and opened on Apple and Android 73 | devices, along with data for "impressions" (notifications seen by users) for 74 | Android apps. 75 | 76 | ## Handling interaction 77 | 78 | When users tap a notification, the default behavior on both Android & iOS is to open the application. If the application is terminated, 79 | it will be started, and if it is in the background, it will be brought to the foreground. 80 | 81 | Depending on the content of a notification, you may want to handle the user's interaction when the application 82 | opens. For example, if a new chat message is sent using a notification and the user selects it, you may want to 83 | open the specific conversation when the application opens. 84 | 85 | The `firebase-messaging` package provides two ways to handle this interaction: 86 | 87 | 1. `getInitialMessage()`: If the application is opened from a terminated state, this method returns a `Future` containing a `RemoteMessage`. Once consumed, the `RemoteMessage` will be removed. 88 | 2. `onMessageOpenedApp`: A `Stream` which posts a `RemoteMessage` when the application is opened from a 89 | background state. 90 | 91 | To ensure a smooth experience for your users, you should handle both scenarios. The code example 92 | below outlines how this can be achieved: 93 | 94 | ```dart 95 | class Application extends StatefulWidget { 96 | @override 97 | State createState() => _Application(); 98 | } 99 | 100 | class _Application extends State { 101 | // In this example, suppose that all messages contain a data field with the key 'type'. 102 | Future setupInteractedMessage() async { 103 | // Get any messages which caused the application to open from 104 | // a terminated state. 105 | RemoteMessage? initialMessage = 106 | await FirebaseMessaging.instance.getInitialMessage(); 107 | 108 | // If the message also contains a data property with a "type" of "chat", 109 | // navigate to a chat screen 110 | if (initialMessage != null) { 111 | _handleMessage(initialMessage); 112 | } 113 | 114 | // Also handle any interaction when the app is in the background via a 115 | // Stream listener 116 | FirebaseMessaging.onMessageOpenedApp.listen(_handleMessage); 117 | } 118 | 119 | void _handleMessage(RemoteMessage message) { 120 | if (message.data['type'] == 'chat') { 121 | Navigator.pushNamed(context, '/chat', 122 | arguments: ChatArguments(message), 123 | ); 124 | } 125 | } 126 | 127 | @override 128 | void initState() { 129 | super.initState(); 130 | 131 | // Run code required to handle interacted messages in an async function 132 | // as initState() must not be async 133 | setupInteractedMessage(); 134 | } 135 | 136 | @override 137 | Widget build(BuildContext context) { 138 | return Text("..."); 139 | } 140 | } 141 | ``` 142 | 143 | How you handle interaction depends on your application setup. The example above 144 | shows a basic example of using a `StatefulWidget`. 145 | 146 | ## Next steps 147 | 148 | ### Send messages to foregrounded apps 149 | 150 | Once you have successfully sent notification messages while your app is in 151 | the background, see 152 | [Receive Messages in a Flutter App](/docs/cloud-messaging/flutter/receive) 153 | to get started sending to foregrounded apps. 154 | 155 | ### Go beyond notification messages 156 | 157 | To add other, more advanced behavior to your app, you'll need a 158 | [server implementation](/docs/cloud-messaging/server). 159 | 160 | Then, in your app client: 161 | 162 | - [Receive messages](/docs/cloud-messaging/flutter/receive) 163 | - [Subscribe to message topics](/docs/cloud-messaging/flutter/topic-messaging) 164 | 165 | -------------------------------------------------------------------------------- /docs/flutter/cloud-messaging/topic-messaging.md: -------------------------------------------------------------------------------- 1 | Project: /docs/cloud-messaging/_project.yaml 2 | Book: /docs/_book.yaml 3 | page_type: guide 4 | 5 | {% include "_shared/apis/console/_local_variables.html" %} 6 | {% include "_local_variables.html" %} 7 | {% include "docs/cloud-messaging/_local_variables.html" %} 8 | {% include "docs/android/_local_variables.html" %} 9 | 10 | 11 | 12 | # Topic messaging on Flutter 13 | 14 | Based on the publish/subscribe model, FCM topic messaging allows you to send a message 15 | to multiple devices that have opted in to a particular topic. You compose topic messages as 16 | needed, and FCM handles routing and delivering the message reliably to the right 17 | devices. 18 | 19 | For example, users of a local tide 20 | forecasting app could opt in to a "tidal currents alerts" topic and receive 21 | notifications of optimal saltwater fishing conditions in specified areas. Users of a sports app 22 | could subscribe to automatic updates in live game scores for their favorite 23 | teams. 24 | 25 | Some things to keep in mind about topics: 26 | 27 | - Topic messaging is best suited for content such as weather, or other publicly 28 | available information. 29 | 30 | - Topic messages are **optimized for throughput rather than latency**. For fast, 31 | secure delivery to single devices or small groups of devices, 32 | [target messages to registration tokens](/docs/cloud-messaging/send-message#send_messages_to_specific_devices), 33 | not topics. 34 | 35 | - If you need to send messages to multiple devices _per user_, consider 36 | [device group messaging](/docs/cloud-messaging/send-message#send_messages_to_device_groups) 37 | for those use cases. 38 | 39 | - Topic messaging supports unlimited subscriptions for each topic. However, FCM 40 | enforces limits in these areas: 41 | 42 | - One app instance can be subscribed to no more than 2000 topics. 43 | - If you are using [batch import](https://developers.google.com/instance-id/reference/server#manage_relationship_maps_for_multiple_app_instances) 44 | to subscribe app instances, each request is limited to 1000 app instances. 45 | - The frequency of new subscriptions is rate-limited per project. If you send 46 | too many subscription requests in a short period of time, FCM servers will 47 | respond with a `429 RESOURCE_EXHAUSTED` ("quota exceeded") response. Retry 48 | with exponential backoff. 49 | 50 | 51 | ## Subscribe the client app to a topic 52 | 53 | Client apps can subscribe to any existing topic, or they can create a new 54 | topic. When a client app subscribes to a new topic name (one that does 55 | not already exist for your Firebase project), a new topic of that name is 56 | created in FCM and any client can subsequently subscribe to it. 57 | 58 | To subscribe to a topic, call `subscribeToTopic()` with the topic name. This method 59 | returns a `Future`, which resolves when the subscription succeeded: 60 | 61 | ```dart 62 | await FirebaseMessaging.instance.subscribeToTopic("topic"); 63 | ``` 64 | 65 | To unsubscribe, call `unsubscribeFromTopic()` with the topic name. 66 | 67 | `subscribeToTopic()` and `unsubscribeFromTopic()` are not supported for web 68 | clients. To learn how to manage subscriptions for web users, see 69 | [Send messages to topics on Web/JavaScript](https://firebase.google.com/docs/cloud-messaging/js/topic-messaging). 70 | 71 | ## Next steps 72 | 73 | * Learn how to [send topic messages](/docs/cloud-messaging/send-message#send-messages-to-topics). 74 | * Learn how to [Manage topic subscriptions on the server](/docs/cloud-messaging/manage-topics). -------------------------------------------------------------------------------- /docs/flutter/crashlytics/_force-test-crash.md: -------------------------------------------------------------------------------- 1 | {# This content gets published to the following location: #} 2 | {# https://firebase.google.com/docs/crashlytics/test-implementation?platform=flutter #} 3 | 4 | 1. Add code to your app that you can use to force a test exception to be 5 | thrown. 6 | 7 | If you’ve added an error handler that calls 8 | `FirebaseCrashlytics.instance.recordError(error, stack, fatal: true)` to the 9 | top-level `Zone`, you can use the following code to add a button to your app 10 | that, when pressed, throws a test exception: 11 | 12 | ```dart 13 | TextButton( 14 | onPressed: () => throw Exception(), 15 | child: const Text("Throw Test Exception"), 16 | ), 17 | ``` 18 | 19 | 1. Build and run your app. 20 | 21 | 1. Force the test exception to be thrown in order to send your app's first 22 | report: 23 | 24 | 1. Open your app from your test device or emulator. 25 | 26 | 1. In your app, press the test exception button that you added using the 27 | code above. 28 | 29 | 1. Go to the 30 | [{{crashlytics}} dashboard](https://console.firebase.google.com/project/_/crashlytics){: .external} 31 | of the {{name_appmanager}} to see your test crash. 32 | -------------------------------------------------------------------------------- /docs/flutter/crashlytics/_start-using-analytics.md: -------------------------------------------------------------------------------- 1 | {# This content gets published to the following location: #} 2 | {# https://firebase.google.com/docs/crashlytics/start-using-analytics?platform=flutter #} 3 | 4 | 1. Make sure that {{firebase_analytics}} is enabled in your Firebase project: 5 | Go to settings > _Project settings_ > _Integrations_ tab, 6 | then follow the on-screen instructions for {{firebase_analytics}}. 7 | 8 | 1. From the root of your Flutter project, run the following command to install 9 | the {{analytics}} Flutter plugin: 10 | 11 |
flutter pub add firebase_analytics
13 |     
14 | 15 | 1. Make sure that your Flutter app's Firebase configuration is up-to-date by 16 | running the following command from the root directory of your Flutter 17 | project: 18 | 19 |
flutterfire configure
21 |     
22 | 23 | 1. Once complete, rebuild your Flutter application: 24 | 25 |
flutter run
27 |     
28 | 29 | Your Flutter project is now set up to use {{firebase_analytics}}. 30 | -------------------------------------------------------------------------------- /docs/flutter/database/_usecase_security_preamble.md: -------------------------------------------------------------------------------- 1 | Note: By default, read and write access to your database is restricted so only 2 | authenticated users can read or write data. To get started without setting up 3 | Firebase Authentication, you can [configure your rules for public access](/docs/rules/basics#default_rules_locked_mode). 4 | This does make your database open to anyone, even people not using your app, so 5 | be sure to restrict your database again when you set up authentication. 6 | -------------------------------------------------------------------------------- /docs/flutter/database/start.md: -------------------------------------------------------------------------------- 1 | Project: /docs/database/_project.yaml 2 | Book: /docs/_book.yaml 3 | page_type: guide 4 | 5 | 6 | 7 | # Get Started with Realtime Database 8 | 9 | ## Prerequisites 10 | 11 | 1. [Install `firebase_core`](/docs/flutter/setup) and add the initialization code 12 | to your app if you haven't already. 13 | 1. Add your app to your Firebase project in the Firebase console. 14 | 15 | ## Create a Database 16 | 17 | {# TODO(markarndt): Decide whether to include common files instead. #} 18 | 19 | 1. Navigate to the **Realtime Database** section of the Firebase console. 20 | You'll be prompted to select an existing Firebase project. 21 | Follow the database creation workflow. 22 | 23 | 1. Select a starting mode for your security rules: 24 | 25 | **Test mode** 26 | 27 | Good for getting started with the mobile and web client libraries, 28 | but allows anyone to read and overwrite your data. After testing, **make 29 | sure to review the [Understand Firebase Realtime Database Rules](/docs/database/security/) 30 | section.** 31 | 32 | Note: If you create a database in Test mode and make no changes to the 33 | default world-readable and world-writeable security rules within a trial 34 | period, you will be alerted by email, then your database rules will 35 | deny all requests. Note the expiration date during the Firebase console 36 | setup flow. 37 | 38 | 39 | To get started, select testmode. 40 | 41 | **Locked mode** 42 | 43 | Denies all reads and writes from mobile and web clients. 44 | Your authenticated application servers can still access your database. 45 | 46 | 1. Choose a region for the database. Depending on your choice of region, 47 | the database namespace will be of the form `.firebaseio.com` or 48 | `..firebasedatabase.app`. For more information, see 49 | [select locations for your project](/docs/projects/locations.md##rtdb-locations). 50 | 51 | 1. Click **Done**. 52 | 53 | When you enable Realtime Database, it also enables the API in the 54 | [Cloud API Manager](https://console.cloud.google.com/projectselector/apis/api/firebasedatabase.googleapis.com/overview). 55 | 56 | ## Add Firebase Realtime Database to your app 57 | 58 | 1. From the root of your Flutter project, run the following command to install the plugin: 59 | 60 | ```bash 61 | flutter pub add firebase_database 62 | ``` 63 | 1. Once complete, rebuild your Flutter application: 64 | 65 | ```bash 66 | flutter run 67 | ``` 68 | 69 | ## Configure database rules 70 | 71 | The Realtime Database provides a declarative rules language that allows you to 72 | define how your data should be structured, how it should be indexed, and when 73 | your data can be read from and written to. 74 | 75 | <<_usecase_security_preamble.md>> 76 | 77 | ## Initialize the Firebase Realtime Database package 78 | 79 | To start using the Realtime Database package within your project, import it at 80 | the top of your project files: 81 | 82 | ```dart 83 | import 'package:firebase_database/firebase_database.dart'; 84 | ``` 85 | 86 | To use the default Database instance, call the `instance` 87 | getter on `FirebaseDatabase`: 88 | 89 | ```dart 90 | FirebaseDatabase database = FirebaseDatabase.instance; 91 | ``` 92 | 93 | If you'd like to use it with a secondary Firebase App, use the static `instanceFor` method: 94 | 95 | ```dart 96 | FirebaseApp secondaryApp = Firebase.app('SecondaryApp'); 97 | FirebaseDatabase database = FirebaseDatabase.instanceFor(app: secondaryApp); 98 | ``` 99 | 100 | If you'd like to use a different RTDB instance on the same project, you can pass in a `databaseUrl` using 101 | the static `instanceFor` method: 102 | 103 | ```dart 104 | final firebaseApp = Firebase.app(); 105 | final rtdb = FirebaseDatabase.instanceFor(app: firebaseApp, databaseURL: 'https://your-realtime-database-url.firebaseio.com/'); 106 | ``` 107 | 108 | ## Next Steps 109 | 110 | * Learn how to [structure data](structure-data) for Realtime Database. 111 | 112 | * [Scale your data across multiple database instances.](/docs/database/usage/sharding) 113 | 114 | * [Read and write data.](read-and-write) 115 | 116 | * [View your database in the 117 | Firebase console.](//console.firebase.google.com/project/_/database/data) 118 | -------------------------------------------------------------------------------- /docs/flutter/database/structure-data.md: -------------------------------------------------------------------------------- 1 | Project: /docs/database/_project.yaml 2 | Book: /docs/_book.yaml 3 | page_type: guide 4 | 5 | 6 | 7 | # Structure Your Database 8 | 9 | This guide covers some of the key concepts in data architecture and best 10 | practices for structuring the JSON data in your Firebase Realtime Database. 11 | 12 | Building a properly structured database requires quite a bit of forethought. 13 | Most importantly, you need to plan for how data is going to be saved and 14 | later retrieved to make that process as easy as possible. 15 | 16 | ## How data is structured: it's a JSON tree 17 | 18 | All Firebase Realtime Database data is stored as JSON objects. You can think of 19 | the database as a cloud-hosted JSON tree. Unlike a SQL database, there are no 20 | tables or records. When you add data to the JSON tree, it becomes a node in the 21 | existing JSON structure with an associated key. You can provide your own keys, 22 | such as user IDs or semantic names, or they can be provided for you using 23 | `push()`. 24 | 25 | If you create your own keys, they must be UTF-8 encoded, can be a maximum 26 | of 768 bytes, and cannot contain `.`, `$`, `#`, `[`, `]`, `/`, or ASCII control 27 | characters 0-31 or 127. You cannot use ASCII control characters in the values 28 | themselves, either. 29 | {: .note } 30 | 31 | For example, consider a chat application that allows users to store a basic 32 | profile and contact list. A typical user profile is located at a path, such as 33 | `/users/$uid`. The user `alovelace` might have a database entry that 34 | looks something like this: 35 | 36 | ``` 37 | { 38 | "users": { 39 | "alovelace": { 40 | "name": "Ada Lovelace", 41 | "contacts": { "ghopper": true }, 42 | }, 43 | "ghopper": { ... }, 44 | "eclarke": { ... } 45 | } 46 | } 47 | ``` 48 | 49 | Although the database uses a JSON tree, data stored in the database can be 50 | represented as certain native types that correspond to available JSON types 51 | to help you write more maintainable code. 52 | 53 | ## Best practices for data structure 54 | 55 | ### Avoid nesting data 56 | 57 | Because the Firebase Realtime Database allows nesting data up to 32 levels deep, 58 | you might be tempted to think that this should be the default structure. 59 | However, when you fetch data at a location in your database, you also retrieve 60 | all of its child nodes. In addition, when you grant someone read or write access 61 | at a node in your database, you also grant them access to all data under that 62 | node. Therefore, in practice, it's best to keep your data structure as flat 63 | as possible. 64 | 65 | For an example of why nested data is bad, consider the following 66 | multiply-nested structure: 67 | 68 | ``` 69 | { 70 | // This is a poorly nested data architecture, because iterating the children 71 | // of the "chats" node to get a list of conversation titles requires 72 | // potentially downloading hundreds of megabytes of messages 73 | "chats": { 74 | "one": { 75 | "title": "Historical Tech Pioneers", 76 | "messages": { 77 | "m1": { "sender": "ghopper", "message": "Relay malfunction found. Cause: moth." }, 78 | "m2": { ... }, 79 | // a very long list of messages 80 | } 81 | }, 82 | "two": { ... } 83 | } 84 | } 85 | ``` 86 | 87 | With this nested design, iterating through the data becomes problematic. For 88 | example, listing the titles of chat conversations requires the entire `chats` 89 | tree, including all members and messages, to be downloaded to the client. 90 | 91 | 92 | ### Flatten data structures 93 | 94 | If the data is instead split into separate paths, also called denormalization, 95 | it can be efficiently downloaded in separate calls, as it is needed. Consider 96 | this flattened structure: 97 | 98 | ``` 99 | { 100 | // Chats contains only meta info about each conversation 101 | // stored under the chats's unique ID 102 | "chats": { 103 | "one": { 104 | "title": "Historical Tech Pioneers", 105 | "lastMessage": "ghopper: Relay malfunction found. Cause: moth.", 106 | "timestamp": 1459361875666 107 | }, 108 | "two": { ... }, 109 | "three": { ... } 110 | }, 111 | 112 | // Conversation members are easily accessible 113 | // and stored by chat conversation ID 114 | "members": { 115 | // we'll talk about indices like this below 116 | "one": { 117 | "ghopper": true, 118 | "alovelace": true, 119 | "eclarke": true 120 | }, 121 | "two": { ... }, 122 | "three": { ... } 123 | }, 124 | 125 | // Messages are separate from data we may want to iterate quickly 126 | // but still easily paginated and queried, and organized by chat 127 | // conversation ID 128 | "messages": { 129 | "one": { 130 | "m1": { 131 | "name": "eclarke", 132 | "message": "The relay seems to be malfunctioning.", 133 | "timestamp": 1459361875337 134 | }, 135 | "m2": { ... }, 136 | "m3": { ... } 137 | }, 138 | "two": { ... }, 139 | "three": { ... } 140 | } 141 | } 142 | ``` 143 | 144 | It's now possible to iterate through the list of rooms by downloading only a 145 | few bytes per conversation, quickly fetching metadata for listing or displaying 146 | rooms in a UI. Messages can be fetched separately and displayed as they arrive, 147 | allowing the UI to stay responsive and fast. 148 | 149 | 150 | ### Create data that scales {:#fanout} 151 | 152 | 153 | When building apps, it's often better to download a subset of a list. 154 | This is particularly common if the list contains thousands of records. 155 | When this relationship is static and one-directional, you can simply nest the 156 | child objects under the parent. 157 | 158 | Sometimes, this relationship is more dynamic, or it may be necessary to 159 | denormalize this data. Many times you can denormalize the data by using a query 160 | to retrieve a subset of the data, as discussed in 161 | [Sorting and filtering data](lists-of-data#sorting_and_filtering_data). 162 | 163 | But even this may be insufficient. Consider, for example, a two-way relationship 164 | between users and groups. Users can belong to a group, and groups comprise a 165 | list of users. When it comes time to decide which groups a user belongs to, 166 | things get complicated. 167 | 168 | What's needed is an elegant way to list the groups a user belongs to and 169 | fetch only data for those groups. An *index* of groups can help a 170 | great deal here: 171 | 172 | ``` 173 | // An index to track Ada's memberships 174 | { 175 | "users": { 176 | "alovelace": { 177 | "name": "Ada Lovelace", 178 | // Index Ada's groups in her profile 179 | "groups": { 180 | // the value here doesn't matter, just that the key exists 181 | "techpioneers": true, 182 | "womentechmakers": true 183 | } 184 | }, 185 | ... 186 | }, 187 | "groups": { 188 | "techpioneers": { 189 | "name": "Historical Tech Pioneers", 190 | "members": { 191 | "alovelace": true, 192 | "ghopper": true, 193 | "eclarke": true 194 | } 195 | }, 196 | ... 197 | } 198 | } 199 | ``` 200 | 201 | You might notice that this duplicates some data by storing the relationship 202 | under both Ada's record and under the group. Now `alovelace` is indexed under a 203 | group, and `techpioneers` is listed in Ada's profile. So to delete Ada 204 | from the group, it has to be updated in two places. 205 | 206 | This is a necessary redundancy for two-way relationships. It allows you to 207 | quickly and efficiently fetch Ada's memberships, even when the list of users or 208 | groups scales into the millions or when Realtime Database security rules 209 | prevent access to some of the records. 210 | 211 | This approach, inverting the data by listing the IDs as keys and setting the 212 | value to true, makes checking for a key as simple as reading 213 | `/users/$uid/groups/$group_id` and checking if it is `null`. The index is faster 214 | and a good deal more efficient than querying or scanning the data. 215 | 216 | ## Next Steps 217 | 218 | * [Read and Write Data to Realtime Database](read-and-write) 219 | -------------------------------------------------------------------------------- /docs/flutter/in-app-messaging/_customize-messages.md: -------------------------------------------------------------------------------- 1 | {# This content gets published to the following location: #} 2 | {# https://firebase.google.com/docs/in-app-messaging/customize-messaging?platform=flutter #} 3 | 4 | Firebase In-App Messaging provides a useful set of preconfigured behaviors and 5 | message types with a default look and feel, but in some cases you may want to 6 | extend behaviors and message content. In-App Messaging allows you to add actions 7 | to messages and customize message look and feel. 8 | 9 | ## Add an action to your message 10 | 11 | With actions you can use your in-app messages to direct users to a 12 | website or a specific screen in your app. 13 | 14 | ### Implement a deep link handler 15 | 16 | Firebase In-App Messaging uses link handlers to process actions. The SDK is 17 | able to use a number of handlers, so if your app already has one, Firebase 18 | In-App Messaging can use that without any further setup. If you don't yet have 19 | a handler, you can use [Firebase Dynamic Links](/docs/dynamic-links). 20 | 21 | ### Add the action to your message using the Firebase console 22 | 23 | Once your app has a link handler, you're ready to compose a campaign with 24 | an action. Open the Firebase console to 25 | [In-App Messaging](https://console.firebase.google.com/project/_/inappmessaging), 26 | and start a new campaign or edit an existing campaign. In that campaign, provide 27 | a **Card**, **Button text** and **Button action**, an **Image action**, or a **Banner 28 | action**, where the action is a relevant deep link. 29 | 30 | The action's format depends on which message layout you choose. Modals get 31 | action buttons with customizable button text content, text color, and background 32 | color. Images and top banners, on the other hand, become interactive and invoke 33 | the specified action when tapped. 34 | -------------------------------------------------------------------------------- /docs/flutter/in-app-messaging/_get-started.md: -------------------------------------------------------------------------------- 1 | {# This content gets published to the following location: #} 2 | {# https://firebase.google.com/docs/in-app-messaging/get-started?platform=flutter #} 3 | 4 | ## Before you begin 5 | 6 | [Install and initialize the Firebase SDKs for Flutter](/docs/flutter/setup) if you 7 | haven't already done so. 8 | 9 | ## Add the Firebase In-App Messaging SDK to your project 10 | 11 | 1. From the root directory of your Flutter project, run the following 12 | command to install the Firebase In-App Messaging plugin: 13 | 14 | ```bash 15 | flutter pub add firebase_in_app_messaging 16 | ``` 17 | 18 | 1. Rebuild your project: 19 | 20 | ```bash 21 | flutter run 22 | ``` 23 | 24 | 1. Import the Firebase In-App Messaging plugin: 25 | 26 | ```dart 27 | import 'package:firebase_in_app_messaging/firebase_in_app_messaging.dart'; 28 | ``` 29 | 30 | ## Send a test message 31 | 32 | ### Get your app's installation ID 33 | 34 | To conserve power, Firebase In-App Messaging only retrieves messages from the 35 | server once per day. That can make testing difficult, so the 36 | Firebase console allows you to specify a test device that displays messages 37 | on demand. 38 | 39 | That testing device is determined by a FID. 40 | Find your testing app's FID by checking the console 41 | output when you run your app. 42 | 43 | On Android, the message looks like the following: 44 | 45 | ``` 46 | I/FIAM.Headless: Starting InAppMessaging runtime with Installation ID YOUR_INSTALLATION_ID 47 | ``` 48 | 49 | On iOS, run the app with the runtime command argument `-FIRDebugEnabled`: 50 | 51 | 1. With your Xcode project open, select **Product > Scheme > Edit scheme...** from 52 | the top menu bar. 53 | 1. Open the **Arguments** tab of the dialog that pops up. 54 | 1. Click **+ Add items** under **Arguments Passed On Launch**. 55 | 1. Enter "-FIRDebugEnabled" in the newly-created field. 56 | 1. Click **Close**, then run your app. 57 | 58 | Once your app starts running, look for the following line in the Xcode console's logs: 59 | 60 | ``` 61 | [Firebase/InAppMessaging][I-IAM180017] Starting InAppMessaging runtime with Firebase Installation ID YOUR_INSTALLATION_ID 62 | ``` 63 | 64 | 65 | ### Send a message to your testing device 66 | 67 | Once you've launched your app on the testing device and you have its 68 | Firebase installation ID (FID), you can try out your Firebase In-App Messaging 69 | setup by sending a test message: 70 | 71 | 1. In the {{name_appmanager}}, open [Messaging](https://console.firebase.google.com/project/_/messaging/). 72 | 1. If this is your first campaign, click **Create your first campaign**. 73 | 1. Select **Firebase In-App messages** and click **Create**. 74 | 1. Otherwise, on the **Campaigns** tab, click **New campaign**. 75 | 1. Select **In-App Messaging**. 76 | 1. Enter a **Title** for your first message. 77 | 1. Click **Test on your Device** 78 | 1. Enter your app's Firebase installation ID in the 79 | **Add an installation ID** field. 80 | 1. Click **Test** to send the message. 81 | 82 | Firebase In-App Messaging sends your test message as soon as you click **Test**. To see the 83 | message, you need to close, then reopen the app on your testing device. 84 | 85 | To confirm whether your device is a test device, look for one of the following 86 | log messages. 87 | 88 | **Android** 89 | 90 | ``` 91 | I/FIAM.Headless: Setting this device as a test device 92 | ``` 93 | 94 | **iOS** 95 | 96 | ``` 97 | [Firebase/InAppMessaging][I-IAM180017] Seeing test message in fetch response. Turn the current instance into a testing instance. 98 | ``` 99 | -------------------------------------------------------------------------------- /docs/flutter/in-app-messaging/_modify-message-behavior.md: -------------------------------------------------------------------------------- 1 | {# This content gets published to the following location: #} 2 | {# https://firebase.google.com/docs/in-app-messaging/modify-message-behavior?platform=flutter #} 3 | 4 | With little to no coding effort, Firebase In-App Messaging allows you to create, 5 | configure and target rich user interactions, leveraging 6 | the capabilities of Google Analytics out of the box 7 | to tie messaging events to actual user characteristics, activities, and choices. 8 | With some additional Firebase In-App Messaging SDK integration, you can tailor 9 | the behavior of in-app messages even further, responding when users interact 10 | with messages, triggering message events outside the Analytics 11 | framework, and allowing users to control sharing of their personal data related 12 | to messaging interactions. 13 | 14 | ## Respond when users interact with in-app messages 15 | 16 | With actions you can use your in-app messages to direct users to a 17 | website or a specific screen in your app. 18 | 19 | Your code can respond to basic interactions (clicks and dismissals), to 20 | impressions (verified views of your messages), and to display errors logged and 21 | confirmed by the SDK. For example, when your message is composed as a Card 22 | modal, you might want to track and follow-up on which of two URLs the user 23 | clicked on the Card. 24 | 25 | To do so, you will have to use the platform-native APIs. 26 | See the documentation for [iOS](/docs/in-app-messaging/modify-message-behavior?platform=ios#respond_when_users_interact_with_in-app_messages) 27 | and [Android](/docs/in-app-messaging/modify-message-behavior?platform=android#respond_when_users_interact_with_in-app_messages). 28 | 29 | ## Trigger in-app messages programmatically 30 | 31 | Firebase In-App Messaging by default allows you to trigger in-app messages with 32 | Google Analytics for Firebase events, with no additional integration. You can 33 | also manually trigger events programmatically with the Firebase In-App Messaging SDK's 34 | programmatic triggers. 35 | 36 | In the In-App Messaging campaign composer, create a new campaign or select an 37 | existing campaign, and in the Scheduling step of the composer workflow, note the 38 | event ID of a newly-created or existing messaging event. Once noted, instrument 39 | your app to trigger the event by its ID. 40 | 41 | ```dart 42 | FirebaseInAppMessaging.instance.triggerEvent("eventName"); 43 | ``` 44 | 45 | ## Use campaign custom metadata 46 | 47 | In your campaigns, you can specify custom data in a series of key/value pairs. 48 | When users interact with messages, this data is available for you to, for example, 49 | display a promo code. 50 | 51 | To do so, you will have to use the platform-native APIs. 52 | See the documentation for [iOS](/docs/in-app-messaging/modify-message-behavior?platform=ios#use_campaign_custom_metadata) 53 | and [Android](/docs/in-app-messaging/modify-message-behavior?platform=android#use_campaign_custom_metadata). 54 | 55 | 56 | ## Temporarily disable in-app messages 57 | 58 | By default, Firebase In-App Messaging renders messages whenever a triggering 59 | condition is satisfied, regardless of an app's current state. If you'd like to 60 | suppress message displays for any reason, for example to avoid interrupting a 61 | sequence of payment processing screens, you can do that with the SDK's 62 | `setMessagesSuppressed()` method: 63 | 64 | ```dart 65 | FirebaseInAppMessaging.instance.setMessagesSuppressed(true); 66 | ``` 67 | 68 | Passing `true` to the method prevents Firebase In-App Messaging from displaying 69 | messages, while `false` reenables message display. The SDK turns off message 70 | suppression on app restart. Suppressed messages are ignored by the SDK. Their 71 | trigger conditions must be met again while suppression is off before Firebase 72 | In-App Messaging can display them. 73 | 74 | ## Enable opt-out message delivery 75 | 76 | By default, Firebase In-App Messaging automatically delivers messages to all app users you target 77 | in messaging campaigns. To deliver those messages, the Firebase In-App Messaging SDK uses 78 | installation IDs to identify each user's app. This means 79 | that In-App Messaging has to send client data, linked to the 80 | installation ID, to Firebase servers. If you'd like to give users 81 | more control over the data they send, disable automatic data collection and give 82 | them a chance to approve data sharing. 83 | 84 | To do that, you have to disable automatic initialization for Firebase In-App Messaging, and 85 | initialize the service manually for opt-in users: 86 | 87 | 1. Turn off automatic initialization. 88 | 89 | **Apple platforms**: Add a new key to your `Info.plist` file: 90 | 91 | - Key: `FirebaseInAppMessagingAutomaticDataCollectionEnabled` 92 | - Value: `NO` 93 | 94 | **Android**: Add a `meta-data` tag to your `AndroidManifest.xml` file: 95 | 96 | ```xml 97 | 100 | ``` 101 | 102 | 1. Initialize Firebase In-App Messaging for selected users manually: 103 | 104 | ```dart 105 | FirebaseInAppMessaging.instance.setAutomaticDataCollectionEnabled(true); 106 | ``` 107 | 108 | Once you set a data collection preference manually, the value persists 109 | through app restarts, overriding the value in your `Info.plist` or 110 | `AndroidManifest.xml`. If you'd like to disable initialization again, for 111 | example if a user opts out of collection later, pass `false` to the 112 | `setAutomaticDataCollectionEnabled()` method. 113 | -------------------------------------------------------------------------------- /docs/flutter/ml/use-custom-models.md: -------------------------------------------------------------------------------- 1 | Project: /docs/_project.yaml 2 | Book: /docs/_book.yaml 3 | 4 | 5 | 6 | # Use a custom TensorFlow Lite model with Flutter 7 | 8 | If your app uses custom 9 | [TensorFlow Lite](https://www.tensorflow.org/lite/){:.external} models, you can 10 | use Firebase ML to deploy your models. By deploying models with Firebase, you 11 | can reduce the initial download size of your app and update your app's ML models 12 | without releasing a new version of your app. And, with Remote Config and A/B 13 | Testing, you can dynamically serve different models to different sets of users. 14 | 15 | ## TensorFlow Lite models 16 | 17 | TensorFlow Lite models are ML models that are optimized to run on mobile 18 | devices. To get a TensorFlow Lite model: 19 | 20 | - Use a pre-built model, such as one of the [official TensorFlow Lite models](https://www.tensorflow.org/lite/models){:.external} 21 | - [Convert a TensorFlow model, Keras model, or concrete function to TensorFlow Lite.](https://www.tensorflow.org/lite/convert){:.external} 22 | 23 | Note that in the absence of a maintained TensorFlow Lite library for Dart, you 24 | will need to integrate with the native TensorFlow Lite library for your 25 | platforms. This integration is not documented here. 26 | 27 | ## Before you begin 28 | 29 | 1. [Install and initialize the Firebase SDKs for Flutter](/docs/flutter/setup) 30 | if you haven't already done so. 31 | 32 | 1. From the root directory of your Flutter project, run the following 33 | command to install the ML model downloader plugin: 34 | 35 | ```bash 36 | flutter pub add firebase_ml_model_downloader 37 | ``` 38 | 39 | 1. Rebuild your project: 40 | 41 | ```bash 42 | flutter run 43 | ``` 44 | 45 | ## 1. Deploy your model {:#deploy} 46 | 47 | Deploy your custom TensorFlow models using either the Firebase console or 48 | the Firebase Admin Python and Node.js SDKs. See 49 | [Deploy and manage custom models](/docs/ml/manage-hosted-models). 50 | 51 | After you add a custom model to your Firebase project, you can reference the 52 | model in your apps using the name you specified. At any time, you can deploy a 53 | new TensorFlow Lite model and download the new model onto users' devices by 54 | calling `getModel()` (see below). 55 | 56 | ## 2. Download the model to the device and initialize a TensorFlow Lite interpreter 57 | 58 | To use your TensorFlow Lite model in your app, first use the model downloader 59 | to download the latest version of the model to the device. Then, instantiate a 60 | TensorFlow Lite interpreter with the model. 61 | 62 | To start the model download, call the model downloader's `getModel()` method, 63 | specifying the name you assigned the model when you uploaded it, whether you 64 | want to always download the latest model, and the conditions under which you 65 | want to allow downloading. 66 | 67 | You can choose from three download behaviors: 68 | 69 | | Download type | Description 70 | |----------------------------------|-------------------------------------------- 71 | | `localModel` | Get the local model from the device. 72 | : : If there is no local model available, this 73 | : : behaves like `latestModel`. Use this 74 | : : download type if you are not interested in 75 | : : checking for model updates. For example, 76 | : : you're using Remote Config to retrieve 77 | : : model names and you always upload models 78 | : : under new names (recommended). 79 | | `localModelUpdateInBackground` | Get the local model from the device and 80 | : : start updating the model in the background. 81 | : : If there is no local model available, this 82 | : : behaves like `latestModel`. 83 | | `latestModel` | Get the latest model. If the local model is 84 | : : the latest version, returns the local 85 | : : model. Otherwise, download the latest 86 | : : model. This behavior will block until the 87 | : : latest version is downloaded (not 88 | : : recommended). Use this behavior only in 89 | : : cases where you explicitly need the latest 90 | : : version. 91 | 92 | You should disable model-related functionality—for example, grey-out or 93 | hide part of your UI—until you confirm the model has been downloaded. 94 | 95 | ```dart 96 | FirebaseModelDownloader.instance 97 | .getModel( 98 | "yourModelName", 99 | FirebaseModelDownloadType.localModel, 100 | FirebaseModelDownloadConditions( 101 | iosAllowsCellularAccess: true, 102 | iosAllowsBackgroundDownloading: false, 103 | androidChargingRequired: false, 104 | androidWifiRequired: false, 105 | androidDeviceIdleRequired: false, 106 | ) 107 | ) 108 | .then((customModel) { 109 | // Download complete. Depending on your app, you could enable the ML 110 | // feature, or switch from the local model to the remote model, etc. 111 | 112 | // The CustomModel object contains the local path of the model file, 113 | // which you can use to instantiate a TensorFlow Lite interpreter. 114 | final localModelPath = customModel.file; 115 | 116 | // ... 117 | }); 118 | ``` 119 | 120 | Many apps start the download task in their initialization code, but you can do 121 | so at any point before you need to use the model. 122 | 123 | 124 | ## 3. Perform inference on input data 125 | 126 | Now that you have your model file on the device you can use it with the 127 | TensorFlow Lite interpreter to perform inference. In the absence of a maintained 128 | TensorFlow Lite library for Dart, you will need to integrate with the 129 | [native TensorFlow Lite libraries](https://www.tensorflow.org/lite){:.external} 130 | for iOS and Android. 131 | 132 | 133 | ## Appendix: Model security {:#model_security} 134 | 135 | Regardless of how you make your TensorFlow Lite models available to 136 | Firebase ML, Firebase ML stores them in the standard serialized protobuf format in 137 | local storage. 138 | 139 | In theory, this means that anybody can copy your model. However, 140 | in practice, most models are so application-specific and obfuscated by 141 | optimizations that the risk is similar to that of competitors disassembling and 142 | reusing your code. Nevertheless, you should be aware of this risk before you use 143 | a custom model in your app. 144 | -------------------------------------------------------------------------------- /docs/flutter/perf-mon/_custom-network-traces.md: -------------------------------------------------------------------------------- 1 | {# This content gets published to the following location: #} 2 | {# https://firebase.google.com/docs/perf-mon/custom-network-traces?platform=flutter #} 3 | 4 | Performance Monitoring collects _traces_ to help you monitor the performance of your app. A 5 | trace is a report of performance data captured between two points in time in 6 | your app. 7 | 8 | The 9 | [network request traces automatically collected by Performance Monitoring](/docs/perf-mon/network-traces) 10 | include most network requests for your app. However, some requests might not be 11 | reported or you might use a different library to make network requests. In these 12 | cases, you can use the Performance Monitoring API to manually instrument 13 | **_custom network request traces_**. Custom network request traces are only 14 | supported for Apple and Android apps. 15 | 16 | The default metrics for a custom network request trace are the same as those for 17 | the network request traces automatically collected by Performance Monitoring, specifically 18 | response time, response and request payload size, and success rate. Custom 19 | network request traces do not support adding custom metrics. 20 | 21 | In your code, you define the beginning and the end of a custom network request 22 | trace using the APIs provided by the Performance Monitoring SDK. 23 | 24 | Custom network request traces appear in the Firebase console alongside the 25 | network requests that Performance Monitoring captures automatically 26 | (in the _Network requests_ subtab of the traces table). 27 | 28 | 29 | ## Add custom network request traces {:#add-custom-network-traces} 30 | 31 | Use the Performance Monitoring HttpMetric API 32 | to add custom network request traces to monitor specific network requests. 33 | 34 | To manually instrument custom network requests in Performance Monitoring, add code similar 35 | to the following: 36 | 37 | ```dart 38 | final metric = FirebasePerformance.instance 39 | .newHttpMetric("https://www.google.com", HttpMethod.Get); 40 | 41 | await metric.start(); 42 | final response = await http.get(Uri.parse("https://www.google.com/")); 43 | await metric.stop(); 44 | ``` 45 | 46 | Custom network request traces also support adding custom attributes 47 | but not custom metrics. 48 | 49 | 50 | ## Next steps 51 | 52 | * [Set up alerts](/docs/perf-mon/alerts) for network requests that are degrading 53 | the performance of your app. For example, you can configure an email alert for 54 | your team if the _response time_ for a specific URL pattern exceeds a 55 | threshold that you set. 56 | -------------------------------------------------------------------------------- /docs/flutter/perf-mon/_disable-sdk.md: -------------------------------------------------------------------------------- 1 | {# This content gets published to the following location: #} 2 | {# https://firebase.google.com/docs/perf-mon/disable-sdk?platform=flutter #} 3 | 4 | During app development and testing, you might find it useful to disable 5 | Performance Monitoring. 6 | 7 | For example, by 8 | [disabling Performance Monitoring during your app build process](#disable-during-build), 9 | you can: 10 | 11 | * Disable certain functionalities of Performance Monitoring in your debug 12 | builds, but re-enable the functionalities for your release build. 13 | 14 | * Disable Performance Monitoring when building your app, but allow your app to 15 | re-enable it at runtime. 16 | 17 | * Disable Performance Monitoring when building your app, and do not allow your 18 | app to re-enable it at runtime. 19 | 20 | You can also build your app with Performance Monitoring _enabled_, but 21 | [use Firebase Remote Config](#disable-with-remote-config) to give you 22 | flexibility to disable (and re-enable) Performance Monitoring in your production 23 | app. With this option, you can even configure your app to let users opt-in or 24 | opt-out of using Performance Monitoring. 25 | 26 | ## Disable Performance Monitoring during your app build process {: #disable-during-build} 27 | 28 | One situation where disabling Performance Monitoring during your app build 29 | process could be useful is to avoid reporting performance data from a 30 | pre-release version of your app during app development and testing. 31 | 32 | To do so, see the platform-specific [iOS+](?platform=ios) and 33 | [Android](?platform=android) docs. 34 | 35 | 36 | ## Disable your app at runtime using Remote Config {: #disable-with-remote-config} 37 | 38 | [Firebase Remote Config](/docs/remote-config/get-started?platform=flutter) lets 39 | you make changes to the behavior and appearance of your app, so it provides an 40 | ideal way to let you disable Performance Monitoring in deployed instances of 41 | your app. 42 | 43 | For example, suppose you want to use a parameter called `perf_disable` to 44 | remotely control Performance Monitoring. Add the following to your startup code 45 | to enable or disable Performance Monitoring: 46 | 47 | ```dart 48 | // Activate previously-fetched values, falling back on the defaults if 49 | // nothing is available yet. 50 | await FirebaseRemoteConfig.instance 51 | .setDefaults(YOUR_REMOTE_CONFIG_DEFAULTS); 52 | await FirebaseRemoteConfig.instance.activate(); 53 | 54 | // Enable or disable Performance Monitoring based on the value of 55 | // "perf_disable". 56 | final perfMonDisabled = 57 | FirebaseRemoteConfig.instance.getBool("perf_disable"); 58 | FirebasePerformance.instance 59 | .setPerformanceCollectionEnabled(!perfMonDisabled); 60 | 61 | // Fetch values for next time. (Don't await the result!) 62 | FirebaseRemoteConfig.instance.fetch(); 63 | ``` 64 | 65 | Note: This snippet requires an app restart to activate configuration changes. 66 | See [Loading strategies](/docs/remote-config/loading) for alternatives. 67 | 68 | 69 | -------------------------------------------------------------------------------- /docs/flutter/perf-mon/get-started.md: -------------------------------------------------------------------------------- 1 | Project: /docs/perf-mon/_project.yaml 2 | Book: /docs/_book.yaml 3 | page_type: guide 4 | 5 | {% include "_shared/apis/console/_local_variables.html" %} 6 | {% include "_local_variables.html" %} 7 | {% include "docs/perf-mon/_local_variables.html" %} 8 | {% include "_shared/firebase/_snippet_include_comment.html" %} 9 | 10 | 11 | 12 | # Get started with Performance Monitoring for Flutter 13 | 14 | This quickstart describes how to set up Firebase Performance Monitoring to help 15 | you to gain insight into the performance characteristics of your Flutter apps. 16 | 17 | 18 | ## Before you begin 19 | 20 | If you haven't already, 21 | [configure and initialize Firebase](/docs/flutter/setup) in your Flutter 22 | project. 23 | 24 | ## **Step 1**: Add Performance Monitoring to your app {:#add-perfmon} 25 | 26 | 1. From the root directory of your Flutter project, run the following 27 | command to install the Performance Monitoring Flutter plugin: 28 | 29 | ```sh {: .devsite-terminal .devsite-click-to-copy data-terminal-prefix="your-flutter-proj$ " } 30 | flutter pub add firebase_performance 31 | ``` 32 | 33 | 1. From the root directory of your Flutter project, run the following command: 34 | 35 | ```sh {: .devsite-terminal .devsite-click-to-copy data-terminal-prefix="your-flutter-proj$ " } 36 | flutterfire configure 37 | ``` 38 | 39 | Running this command ensures that your Flutter app's Firebase configuration 40 | is up-to-date and, for Android, adds the required Performance Monitoring 41 | Gradle plugin to your app. 42 | 43 | 1. Once complete, rebuild your Flutter project: 44 | 45 | ```sh {: .devsite-terminal .devsite-click-to-copy data-terminal-prefix="your-flutter-proj$ " } 46 | flutter run 47 | ``` 48 | 49 | After you've added the Performance Monitoring SDK, Firebase automatically starts collecting 50 | data related to your app's lifecycle (like 51 | [app start time](/docs/perf-mon/app-start-foreground-background-traces)), and 52 | data for [HTTP/S network requests](/docs/perf-mon/network-traces). 53 | 54 | On Flutter, automatic screen rendering performance monitoring is not possible 55 | for individual Flutter screens. A single view controller encapsulates your 56 | entire Flutter application natively so the underlying native Firebase SDK is 57 | not aware of screen transitions. 58 | 59 | Note: When you add Performance Monitoring to your app, the Remote Config SDK is 60 | included as a dependency. If you already use Remote Config, you won't see 61 | any difference. However, if you're new to Remote Config, explore the 62 | [Remote Config documentation](/docs/remote-config) to learn more 63 | about the various features you'll be able to access in your app. 64 | 65 | 66 | ## **Step 2**: Generate performance events for initial data display {:#data-in-console} 67 | 68 | Firebase starts processing the events when you successfully add the SDK to your 69 | app. If you're still developing locally, interact with your app to generate 70 | events for initial data collection and processing. 71 | 72 | Note: The Performance Monitoring SDK batches events locally then sends them to Firebase 73 | periodically (every 30 seconds) or when the app comes back to foreground. So, 74 | there's a delay between an app interaction and when Firebase receives the event 75 | information from your app. 76 | 77 | 1. Continue to develop your app using a simulator or test device. 78 | 79 | 1. Generate events by switching your app between background and foreground 80 | several times, interacting with your app by navigating across screens, 81 | and/or triggering network requests. 82 | 83 | 1. Go to the [_Performance_ dashboard](//console.firebase.google.com/project/_/performance) 84 | of the Firebase console. You should see your initial data display within 85 | a few minutes. 86 | 87 | If you don't see a display of your initial data, review the [troubleshooting 88 | tips](/docs/perf-mon/troubleshooting?platform=ios#sdk-detected-no-data). 89 | 90 | 91 | ## **Step 3**: _(Optional)_ View log messages for performance events {:#view-log-messages} 92 | 93 | 1. Check your log messages for any error messages. 94 | 95 | Performance Monitoring tags its log messages with the following tags so that 96 | you can filter your log messages: 97 | 98 | * iOS+: `Firebase/Performance` 99 | * Android: `FirebasePerformance` 100 | 101 | 1. Check for the following types of logs which indicate that Performance Monitoring is 102 | logging performance events: 103 | 104 | * Logging trace metric: TRACE_NAME, FIREBASE_PERFORMANCE_CONSOLE_URL 105 | * Logging network request trace: URL 106 | 107 | 1. Click on the URL to view your data in the Firebase console. It may take a few 108 | moments for the data to update in the dashboard. 109 | 110 | ## **Step 4**: _(Optional)_ Add custom monitoring for specific code {:#add-custom-trace} 111 | 112 | To monitor performance data associated with specific code in your app, you can 113 | instrument [**custom code traces**](/docs/perf-mon/custom-code-traces?platform=flutter). 114 | 115 | With a custom code trace, you can measure how long it takes your app to complete 116 | a specific task or set of tasks, such as loading a set of images or querying 117 | your database. The default metric for a custom code trace is its duration, but 118 | you can also add custom metrics, such as cache hits and memory warnings. 119 | 120 | In your code, you define the beginning and the end of a custom code trace (and 121 | add any desired custom metrics) using the API provided by the Performance Monitoring SDK. 122 | 123 | Visit [Add monitoring for specific code](/docs/perf-mon/custom-code-traces?platform=flutter) 124 | to learn more about these features and how to add them to your app. 125 | 126 | ## **Step 5**: Deploy your app then review results {:#deploy-then-review-results} 127 | 128 | After you've validated Performance Monitoring using the an emulator and one or more 129 | test devices, you can deploy the updated version of your app to your users. 130 | 131 | You can monitor performance data in the 132 | [_Performance_ dashboard](//console.firebase.google.com/project/_/performance) 133 | of the Firebase console. 134 | 135 | 136 | ## Next steps 137 | 138 | * Learn more about data automatically collected by Performance Monitoring: 139 | 140 | * Data related to your app's lifecycle, like 141 | [app start time](/docs/perf-mon/app-start-foreground-background-traces) 142 | * Data for [HTTP/S network requests](/docs/perf-mon/network-traces) issued 143 | by your app 144 | 145 | * [View, track, and filter](/docs/perf-mon/console) your 146 | performance data in the Firebase console. 147 | 148 | * Add monitoring for specific tasks or workflows in your app by 149 | [instrumenting custom code traces](/docs/perf-mon/custom-code-traces?platform=flutter). 150 | 151 | * [Use attributes to filter performance data](/docs/perf-mon/attributes). 152 | -------------------------------------------------------------------------------- /docs/flutter/reference/_toc.yaml: -------------------------------------------------------------------------------- 1 | toc: 2 | - title: cloud_firestore 3 | path: https://pub.dev/documentation/cloud_firestore/latest/ 4 | status: external 5 | - title: cloud_firestore_web 6 | path: https://pub.dev/documentation/cloud_firestore_web/latest/ 7 | status: external 8 | - title: cloud_functions 9 | path: https://pub.dev/documentation/cloud_functions/latest/ 10 | status: external 11 | - title: cloud_functions_web 12 | path: https://pub.dev/documentation/cloud_functions_web/latest/ 13 | status: external 14 | - title: firebase_analytics 15 | path: https://pub.dev/documentation/firebase_analytics/latest/ 16 | status: external 17 | - title: firebase_analytics_web 18 | path: https://pub.dev/documentation/firebase_analytics_web/latest/ 19 | status: external 20 | - title: firebase_app_check 21 | path: https://pub.dev/documentation/firebase_app_check/latest/ 22 | status: external 23 | - title: firebase_app_installations 24 | path: https://pub.dev/documentation/firebase_app_installations/latest/ 25 | status: external 26 | - title: firebase_auth 27 | path: https://pub.dev/documentation/firebase_auth/latest/ 28 | status: external 29 | - title: firebase_auth_web 30 | path: https://pub.dev/documentation/firebase_auth_web/latest/ 31 | status: external 32 | - title: firebase_core 33 | path: https://pub.dev/documentation/firebase_core/latest/ 34 | status: external 35 | - title: firebase_core_web 36 | path: https://pub.dev/documentation/firebase_core_web/latest/ 37 | status: external 38 | - title: firebase_crashlytics 39 | path: https://pub.dev/documentation/firebase_crashlytics/latest/ 40 | status: external 41 | - title: firebase_database 42 | path: https://pub.dev/documentation/firebase_database/latest/ 43 | status: external 44 | - title: firebase_dynamic_links 45 | path: https://pub.dev/documentation/firebase_dynamic_links/latest/ 46 | status: external 47 | - title: firebase_in_app_messaging 48 | path: https://pub.dev/documentation/firebase_in_app_messaging/latest/ 49 | status: external 50 | - title: firebase_messaging 51 | path: https://pub.dev/documentation/firebase_messaging/latest/ 52 | status: external 53 | - title: firebase_messaging_web 54 | path: https://pub.dev/documentation/firebase_messaging_web/latest/ 55 | status: external 56 | - title: firebase_ml_model_downloader 57 | path: https://pub.dev/documentation/firebase_ml_model_downloader/latest/ 58 | status: external 59 | - title: firebase_performance 60 | path: https://pub.dev/documentation/firebase_performance/latest/ 61 | status: external 62 | # - title: firebase_performance_platform_interface 63 | # path: https://pub.dev/documentation/firebase_performance_platform_interface/latest/ 64 | # status: external 65 | - title: firebase_performance_web 66 | path: https://pub.dev/documentation/firebase_performance_web/latest/ 67 | status: external 68 | - title: firebase_remote_config 69 | path: https://pub.dev/documentation/firebase_remote_config/latest/ 70 | status: external 71 | - title: firebase_storage 72 | path: https://pub.dev/documentation/firebase_storage/latest/ 73 | status: external 74 | - title: firebase_storage_web 75 | path: https://pub.dev/documentation/firebase_storage_web/latest/ 76 | status: external 77 | - title: flutterfire_ui 78 | path: https://pub.dev/documentation/flutterfire_ui/latest/ 79 | status: external 80 | -------------------------------------------------------------------------------- /docs/flutter/setup/_setup_prereq_android.md: -------------------------------------------------------------------------------- 1 | {# This content gets published to the following location: #} 2 | {# https://firebase.google.com/docs/flutter/setup #} 3 | 4 | * Install your preferred [editor or IDE](//docs.flutter.dev/get-started/editor/). 5 | 6 | * Set up a device or emulator for running your app. 7 | [Emulators](https://developer.android.com/studio/run/managing-avds){: .external} 8 | must use an emulator image with Google Play. 9 | 10 | * Make sure that your app meets the following requirements: 11 | 12 | * Targets API level {{android_min_api_level}} ({{android_min_api_codename}}) 13 | or higher 14 | * Uses Android {{android_min_version}} or higher 15 | 16 | * [Install Flutter](//docs.flutter.dev/get-started/install/) for your specific 17 | operating system, including the following: 18 | 19 | * Flutter SDK 20 | * Supporting libraries 21 | * Platform-specific software and SDKs 22 | 23 | * [Sign into Firebase]({{name_appmanagerURL}}){: .external} using your Google 24 | account. 25 | 26 | If you don't already have a Flutter app, you can complete the [Get 27 | Started: Test Drive](//docs.flutter.dev/get-started/test-drive#androidstudio) to 28 | create a new Flutter app using your preferred editor or IDE. 29 | -------------------------------------------------------------------------------- /docs/flutter/setup/_setup_prereq_ios.md: -------------------------------------------------------------------------------- 1 | {# This content gets published to the following location: #} 2 | {# https://firebase.google.com/docs/flutter/setup #} 3 | 4 | * Install your preferred [editor or IDE](//docs.flutter.dev/get-started/editor/). 5 | 6 | * Set up a physical Apple device or use a simulator to run your app. 7 | 8 | {# Google-internal file; not on GitHub. #} 9 | <<../../../_internal/includes/docs/guides/_setup-ios_prereq_want-to-use-fcm.md>> 10 | 11 | * Make sure that your Flutter app targets the following platform versions or 12 | later: 13 | * iOS {{min_ios_os_version}} 14 | * macOS {{min_mac_os_version}} 15 | 16 | * [Install Flutter](//docs.flutter.dev/get-started/install/) for your specific 17 | operating system, including the following: 18 | 19 | * Flutter SDK 20 | * Supporting libraries 21 | * Platform-specific software and SDKs 22 | 23 | * [Sign into Firebase]({{name_appmanagerURL}}){: .external} using your Google 24 | account. 25 | 26 | If you don't already have a Flutter app, you can complete the [Get 27 | Started: Test Drive](//docs.flutter.dev/get-started/test-drive/#androidstudio) to 28 | create a new Flutter app using your preferred editor or IDE. 29 | 30 | Note: If you're targeting macOS or macOS Catalyst, you must add the [Keychain Sharing capability](https://firebase.google.com/docs/ios/troubleshooting-faq#macos-keychain-sharing) to your target. In Xcode, navigate to your target's *Signing & Capabilities* tab, and then click **+ Capabilities** to add a new capability. 31 | -------------------------------------------------------------------------------- /docs/flutter/setup/_setup_prereq_web.md: -------------------------------------------------------------------------------- 1 | {# This content gets published to the following location: #} 2 | {# https://firebase.google.com/docs/flutter/setup #} 3 | 4 | * Install your preferred [editor or IDE](//docs.flutter.dev/get-started/editor/). 5 | 6 | * [Install Flutter](//docs.flutter.dev/get-started/install/) for your specific 7 | operating system, including the following: 8 | 9 | * Flutter SDK 10 | * Supporting libraries 11 | * Platform-specific software and SDKs 12 | 13 | * [Sign into Firebase]({{name_appmanagerURL}}){: .external} using your Google 14 | account. 15 | 16 | If you don't already have a Flutter app, you can complete the [Get 17 | Started: Test Drive](//docs.flutter.dev/get-started/test-drive?tab=vscode) to 18 | create a new Flutter app using your preferred editor or IDE. 19 | -------------------------------------------------------------------------------- /docs/flutter/storage/create-reference.md: -------------------------------------------------------------------------------- 1 | Project: /docs/storage/_project.yaml 2 | Book: /docs/_book.yaml 3 | page_type: guide 4 | 5 | 6 | 7 | # Create a Cloud Storage reference on Flutter 8 | 9 | Your files are stored in a 10 | [Cloud Storage](//cloud.google.com/storage) bucket. The 11 | files in this bucket are presented in a hierarchical structure, just like the 12 | file system on your local hard disk, or the data in the Firebase Realtime Database. 13 | By creating a reference to a file, your app gains access to it. These references 14 | can then be used to upload or download data, get or update metadata or delete 15 | the file. A reference can either point to a specific file or to a higher level 16 | node in the hierarchy. 17 | 18 | If you've used the [Firebase Realtime Database](/docs/database), these paths should 19 | seem very familiar to you. However, your file data is stored in 20 | Cloud Storage, **not** in the Realtime Database. 21 | 22 | 23 | ## Create a Reference 24 | 25 | Create a reference to upload, download, or delete a file, 26 | or to get or update its metadata. A reference 27 | can be thought of as a pointer to a file in the cloud. References are 28 | lightweight, so you can create as many as you need. They are also reusable for 29 | multiple operations. 30 | 31 | Create a reference using the `FirebaseStorage` singleton instance and 32 | calling its `ref()` method. 33 | 34 | ```dart 35 | final storageRef = FirebaseStorage.instance.ref(); 36 | ``` 37 | 38 | Next, you can create a reference to a location lower in the tree, 39 | say `"images/space.jpg"` by using the `child()` method on an existing reference. 40 | 41 | ```dart 42 | // Create a child reference 43 | // imagesRef now points to "images" 44 | final imagesRef = storageRef.child("images"); 45 | 46 | // Child references can also take paths 47 | // spaceRef now points to "images/space.jpg 48 | // imagesRef still points to "images" 49 | final spaceRef = storageRef.child("images/space.jpg"); 50 | ``` 51 | 52 | ## Navigate with References 53 | 54 | You can also use the `parent` and `root` properties to navigate up in our 55 | file hierarchy. `parent` navigates up one level, 56 | while `root` navigates all the way to the top. 57 | 58 | ```dart 59 | // parent allows us to move our reference to a parent node 60 | // imagesRef2 now points to 'images' 61 | final imagesRef2 = spaceRef.parent; 62 | 63 | // root allows us to move all the way back to the top of our bucket 64 | // rootRef now points to the root 65 | final rootRef = spaceRef.root; 66 | ``` 67 | 68 | `child()`, `parent`, and `root` can be chained together multiple 69 | times, as each is a reference. But accessing `root.parent` results in `null`. 70 | 71 | ```dart 72 | // References can be chained together multiple times 73 | // earthRef points to 'images/earth.jpg' 74 | final earthRef = spaceRef.parent?.child("earth.jpg"); 75 | 76 | // nullRef is null, since the parent of root is null 77 | final nullRef = spaceRef.root.parent; 78 | ``` 79 | 80 | 81 | ## Reference Properties 82 | 83 | You can inspect references to better understand the files they point to 84 | using the `fullPath`, `name`, and `bucket` properties. These properties 85 | get the file's full path, name and bucket. 86 | 87 | ```dart 88 | // Reference's path is: "images/space.jpg" 89 | // This is analogous to a file path on disk 90 | spaceRef.fullPath; 91 | 92 | // Reference's name is the last segment of the full path: "space.jpg" 93 | // This is analogous to the file name 94 | spaceRef.name; 95 | 96 | // Reference's bucket is the name of the storage bucket that the files are stored in 97 | spaceRef.bucket; 98 | ``` 99 | 100 | ## Limitations on References 101 | 102 | Reference paths and names can contain any sequence of valid Unicode characters, 103 | but certain restrictions are imposed including: 104 | 105 | 1. Total length of reference.fullPath must be between 1 and 1024 bytes when UTF-8 encoded. 106 | 1. No Carriage Return or Line Feed characters. 107 | 1. Avoid using `#`, `[`, `]`, `*`, or `?`, as these do not work well with 108 | other tools such as the [Firebase Realtime Database](/docs/database/overview) 109 | or [gsutil](https://cloud.google.com/storage/docs/gsutil). 110 | 111 | ## Full Example 112 | 113 | ```dart 114 | // Points to the root reference 115 | final storageRef = FirebaseStorage.instance.ref(); 116 | 117 | // Points to "images" 118 | Reference? imagesRef = storageRef.child("images"); 119 | 120 | // Points to "images/space.jpg" 121 | // Note that you can use variables to create child values 122 | final fileName = "space.jpg"; 123 | final spaceRef = imagesRef.child(fileName); 124 | 125 | // File path is "images/space.jpg" 126 | final path = spaceRef.fullPath; 127 | 128 | // File name is "space.jpg" 129 | final name = spaceRef.name; 130 | 131 | // Points to "images" 132 | imagesRef = spaceRef.parent; 133 | ``` 134 | 135 | Next, let's learn how to [upload files](upload-files) to Cloud Storage. 136 | -------------------------------------------------------------------------------- /docs/flutter/storage/delete-files.md: -------------------------------------------------------------------------------- 1 | Project: /docs/storage/_project.yaml 2 | Book: /docs/_book.yaml 3 | page_type: guide 4 | 5 | 6 | 7 | # Delete files with Cloud Storage on Flutter 8 | 9 | After uploading files to Cloud Storage, you can also delete them. 10 | 11 | Note: By default, a Cloud Storage bucket requires Firebase Authentication to 12 | perform any action on the bucket's data or files. You can 13 | [change your Firebase Security Rules for Cloud Storage](/docs/storage/security/rules-conditions#public) 14 | to allow unauthenticated access. Since Firebase and your project's default 15 | App Engine app share this bucket, configuring public access may make newly 16 | uploaded App Engine files publicly accessible, as well. Be sure to restrict 17 | access to your Cloud Storage bucket again when you set up Authentication. 18 | 19 | 20 | ## Delete a File 21 | 22 | To delete a file, first [create a reference](create-reference) 23 | to that file. Then call the `delete()` method on that reference. 24 | 25 | ```dart 26 | // Create a reference to the file to delete 27 | final desertRef = storageRef.child("images/desert.jpg"); 28 | 29 | // Delete the file 30 | await desertRef.delete(); 31 | ``` 32 | 33 | Warning: Deleting a file is a permanent action! If you care about restoring 34 | deleted files, make sure to back up your files, or 35 | [enable Object Versioning](https://cloud.google.com/storage/docs/using-object-versioning#set) 36 | on your Cloud Storage bucket. 37 | 38 | 39 | ## Handle Errors 40 | 41 | There are a number of reasons why errors may occur on file deletes, 42 | including the file not existing, or the user not having permission 43 | to delete the desired file. More information on errors can be found in the 44 | [Handle Errors](handle-errors) section of the docs. 45 | -------------------------------------------------------------------------------- /docs/flutter/storage/download-files.md: -------------------------------------------------------------------------------- 1 | Project: /docs/storage/_project.yaml 2 | Book: /docs/_book.yaml 3 | page_type: guide 4 | 5 | 6 | 7 | # Download files with Cloud Storage on Flutter 8 | 9 | Cloud Storage for Firebase allows you to quickly and easily download 10 | files from a [Cloud Storage](//cloud.google.com/storage) 11 | bucket provided and managed by Firebase. 12 | 13 | Note: By default, a Cloud Storage bucket requires Firebase Authentication to 14 | perform any action on the bucket's data or files. You can 15 | [change your Firebase Security Rules for Cloud Storage](/docs/storage/security/rules-conditions#public) 16 | to allow unauthenticated access. Since Firebase and your project's default 17 | App Engine app share this bucket, configuring public access may make newly 18 | uploaded App Engine files publicly accessible, as well. Be sure to restrict 19 | access to your Cloud Storage bucket again when you set up Authentication. 20 | 21 | 22 | 23 | ## Create a Reference 24 | 25 | To download a file, first [create a Cloud Storage reference](create-reference) 26 | to the file you want to download. 27 | 28 | You can create a reference by appending child paths to the root of your 29 | Cloud Storage bucket, or you can create a reference from an existing 30 | `gs://` or `https://` URL referencing an object in Cloud Storage. 31 | 32 | ```dart 33 | // Create a storage reference from our app 34 | final storageRef = FirebaseStorage.instance.ref(); 35 | 36 | // Create a reference with an initial file path and name 37 | final pathReference = storageRef.child("images/stars.jpg"); 38 | 39 | // Create a reference to a file from a Google Cloud Storage URI 40 | final gsReference = 41 | FirebaseStorage.instance.refFromURL("gs://YOUR_BUCKET/images/stars.jpg"); 42 | 43 | // Create a reference from an HTTPS URL 44 | // Note that in the URL, characters are URL escaped! 45 | final httpsReference = FirebaseStorage.instance.refFromURL( 46 | "https://firebasestorage.googleapis.com/b/YOUR_BUCKET/o/images%20stars.jpg"); 47 | ``` 48 | 49 | 50 | ## Download Files 51 | 52 | Once you have a reference, you can download files from Cloud Storage 53 | by calling the `getData()` or `getStream()`. If you prefer to download the file 54 | with another library, you can get a download URL with `getDownloadUrl()`. 55 | 56 | ### Download in memory 57 | 58 | Download the file to a `UInt8List` with the `getData()` method. This is the 59 | easiest way to download a file, but it must load the entire contents of 60 | your file into memory. If you request a file larger than your app's available 61 | memory, your app will crash. To protect against memory issues, `getData()` 62 | takes a maximum amount of bytes to download. Set the maximum size to something 63 | you know your app can handle, or use another download method. 64 | 65 | ```dart 66 | final islandRef = storageRef.child("images/island.jpg"); 67 | 68 | try { 69 | const oneMegabyte = 1024 * 1024; 70 | final Uint8List? data = await islandRef.getData(oneMegabyte); 71 | // Data for "images/island.jpg" is returned, use this as needed. 72 | } on FirebaseException catch (e) { 73 | // Handle any errors. 74 | } 75 | ``` 76 | 77 | ### Download to a local file 78 | 79 | The `writeToFile()` method downloads a file directly to a local device. Use this if 80 | your users want to have access to the file while offline or to share the file in a 81 | different app. `writeToFile()` returns a `DownloadTask` which you can use to manage 82 | your download and monitor the status of the download. 83 | 84 | ```dart 85 | final islandRef = storageRef.child("images/island.jpg"); 86 | 87 | final appDocDir = await getApplicationDocumentsDirectory(); 88 | final filePath = "${appDocDir.absolute}/images/island.jpg"; 89 | final file = File(filePath); 90 | 91 | final downloadTask = islandRef.writeToFile(file); 92 | downloadTask.snapshotEvents.listen((taskSnapshot) { 93 | switch (taskSnapshot.state) { 94 | case TaskState.running: 95 | // TODO: Handle this case. 96 | break; 97 | case TaskState.paused: 98 | // TODO: Handle this case. 99 | break; 100 | case TaskState.success: 101 | // TODO: Handle this case. 102 | break; 103 | case TaskState.canceled: 104 | // TODO: Handle this case. 105 | break; 106 | case TaskState.error: 107 | // TODO: Handle this case. 108 | break; 109 | } 110 | }); 111 | ``` 112 | 113 | ## Download Data via URL 114 | 115 | If you already have download infrastructure based around URLs, or just want 116 | a URL to share, you can get the download URL for a file by calling the 117 | `getDownloadURL()` method on a Cloud Storage reference. 118 | 119 | ```dart 120 | final imageUrl = 121 | await storageRef.child("users/me/profile.png").getDownloadURL(); 122 | ``` 123 | 124 | ## Handle Errors 125 | 126 | There are a number of reasons why errors may occur on download, including the 127 | file not existing, or the user not having permission to access the desired file. 128 | More information on errors can be found in the [Handle Errors](handle-errors) 129 | section of the docs. 130 | 131 | ## Full Example 132 | 133 | A full example of a download with error handling is shown below: 134 | 135 | ```dart 136 | final islandRef = storageRef.child("images/island.jpg"); 137 | 138 | final appDocDir = await getApplicationDocumentsDirectory(); 139 | final filePath = "${appDocDir.absolute}/images/island.jpg"; 140 | final file = File(filePath); 141 | 142 | final downloadTask = islandRef.writeToFile(file); 143 | downloadTask.snapshotEvents.listen((taskSnapshot) { 144 | switch (taskSnapshot.state) { 145 | case TaskState.running: 146 | // TODO: Handle this case. 147 | break; 148 | case TaskState.paused: 149 | // TODO: Handle this case. 150 | break; 151 | case TaskState.success: 152 | // TODO: Handle this case. 153 | break; 154 | case TaskState.canceled: 155 | // TODO: Handle this case. 156 | break; 157 | case TaskState.error: 158 | // TODO: Handle this case. 159 | break; 160 | } 161 | }); 162 | ``` 163 | 164 | You can also [get and update metadata](file-metadata) for files that are stored 165 | in Cloud Storage. 166 | -------------------------------------------------------------------------------- /docs/flutter/storage/file-metadata.md: -------------------------------------------------------------------------------- 1 | Project: /docs/storage/_project.yaml 2 | Book: /docs/_book.yaml 3 | page_type: guide 4 | 5 | 6 | 7 | # Use file metadata with Cloud Storage on Flutter 8 | 9 | After uploading a file to Cloud Storage reference, you can also get 10 | and update the file metadata, for example to view or update the content type. 11 | Files can also store custom key/value pairs with additional file metadata. 12 | 13 | Note: By default, a Cloud Storage bucket requires Firebase Authentication to 14 | perform any action on the bucket's data or files. You can 15 | [change your Firebase Security Rules for Cloud Storage](/docs/storage/security/rules-conditions#public) 16 | to allow unauthenticated access. Since Firebase and your project's default 17 | App Engine app share this bucket, configuring public access may make newly 18 | uploaded App Engine files publicly accessible, as well. Be sure to restrict 19 | access to your Cloud Storage bucket again when you set up Authentication. 20 | 21 | 22 | 23 | ## Get File Metadata 24 | 25 | File metadata contains common properties such as `name`, `size`, and 26 | `contentType` (often referred to as MIME type) in addition to some less 27 | common ones like `contentDisposition` and `timeCreated`. This metadata can be 28 | retrieved from a Cloud Storage reference using 29 | the `getMetadata()` method. 30 | 31 | ```dart 32 | // Create reference to the file whose metadata we want to retrieve 33 | final forestRef = storageRef.child("images/forest.jpg"); 34 | 35 | // Get metadata properties 36 | final metadata = await forestRef.getMetadata(); 37 | 38 | // Metadata now contains the metadata for 'images/forest.jpg' 39 | ``` 40 | 41 | 42 | ## Update File Metadata 43 | 44 | You can update file metadata at any time after the file upload completes by 45 | using the `updateMetadata()` method. Refer to the 46 | [full list](#file-metadata-properties) for more information on what properties 47 | can be updated. Only the properties specified in the metadata are updated, 48 | all others are left unmodified. 49 | 50 | ```dart 51 | // Create reference to the file whose metadata we want to change 52 | final forestRef = storageRef.child("images/forest.jpg"); 53 | 54 | // Create file metadata to update 55 | final newMetadata = SettableMetadata( 56 | cacheControl: "public,max-age=300", 57 | contentType: "image/jpeg", 58 | ); 59 | 60 | // Update metadata properties 61 | final metadata = await forestRef.updateMetadata(newMetadata); 62 | 63 | // Updated metadata for 'images/forest.jpg' is returned 64 | ``` 65 | 66 | You can delete writable metadata properties by passing `null`: 67 | 68 | ```dart 69 | // Delete the cacheControl property 70 | final newMetadata = SettableMetadata(cacheControl: null); 71 | final metadata = await forestRef.updateMetadata(newMetadata); 72 | ``` 73 | 74 | 75 | ## Handle Errors 76 | 77 | There are a number of reasons why errors may occur on getting or updating 78 | metadata, including the file not existing, or the user not having permission 79 | to access the desired file. More information on errors can be found in the 80 | [Handle Errors](handle-errors) section of the docs. 81 | 82 | ## Custom Metadata 83 | 84 | You can specify custom metadata using the `customMetadata` parameter of the 85 | `SettableMetadata` constructor: 86 | 87 | ```dart 88 | // Create reference to the file whose metadata we want to change 89 | final forestRef = storageRef.child("images/forest.jpg"); 90 | 91 | // Create file metadata to update 92 | final newCustomMetadata = SettableMetadata( 93 | customMetadata: { 94 | "location": "Yosemite, CA, USA", 95 | "activity": "Hiking", 96 | }, 97 | ); 98 | 99 | // Update metadata properties 100 | final metadata = await forestRef.updateMetadata(newCustomMetadata); 101 | 102 | // Updated metadata for 'images/forest.jpg' is returned 103 | ``` 104 | 105 | You can store app-specific data for each file in custom metadata, but we highly 106 | recommend using a database (such as the 107 | [Firebase Realtime Database](/docs/database/overview)) to store and synchronize this type of 108 | data. 109 | 110 | 111 | ## File Metadata Properties {:#file-metadata-properties} 112 | 113 | A full list of metadata properties on a file is available below: 114 | 115 | Property | Type | Settable? 116 | ---------------------|-----------------------|---------- 117 | `bucket` | `String` | No 118 | `generation` | `String` | No 119 | `metageneration` | `String` | No 120 | `metadataGeneration` | `String` | No 121 | `fullPath` | `String` | No 122 | `name` | `String` | No 123 | `size` | `int` | No 124 | `timeCreated` | `DateTime` | No 125 | `updated` | `DateTime` | No 126 | `md5Hash` | `String` | No 127 | `cacheControl` | `String` | Yes 128 | `contentDisposition` | `String` | Yes 129 | `contentEncoding` | `String` | Yes 130 | `contentLanguage` | `String` | Yes 131 | `contentType` | `String` | Yes 132 | `customMetadata` | `Map` | Yes 133 | 134 | Uploading, downloading, and updating files is important, but so is being able 135 | to remove them. Let's learn how to [delete files](delete-files) 136 | from Cloud Storage. 137 | -------------------------------------------------------------------------------- /docs/flutter/storage/handle-errors.md: -------------------------------------------------------------------------------- 1 | Project: /docs/storage/_project.yaml 2 | Book: /docs/_book.yaml 3 | page_type: guide 4 | 5 | 6 | 7 | # Handle errors for Cloud Storage on Flutter 8 | 9 | Sometimes when you're building an app, things don't go as planned and an 10 | error occurs! 11 | 12 | When in doubt, catch the exception thrown by the function 13 | and see what the error message has to say. 14 | 15 | ```dart 16 | final storageRef = FirebaseStorage.instance.ref().child("files/uid"); 17 | try { 18 | final listResult = await storageRef.listAll(); 19 | } on FirebaseException catch (e) { 20 | // Caught an exception from Firebase. 21 | print("Failed with error '${e.code}': ${e.message}"); 22 | } 23 | ``` 24 | 25 | Note: By default, a Cloud Storage bucket requires Firebase Authentication to 26 | perform any action on the bucket's data or files. You can 27 | [change your Firebase Security Rules for Cloud Storage](/docs/storage/security/rules-conditions#public) 28 | to allow unauthenticated access. Since Firebase and your project's default 29 | App Engine app share this bucket, configuring public access may make newly 30 | uploaded App Engine files publicly accessible, as well. Be sure to restrict 31 | access to your Cloud Storage bucket again when you set up Authentication. 32 | 33 | 34 | ## Handle Error Messages 35 | 36 | There are a number of reasons why errors may occur, including the file 37 | not existing, the user not having permission to access the desired file, or the 38 | user cancelling the file upload. 39 | 40 | To properly diagnose the issue and handle the error, here is a full list of 41 | all the errors our client will raise, and how they occurred. 42 | 43 | Code | Description 44 | ---------------------------------|-------------------------------------------- 45 | `storage/unknown` | An unknown error occurred. 46 | `storage/object-not-found` | No object exists at the desired reference. 47 | `storage/bucket-not-found` | No bucket is configured for Cloud Storage 48 | `storage/project-not-found` | No project is configured for Cloud Storage 49 | `storage/quota-exceeded` | Quota on your Cloud Storage bucket has been exceeded. If you're on the no-cost tier, upgrade to a paid plan. If you're on a paid plan, reach out to Firebase support. 50 | `storage/unauthenticated` | User is unauthenticated, please authenticate and try again. 51 | `storage/unauthorized` | User is not authorized to perform the desired action, check your security rules to ensure they are correct. 52 | `storage/retry-limit-exceeded` | The maximum time limit on an operation (upload, download, delete, etc.) has been excceded. Try uploading again. 53 | `storage/invalid-checksum` | File on the client does not match the checksum of the file received by the server. Try uploading again. 54 | `storage/canceled` | User canceled the operation. 55 | `storage/invalid-event-name` | Invalid event name provided. Must be one of [`running`, `progress`, `pause`] 56 | `storage/invalid-url` | Invalid URL provided to `refFromURL()`. Must be of the form: `gs://bucket/object` or `https://firebasestorage.googleapis.com/v0/b/bucket/o/object?token=` 57 | `storage/invalid-argument` | The argument passed to `put()` must be `File`, `Blob`, or `UInt8` Array. The argument passed to `putString()` must be a raw, `Base64`, or `Base64URL` string. 58 | `storage/no-default-bucket` | No bucket has been set in your config's `storageBucket` property. 59 | `storage/cannot-slice-blob` | Commonly occurs when the local file has changed (deleted, saved again, etc.). Try uploading again after verifying that the file hasn't changed. 60 | `storage/server-file-wrong-size` | File on the client does not match the size of the file received by the server. Try uploading again. 61 | -------------------------------------------------------------------------------- /docs/flutter/storage/list-files.md: -------------------------------------------------------------------------------- 1 | Project: /docs/storage/_project.yaml 2 | Book: /docs/_book.yaml 3 | page_type: guide 4 | 5 | 6 | 7 | # List files with Cloud Storage on Flutter 8 | 9 | Cloud Storage for Firebase allows you to list the contents of your 10 | Cloud Storage bucket. The SDKs return both the items and the prefixes of 11 | objects under the current Cloud Storage reference. 12 | 13 | Projects that use the List API require Cloud Storage for Firebase 14 | Rules version 2. If you have an existing Firebase project, follow the steps in 15 | the [Security Rules Guide](/docs/storage/security/core-syntax). 16 | 17 | Note: The List API is only allowed for Rules version 2. 18 | In Rules version 2, `allow read` is the shorthand for `allow get, list`. 19 | 20 | 21 | `list()` uses the 22 | [Google Cloud Storage List API](//cloud.google.com/storage/docs/json_api/v1/objects/list). 23 | In Cloud Storage for Firebase, we use `/` as a delimiter, which allows us to 24 | emulate file system semantics. To allow for efficient traversal of large, 25 | hierarchical Cloud Storage buckets, the List API returns prefixes and 26 | items separately. For example, if you upload one file `/images/uid/file1`, 27 | 28 | * `root.child('images').listAll()` will return `/images/uid` as a prefix. 29 | * `root.child('images/uid').listAll()` will return the file as an item. 30 | 31 | The Cloud Storage for Firebase SDK does not return object paths that contain two 32 | consecutive `/`s or end with a `/`. For example, consider a bucket that has the 33 | following objects: 34 | 35 | * `correctPrefix/happyItem` 36 | * `wrongPrefix//sadItem` 37 | * `lonelyItem/` 38 | 39 | The list operations on items in this bucket will give the following results: 40 | 41 | * The list operation at the root returns the references to `correctPrefix`, 42 | `wrongPrefix` and `lonelyItem` as `prefixes`. 43 | * The list operation at the `correctPrefix/` returns the references to 44 | `correctPrefix/happyItem` as `items`. 45 | * The list operation at the `wrongPrefix/` doesn't return any references 46 | because `wrongPrefix//sadItem` contains two consecutive `/`s. 47 | * The list operation at the `lonelyItem/` doesn't return any references 48 | because the object `lonelyItem/` ends with `/`. 49 | 50 | ## List all files 51 | 52 | You can use `listAll` to fetch all results for a directory. 53 | This is best used for small directories as all results are buffered in memory. 54 | The operation also may not return a consistent snapshot if objects are added or 55 | removed during the process. 56 | 57 | For a large list, use the paginated `list()` method as `listAll()` buffers all 58 | results in memory. 59 | 60 | The following example demonstrates `listAll`. 61 | 62 | ```dart 63 | final storageRef = FirebaseStorage.instance.ref().child("files/uid"); 64 | final listResult = await storageRef.listAll(); 65 | for (var prefix in listResult.prefixes) { 66 | // The prefixes under storageRef. 67 | // You can call listAll() recursively on them. 68 | } 69 | for (var item in listResult.items) { 70 | // The items under storageRef. 71 | } 72 | ``` 73 | 74 | ## Paginate list results 75 | 76 | The `list()` API places a limit on the number of results it returns. `list()` 77 | provides a consistent pageview and exposes a pageToken that allows control over 78 | when to fetch additional results. 79 | 80 | The pageToken encodes the path and version of the last item returned in the 81 | previous result. In a subsequent request using the pageToken, items that come 82 | after the pageToken are shown. 83 | 84 | The following example demonstrates paginating a result: 85 | 86 | ```dart 87 | Stream listAllPaginated(Reference storageRef) async* { 88 | String? pageToken; 89 | do { 90 | final listResult = await storageRef.list(ListOptions( 91 | maxResults: 100, 92 | pageToken: pageToken, 93 | )); 94 | yield listResult; 95 | pageToken = listResult.nextPageToken; 96 | } while (pageToken != null); 97 | } 98 | ``` 99 | 100 | ## Handle errors 101 | 102 | `list()` and `listAll()` fail if you haven't upgraded 103 | the Security Rules to version 2. Upgrade your Security Rules if you see this 104 | error: 105 | 106 | ``` 107 | Listing objects in a bucket is disallowed for rules_version = "1". 108 | Please update storage security rules to rules_version = "2" to use list. 109 | ``` 110 | 111 | Other possible errors may indicate the user does not have the right permission. 112 | More information on errors can be found in the [Handle Errors](handle-errors) 113 | page. 114 | -------------------------------------------------------------------------------- /docs/flutter/storage/start.md: -------------------------------------------------------------------------------- 1 | Project: /docs/storage/_project.yaml 2 | Book: /docs/_book.yaml 3 | page_type: guide 4 | 5 | 6 | 7 | # Get started with Cloud Storage on Flutter 8 | 9 | Cloud Storage for Firebase lets you upload and share user generated content, such 10 | as images and video, which allows you to build rich media content into your 11 | apps. Your data is stored in a 12 | [Google Cloud Storage](//cloud.google.com/storage) bucket — an 13 | exabyte scale object storage solution with high availability and global 14 | redundancy. Cloud Storage for Firebase lets you securely upload these files 15 | directly from mobile devices and web browsers, handling spotty networks with 16 | ease. 17 | 18 | 19 | ## Prerequisites 20 | 21 | [Install and initialize the Firebase SDKs for Flutter](/docs/flutter/setup) if you 22 | haven't already done so. 23 | 24 | 25 | ## Create a default Cloud Storage bucket {:#create-default-bucket} 26 | 27 | 1. From the navigation pane of the [Firebase console](https://console.firebase.google.com/), select **Storage**, 28 | then click **Get started**. 29 | 30 | 1. Review the messaging about securing your Cloud Storage data using security 31 | rules. During development, consider 32 | [setting up your rules for public access](#set_up_public_access). 33 | 34 | 1. Select a [location](/docs/projects/locations#types) for your default 35 | Cloud Storage bucket. 36 | 37 | * This location setting is your project's 38 | [_default Google Cloud Platform (GCP) resource location_](/docs/firestore/locations#default-cloud-location). 39 | Note that this location will be used for GCP services in your project 40 | that require a location setting, specifically, your 41 | [Cloud Firestore](/docs/firestore) database and your 42 | [App Engine](//cloud.google.com/appengine/docs/) app 43 | (which is required if you use Cloud Scheduler). 44 | 45 | * If you aren't able to select a location, then your project already 46 | has a default GCP resource location. It was set either during project 47 | creation or when setting up another service that requires a location 48 | setting. 49 | 50 | If you're on the Blaze plan, you can 51 | [create multiple buckets](#use_multiple_storage_buckets), each with its own 52 | [location](//cloud.google.com/storage/docs/bucket-locations). 53 | 54 | Note: After you set your project's default GCP resource location, you 55 | cannot change it. 56 | 57 | 1. Click **Done**. 58 | 59 | 60 | ## Set up public access {:#set_up_public_access} 61 | 62 | Cloud Storage for Firebase provides a declarative rules language that allows you 63 | to define how your data should be structured, how it should be indexed, and when 64 | your data can be read from and written to. By default, read and write access to 65 | Cloud Storage is restricted so only authenticated users can read or write 66 | data. To get started without setting up [Firebase Authentication](/docs/auth), you can 67 | [configure your rules for public access](/docs/storage/security/rules-conditions#public). 68 | 69 | This does make Cloud Storage open to anyone, even people not using your 70 | app, so be sure to restrict your Cloud Storage again when you set up 71 | authentication. 72 | 73 | 74 | ## Add the Cloud Storage SDK to your app {:#add-sdk} 75 | 76 | 1. From the root of your Flutter project, run the following command to install 77 | the plugin: 78 | 79 | ```bash 80 | flutter pub add firebase_storage 81 | ``` 82 | 83 | 1. Once complete, rebuild your Flutter application: 84 | 85 | ```bash 86 | flutter run 87 | ``` 88 | 89 | 1. Import the plugin in your Dart code: 90 | 91 | ```dart 92 | import 'package:firebase_storage/firebase_storage.dart'; 93 | ``` 94 | 95 | 96 | ## Set up Cloud Storage {:#set-up-cloud-storage} 97 | 98 | The first step in accessing your Cloud Storage bucket is to create an 99 | instance of `FirebaseStorage`: 100 | 101 | ```dart 102 | final storage = FirebaseStorage.instance; 103 | ``` 104 | 105 | You're ready to start using Cloud Storage! 106 | 107 | First, let's learn how to [create a Cloud Storage reference](create-reference). 108 | 109 | ## Advanced setup 110 | 111 | There are a few use cases that require additional setup: 112 | 113 | - Using Cloud Storage buckets in 114 | [multiple geographic regions](//cloud.google.com/storage/docs/bucket-locations) 115 | - Using Cloud Storage buckets in 116 | [different storage classes](//cloud.google.com/storage/docs/storage-classes) 117 | - Using Cloud Storage buckets with multiple authenticated users in the same app 118 | 119 | The first use case is perfect if you have users across the world, and want to 120 | store their data near them. For instance, you can create buckets in the US, 121 | Europe, and Asia to store data for users in those regions to reduce latency. 122 | 123 | The second use case is helpful if you have data with different access patterns. 124 | For instance: you can set up a multi-regional or regional bucket that stores 125 | pictures or other frequently accessed content, and a nearline or coldline bucket 126 | that stores user backups or other infrequently accessed content. 127 | 128 | In either of these use cases, you'll want to 129 | [use multiple Cloud Storage buckets](#use_multiple_storage_buckets). 130 | 131 | The third use case is useful if you're building an app, like Google Drive, which 132 | lets users have multiple logged in accounts (for instance, a personal account 133 | and a work account). You can 134 | [use a custom Firebase App](#use_a_custom_firebaseapp) 135 | instance to authenticate each additional account. 136 | 137 | ### Use multiple Cloud Storage buckets {:#use_multiple_storage_buckets} 138 | 139 | If you want to use a Cloud Storage bucket other than the default provided above, 140 | or use multiple Cloud Storage buckets in a single app, you can create an instance 141 | of `FirebaseStorage` that references your custom bucket: 142 | 143 | ```dart 144 | // Get a non-default Storage bucket 145 | final storage = FirebaseStorage.instanceFor(bucket: "gs://my-custom-bucket"); 146 | ``` 147 | 148 | ### Working with imported buckets 149 | 150 | When importing an existing Cloud Storage bucket into Firebase, you'll 151 | have to grant Firebase the ability to access these files using the 152 | `gsutil` tool, included in the 153 | [Google Cloud SDK](//cloud.google.com/sdk/docs/): 154 | 155 | ```bash 156 | gsutil -m acl ch -r -u service-PROJECT_NUMBER@gcp-sa-firebasestorage.iam.gserviceaccount.com gs://YOUR-CLOUD-STORAGE-BUCKET 157 | ``` 158 | 159 | You can find your project number as described in the [introduction to 160 | Firebase projects](/docs/projects/learn-more#project-number). 161 | 162 | This does not affect newly created buckets, as those have the default access 163 | control set to allow Firebase. This is a temporary measure, and will be 164 | performed automatically in the future. 165 | 166 | ### Use a custom Firebase App {:#use_a_custom_firebaseapp} 167 | 168 | If you're building a more complicated app using a custom `FirebaseApp`, you can 169 | create an instance of `FirebaseStorage` initialized with that app: 170 | 171 | ```dart 172 | // Use a non-default App 173 | final storage = FirebaseStorage.instanceFor(app: customApp); 174 | ``` 175 | 176 | 177 | ## Next steps 178 | 179 | * Prepare to launch your app: 180 | * Enable [App Check](/docs/app-check/overview) to help ensure that only 181 | your apps can access your storage buckets. 182 | * Set up [budget alerts](/docs/projects/billing/avoid-surprise-bills#set-up-budget-alert-emails) 183 | for your project in the Google Cloud Console. 184 | * Monitor the [_Usage and billing_ dashboard](//console.firebase.google.com/project/_/usage) 185 | in the Firebase console to get an overall picture of your project's 186 | usage across multiple Firebase services. You can also visit the 187 | [Cloud Storage _Usage_ dashboard](//console.firebase.google.com/project/_/storage/usage) for more 188 | detailed usage information. 189 | * Review the [Firebase launch checklist](/support/guides/launch-checklist). 190 | -------------------------------------------------------------------------------- /docs/flutter/test-lab/integration-testing-with-flutter.md: -------------------------------------------------------------------------------- 1 | Project: /docs/test-lab/_project.yaml 2 | Book: /docs/_book.yaml 3 | page_type: guide 4 | 5 | {% include "_shared/apis/console/_local_variables.html" %} 6 | {% include "_local_variables.html" %} 7 | {% include "docs/test-lab/_local_variables.html" %} 8 | {% include "docs/ios/_local_variables.html" %} 9 | {% include "_shared/firebase/_snippet_include_comment.html" %} 10 | 11 | 12 | 13 | 14 | # Integration Testing with Flutter 15 | 16 | To test Flutter apps with {{firebase_testlab}}, you can write Flutter 17 | integration tests, build Android APKs or iOS test zip files, and run as 18 | regular Android instrumentation tests or iOS XCTests. 19 | 20 | 21 | # Flutter integration test types {: #test-types} 22 | 23 | Flutter supports three types of tests: unit tests, widget tests, and 24 | integration tests. A *unit test* verifies the behavior of a method or class. 25 | A *widget test* verifies the behavior of Flutter widgets without running the 26 | app itself. An *integration test*, also called end-to-end testing or GUI 27 | testing, runs the full app. 28 | 29 | To learn more about integration tests, see [Flutter integration testing](https://docs.flutter.dev/testing/integration-tests){:.external}. 30 | 31 | 32 | # Write Flutter integration tests {: #write-flutter-integration-tests} 33 | 34 | To learn how to write integration tests, see the [project setup](https://docs.flutter.dev/testing/integration-tests#project-setup){:.external} 35 | section of the Flutter integration tests documentation. Optionally, you can 36 | follow [running using Flutter command](https://docs.flutter.dev/testing/integration-tests#running-using-the-flutter-command){:.external} 37 | to run and verify the tests locally. 38 | 39 | 40 | # Test on {{testlab}} {: #test-on-test-lab} 41 | 42 | You can use {{testlab}} with both Android and iOS targets. 43 | 44 | 45 | ## Android setup {: #android-setup} 46 | 47 | Follow the instructions in the 48 | [Android Device Testing](https://github.com/flutter/flutter/tree/main/packages/integration_test#android-device-testing){:.external} 49 | section of the README. 50 | 51 | 52 | ## iOS setup {: #ios-setup} 53 | 54 | Follow the instructions in the 55 | [iOS Device Testing](https://github.com/flutter/flutter/tree/main/packages/integration_test#ios-device-testing){:.external} 56 | section of the README. 57 | 58 | ## Robo test support {: #robo-test-support} 59 | 60 | [Robo tests](/docs/test-lab/android/robo-ux-test) do not natively support 61 | Flutter. To improve crawling of your app, use [Robo scripts](/docs/test-lab/android/run-robo-scripts), 62 | which are tests that automate manual QA tasks for mobile apps, and enable 63 | continuous integration (CI) and pre-launch testing strategies. 64 | For example, to control Robo behavior in a more precise and robust way, you can 65 | use clicks with [visionText](/docs/test-lab/android/robo-scripts-reference#click). 66 | 67 | 68 | # Analyze test results {: #analyze-test-results} 69 | 70 | You can run Flutter integration tests as an Android instrumentation test or an 71 | iOS XCTest. To analyze the result of an integration test, see the 72 | documentation for [Android](https://firebase.google.com/docs/test-lab/android/analyzing-results){:.external} 73 | and [iOS](https://firebase.google.com/docs/test-lab/ios/analyzing-results){:.external}, 74 | depending on your platform. 75 | 76 | 77 | ## Limitations {: #limitations} 78 | 79 | Test timing information for individual test cases is not available, which means 80 | that features like test case duration and videos for individual test cases 81 | don't work as expected. 82 | 83 | ## Troubleshooting {: #troubleshooting} 84 | 85 | If you encounter issues, check the [public issue tracker for integration tests](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3Aintegration_test){:.external}. 86 | 87 | If you encounter a new issue caused by the integration test framework, 88 | file a new issue in the public issue tracker following the guidance in 89 | [Creating useful bug reports](https://docs.flutter.dev/resources/bug-reports){:.external}. 90 | --------------------------------------------------------------------------------