├── .fvmrc ├── .gitignore ├── .metadata ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── example ├── .fvmrc ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ ├── google-services.json │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── phone_auth_handler_demo │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ └── contents.xcworkspacedata │ ├── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ │ └── LaunchImage.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ └── README.md │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── GoogleService-Info.plist │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h │ ├── RunnerTests │ │ └── RunnerTests.swift │ └── firebase_app_id_file.json ├── lib │ ├── firebase_options.dart │ ├── main.dart │ ├── screens │ │ ├── authentication_screen.dart │ │ ├── home_screen.dart │ │ ├── splash_screen.dart │ │ └── verify_phone_number_screen.dart │ ├── utils │ │ ├── app_theme.dart │ │ ├── globals.dart │ │ ├── helpers.dart │ │ └── route_generator.dart │ └── widgets │ │ ├── custom_loader.dart │ │ └── pin_input_field.dart ├── pubspec.lock ├── pubspec.yaml └── web │ ├── favicon.png │ ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png │ ├── index.html │ └── manifest.json ├── lib ├── firebase_phone_auth_handler.dart └── src │ ├── auth_controller.dart │ ├── auth_handler.dart │ ├── auth_provider.dart │ └── type_definitions.dart ├── pubspec.lock └── pubspec.yaml /.fvmrc: -------------------------------------------------------------------------------- 1 | { 2 | "flutter": "3.24.3" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | build/ 32 | 33 | # Android related 34 | **/android/**/gradle-wrapper.jar 35 | **/android/.gradle 36 | **/android/captures/ 37 | **/android/gradlew 38 | **/android/gradlew.bat 39 | **/android/local.properties 40 | **/android/**/GeneratedPluginRegistrant.java 41 | 42 | # iOS/XCode related 43 | **/ios/**/*.mode1v3 44 | **/ios/**/*.mode2v3 45 | **/ios/**/*.moved-aside 46 | **/ios/**/*.pbxuser 47 | **/ios/**/*.perspectivev3 48 | **/ios/**/*sync/ 49 | **/ios/**/.sconsign.dblite 50 | **/ios/**/.tags* 51 | **/ios/**/.vagrant/ 52 | **/ios/**/DerivedData/ 53 | **/ios/**/Icon? 54 | **/ios/**/Pods/ 55 | **/ios/**/.symlinks/ 56 | **/ios/**/profile 57 | **/ios/**/xcuserdata 58 | **/ios/.generated/ 59 | **/ios/Flutter/App.framework 60 | **/ios/Flutter/Flutter.framework 61 | **/ios/Flutter/Flutter.podspec 62 | **/ios/Flutter/Generated.xcconfig 63 | **/ios/Flutter/app.flx 64 | **/ios/Flutter/app.zip 65 | **/ios/Flutter/flutter_assets/ 66 | **/ios/Flutter/flutter_export_environment.sh 67 | **/ios/ServiceDefinitions.json 68 | **/ios/Runner/GeneratedPluginRegistrant.* 69 | 70 | # Exceptions to above rules. 71 | !**/ios/**/default.mode1v3 72 | !**/ios/**/default.mode2v3 73 | !**/ios/**/default.pbxuser 74 | !**/ios/**/default.perspectivev3 75 | 76 | # FVM Version Cache 77 | .fvm/ -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: b1395592de68cc8ac4522094ae59956dd21a91db 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.colorCustomizations": { 3 | "activityBar.background": "#312948", 4 | "titleBar.activeBackground": "#453964", 5 | "titleBar.activeForeground": "#FBFAFC" 6 | }, 7 | "dart.lineLength": 80, 8 | "dart.flutterSdkPath": ".fvm/versions/3.24.3" 9 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [1.1.0+1] 2 | 3 | * Fix dart static analysis warnings 4 | 5 | # [1.1.0] 6 | 7 | * Updated README.md 8 | * Updated dependencies 9 | * Updated example app 10 | 11 | # [1.0.8] 12 | 13 | * Added sendOtpOnInitialize parameter to the handler 14 | * Fixed OTP resend issue if the OTP expiration timer is still active 15 | * Fixed #15 16 | * Added shouldAwaitCodeSend to the sendOTP fn to give more control over the function 17 | * Updated dependencies 18 | 19 | # [1.0.7] 20 | 21 | * Stacktrace in onLoginFailed is now non-nullable 22 | * Updated a dependency to the latest release 23 | * Updated example app 24 | 25 | # [1.0.6] 26 | 27 | * **BREAKING:** Added stack trace to onLoginFailed and onError callbacks 28 | * Added a boolean linkWithExistingUser to link the new credentials with an existing signed-in user, instead of creating a new one. 29 | * Added onError callback for general purpose errors by the library 30 | * Updated example app 31 | * Updated dependencies 32 | 33 | # [1.0.5+1] 34 | 35 | * Fixed files formatting 36 | * Updated example app 37 | 38 | # [1.0.5] 39 | 40 | * **BREAKING:** Renamed flag timeOutDuration to autoRetrievalTimeOutDuration 41 | * **BREAKING:** Renamed verifyOTP to verifyOtp 42 | * **BREAKING:** Updated verifyOtp function signature to not take a named argument, and accept otp as a positional argument 43 | * Added a new otpExpirationDuration flag, as autoRetrievalTimeOutDuration is a completely different parameter. 44 | * Added callback onCodeSent and flag signOutOnSuccessfulVerification 45 | * Added isSendingCode flag to controller 46 | * Optimized code to reduce number of rebuilds 47 | * Updated example app 48 | * Updated dependencies 49 | * Refactored code 50 | 51 | # [1.0.4] 52 | 53 | * Updated example app 54 | * Updated dependencies 55 | * Fixed linter warnings 56 | 57 | # [1.0.3] 58 | 59 | * Updated example app 60 | * Updated README.md 61 | 62 | # [1.0.2] 63 | 64 | * Renamed auth_service to auth_controller 65 | * Updated dependencies 66 | * Minor bug fixes 67 | * Updated example app 68 | * Updated README.md 69 | 70 | # [1.0.1] 71 | 72 | * Updated license 73 | * Updated README.md 74 | 75 | # [1.0.0] 76 | 77 | * Added linter and updated code accordingly 78 | * Updated example app 79 | * Updated dependencies 80 | * Updated README.md 81 | 82 | # [0.0.8] 83 | 84 | * Updated dependencies 85 | * Updated example app 86 | 87 | # [0.0.7] 88 | 89 | * Renamed FirebasePhoneAuthSupporter to FirebasePhoneAuthProvider. 90 | * Supports sending OTP on web out of the box. 91 | * Updated dependencies 92 | * Updated README.md 93 | * Updated package description. 94 | 95 | # [0.0.6] 96 | 97 | * Added key parameter 98 | * Fixed README.md 99 | * Updated dependencies 100 | 101 | # [0.0.5] 102 | 103 | * Added boolean in onLoginSuccess to provide info whether OTP was auto fetched or verified manually 104 | * Updated example app 105 | * Updated README.md 106 | 107 | # [0.0.4] 108 | 109 | * Updated example app 110 | * Updated README.md 111 | 112 | # [0.0.3] 113 | 114 | * Updated screenshots 115 | * Updated package description 116 | 117 | # [0.0.2] 118 | 119 | * Added FirebasePhoneAuthSupporter which has to be wrapped above the MaterialApp in order for the app to support phone authentication. 120 | * Fixed sign out function 121 | * Updated README.md 122 | 123 | # [0.0.1] 124 | 125 | * An easy to use firebase phone authentication library to easily send and verify OTP's. 126 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Rithik Bhandari 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [FirebasePhoneAuthHandler](https://pub.dev/packages/firebase_phone_auth_handler) For Flutter 2 | [![pub package](https://img.shields.io/pub/v/firebase_phone_auth_handler.svg)](https://pub.dev/packages/firebase_phone_auth_handler) 3 | [![likes](https://img.shields.io/pub/likes/firebase_phone_auth_handler)](https://pub.dev/packages/firebase_phone_auth_handler/score) 4 | [![popularity](https://img.shields.io/pub/popularity/firebase_phone_auth_handler)](https://pub.dev/packages/firebase_phone_auth_handler/score) 5 | [![pub points](https://img.shields.io/pub/points/firebase_phone_auth_handler)](https://pub.dev/packages/firebase_phone_auth_handler/score) 6 | [![code size](https://img.shields.io/github/languages/code-size/rithik-dev/firebase_phone_auth_handler)](https://github.com/rithik-dev/firebase_phone_auth_handler) 7 | [![license MIT](https://img.shields.io/badge/license-MIT-purple.svg)](https://opensource.org/licenses/MIT) 8 | 9 | --- 10 | 11 | An easy-to-use firebase phone authentication package to easily send and verify OTP's with auto-fetch OTP support via SMS. Supports OTP on web out of the box. 12 | 13 | --- 14 | 15 | # 🗂️ Table of Contents 16 | 17 | - **[📷 Screenshots](#-screenshots)** 18 | - **[✨ Features](#-features)** 19 | - **[🚀 Getting Started](#-getting-started)** 20 | - **[🛠️ Platform-specific Setup](#%EF%B8%8F-platform-specific-setup)** 21 | - [Web](#web-recaptcha) 22 | - **[❓ Usage](#-usage)** 23 | - **[🎯 Sample Usage](#-sample-usage)** 24 | - **[👤 Collaborators](#-collaborators)** 25 | 26 | --- 27 | 28 | # 📷 Screenshots 29 | 30 | | Demo | Sending OTP | Auto Fetch OTP | 31 | |-----------------------------------|-------------------------------------|-------------------------------------| 32 | | | | | 33 | 34 | --- 35 | 36 | # ✨ Features 37 | 38 | - **Simple OTP Verification Process:** This package simplifies phone number authentication with Firebase, automatically managing OTP request and verification for you. 39 | - **SMS Autofill Support:** Automatically fetches and enters the received OTP from the SMS, streamlining the user experience on Android. 40 | - **Easy-to-use Callbacks:** You can define custom callbacks like `onLoginSuccess`, `onLoginFailed` etc., making the widget simple to use. 41 | - **Configurable Resend OTP Timer:** You can easily configure the time interval for OTP resend requests, ensuring users don’t spam the request button. 42 | - **Cross-Platform Support:** It provides full support for Android, iOS and Web, ensuring a consistent experience across platforms. 43 | - **Widget-Based Approach:** The package integrates well with Flutter’s UI-driven architecture, offering a widget-based solution for handling phone authentication. 44 | - **Seamless Integration:** The package can be easily integrated into any Flutter app, allowing quick and reliable phone authentication with Firebase. 45 | 46 | --- 47 | 48 | # 🚀 Getting Started 49 | 50 | ## Step 1: Create Firebase Project 51 | Create a Firebase project. Learn more about Firebase projects [**here**](https://firebase.google.com/docs/projects/learn-more). 52 | 53 | ## Step 2: Register your apps and configure Firebase 54 | Add your Android, iOS, Web apps to your Firebase project and configure the Firebase the apps by following the setup instructions for [Android](https://firebase.google.com/docs/flutter/setup?platform=android), [iOS](https://firebase.google.com/docs/flutter/setup?platform=ios) and [Web](https://firebase.google.com/docs/flutter/setup?platform=web) separately. 55 | 56 | > [!IMPORTANT] 57 | > Follow additional configration steps for Firebase Auth [here](https://firebase.google.com/docs/auth/flutter/start) 58 | 59 | ## Step 3: Enable Phone Authentication 60 | Open the Firebase Console, go to the **Authentication** section in your project. 61 | Select **Sign-in method** and enable **Phone**. 62 | 63 | ## Step 4: Enable Google Play Integrity API (Android Only) 64 | For Android, enable the [`Google Play Integrity API`](https://console.cloud.google.com/apis/library/playintegrity.googleapis.com) from Google Cloud Platform. 65 | 66 | ## Step 5: Add firebase_core dependency 67 | Add [`firebase_core`](https://pub.dev/packages/firebase_core) as a dependency in your pubspec.yaml file. 68 | ```yaml 69 | dependencies: 70 | flutter: 71 | sdk: flutter 72 | 73 | firebase_core: 74 | ``` 75 | 76 | ## Step 6: Initialize Firebase 77 | Call `Firebase.initializeApp()` in the `main()` method as shown to intialize Firebase in your project. 78 | 79 | ```dart 80 | import 'package:firebase_core/firebase_core.dart'; 81 | 82 | void main() async { 83 | WidgetsFlutterBinding.ensureInitialized(); 84 | await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); 85 | runApp(MyApp()); 86 | } 87 | ``` 88 | 89 | --- 90 | 91 | # 🛠️ Platform-Specific Setup 92 | 93 | ## Web (reCAPTCHA) 94 | 95 | On Web, the reCAPTCHA widget is a fully managed flow which provides security to your web application. 96 | The widget will render as an invisible widget when the sign-in flow is triggered. An "invisible" 97 | widget will appear as a full-page modal on-top of your application like demonstrated below. 98 | 99 | ![reCAPTCHA1](https://user-images.githubusercontent.com/56810766/119164921-8da35480-ba7a-11eb-8169-eafd67bfdc12.png) 100 | 101 | Although, a `RecaptchaVerifier` instance can be passed which can be used to manage the widget. 102 | 103 | Use the function `recaptchaVerifierForWebProvider` in `FirebasePhoneAuthHandler` which gives a boolean 104 | to check whether the current platform is Web or not. 105 | 106 | `NOTE`: Do not pass a `RecaptchaVerifier` instance if the platform is not web, else an error occurs. 107 | 108 | Example: 109 | ```dart 110 | recaptchaVerifierForWebProvider: (isWeb) { 111 | if (isWeb) return RecaptchaVerifier(); 112 | }, 113 | ``` 114 | 115 | It is however possible to display an inline widget which the user has to explicitly press to verify themselves. 116 | 117 | ![reCAPTCHA2](https://user-images.githubusercontent.com/56810766/119164930-8f6d1800-ba7a-11eb-9e3d-d58a50c959bd.png) 118 | 119 | To add an inline widget, specify a DOM element ID to the container argument of the `RecaptchaVerifier` instance. 120 | The element must exist and be empty otherwise an error will be thrown. 121 | If no container argument is provided, the widget will be rendered as "invisible". 122 | 123 | ```dart 124 | RecaptchaVerifier( 125 | container: 'recaptcha', 126 | size: RecaptchaVerifierSize.compact, 127 | theme: RecaptchaVerifierTheme.dark, 128 | onSuccess: () => print('reCAPTCHA Completed!'), 129 | onError: (FirebaseAuthException error) => print(error), 130 | onExpired: () => print('reCAPTCHA Expired!'), 131 | ), 132 | ``` 133 | 134 | If the reCAPTCHA badge does not disappear automatically after authentication is done, 135 | try adding the following code in `onLoginSuccess` so that it disappears when the login process is done. 136 | 137 | Firstly import `querySelector` from `dart:html`. 138 | ```dart 139 | import 'dart:html' show querySelector; 140 | ``` 141 | 142 | Then add this in `onLoginSuccess` callback. 143 | ```dart 144 | final captcha = querySelector('#__ff-recaptcha-container'); 145 | if (captcha != null) captcha.hidden = true; 146 | ``` 147 | 148 | If you want to completely disable the reCAPTCHA badge (typically appears on the bottom right), 149 | add this CSS style in the `web/index.html` outside any other tag. 150 | 151 | ```html 152 | 155 | ``` 156 | 157 | --- 158 | 159 | # ❓ Usage 160 | 161 | 1. Add [`firebase_phone_auth_handler`](https://pub.dev/packages/firebase_phone_auth_handler) as a dependency in your pubspec.yaml file. 162 | ```yaml 163 | dependencies: 164 | flutter: 165 | sdk: flutter 166 | 167 | firebase_phone_auth_handler: 168 | ``` 169 | 170 | 2. Wrap the `MaterialApp` with `FirebasePhoneAuthProvider` to enable your application to support phone authentication. 171 | ```dart 172 | import 'package:firebase_phone_auth_handler/firebase_phone_auth_handler.dart'; 173 | 174 | class _MainApp extends StatelessWidget { 175 | @override 176 | Widget build(BuildContext context) { 177 | return FirebasePhoneAuthProvider( 178 | child: MaterialApp( 179 | debugShowCheckedModeBanner: false, 180 | home: HomeScreen(), 181 | ), 182 | ); 183 | } 184 | } 185 | ``` 186 | 187 | 4. Use [`FirebasePhoneAuthHandler`](https://github.com/rithik-dev/firebase_phone_auth_handler/blob/master/lib/firebase_phone_auth_handler.dart) widget in your widget tree and pass all the required parameters to get started. 188 | ```dart 189 | FirebasePhoneAuthHandler( 190 | // required 191 | phoneNumber: "+919876543210", 192 | // If true, the user is signed out before the onLoginSuccess callback is fired when the OTP is verified successfully. 193 | signOutOnSuccessfulVerification: false, 194 | linkWithExistingUser: false, 195 | // required 196 | builder: (context, controller) { 197 | return SizedBox.shrink(); 198 | }, 199 | onLoginSuccess: (userCredential, autoVerified) { 200 | debugPrint("autoVerified: $autoVerified"); 201 | debugPrint("Login success UID: ${userCredential.user?.uid}"); 202 | }, 203 | onLoginFailed: (authException, stackTrace) { 204 | debugPrint("An error occurred: ${authException.message}"); 205 | }, 206 | onError: (error, stackTrace) {}, 207 | ), 208 | ``` 209 | 210 | 5. To logout the current user(if any), call 211 | ```dart 212 | await FirebasePhoneAuthHandler.signOut(context); 213 | 214 | // OR 215 | 216 | controller.signOut(); // can also be used to logout the current user. 217 | ``` 218 | 219 | --- 220 | 221 | # 🎯 Sample Usage 222 | 223 | See the [example](https://github.com/rithik-dev/firebase_phone_auth_handler/blob/master/example) app for a complete app. Learn how to setup the example app for testing [here](https://github.com/rithik-dev/firebase_phone_auth_handler/blob/master/example/README.md). 224 | 225 | Check out the full API reference of the widget [here](https://pub.dev/documentation/firebase_phone_auth_handler/latest/firebase_phone_auth_handler/FirebasePhoneAuthHandler-class.html). 226 | 227 | ```dart 228 | import 'package:firebase_phone_auth_handler/firebase_phone_auth_handler.dart'; 229 | import 'package:flutter/material.dart'; 230 | import 'package:phone_auth_handler_demo/screens/home_screen.dart'; 231 | import 'package:phone_auth_handler_demo/utils/helpers.dart'; 232 | import 'package:phone_auth_handler_demo/widgets/custom_loader.dart'; 233 | import 'package:phone_auth_handler_demo/widgets/pin_input_field.dart'; 234 | 235 | class VerifyPhoneNumberScreen extends StatefulWidget { 236 | static const id = 'VerifyPhoneNumberScreen'; 237 | 238 | final String phoneNumber; 239 | 240 | const VerifyPhoneNumberScreen({ 241 | super.key, 242 | required this.phoneNumber, 243 | }); 244 | 245 | @override 246 | State createState() => 247 | _VerifyPhoneNumberScreenState(); 248 | } 249 | 250 | class _VerifyPhoneNumberScreenState extends State 251 | with WidgetsBindingObserver { 252 | bool isKeyboardVisible = false; 253 | 254 | late final ScrollController scrollController; 255 | 256 | @override 257 | void initState() { 258 | scrollController = ScrollController(); 259 | WidgetsBinding.instance.addObserver(this); 260 | super.initState(); 261 | } 262 | 263 | @override 264 | void dispose() { 265 | WidgetsBinding.instance.removeObserver(this); 266 | scrollController.dispose(); 267 | super.dispose(); 268 | } 269 | 270 | @override 271 | void didChangeMetrics() { 272 | final bottomViewInsets = WidgetsBinding 273 | .instance.platformDispatcher.views.first.viewInsets.bottom; 274 | isKeyboardVisible = bottomViewInsets > 0; 275 | } 276 | 277 | // scroll to bottom of screen, when pin input field is in focus. 278 | Future _scrollToBottomOnKeyboardOpen() async { 279 | while (!isKeyboardVisible) { 280 | await Future.delayed(const Duration(milliseconds: 50)); 281 | } 282 | 283 | await Future.delayed(const Duration(milliseconds: 250)); 284 | 285 | await scrollController.animateTo( 286 | scrollController.position.maxScrollExtent, 287 | duration: const Duration(milliseconds: 250), 288 | curve: Curves.easeIn, 289 | ); 290 | } 291 | 292 | @override 293 | Widget build(BuildContext context) { 294 | return SafeArea( 295 | child: FirebasePhoneAuthHandler( 296 | phoneNumber: widget.phoneNumber, 297 | signOutOnSuccessfulVerification: false, 298 | sendOtpOnInitialize: true, 299 | linkWithExistingUser: false, 300 | autoRetrievalTimeOutDuration: const Duration(seconds: 60), 301 | otpExpirationDuration: const Duration(seconds: 60), 302 | onCodeSent: () { 303 | log(VerifyPhoneNumberScreen.id, msg: 'OTP sent!'); 304 | }, 305 | onLoginSuccess: (userCredential, autoVerified) async { 306 | log( 307 | VerifyPhoneNumberScreen.id, 308 | msg: autoVerified 309 | ? 'OTP was fetched automatically!' 310 | : 'OTP was verified manually!', 311 | ); 312 | 313 | showSnackBar('Phone number verified successfully!'); 314 | 315 | log( 316 | VerifyPhoneNumberScreen.id, 317 | msg: 'Login Success UID: ${userCredential.user?.uid}', 318 | ); 319 | 320 | Navigator.pushNamedAndRemoveUntil( 321 | context, 322 | HomeScreen.id, 323 | (route) => false, 324 | ); 325 | }, 326 | onLoginFailed: (authException, stackTrace) { 327 | log( 328 | VerifyPhoneNumberScreen.id, 329 | msg: authException.message, 330 | error: authException, 331 | stackTrace: stackTrace, 332 | ); 333 | 334 | switch (authException.code) { 335 | case 'invalid-phone-number': 336 | // invalid phone number 337 | return showSnackBar('Invalid phone number!'); 338 | case 'invalid-verification-code': 339 | // invalid otp entered 340 | return showSnackBar('The entered OTP is invalid!'); 341 | // handle other error codes 342 | default: 343 | showSnackBar('Something went wrong!'); 344 | // handle error further if needed 345 | } 346 | }, 347 | onError: (error, stackTrace) { 348 | log( 349 | VerifyPhoneNumberScreen.id, 350 | error: error, 351 | stackTrace: stackTrace, 352 | ); 353 | 354 | showSnackBar('An error occurred!'); 355 | }, 356 | builder: (context, controller) { 357 | return Scaffold( 358 | appBar: AppBar( 359 | leadingWidth: 0, 360 | leading: const SizedBox.shrink(), 361 | title: const Text('Verify Phone Number'), 362 | actions: [ 363 | if (controller.codeSent) 364 | TextButton( 365 | onPressed: controller.isOtpExpired 366 | ? () async { 367 | log(VerifyPhoneNumberScreen.id, msg: 'Resend OTP'); 368 | await controller.sendOTP(); 369 | } 370 | : null, 371 | child: Text( 372 | controller.isOtpExpired 373 | ? 'Resend' 374 | : '${controller.otpExpirationTimeLeft.inSeconds}s', 375 | style: const TextStyle(color: Colors.blue, fontSize: 18), 376 | ), 377 | ), 378 | const SizedBox(width: 5), 379 | ], 380 | ), 381 | body: controller.isSendingCode 382 | ? Column( 383 | mainAxisAlignment: MainAxisAlignment.center, 384 | crossAxisAlignment: CrossAxisAlignment.center, 385 | children: const [ 386 | CustomLoader(), 387 | SizedBox(height: 50), 388 | Center( 389 | child: Text( 390 | 'Sending OTP', 391 | style: TextStyle(fontSize: 25), 392 | ), 393 | ), 394 | ], 395 | ) 396 | : ListView( 397 | padding: const EdgeInsets.all(20), 398 | controller: scrollController, 399 | children: [ 400 | Text( 401 | "We've sent an SMS with a verification code to ${widget.phoneNumber}", 402 | style: const TextStyle(fontSize: 25), 403 | ), 404 | const SizedBox(height: 10), 405 | const Divider(), 406 | if (controller.isListeningForOtpAutoRetrieve) 407 | Column( 408 | children: const [ 409 | CustomLoader(), 410 | SizedBox(height: 50), 411 | Text( 412 | 'Listening for OTP', 413 | textAlign: TextAlign.center, 414 | style: TextStyle( 415 | fontSize: 25, 416 | fontWeight: FontWeight.w600, 417 | ), 418 | ), 419 | SizedBox(height: 15), 420 | Divider(), 421 | Text('OR', textAlign: TextAlign.center), 422 | Divider(), 423 | ], 424 | ), 425 | const SizedBox(height: 15), 426 | const Text( 427 | 'Enter OTP', 428 | style: TextStyle( 429 | fontSize: 20, 430 | fontWeight: FontWeight.w600, 431 | ), 432 | ), 433 | const SizedBox(height: 15), 434 | PinInputField( 435 | length: 6, 436 | onFocusChange: (hasFocus) async { 437 | if (hasFocus) await _scrollToBottomOnKeyboardOpen(); 438 | }, 439 | onSubmit: (enteredOtp) async { 440 | final verified = 441 | await controller.verifyOtp(enteredOtp); 442 | if (verified) { 443 | // number verify success 444 | // will call onLoginSuccess handler 445 | } else { 446 | // phone verification failed 447 | // will call onLoginFailed or onError callbacks with the error 448 | } 449 | }, 450 | ), 451 | ], 452 | ), 453 | ); 454 | }, 455 | ), 456 | ); 457 | } 458 | } 459 | ``` 460 | 461 | --- 462 | 463 | # 👤 Collaborators 464 | 465 | 466 | | Name | GitHub | Linkedin | 467 | |-----------------------------------|-------------------------------------|-------------------------------------| 468 | | Rithik Bhandari | [github/rithik-dev](https://github.com/rithik-dev) | [linkedin/rithik-bhandari](https://www.linkedin.com/in/rithik-bhandari) | 469 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /example/.fvmrc: -------------------------------------------------------------------------------- 1 | { 2 | "flutter": "3.24.3" 3 | } -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | 48 | # FVM Version Cache 49 | .fvm/ -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 7e9793dee1b85a243edd0e06cb1658e98b077561 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Firebase Phone Auth Handler Demo 2 | 3 | Build the app and run it. 4 | 5 | ## How to test the app 6 | 7 | To test the example app, you can use "+91 9999999999" as the phone number and "000000" as the OTP. 8 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/to/reference-keystore 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 5 | id "dev.flutter.flutter-gradle-plugin" 6 | } 7 | 8 | android { 9 | namespace = "com.example.phone_auth_handler_demo" 10 | compileSdk = flutter.compileSdkVersion 11 | ndkVersion = flutter.ndkVersion 12 | 13 | compileOptions { 14 | sourceCompatibility = JavaVersion.VERSION_1_8 15 | targetCompatibility = JavaVersion.VERSION_1_8 16 | } 17 | 18 | kotlinOptions { 19 | jvmTarget = JavaVersion.VERSION_1_8 20 | } 21 | 22 | defaultConfig { 23 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 24 | applicationId = "com.example.phone_auth_handler_demo" 25 | // You can update the following values to match your application needs. 26 | // For more information, see: https://flutter.dev/to/review-gradle-config. 27 | minSdk = 23 28 | targetSdk = flutter.targetSdkVersion 29 | versionCode = flutter.versionCode 30 | versionName = flutter.versionName 31 | } 32 | 33 | buildTypes { 34 | release { 35 | // TODO: Add your own signing config for the release build. 36 | // Signing with the debug keys for now, so `flutter run --release` works. 37 | signingConfig = signingConfigs.debug 38 | } 39 | } 40 | } 41 | 42 | flutter { 43 | source = "../.." 44 | } 45 | 46 | dependencies { 47 | // Import the Firebase BoM 48 | implementation platform('com.google.firebase:firebase-bom:29.3.1') 49 | 50 | 51 | // Add the dependencies for the desired Firebase products 52 | // https://firebase.google.com/docs/android/setup#available-libraries 53 | } -------------------------------------------------------------------------------- /example/android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "1086627119682", 4 | "project_id": "packages-demo-119b7", 5 | "storage_bucket": "packages-demo-119b7.appspot.com" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:1086627119682:android:3aecab4a98fe263176a3db", 11 | "android_client_info": { 12 | "package_name": "com.example.phone_auth_handler_demo" 13 | } 14 | }, 15 | "oauth_client": [ 16 | { 17 | "client_id": "1086627119682-3a0smf3p0pcaf60pl2vd88da7afb5bq1.apps.googleusercontent.com", 18 | "client_type": 1, 19 | "android_info": { 20 | "package_name": "com.example.phone_auth_handler_demo", 21 | "certificate_hash": "f8c8a96a4c7e118a82111ba3b1ef9ccd0b4c2dd2" 22 | } 23 | }, 24 | { 25 | "client_id": "1086627119682-84l28slbplgl98tt9johd2fa7m2lh4ql.apps.googleusercontent.com", 26 | "client_type": 3 27 | } 28 | ], 29 | "api_key": [ 30 | { 31 | "current_key": "AIzaSyBP2BlxWwlm0GI4PZHZRl-NAf6fiG1IhJY" 32 | } 33 | ], 34 | "services": { 35 | "appinvite_service": { 36 | "other_platform_oauth_client": [ 37 | { 38 | "client_id": "1086627119682-84l28slbplgl98tt9johd2fa7m2lh4ql.apps.googleusercontent.com", 39 | "client_type": 3 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | ], 46 | "configuration_version": "1" 47 | } -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/phone_auth_handler_demo/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.phone_auth_handler_demo 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | dependencies { 3 | classpath 'com.android.tools.build:gradle:4.1.0' 4 | classpath 'com.google.gms:google-services:4.3.10' 5 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 6 | } 7 | } 8 | 9 | allprojects { 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | 16 | rootProject.buildDir = "../build" 17 | subprojects { 18 | project.buildDir = "${rootProject.buildDir}/${project.name}" 19 | } 20 | subprojects { 21 | project.evaluationDependsOn(":app") 22 | } 23 | 24 | tasks.register("clean", Delete) { 25 | delete rootProject.buildDir 26 | } 27 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip 6 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.1.0" apply false 22 | id "org.jetbrains.kotlin.android" version "1.8.22" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '13.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Firebase/Auth (11.2.0): 3 | - Firebase/CoreOnly 4 | - FirebaseAuth (~> 11.2.0) 5 | - Firebase/CoreOnly (11.2.0): 6 | - FirebaseCore (= 11.2.0) 7 | - firebase_auth (5.3.1): 8 | - Firebase/Auth (= 11.2.0) 9 | - firebase_core 10 | - Flutter 11 | - firebase_core (3.6.0): 12 | - Firebase/CoreOnly (= 11.2.0) 13 | - Flutter 14 | - FirebaseAppCheckInterop (11.3.0) 15 | - FirebaseAuth (11.2.0): 16 | - FirebaseAppCheckInterop (~> 11.0) 17 | - FirebaseAuthInterop (~> 11.0) 18 | - FirebaseCore (~> 11.0) 19 | - FirebaseCoreExtension (~> 11.0) 20 | - GoogleUtilities/AppDelegateSwizzler (~> 8.0) 21 | - GoogleUtilities/Environment (~> 8.0) 22 | - GTMSessionFetcher/Core (~> 3.4) 23 | - RecaptchaInterop (~> 100.0) 24 | - FirebaseAuthInterop (11.3.0) 25 | - FirebaseCore (11.2.0): 26 | - FirebaseCoreInternal (~> 11.0) 27 | - GoogleUtilities/Environment (~> 8.0) 28 | - GoogleUtilities/Logger (~> 8.0) 29 | - FirebaseCoreExtension (11.3.0): 30 | - FirebaseCore (~> 11.0) 31 | - FirebaseCoreInternal (11.3.0): 32 | - "GoogleUtilities/NSData+zlib (~> 8.0)" 33 | - Flutter (1.0.0) 34 | - GoogleUtilities/AppDelegateSwizzler (8.0.2): 35 | - GoogleUtilities/Environment 36 | - GoogleUtilities/Logger 37 | - GoogleUtilities/Network 38 | - GoogleUtilities/Privacy 39 | - GoogleUtilities/Environment (8.0.2): 40 | - GoogleUtilities/Privacy 41 | - GoogleUtilities/Logger (8.0.2): 42 | - GoogleUtilities/Environment 43 | - GoogleUtilities/Privacy 44 | - GoogleUtilities/Network (8.0.2): 45 | - GoogleUtilities/Logger 46 | - "GoogleUtilities/NSData+zlib" 47 | - GoogleUtilities/Privacy 48 | - GoogleUtilities/Reachability 49 | - "GoogleUtilities/NSData+zlib (8.0.2)": 50 | - GoogleUtilities/Privacy 51 | - GoogleUtilities/Privacy (8.0.2) 52 | - GoogleUtilities/Reachability (8.0.2): 53 | - GoogleUtilities/Logger 54 | - GoogleUtilities/Privacy 55 | - GTMSessionFetcher/Core (3.5.0) 56 | - RecaptchaInterop (100.0.0) 57 | 58 | DEPENDENCIES: 59 | - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) 60 | - firebase_core (from `.symlinks/plugins/firebase_core/ios`) 61 | - Flutter (from `Flutter`) 62 | 63 | SPEC REPOS: 64 | trunk: 65 | - Firebase 66 | - FirebaseAppCheckInterop 67 | - FirebaseAuth 68 | - FirebaseAuthInterop 69 | - FirebaseCore 70 | - FirebaseCoreExtension 71 | - FirebaseCoreInternal 72 | - GoogleUtilities 73 | - GTMSessionFetcher 74 | - RecaptchaInterop 75 | 76 | EXTERNAL SOURCES: 77 | firebase_auth: 78 | :path: ".symlinks/plugins/firebase_auth/ios" 79 | firebase_core: 80 | :path: ".symlinks/plugins/firebase_core/ios" 81 | Flutter: 82 | :path: Flutter 83 | 84 | SPEC CHECKSUMS: 85 | Firebase: 98e6bf5278170668a7983e12971a66b2cd57fc8c 86 | firebase_auth: 0c77e299a8f2d1c74d1b1f6b78b3d4d802c19f47 87 | firebase_core: 2bedc3136ec7c7b8561c6123ed0239387b53f2af 88 | FirebaseAppCheckInterop: 7789a8adfb09e905ce02a76540b94b059029ea81 89 | FirebaseAuth: 2a198b8cdbbbd457f08d74df7040feb0a0e7777a 90 | FirebaseAuthInterop: c453b7ba7c49b88b2f519bb8d2e29edf7ada4a2a 91 | FirebaseCore: a282032ae9295c795714ded2ec9c522fc237f8da 92 | FirebaseCoreExtension: 30bb063476ef66cd46925243d64ad8b2c8ac3264 93 | FirebaseCoreInternal: ac26d09a70c730e497936430af4e60fb0c68ec4e 94 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 95 | GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d 96 | GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 97 | RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21 98 | 99 | PODFILE CHECKSUM: a57f30d18f102dd3ce366b1d62a55ecbef2158e5 100 | 101 | COCOAPODS: 1.15.2 102 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 47EBB0C6F5F0E7D3FBAAF4A9 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 773E200F228B4024B09261E3 /* Pods_RunnerTests.framework */; }; 14 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 15 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 16 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 17 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 18 | E41DCAB0D76D557DCC08A9CC /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3216431CC2A65CB4D83D47BF /* Pods_Runner.framework */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = 97C146E61CF9000F007C117D /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = 97C146ED1CF9000F007C117D; 27 | remoteInfo = Runner; 28 | }; 29 | /* End PBXContainerItemProxy section */ 30 | 31 | /* Begin PBXCopyFilesBuildPhase section */ 32 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 33 | isa = PBXCopyFilesBuildPhase; 34 | buildActionMask = 2147483647; 35 | dstPath = ""; 36 | dstSubfolderSpec = 10; 37 | files = ( 38 | ); 39 | name = "Embed Frameworks"; 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXCopyFilesBuildPhase section */ 43 | 44 | /* Begin PBXFileReference section */ 45 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 46 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 47 | 3216431CC2A65CB4D83D47BF /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 49 | 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 51 | 4AEC58320FF68ABEAD229D71 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 52 | 5E6200E72691A6976FD0DE9B /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 53 | 6538E6EDAEEEB11EBAD802DC /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 54 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 55 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 56 | 773E200F228B4024B09261E3 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 784EED5E6C728EB6410EFDE2 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 58 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 59 | 8FB1CD637CAC640205FC1376 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 60 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 61 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 62 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 64 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 65 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 66 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 67 | C301543E624FB5785E7CDB9F /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 68 | /* End PBXFileReference section */ 69 | 70 | /* Begin PBXFrameworksBuildPhase section */ 71 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | E41DCAB0D76D557DCC08A9CC /* Pods_Runner.framework in Frameworks */, 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | F5601865CA80CDBD5F715478 /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | 47EBB0C6F5F0E7D3FBAAF4A9 /* Pods_RunnerTests.framework in Frameworks */, 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | /* End PBXFrameworksBuildPhase section */ 88 | 89 | /* Begin PBXGroup section */ 90 | 331C8082294A63A400263BE5 /* RunnerTests */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 331C807B294A618700263BE5 /* RunnerTests.swift */, 94 | ); 95 | path = RunnerTests; 96 | sourceTree = ""; 97 | }; 98 | 8834018B44948394AE4CB5BA /* Pods */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | 784EED5E6C728EB6410EFDE2 /* Pods-Runner.debug.xcconfig */, 102 | 4AEC58320FF68ABEAD229D71 /* Pods-Runner.release.xcconfig */, 103 | 6538E6EDAEEEB11EBAD802DC /* Pods-Runner.profile.xcconfig */, 104 | 8FB1CD637CAC640205FC1376 /* Pods-RunnerTests.debug.xcconfig */, 105 | C301543E624FB5785E7CDB9F /* Pods-RunnerTests.release.xcconfig */, 106 | 5E6200E72691A6976FD0DE9B /* Pods-RunnerTests.profile.xcconfig */, 107 | ); 108 | name = Pods; 109 | path = Pods; 110 | sourceTree = ""; 111 | }; 112 | 89783E006638BD14B0900074 /* Frameworks */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 3216431CC2A65CB4D83D47BF /* Pods_Runner.framework */, 116 | 773E200F228B4024B09261E3 /* Pods_RunnerTests.framework */, 117 | ); 118 | name = Frameworks; 119 | sourceTree = ""; 120 | }; 121 | 9740EEB11CF90186004384FC /* Flutter */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 125 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 126 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 127 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 128 | ); 129 | name = Flutter; 130 | sourceTree = ""; 131 | }; 132 | 97C146E51CF9000F007C117D = { 133 | isa = PBXGroup; 134 | children = ( 135 | 9740EEB11CF90186004384FC /* Flutter */, 136 | 97C146F01CF9000F007C117D /* Runner */, 137 | 97C146EF1CF9000F007C117D /* Products */, 138 | 331C8082294A63A400263BE5 /* RunnerTests */, 139 | 8834018B44948394AE4CB5BA /* Pods */, 140 | 89783E006638BD14B0900074 /* Frameworks */, 141 | ); 142 | sourceTree = ""; 143 | }; 144 | 97C146EF1CF9000F007C117D /* Products */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 97C146EE1CF9000F007C117D /* Runner.app */, 148 | 331C8081294A63A400263BE5 /* RunnerTests.xctest */, 149 | ); 150 | name = Products; 151 | sourceTree = ""; 152 | }; 153 | 97C146F01CF9000F007C117D /* Runner */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 157 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 158 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 159 | 97C147021CF9000F007C117D /* Info.plist */, 160 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 161 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 162 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 163 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 164 | ); 165 | path = Runner; 166 | sourceTree = ""; 167 | }; 168 | /* End PBXGroup section */ 169 | 170 | /* Begin PBXNativeTarget section */ 171 | 331C8080294A63A400263BE5 /* RunnerTests */ = { 172 | isa = PBXNativeTarget; 173 | buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; 174 | buildPhases = ( 175 | 6E3FFD9177B588A4D38C86E9 /* [CP] Check Pods Manifest.lock */, 176 | 331C807D294A63A400263BE5 /* Sources */, 177 | 331C807F294A63A400263BE5 /* Resources */, 178 | F5601865CA80CDBD5F715478 /* Frameworks */, 179 | ); 180 | buildRules = ( 181 | ); 182 | dependencies = ( 183 | 331C8086294A63A400263BE5 /* PBXTargetDependency */, 184 | ); 185 | name = RunnerTests; 186 | productName = RunnerTests; 187 | productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; 188 | productType = "com.apple.product-type.bundle.unit-test"; 189 | }; 190 | 97C146ED1CF9000F007C117D /* Runner */ = { 191 | isa = PBXNativeTarget; 192 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 193 | buildPhases = ( 194 | B56EAF422780B3B028D5C925 /* [CP] Check Pods Manifest.lock */, 195 | 9740EEB61CF901F6004384FC /* Run Script */, 196 | 97C146EA1CF9000F007C117D /* Sources */, 197 | 97C146EB1CF9000F007C117D /* Frameworks */, 198 | 97C146EC1CF9000F007C117D /* Resources */, 199 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 200 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 201 | BF80D9787F0C3FEBFB0E17A3 /* [CP] Embed Pods Frameworks */, 202 | ); 203 | buildRules = ( 204 | ); 205 | dependencies = ( 206 | ); 207 | name = Runner; 208 | productName = Runner; 209 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 210 | productType = "com.apple.product-type.application"; 211 | }; 212 | /* End PBXNativeTarget section */ 213 | 214 | /* Begin PBXProject section */ 215 | 97C146E61CF9000F007C117D /* Project object */ = { 216 | isa = PBXProject; 217 | attributes = { 218 | BuildIndependentTargetsInParallel = YES; 219 | LastUpgradeCheck = 1510; 220 | ORGANIZATIONNAME = ""; 221 | TargetAttributes = { 222 | 331C8080294A63A400263BE5 = { 223 | CreatedOnToolsVersion = 14.0; 224 | TestTargetID = 97C146ED1CF9000F007C117D; 225 | }; 226 | 97C146ED1CF9000F007C117D = { 227 | CreatedOnToolsVersion = 7.3.1; 228 | LastSwiftMigration = 1100; 229 | }; 230 | }; 231 | }; 232 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 233 | compatibilityVersion = "Xcode 9.3"; 234 | developmentRegion = en; 235 | hasScannedForEncodings = 0; 236 | knownRegions = ( 237 | en, 238 | Base, 239 | ); 240 | mainGroup = 97C146E51CF9000F007C117D; 241 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 242 | projectDirPath = ""; 243 | projectRoot = ""; 244 | targets = ( 245 | 97C146ED1CF9000F007C117D /* Runner */, 246 | 331C8080294A63A400263BE5 /* RunnerTests */, 247 | ); 248 | }; 249 | /* End PBXProject section */ 250 | 251 | /* Begin PBXResourcesBuildPhase section */ 252 | 331C807F294A63A400263BE5 /* Resources */ = { 253 | isa = PBXResourcesBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | ); 257 | runOnlyForDeploymentPostprocessing = 0; 258 | }; 259 | 97C146EC1CF9000F007C117D /* Resources */ = { 260 | isa = PBXResourcesBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 264 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 265 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 266 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 267 | ); 268 | runOnlyForDeploymentPostprocessing = 0; 269 | }; 270 | /* End PBXResourcesBuildPhase section */ 271 | 272 | /* Begin PBXShellScriptBuildPhase section */ 273 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 274 | isa = PBXShellScriptBuildPhase; 275 | alwaysOutOfDate = 1; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | ); 279 | inputPaths = ( 280 | "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", 281 | ); 282 | name = "Thin Binary"; 283 | outputPaths = ( 284 | ); 285 | runOnlyForDeploymentPostprocessing = 0; 286 | shellPath = /bin/sh; 287 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 288 | }; 289 | 6E3FFD9177B588A4D38C86E9 /* [CP] Check Pods Manifest.lock */ = { 290 | isa = PBXShellScriptBuildPhase; 291 | buildActionMask = 2147483647; 292 | files = ( 293 | ); 294 | inputFileListPaths = ( 295 | ); 296 | inputPaths = ( 297 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 298 | "${PODS_ROOT}/Manifest.lock", 299 | ); 300 | name = "[CP] Check Pods Manifest.lock"; 301 | outputFileListPaths = ( 302 | ); 303 | outputPaths = ( 304 | "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | shellPath = /bin/sh; 308 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 309 | showEnvVarsInLog = 0; 310 | }; 311 | 9740EEB61CF901F6004384FC /* Run Script */ = { 312 | isa = PBXShellScriptBuildPhase; 313 | alwaysOutOfDate = 1; 314 | buildActionMask = 2147483647; 315 | files = ( 316 | ); 317 | inputPaths = ( 318 | ); 319 | name = "Run Script"; 320 | outputPaths = ( 321 | ); 322 | runOnlyForDeploymentPostprocessing = 0; 323 | shellPath = /bin/sh; 324 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 325 | }; 326 | B56EAF422780B3B028D5C925 /* [CP] Check Pods Manifest.lock */ = { 327 | isa = PBXShellScriptBuildPhase; 328 | buildActionMask = 2147483647; 329 | files = ( 330 | ); 331 | inputFileListPaths = ( 332 | ); 333 | inputPaths = ( 334 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 335 | "${PODS_ROOT}/Manifest.lock", 336 | ); 337 | name = "[CP] Check Pods Manifest.lock"; 338 | outputFileListPaths = ( 339 | ); 340 | outputPaths = ( 341 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 342 | ); 343 | runOnlyForDeploymentPostprocessing = 0; 344 | shellPath = /bin/sh; 345 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 346 | showEnvVarsInLog = 0; 347 | }; 348 | BF80D9787F0C3FEBFB0E17A3 /* [CP] Embed Pods Frameworks */ = { 349 | isa = PBXShellScriptBuildPhase; 350 | buildActionMask = 2147483647; 351 | files = ( 352 | ); 353 | inputFileListPaths = ( 354 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", 355 | ); 356 | name = "[CP] Embed Pods Frameworks"; 357 | outputFileListPaths = ( 358 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", 359 | ); 360 | runOnlyForDeploymentPostprocessing = 0; 361 | shellPath = /bin/sh; 362 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 363 | showEnvVarsInLog = 0; 364 | }; 365 | /* End PBXShellScriptBuildPhase section */ 366 | 367 | /* Begin PBXSourcesBuildPhase section */ 368 | 331C807D294A63A400263BE5 /* Sources */ = { 369 | isa = PBXSourcesBuildPhase; 370 | buildActionMask = 2147483647; 371 | files = ( 372 | 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, 373 | ); 374 | runOnlyForDeploymentPostprocessing = 0; 375 | }; 376 | 97C146EA1CF9000F007C117D /* Sources */ = { 377 | isa = PBXSourcesBuildPhase; 378 | buildActionMask = 2147483647; 379 | files = ( 380 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 381 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 382 | ); 383 | runOnlyForDeploymentPostprocessing = 0; 384 | }; 385 | /* End PBXSourcesBuildPhase section */ 386 | 387 | /* Begin PBXTargetDependency section */ 388 | 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { 389 | isa = PBXTargetDependency; 390 | target = 97C146ED1CF9000F007C117D /* Runner */; 391 | targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; 392 | }; 393 | /* End PBXTargetDependency section */ 394 | 395 | /* Begin PBXVariantGroup section */ 396 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 397 | isa = PBXVariantGroup; 398 | children = ( 399 | 97C146FB1CF9000F007C117D /* Base */, 400 | ); 401 | name = Main.storyboard; 402 | sourceTree = ""; 403 | }; 404 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 405 | isa = PBXVariantGroup; 406 | children = ( 407 | 97C147001CF9000F007C117D /* Base */, 408 | ); 409 | name = LaunchScreen.storyboard; 410 | sourceTree = ""; 411 | }; 412 | /* End PBXVariantGroup section */ 413 | 414 | /* Begin XCBuildConfiguration section */ 415 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 416 | isa = XCBuildConfiguration; 417 | buildSettings = { 418 | ALWAYS_SEARCH_USER_PATHS = NO; 419 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 420 | CLANG_ANALYZER_NONNULL = YES; 421 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 422 | CLANG_CXX_LIBRARY = "libc++"; 423 | CLANG_ENABLE_MODULES = YES; 424 | CLANG_ENABLE_OBJC_ARC = YES; 425 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 426 | CLANG_WARN_BOOL_CONVERSION = YES; 427 | CLANG_WARN_COMMA = YES; 428 | CLANG_WARN_CONSTANT_CONVERSION = YES; 429 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 430 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 431 | CLANG_WARN_EMPTY_BODY = YES; 432 | CLANG_WARN_ENUM_CONVERSION = YES; 433 | CLANG_WARN_INFINITE_RECURSION = YES; 434 | CLANG_WARN_INT_CONVERSION = YES; 435 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 436 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 437 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 438 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 439 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 440 | CLANG_WARN_STRICT_PROTOTYPES = YES; 441 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 442 | CLANG_WARN_UNREACHABLE_CODE = YES; 443 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 444 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 445 | COPY_PHASE_STRIP = NO; 446 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 447 | ENABLE_NS_ASSERTIONS = NO; 448 | ENABLE_STRICT_OBJC_MSGSEND = YES; 449 | ENABLE_USER_SCRIPT_SANDBOXING = NO; 450 | GCC_C_LANGUAGE_STANDARD = gnu99; 451 | GCC_NO_COMMON_BLOCKS = YES; 452 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 453 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 454 | GCC_WARN_UNDECLARED_SELECTOR = YES; 455 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 456 | GCC_WARN_UNUSED_FUNCTION = YES; 457 | GCC_WARN_UNUSED_VARIABLE = YES; 458 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 459 | MTL_ENABLE_DEBUG_INFO = NO; 460 | SDKROOT = iphoneos; 461 | SUPPORTED_PLATFORMS = iphoneos; 462 | TARGETED_DEVICE_FAMILY = "1,2"; 463 | VALIDATE_PRODUCT = YES; 464 | }; 465 | name = Profile; 466 | }; 467 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 468 | isa = XCBuildConfiguration; 469 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 470 | buildSettings = { 471 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 472 | CLANG_ENABLE_MODULES = YES; 473 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 474 | DEVELOPMENT_TEAM = KFVRB3N543; 475 | ENABLE_BITCODE = NO; 476 | INFOPLIST_FILE = Runner/Info.plist; 477 | LD_RUNPATH_SEARCH_PATHS = ( 478 | "$(inherited)", 479 | "@executable_path/Frameworks", 480 | ); 481 | PRODUCT_BUNDLE_IDENTIFIER = com.example.phoneAuthHandlerDemo; 482 | PRODUCT_NAME = "$(TARGET_NAME)"; 483 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 484 | SWIFT_VERSION = 5.0; 485 | VERSIONING_SYSTEM = "apple-generic"; 486 | }; 487 | name = Profile; 488 | }; 489 | 331C8088294A63A400263BE5 /* Debug */ = { 490 | isa = XCBuildConfiguration; 491 | baseConfigurationReference = 8FB1CD637CAC640205FC1376 /* Pods-RunnerTests.debug.xcconfig */; 492 | buildSettings = { 493 | BUNDLE_LOADER = "$(TEST_HOST)"; 494 | CODE_SIGN_STYLE = Automatic; 495 | CURRENT_PROJECT_VERSION = 1; 496 | GENERATE_INFOPLIST_FILE = YES; 497 | MARKETING_VERSION = 1.0; 498 | PRODUCT_BUNDLE_IDENTIFIER = com.example.phoneAuthHandlerDemo.RunnerTests; 499 | PRODUCT_NAME = "$(TARGET_NAME)"; 500 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 501 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 502 | SWIFT_VERSION = 5.0; 503 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; 504 | }; 505 | name = Debug; 506 | }; 507 | 331C8089294A63A400263BE5 /* Release */ = { 508 | isa = XCBuildConfiguration; 509 | baseConfigurationReference = C301543E624FB5785E7CDB9F /* Pods-RunnerTests.release.xcconfig */; 510 | buildSettings = { 511 | BUNDLE_LOADER = "$(TEST_HOST)"; 512 | CODE_SIGN_STYLE = Automatic; 513 | CURRENT_PROJECT_VERSION = 1; 514 | GENERATE_INFOPLIST_FILE = YES; 515 | MARKETING_VERSION = 1.0; 516 | PRODUCT_BUNDLE_IDENTIFIER = com.example.phoneAuthHandlerDemo.RunnerTests; 517 | PRODUCT_NAME = "$(TARGET_NAME)"; 518 | SWIFT_VERSION = 5.0; 519 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; 520 | }; 521 | name = Release; 522 | }; 523 | 331C808A294A63A400263BE5 /* Profile */ = { 524 | isa = XCBuildConfiguration; 525 | baseConfigurationReference = 5E6200E72691A6976FD0DE9B /* Pods-RunnerTests.profile.xcconfig */; 526 | buildSettings = { 527 | BUNDLE_LOADER = "$(TEST_HOST)"; 528 | CODE_SIGN_STYLE = Automatic; 529 | CURRENT_PROJECT_VERSION = 1; 530 | GENERATE_INFOPLIST_FILE = YES; 531 | MARKETING_VERSION = 1.0; 532 | PRODUCT_BUNDLE_IDENTIFIER = com.example.phoneAuthHandlerDemo.RunnerTests; 533 | PRODUCT_NAME = "$(TARGET_NAME)"; 534 | SWIFT_VERSION = 5.0; 535 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; 536 | }; 537 | name = Profile; 538 | }; 539 | 97C147031CF9000F007C117D /* Debug */ = { 540 | isa = XCBuildConfiguration; 541 | buildSettings = { 542 | ALWAYS_SEARCH_USER_PATHS = NO; 543 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 544 | CLANG_ANALYZER_NONNULL = YES; 545 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 546 | CLANG_CXX_LIBRARY = "libc++"; 547 | CLANG_ENABLE_MODULES = YES; 548 | CLANG_ENABLE_OBJC_ARC = YES; 549 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 550 | CLANG_WARN_BOOL_CONVERSION = YES; 551 | CLANG_WARN_COMMA = YES; 552 | CLANG_WARN_CONSTANT_CONVERSION = YES; 553 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 554 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 555 | CLANG_WARN_EMPTY_BODY = YES; 556 | CLANG_WARN_ENUM_CONVERSION = YES; 557 | CLANG_WARN_INFINITE_RECURSION = YES; 558 | CLANG_WARN_INT_CONVERSION = YES; 559 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 560 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 561 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 562 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 563 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 564 | CLANG_WARN_STRICT_PROTOTYPES = YES; 565 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 566 | CLANG_WARN_UNREACHABLE_CODE = YES; 567 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 568 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 569 | COPY_PHASE_STRIP = NO; 570 | DEBUG_INFORMATION_FORMAT = dwarf; 571 | ENABLE_STRICT_OBJC_MSGSEND = YES; 572 | ENABLE_TESTABILITY = YES; 573 | ENABLE_USER_SCRIPT_SANDBOXING = NO; 574 | GCC_C_LANGUAGE_STANDARD = gnu99; 575 | GCC_DYNAMIC_NO_PIC = NO; 576 | GCC_NO_COMMON_BLOCKS = YES; 577 | GCC_OPTIMIZATION_LEVEL = 0; 578 | GCC_PREPROCESSOR_DEFINITIONS = ( 579 | "DEBUG=1", 580 | "$(inherited)", 581 | ); 582 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 583 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 584 | GCC_WARN_UNDECLARED_SELECTOR = YES; 585 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 586 | GCC_WARN_UNUSED_FUNCTION = YES; 587 | GCC_WARN_UNUSED_VARIABLE = YES; 588 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 589 | MTL_ENABLE_DEBUG_INFO = YES; 590 | ONLY_ACTIVE_ARCH = YES; 591 | SDKROOT = iphoneos; 592 | TARGETED_DEVICE_FAMILY = "1,2"; 593 | }; 594 | name = Debug; 595 | }; 596 | 97C147041CF9000F007C117D /* Release */ = { 597 | isa = XCBuildConfiguration; 598 | buildSettings = { 599 | ALWAYS_SEARCH_USER_PATHS = NO; 600 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 601 | CLANG_ANALYZER_NONNULL = YES; 602 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 603 | CLANG_CXX_LIBRARY = "libc++"; 604 | CLANG_ENABLE_MODULES = YES; 605 | CLANG_ENABLE_OBJC_ARC = YES; 606 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 607 | CLANG_WARN_BOOL_CONVERSION = YES; 608 | CLANG_WARN_COMMA = YES; 609 | CLANG_WARN_CONSTANT_CONVERSION = YES; 610 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 611 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 612 | CLANG_WARN_EMPTY_BODY = YES; 613 | CLANG_WARN_ENUM_CONVERSION = YES; 614 | CLANG_WARN_INFINITE_RECURSION = YES; 615 | CLANG_WARN_INT_CONVERSION = YES; 616 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 617 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 618 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 619 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 620 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 621 | CLANG_WARN_STRICT_PROTOTYPES = YES; 622 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 623 | CLANG_WARN_UNREACHABLE_CODE = YES; 624 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 625 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 626 | COPY_PHASE_STRIP = NO; 627 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 628 | ENABLE_NS_ASSERTIONS = NO; 629 | ENABLE_STRICT_OBJC_MSGSEND = YES; 630 | ENABLE_USER_SCRIPT_SANDBOXING = NO; 631 | GCC_C_LANGUAGE_STANDARD = gnu99; 632 | GCC_NO_COMMON_BLOCKS = YES; 633 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 634 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 635 | GCC_WARN_UNDECLARED_SELECTOR = YES; 636 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 637 | GCC_WARN_UNUSED_FUNCTION = YES; 638 | GCC_WARN_UNUSED_VARIABLE = YES; 639 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 640 | MTL_ENABLE_DEBUG_INFO = NO; 641 | SDKROOT = iphoneos; 642 | SUPPORTED_PLATFORMS = iphoneos; 643 | SWIFT_COMPILATION_MODE = wholemodule; 644 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 645 | TARGETED_DEVICE_FAMILY = "1,2"; 646 | VALIDATE_PRODUCT = YES; 647 | }; 648 | name = Release; 649 | }; 650 | 97C147061CF9000F007C117D /* Debug */ = { 651 | isa = XCBuildConfiguration; 652 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 653 | buildSettings = { 654 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 655 | CLANG_ENABLE_MODULES = YES; 656 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 657 | DEVELOPMENT_TEAM = KFVRB3N543; 658 | ENABLE_BITCODE = NO; 659 | INFOPLIST_FILE = Runner/Info.plist; 660 | LD_RUNPATH_SEARCH_PATHS = ( 661 | "$(inherited)", 662 | "@executable_path/Frameworks", 663 | ); 664 | PRODUCT_BUNDLE_IDENTIFIER = com.example.phoneAuthHandlerDemo; 665 | PRODUCT_NAME = "$(TARGET_NAME)"; 666 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 667 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 668 | SWIFT_VERSION = 5.0; 669 | VERSIONING_SYSTEM = "apple-generic"; 670 | }; 671 | name = Debug; 672 | }; 673 | 97C147071CF9000F007C117D /* Release */ = { 674 | isa = XCBuildConfiguration; 675 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 676 | buildSettings = { 677 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 678 | CLANG_ENABLE_MODULES = YES; 679 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 680 | DEVELOPMENT_TEAM = KFVRB3N543; 681 | ENABLE_BITCODE = NO; 682 | INFOPLIST_FILE = Runner/Info.plist; 683 | LD_RUNPATH_SEARCH_PATHS = ( 684 | "$(inherited)", 685 | "@executable_path/Frameworks", 686 | ); 687 | PRODUCT_BUNDLE_IDENTIFIER = com.example.phoneAuthHandlerDemo; 688 | PRODUCT_NAME = "$(TARGET_NAME)"; 689 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 690 | SWIFT_VERSION = 5.0; 691 | VERSIONING_SYSTEM = "apple-generic"; 692 | }; 693 | name = Release; 694 | }; 695 | /* End XCBuildConfiguration section */ 696 | 697 | /* Begin XCConfigurationList section */ 698 | 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { 699 | isa = XCConfigurationList; 700 | buildConfigurations = ( 701 | 331C8088294A63A400263BE5 /* Debug */, 702 | 331C8089294A63A400263BE5 /* Release */, 703 | 331C808A294A63A400263BE5 /* Profile */, 704 | ); 705 | defaultConfigurationIsVisible = 0; 706 | defaultConfigurationName = Release; 707 | }; 708 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 709 | isa = XCConfigurationList; 710 | buildConfigurations = ( 711 | 97C147031CF9000F007C117D /* Debug */, 712 | 97C147041CF9000F007C117D /* Release */, 713 | 249021D3217E4FDB00AE95B9 /* Profile */, 714 | ); 715 | defaultConfigurationIsVisible = 0; 716 | defaultConfigurationName = Release; 717 | }; 718 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 719 | isa = XCConfigurationList; 720 | buildConfigurations = ( 721 | 97C147061CF9000F007C117D /* Debug */, 722 | 97C147071CF9000F007C117D /* Release */, 723 | 249021D4217E4FDB00AE95B9 /* Profile */, 724 | ); 725 | defaultConfigurationIsVisible = 0; 726 | defaultConfigurationName = Release; 727 | }; 728 | /* End XCConfigurationList section */ 729 | }; 730 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 731 | } 732 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 1086627119682-cpa5vejcfs9voe3bp0ltf9kdstkc375u.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.1086627119682-cpa5vejcfs9voe3bp0ltf9kdstkc375u 9 | ANDROID_CLIENT_ID 10 | 1086627119682-3a0smf3p0pcaf60pl2vd88da7afb5bq1.apps.googleusercontent.com 11 | API_KEY 12 | AIzaSyB4sl8UO3b0YzZG-x_gbim52EpKzl1aVoU 13 | GCM_SENDER_ID 14 | 1086627119682 15 | PLIST_VERSION 16 | 1 17 | BUNDLE_ID 18 | com.example.phoneAuthHandlerDemo 19 | PROJECT_ID 20 | packages-demo-119b7 21 | STORAGE_BUCKET 22 | packages-demo-119b7.appspot.com 23 | IS_ADS_ENABLED 24 | 25 | IS_ANALYTICS_ENABLED 26 | 27 | IS_APPINVITE_ENABLED 28 | 29 | IS_GCM_ENABLED 30 | 31 | IS_SIGNIN_ENABLED 32 | 33 | GOOGLE_APP_ID 34 | 1:1086627119682:ios:bcc3c8c5820ecb1076a3db 35 | 36 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Phone Auth Handler Demo 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | FirebasePhoneAuthHandler Demo 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/ios/firebase_app_id_file.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_generated_by": "FlutterFire CLI", 3 | "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", 4 | "GOOGLE_APP_ID": "1:1086627119682:ios:c6b466bf06bbdce976a3db", 5 | "FIREBASE_PROJECT_ID": "packages-demo-119b7", 6 | "GCM_SENDER_ID": "1086627119682" 7 | } -------------------------------------------------------------------------------- /example/lib/firebase_options.dart: -------------------------------------------------------------------------------- 1 | // File generated by FlutterFire CLI. 2 | // ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members 3 | import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; 4 | import 'package:flutter/foundation.dart' 5 | show defaultTargetPlatform, kIsWeb, TargetPlatform; 6 | 7 | /// Default [FirebaseOptions] for use with your Firebase apps. 8 | /// 9 | /// Example: 10 | /// ```dart 11 | /// import 'firebase_options.dart'; 12 | /// // ... 13 | /// await Firebase.initializeApp( 14 | /// options: DefaultFirebaseOptions.currentPlatform, 15 | /// ); 16 | /// ``` 17 | class DefaultFirebaseOptions { 18 | static FirebaseOptions get currentPlatform { 19 | if (kIsWeb) { 20 | return web; 21 | } 22 | switch (defaultTargetPlatform) { 23 | case TargetPlatform.android: 24 | return android; 25 | case TargetPlatform.iOS: 26 | return ios; 27 | case TargetPlatform.macOS: 28 | throw UnsupportedError( 29 | 'DefaultFirebaseOptions have not been configured for macos - ' 30 | 'you can reconfigure this by running the FlutterFire CLI again.', 31 | ); 32 | default: 33 | throw UnsupportedError( 34 | 'DefaultFirebaseOptions are not supported for this platform.', 35 | ); 36 | } 37 | } 38 | 39 | static const FirebaseOptions web = FirebaseOptions( 40 | apiKey: 'AIzaSyDFpaHn58NWiR90pPWJsEAOPhd-2oiha2Y', 41 | appId: '1:1086627119682:web:85235bbffc017f0d76a3db', 42 | messagingSenderId: '1086627119682', 43 | projectId: 'packages-demo-119b7', 44 | authDomain: 'packages-demo-119b7.firebaseapp.com', 45 | storageBucket: 'packages-demo-119b7.appspot.com', 46 | ); 47 | 48 | static const FirebaseOptions android = FirebaseOptions( 49 | apiKey: 'AIzaSyBP2BlxWwlm0GI4PZHZRl-NAf6fiG1IhJY', 50 | appId: '1:1086627119682:android:3aecab4a98fe263176a3db', 51 | messagingSenderId: '1086627119682', 52 | projectId: 'packages-demo-119b7', 53 | storageBucket: 'packages-demo-119b7.appspot.com', 54 | ); 55 | 56 | static const FirebaseOptions ios = FirebaseOptions( 57 | apiKey: 'AIzaSyB4sl8UO3b0YzZG-x_gbim52EpKzl1aVoU', 58 | appId: '1:1086627119682:ios:c6b466bf06bbdce976a3db', 59 | messagingSenderId: '1086627119682', 60 | projectId: 'packages-demo-119b7', 61 | storageBucket: 'packages-demo-119b7.appspot.com', 62 | androidClientId: '1086627119682-3a0smf3p0pcaf60pl2vd88da7afb5bq1.apps.googleusercontent.com', 63 | iosClientId: '1086627119682-cpa5vejcfs9voe3bp0ltf9kdstkc375u.apps.googleusercontent.com', 64 | iosBundleId: 'com.example.phoneAuthHandlerDemo', 65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_core/firebase_core.dart'; 2 | import 'package:firebase_phone_auth_handler/firebase_phone_auth_handler.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:phone_auth_handler_demo/firebase_options.dart'; 5 | import 'package:phone_auth_handler_demo/screens/splash_screen.dart'; 6 | import 'package:phone_auth_handler_demo/utils/app_theme.dart'; 7 | import 'package:phone_auth_handler_demo/utils/globals.dart'; 8 | import 'package:phone_auth_handler_demo/utils/route_generator.dart'; 9 | 10 | void main() async { 11 | WidgetsFlutterBinding.ensureInitialized(); 12 | await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); 13 | runApp(const _MainApp()); 14 | } 15 | 16 | class _MainApp extends StatelessWidget { 17 | const _MainApp(); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return FirebasePhoneAuthProvider( 22 | child: MaterialApp( 23 | debugShowCheckedModeBanner: false, 24 | title: 'FirebasePhoneAuthHandler Demo', 25 | scaffoldMessengerKey: Globals.scaffoldMessengerKey, 26 | theme: AppTheme.lightTheme, 27 | darkTheme: AppTheme.darkTheme, 28 | onGenerateRoute: RouteGenerator.generateRoute, 29 | initialRoute: SplashScreen.id, 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /example/lib/screens/authentication_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_container/easy_container.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:intl_phone_field/intl_phone_field.dart'; 4 | import 'package:phone_auth_handler_demo/screens/verify_phone_number_screen.dart'; 5 | import 'package:phone_auth_handler_demo/utils/helpers.dart'; 6 | 7 | class AuthenticationScreen extends StatefulWidget { 8 | static const id = 'AuthenticationScreen'; 9 | 10 | const AuthenticationScreen({super.key}); 11 | 12 | @override 13 | State createState() => _AuthenticationScreenState(); 14 | } 15 | 16 | class _AuthenticationScreenState extends State { 17 | String? phoneNumber; 18 | 19 | final _formKey = GlobalKey(); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return SafeArea( 24 | child: Scaffold( 25 | body: Padding( 26 | padding: const EdgeInsets.all(15), 27 | child: Column( 28 | mainAxisAlignment: MainAxisAlignment.center, 29 | children: [ 30 | const Text( 31 | "We'll send an SMS with a verification code...", 32 | style: TextStyle(fontSize: 22), 33 | ), 34 | const SizedBox(height: 15), 35 | EasyContainer( 36 | elevation: 0, 37 | borderRadius: 10, 38 | color: Colors.transparent, 39 | child: Form( 40 | key: _formKey, 41 | child: IntlPhoneField( 42 | autofocus: true, 43 | invalidNumberMessage: 'Invalid Phone Number!', 44 | textAlignVertical: TextAlignVertical.center, 45 | style: const TextStyle(fontSize: 25), 46 | onChanged: (phone) => phoneNumber = phone.completeNumber, 47 | initialCountryCode: 'IN', 48 | flagsButtonPadding: const EdgeInsets.only(right: 10), 49 | showDropdownIcon: false, 50 | keyboardType: TextInputType.phone, 51 | ), 52 | ), 53 | ), 54 | const SizedBox(height: 15), 55 | EasyContainer( 56 | width: double.infinity, 57 | onTap: () async { 58 | if (isNullOrBlank(phoneNumber) || 59 | !_formKey.currentState!.validate()) { 60 | showSnackBar('Please enter a valid phone number!'); 61 | } else { 62 | Navigator.pushNamed( 63 | context, 64 | VerifyPhoneNumberScreen.id, 65 | arguments: phoneNumber, 66 | ); 67 | } 68 | }, 69 | child: const Text( 70 | 'Verify', 71 | style: TextStyle(fontSize: 18), 72 | ), 73 | ), 74 | ], 75 | ), 76 | ), 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /example/lib/screens/home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_container/easy_container.dart'; 2 | import 'package:firebase_phone_auth_handler/firebase_phone_auth_handler.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:phone_auth_handler_demo/screens/authentication_screen.dart'; 5 | import 'package:phone_auth_handler_demo/utils/globals.dart'; 6 | import 'package:phone_auth_handler_demo/utils/helpers.dart'; 7 | 8 | class HomeScreen extends StatelessWidget { 9 | static const id = 'HomeScreen'; 10 | 11 | const HomeScreen({super.key}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return SafeArea( 16 | child: Scaffold( 17 | body: Padding( 18 | padding: const EdgeInsets.all(15), 19 | child: Column( 20 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 21 | children: [ 22 | const Padding( 23 | padding: EdgeInsets.all(15), 24 | child: SizedBox( 25 | width: double.infinity, 26 | child: FittedBox( 27 | child: Text('Logged in user UID'), 28 | ), 29 | ), 30 | ), 31 | SizedBox( 32 | width: double.infinity, 33 | child: FittedBox( 34 | child: Text(Globals.firebaseUser!.uid), 35 | ), 36 | ), 37 | EasyContainer( 38 | onTap: () async { 39 | await FirebasePhoneAuthHandler.signOut(context); 40 | showSnackBar('Logged out successfully!'); 41 | 42 | if (context.mounted) { 43 | Navigator.pushNamedAndRemoveUntil( 44 | context, 45 | AuthenticationScreen.id, 46 | (route) => false, 47 | ); 48 | } 49 | }, 50 | child: const Text('Logout'), 51 | ), 52 | ], 53 | ), 54 | ), 55 | ), 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /example/lib/screens/splash_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:phone_auth_handler_demo/screens/authentication_screen.dart'; 3 | import 'package:phone_auth_handler_demo/screens/home_screen.dart'; 4 | import 'package:phone_auth_handler_demo/utils/globals.dart'; 5 | import 'package:phone_auth_handler_demo/widgets/custom_loader.dart'; 6 | 7 | class SplashScreen extends StatefulWidget { 8 | static const id = 'SplashScreen'; 9 | 10 | const SplashScreen({super.key}); 11 | 12 | @override 13 | State createState() => _SplashScreenState(); 14 | } 15 | 16 | class _SplashScreenState extends State { 17 | @override 18 | void initState() { 19 | (() async { 20 | await Future.delayed(Duration.zero); 21 | final isLoggedIn = Globals.firebaseUser != null; 22 | 23 | if (!mounted) return; 24 | Navigator.pushReplacementNamed( 25 | context, 26 | isLoggedIn ? HomeScreen.id : AuthenticationScreen.id, 27 | ); 28 | })(); 29 | super.initState(); 30 | } 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | return const SafeArea( 35 | child: Scaffold( 36 | body: CustomLoader(), 37 | ), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /example/lib/screens/verify_phone_number_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_phone_auth_handler/firebase_phone_auth_handler.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:phone_auth_handler_demo/screens/home_screen.dart'; 4 | import 'package:phone_auth_handler_demo/utils/helpers.dart'; 5 | import 'package:phone_auth_handler_demo/widgets/custom_loader.dart'; 6 | import 'package:phone_auth_handler_demo/widgets/pin_input_field.dart'; 7 | 8 | class VerifyPhoneNumberScreen extends StatefulWidget { 9 | static const id = 'VerifyPhoneNumberScreen'; 10 | 11 | final String phoneNumber; 12 | 13 | const VerifyPhoneNumberScreen({ 14 | super.key, 15 | required this.phoneNumber, 16 | }); 17 | 18 | @override 19 | State createState() => 20 | _VerifyPhoneNumberScreenState(); 21 | } 22 | 23 | class _VerifyPhoneNumberScreenState extends State 24 | with WidgetsBindingObserver { 25 | bool isKeyboardVisible = false; 26 | 27 | late final ScrollController scrollController; 28 | 29 | @override 30 | void initState() { 31 | scrollController = ScrollController(); 32 | WidgetsBinding.instance.addObserver(this); 33 | super.initState(); 34 | } 35 | 36 | @override 37 | void dispose() { 38 | WidgetsBinding.instance.removeObserver(this); 39 | scrollController.dispose(); 40 | super.dispose(); 41 | } 42 | 43 | @override 44 | void didChangeMetrics() { 45 | final bottomViewInsets = WidgetsBinding 46 | .instance.platformDispatcher.views.first.viewInsets.bottom; 47 | isKeyboardVisible = bottomViewInsets > 0; 48 | } 49 | 50 | // scroll to bottom of screen, when pin input field is in focus. 51 | Future _scrollToBottomOnKeyboardOpen() async { 52 | while (!isKeyboardVisible) { 53 | await Future.delayed(const Duration(milliseconds: 50)); 54 | } 55 | 56 | await Future.delayed(const Duration(milliseconds: 250)); 57 | 58 | await scrollController.animateTo( 59 | scrollController.position.maxScrollExtent, 60 | duration: const Duration(milliseconds: 250), 61 | curve: Curves.easeIn, 62 | ); 63 | } 64 | 65 | @override 66 | Widget build(BuildContext context) { 67 | return SafeArea( 68 | child: FirebasePhoneAuthHandler( 69 | phoneNumber: widget.phoneNumber, 70 | signOutOnSuccessfulVerification: false, 71 | sendOtpOnInitialize: true, 72 | linkWithExistingUser: false, 73 | autoRetrievalTimeOutDuration: const Duration(seconds: 60), 74 | otpExpirationDuration: const Duration(seconds: 60), 75 | onCodeSent: () { 76 | log(VerifyPhoneNumberScreen.id, msg: 'OTP sent!'); 77 | }, 78 | onLoginSuccess: (userCredential, autoVerified) async { 79 | log( 80 | VerifyPhoneNumberScreen.id, 81 | msg: autoVerified 82 | ? 'OTP was fetched automatically!' 83 | : 'OTP was verified manually!', 84 | ); 85 | 86 | showSnackBar('Phone number verified successfully!'); 87 | 88 | log( 89 | VerifyPhoneNumberScreen.id, 90 | msg: 'Login Success UID: ${userCredential.user?.uid}', 91 | ); 92 | 93 | Navigator.pushNamedAndRemoveUntil( 94 | context, 95 | HomeScreen.id, 96 | (route) => false, 97 | ); 98 | }, 99 | onLoginFailed: (authException, stackTrace) { 100 | log( 101 | VerifyPhoneNumberScreen.id, 102 | msg: authException.message, 103 | error: authException, 104 | stackTrace: stackTrace, 105 | ); 106 | 107 | switch (authException.code) { 108 | case 'invalid-phone-number': 109 | // invalid phone number 110 | return showSnackBar('Invalid phone number!'); 111 | case 'invalid-verification-code': 112 | // invalid otp entered 113 | return showSnackBar('The entered OTP is invalid!'); 114 | // handle other error codes 115 | default: 116 | showSnackBar('Something went wrong!'); 117 | // handle error further if needed 118 | } 119 | }, 120 | onError: (error, stackTrace) { 121 | log( 122 | VerifyPhoneNumberScreen.id, 123 | error: error, 124 | stackTrace: stackTrace, 125 | ); 126 | 127 | showSnackBar('An error occurred!'); 128 | }, 129 | builder: (context, controller) { 130 | return Scaffold( 131 | appBar: AppBar( 132 | leadingWidth: 0, 133 | leading: const SizedBox.shrink(), 134 | title: const Text('Verify Phone Number'), 135 | actions: [ 136 | if (controller.codeSent) 137 | TextButton( 138 | onPressed: controller.isOtpExpired 139 | ? () async { 140 | log(VerifyPhoneNumberScreen.id, msg: 'Resend OTP'); 141 | await controller.sendOTP(); 142 | } 143 | : null, 144 | child: Text( 145 | controller.isOtpExpired 146 | ? 'Resend' 147 | : '${controller.otpExpirationTimeLeft.inSeconds}s', 148 | style: const TextStyle(color: Colors.blue, fontSize: 18), 149 | ), 150 | ), 151 | const SizedBox(width: 5), 152 | ], 153 | ), 154 | body: controller.isSendingCode 155 | ? Column( 156 | mainAxisAlignment: MainAxisAlignment.center, 157 | crossAxisAlignment: CrossAxisAlignment.center, 158 | children: const [ 159 | CustomLoader(), 160 | SizedBox(height: 50), 161 | Center( 162 | child: Text( 163 | 'Sending OTP', 164 | style: TextStyle(fontSize: 25), 165 | ), 166 | ), 167 | ], 168 | ) 169 | : ListView( 170 | padding: const EdgeInsets.all(20), 171 | controller: scrollController, 172 | children: [ 173 | Text( 174 | "We've sent an SMS with a verification code to ${widget.phoneNumber}", 175 | style: const TextStyle(fontSize: 25), 176 | ), 177 | const SizedBox(height: 10), 178 | const Divider(), 179 | if (controller.isListeningForOtpAutoRetrieve) 180 | Column( 181 | children: const [ 182 | CustomLoader(), 183 | SizedBox(height: 50), 184 | Text( 185 | 'Listening for OTP', 186 | textAlign: TextAlign.center, 187 | style: TextStyle( 188 | fontSize: 25, 189 | fontWeight: FontWeight.w600, 190 | ), 191 | ), 192 | SizedBox(height: 15), 193 | Divider(), 194 | Text('OR', textAlign: TextAlign.center), 195 | Divider(), 196 | ], 197 | ), 198 | const SizedBox(height: 15), 199 | const Text( 200 | 'Enter OTP', 201 | style: TextStyle( 202 | fontSize: 20, 203 | fontWeight: FontWeight.w600, 204 | ), 205 | ), 206 | const SizedBox(height: 15), 207 | PinInputField( 208 | length: 6, 209 | onFocusChange: (hasFocus) async { 210 | if (hasFocus) await _scrollToBottomOnKeyboardOpen(); 211 | }, 212 | onSubmit: (enteredOtp) async { 213 | final verified = 214 | await controller.verifyOtp(enteredOtp); 215 | if (verified) { 216 | // number verify success 217 | // will call onLoginSuccess handler 218 | } else { 219 | // phone verification failed 220 | // will call onLoginFailed or onError callbacks with the error 221 | } 222 | }, 223 | ), 224 | ], 225 | ), 226 | ); 227 | }, 228 | ), 229 | ); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /example/lib/utils/app_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | 4 | class AppTheme { 5 | const AppTheme._(); 6 | 7 | static const _defaultElevation = 2.5; 8 | 9 | static ThemeData _baseTheme( 10 | Brightness brightness, { 11 | Color? textColor, 12 | Color? accentColor, 13 | Color? onAccentColor, 14 | Color? scaffoldBackgroundColor, 15 | }) { 16 | late final ColorScheme defaultColorScheme; 17 | late final SystemUiOverlayStyle systemUiOverlayStyle; 18 | switch (brightness) { 19 | case Brightness.light: 20 | defaultColorScheme = const ColorScheme.light(); 21 | systemUiOverlayStyle = SystemUiOverlayStyle.dark; 22 | textColor ??= Colors.black; 23 | break; 24 | case Brightness.dark: 25 | defaultColorScheme = const ColorScheme.dark(); 26 | systemUiOverlayStyle = SystemUiOverlayStyle.light; 27 | textColor ??= Colors.white; 28 | break; 29 | } 30 | 31 | return ThemeData( 32 | brightness: brightness, 33 | iconTheme: IconThemeData(color: textColor), 34 | scaffoldBackgroundColor: scaffoldBackgroundColor, 35 | appBarTheme: AppBarTheme( 36 | elevation: _defaultElevation, 37 | systemOverlayStyle: systemUiOverlayStyle, 38 | color: scaffoldBackgroundColor, 39 | iconTheme: IconThemeData(color: textColor), 40 | actionsIconTheme: IconThemeData(color: textColor), 41 | titleTextStyle: TextStyle( 42 | color: textColor, 43 | fontWeight: FontWeight.w600, 44 | fontSize: 20, 45 | ), 46 | ), 47 | colorScheme: defaultColorScheme.copyWith( 48 | brightness: brightness, 49 | // primary: accentColor, 50 | secondary: accentColor, 51 | onSecondary: onAccentColor ?? textColor, 52 | ), 53 | // toggleableActiveColor: accentColor, 54 | // textSelectionTheme: TextSelectionThemeData( 55 | // cursorColor: accentColor, 56 | // selectionColor: accentColor?.withOpacity(0.75), 57 | // selectionHandleColor: accentColor?.withOpacity(0.75), 58 | // ), 59 | dialogTheme: DialogTheme( 60 | elevation: _defaultElevation, 61 | shape: RoundedRectangleBorder( 62 | borderRadius: BorderRadius.circular(15), 63 | ), 64 | ), 65 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 66 | elevation: _defaultElevation, 67 | highlightElevation: _defaultElevation * 2, 68 | // backgroundColor: accentColor, 69 | ), 70 | snackBarTheme: SnackBarThemeData( 71 | elevation: _defaultElevation, 72 | backgroundColor: accentColor, 73 | contentTextStyle: TextStyle( 74 | fontSize: 16, 75 | color: onAccentColor ?? textColor, 76 | ), 77 | behavior: SnackBarBehavior.floating, 78 | // shape: RoundedRectangleBorder( 79 | // borderRadius: BorderRadius.circular(20), 80 | // ), 81 | ), 82 | // cupertinoOverrideTheme: CupertinoThemeData( 83 | // brightness: brightness, 84 | // scaffoldBackgroundColor: scaffoldBackgroundColor, 85 | // ), 86 | pageTransitionsTheme: PageTransitionsTheme( 87 | builders: { 88 | for (final targetValue in TargetPlatform.values) 89 | targetValue: const _SlideLeftTransitionsBuilder(), 90 | }, 91 | ), 92 | ); 93 | } 94 | 95 | static final lightTheme = _baseTheme( 96 | Brightness.light, 97 | accentColor: const Color(0xFF0669F8), 98 | onAccentColor: Colors.white, 99 | scaffoldBackgroundColor: const Color(0xFFDCDFE2), 100 | ).copyWith( 101 | cardColor: const Color(0xFFCACFD6), 102 | bottomNavigationBarTheme: const BottomNavigationBarThemeData( 103 | backgroundColor: Color(0xFFFAFAFA), 104 | ), 105 | ); 106 | 107 | static final darkTheme = _baseTheme( 108 | Brightness.dark, 109 | accentColor: Colors.tealAccent, 110 | onAccentColor: Colors.black, 111 | scaffoldBackgroundColor: const Color(0xFF040F2D), 112 | ).copyWith( 113 | cardColor: const Color(0xFF091642), 114 | bottomNavigationBarTheme: const BottomNavigationBarThemeData( 115 | backgroundColor: Color(0xFF050C25), 116 | ), 117 | ); 118 | } 119 | 120 | class _SlideLeftTransitionsBuilder extends PageTransitionsBuilder { 121 | const _SlideLeftTransitionsBuilder(); 122 | 123 | @override 124 | Widget buildTransitions(_, __, animation, ___, child) { 125 | return SlideTransition( 126 | position: CurvedAnimation( 127 | parent: animation, 128 | curve: Curves.easeIn, 129 | reverseCurve: Curves.easeOut, 130 | ).drive(Tween( 131 | begin: const Offset(1, 0), 132 | end: const Offset(0, 0), 133 | )), 134 | child: child, 135 | ); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /example/lib/utils/globals.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_phone_auth_handler/firebase_phone_auth_handler.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class Globals { 5 | const Globals._(); 6 | 7 | static final auth = FirebaseAuth.instance; 8 | 9 | static User? get firebaseUser => auth.currentUser; 10 | 11 | static final scaffoldMessengerKey = GlobalKey(); 12 | } 13 | -------------------------------------------------------------------------------- /example/lib/utils/helpers.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer' as devtools show log; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:phone_auth_handler_demo/utils/globals.dart'; 5 | 6 | void showSnackBar( 7 | String text, { 8 | Duration duration = const Duration(seconds: 2), 9 | }) { 10 | Globals.scaffoldMessengerKey.currentState 11 | ?..clearSnackBars() 12 | ..showSnackBar( 13 | SnackBar(content: Text(text), duration: duration), 14 | ); 15 | } 16 | 17 | bool isNullOrBlank(String? data) => data?.trim().isEmpty ?? true; 18 | 19 | void log( 20 | String screenId, { 21 | dynamic msg, 22 | dynamic error, 23 | StackTrace? stackTrace, 24 | }) => 25 | devtools.log( 26 | msg.toString(), 27 | error: error, 28 | name: screenId, 29 | stackTrace: stackTrace, 30 | ); 31 | -------------------------------------------------------------------------------- /example/lib/utils/route_generator.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:phone_auth_handler_demo/screens/authentication_screen.dart'; 3 | import 'package:phone_auth_handler_demo/screens/home_screen.dart'; 4 | import 'package:phone_auth_handler_demo/screens/splash_screen.dart'; 5 | import 'package:phone_auth_handler_demo/screens/verify_phone_number_screen.dart'; 6 | import 'package:phone_auth_handler_demo/utils/helpers.dart'; 7 | 8 | class RouteGenerator { 9 | static const _id = 'RouteGenerator'; 10 | 11 | static Route generateRoute(RouteSettings settings) { 12 | final args = settings.arguments as dynamic; 13 | log(_id, msg: "Pushed ${settings.name}(${args ?? ''})"); 14 | switch (settings.name) { 15 | case SplashScreen.id: 16 | return _route(const SplashScreen()); 17 | case AuthenticationScreen.id: 18 | return _route(const AuthenticationScreen()); 19 | case VerifyPhoneNumberScreen.id: 20 | return _route(VerifyPhoneNumberScreen(phoneNumber: args)); 21 | case HomeScreen.id: 22 | return _route(const HomeScreen()); 23 | default: 24 | return _errorRoute(settings.name); 25 | } 26 | } 27 | 28 | static MaterialPageRoute _route(Widget widget) => 29 | MaterialPageRoute(builder: (context) => widget); 30 | 31 | static Route _errorRoute(String? name) { 32 | return MaterialPageRoute( 33 | builder: (_) => Scaffold( 34 | body: Center( 35 | child: Text('ROUTE \n\n$name\n\nNOT FOUND'), 36 | ), 37 | ), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /example/lib/widgets/custom_loader.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CustomLoader extends StatelessWidget { 4 | static const id = 'CustomLoader'; 5 | 6 | final Color? color; 7 | final double radius; 8 | final double padding; 9 | 10 | const CustomLoader({ 11 | super.key, 12 | this.color, 13 | this.radius = 15, 14 | this.padding = 5, 15 | }); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Padding( 20 | padding: EdgeInsets.all(padding), 21 | child: Center( 22 | child: SizedBox( 23 | height: radius * 2, 24 | width: radius * 2, 25 | child: CircularProgressIndicator.adaptive( 26 | valueColor: AlwaysStoppedAnimation( 27 | color ?? Theme.of(context).colorScheme.secondary, 28 | ), 29 | ), 30 | ), 31 | ), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /example/lib/widgets/pin_input_field.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pinput/pinput.dart'; 3 | 4 | class PinInputField extends StatefulWidget { 5 | final int length; 6 | final void Function(bool)? onFocusChange; 7 | final void Function(String) onSubmit; 8 | 9 | const PinInputField({ 10 | super.key, 11 | this.length = 6, 12 | this.onFocusChange, 13 | required this.onSubmit, 14 | }); 15 | 16 | @override 17 | State createState() => _PinInputFieldState(); 18 | } 19 | 20 | class _PinInputFieldState extends State { 21 | late final TextEditingController _pinPutController; 22 | late final FocusNode _pinPutFocusNode; 23 | late final int _length; 24 | 25 | Size _findContainerSize(BuildContext context) { 26 | // full screen width 27 | double width = MediaQuery.of(context).size.width * 0.85; 28 | 29 | // using left-over space to get width of each container 30 | width /= _length; 31 | 32 | return Size.square(width); 33 | } 34 | 35 | @override 36 | void initState() { 37 | _pinPutController = TextEditingController(); 38 | _pinPutFocusNode = FocusNode(); 39 | 40 | if (widget.onFocusChange != null) { 41 | _pinPutFocusNode.addListener(() { 42 | widget.onFocusChange!(_pinPutFocusNode.hasFocus); 43 | }); 44 | } 45 | 46 | _length = widget.length; 47 | super.initState(); 48 | } 49 | 50 | @override 51 | void dispose() { 52 | _pinPutController.dispose(); 53 | _pinPutFocusNode.dispose(); 54 | super.dispose(); 55 | } 56 | 57 | PinTheme _getPinTheme( 58 | BuildContext context, { 59 | required Size size, 60 | }) { 61 | return PinTheme( 62 | height: size.height, 63 | width: size.width, 64 | textStyle: const TextStyle( 65 | fontSize: 25, 66 | fontWeight: FontWeight.bold, 67 | ), 68 | decoration: BoxDecoration( 69 | color: Theme.of(context).cardColor, 70 | borderRadius: BorderRadius.circular(7.5), 71 | ), 72 | ); 73 | } 74 | 75 | static const _focusScaleFactor = 1.15; 76 | 77 | @override 78 | Widget build(BuildContext context) { 79 | final size = _findContainerSize(context); 80 | final defaultPinTheme = _getPinTheme(context, size: size); 81 | 82 | return SizedBox( 83 | height: size.height * _focusScaleFactor, 84 | child: Pinput( 85 | length: _length, 86 | defaultPinTheme: defaultPinTheme, 87 | focusedPinTheme: defaultPinTheme.copyWith( 88 | height: size.height * _focusScaleFactor, 89 | width: size.width * _focusScaleFactor, 90 | decoration: defaultPinTheme.decoration!.copyWith( 91 | border: Border.all(color: Theme.of(context).colorScheme.secondary), 92 | ), 93 | ), 94 | errorPinTheme: defaultPinTheme.copyWith( 95 | decoration: BoxDecoration( 96 | color: Theme.of(context).colorScheme.error, 97 | borderRadius: BorderRadius.circular(8), 98 | ), 99 | ), 100 | focusNode: _pinPutFocusNode, 101 | controller: _pinPutController, 102 | onCompleted: widget.onSubmit, 103 | pinAnimationType: PinAnimationType.scale, 104 | // submittedFieldDecoration: _pinPutDecoration, 105 | // selectedFieldDecoration: _pinPutDecoration, 106 | // followingFieldDecoration: _pinPutDecoration, 107 | // textStyle: const TextStyle( 108 | // color: Colors.black, 109 | // fontSize: 20.0, 110 | // fontWeight: FontWeight.w600, 111 | // ), 112 | ), 113 | ); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _flutterfire_internals: 5 | dependency: transitive 6 | description: 7 | name: _flutterfire_internals 8 | sha256: "5534e701a2c505fed1f0799e652dd6ae23bd4d2c4cf797220e5ced5764a7c1c2" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "1.3.44" 12 | async: 13 | dependency: transitive 14 | description: 15 | name: async 16 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.11.0" 20 | boolean_selector: 21 | dependency: transitive 22 | description: 23 | name: boolean_selector 24 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "2.1.1" 28 | characters: 29 | dependency: transitive 30 | description: 31 | name: characters 32 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.3.0" 36 | clock: 37 | dependency: transitive 38 | description: 39 | name: clock 40 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.1.1" 44 | collection: 45 | dependency: transitive 46 | description: 47 | name: collection 48 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.18.0" 52 | easy_container: 53 | dependency: "direct main" 54 | description: 55 | name: easy_container 56 | sha256: a8a33030e426329b4c36954ddddee60d2e8bb92a90b48c84d918c8194a13d3b2 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "1.0.5" 60 | fake_async: 61 | dependency: transitive 62 | description: 63 | name: fake_async 64 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "1.3.1" 68 | firebase_auth: 69 | dependency: transitive 70 | description: 71 | name: firebase_auth 72 | sha256: d453acec0d958ba0e25d41a9901b32cb77d1535766903dea7a61b2788c304596 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "5.3.1" 76 | firebase_auth_platform_interface: 77 | dependency: transitive 78 | description: 79 | name: firebase_auth_platform_interface 80 | sha256: "78966c2ef774f5bf2a8381a307222867e9ece3509110500f7a138c115926aa65" 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "7.4.7" 84 | firebase_auth_web: 85 | dependency: transitive 86 | description: 87 | name: firebase_auth_web 88 | sha256: "77ad3b252badedd3f08dfa21a4c7fe244be96c6da3a4067f253b13ea5d32424c" 89 | url: "https://pub.dev" 90 | source: hosted 91 | version: "5.13.2" 92 | firebase_core: 93 | dependency: "direct main" 94 | description: 95 | name: firebase_core 96 | sha256: "51dfe2fbf3a984787a2e7b8592f2f05c986bfedd6fdacea3f9e0a7beb334de96" 97 | url: "https://pub.dev" 98 | source: hosted 99 | version: "3.6.0" 100 | firebase_core_platform_interface: 101 | dependency: transitive 102 | description: 103 | name: firebase_core_platform_interface 104 | sha256: e30da58198a6d4b49d5bce4e852f985c32cb10db329ebef9473db2b9f09ce810 105 | url: "https://pub.dev" 106 | source: hosted 107 | version: "5.3.0" 108 | firebase_core_web: 109 | dependency: transitive 110 | description: 111 | name: firebase_core_web 112 | sha256: f967a7138f5d2ffb1ce15950e2a382924239eaa521150a8f144af34e68b3b3e5 113 | url: "https://pub.dev" 114 | source: hosted 115 | version: "2.18.1" 116 | firebase_phone_auth_handler: 117 | dependency: "direct main" 118 | description: 119 | path: ".." 120 | relative: true 121 | source: path 122 | version: "1.1.0+1" 123 | flutter: 124 | dependency: "direct main" 125 | description: flutter 126 | source: sdk 127 | version: "0.0.0" 128 | flutter_lints: 129 | dependency: "direct dev" 130 | description: 131 | name: flutter_lints 132 | sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" 133 | url: "https://pub.dev" 134 | source: hosted 135 | version: "5.0.0" 136 | flutter_test: 137 | dependency: transitive 138 | description: flutter 139 | source: sdk 140 | version: "0.0.0" 141 | flutter_web_plugins: 142 | dependency: transitive 143 | description: flutter 144 | source: sdk 145 | version: "0.0.0" 146 | http_parser: 147 | dependency: transitive 148 | description: 149 | name: http_parser 150 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" 151 | url: "https://pub.dev" 152 | source: hosted 153 | version: "4.0.2" 154 | intl_phone_field: 155 | dependency: "direct main" 156 | description: 157 | name: intl_phone_field 158 | sha256: "73819d3dfcb68d2c85663606f6842597c3ddf6688ac777f051b17814fe767bbf" 159 | url: "https://pub.dev" 160 | source: hosted 161 | version: "3.2.0" 162 | leak_tracker: 163 | dependency: transitive 164 | description: 165 | name: leak_tracker 166 | sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" 167 | url: "https://pub.dev" 168 | source: hosted 169 | version: "10.0.5" 170 | leak_tracker_flutter_testing: 171 | dependency: transitive 172 | description: 173 | name: leak_tracker_flutter_testing 174 | sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" 175 | url: "https://pub.dev" 176 | source: hosted 177 | version: "3.0.5" 178 | leak_tracker_testing: 179 | dependency: transitive 180 | description: 181 | name: leak_tracker_testing 182 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" 183 | url: "https://pub.dev" 184 | source: hosted 185 | version: "3.0.1" 186 | lints: 187 | dependency: transitive 188 | description: 189 | name: lints 190 | sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" 191 | url: "https://pub.dev" 192 | source: hosted 193 | version: "5.0.0" 194 | matcher: 195 | dependency: transitive 196 | description: 197 | name: matcher 198 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb 199 | url: "https://pub.dev" 200 | source: hosted 201 | version: "0.12.16+1" 202 | material_color_utilities: 203 | dependency: transitive 204 | description: 205 | name: material_color_utilities 206 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 207 | url: "https://pub.dev" 208 | source: hosted 209 | version: "0.11.1" 210 | meta: 211 | dependency: transitive 212 | description: 213 | name: meta 214 | sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 215 | url: "https://pub.dev" 216 | source: hosted 217 | version: "1.15.0" 218 | nested: 219 | dependency: transitive 220 | description: 221 | name: nested 222 | sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" 223 | url: "https://pub.dev" 224 | source: hosted 225 | version: "1.0.0" 226 | path: 227 | dependency: transitive 228 | description: 229 | name: path 230 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 231 | url: "https://pub.dev" 232 | source: hosted 233 | version: "1.9.0" 234 | pinput: 235 | dependency: "direct main" 236 | description: 237 | name: pinput 238 | sha256: "7bf9aa7d0eeb3da9f7d49d2087c7bc7d36cd277d2e94cc31c6da52e1ebb048d0" 239 | url: "https://pub.dev" 240 | source: hosted 241 | version: "5.0.0" 242 | plugin_platform_interface: 243 | dependency: transitive 244 | description: 245 | name: plugin_platform_interface 246 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" 247 | url: "https://pub.dev" 248 | source: hosted 249 | version: "2.1.8" 250 | provider: 251 | dependency: transitive 252 | description: 253 | name: provider 254 | sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c 255 | url: "https://pub.dev" 256 | source: hosted 257 | version: "6.1.2" 258 | sky_engine: 259 | dependency: transitive 260 | description: flutter 261 | source: sdk 262 | version: "0.0.99" 263 | source_span: 264 | dependency: transitive 265 | description: 266 | name: source_span 267 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 268 | url: "https://pub.dev" 269 | source: hosted 270 | version: "1.10.0" 271 | stack_trace: 272 | dependency: transitive 273 | description: 274 | name: stack_trace 275 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 276 | url: "https://pub.dev" 277 | source: hosted 278 | version: "1.11.1" 279 | stream_channel: 280 | dependency: transitive 281 | description: 282 | name: stream_channel 283 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 284 | url: "https://pub.dev" 285 | source: hosted 286 | version: "2.1.2" 287 | string_scanner: 288 | dependency: transitive 289 | description: 290 | name: string_scanner 291 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 292 | url: "https://pub.dev" 293 | source: hosted 294 | version: "1.2.0" 295 | term_glyph: 296 | dependency: transitive 297 | description: 298 | name: term_glyph 299 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 300 | url: "https://pub.dev" 301 | source: hosted 302 | version: "1.2.1" 303 | test_api: 304 | dependency: transitive 305 | description: 306 | name: test_api 307 | sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" 308 | url: "https://pub.dev" 309 | source: hosted 310 | version: "0.7.2" 311 | typed_data: 312 | dependency: transitive 313 | description: 314 | name: typed_data 315 | sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c 316 | url: "https://pub.dev" 317 | source: hosted 318 | version: "1.3.2" 319 | universal_platform: 320 | dependency: transitive 321 | description: 322 | name: universal_platform 323 | sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" 324 | url: "https://pub.dev" 325 | source: hosted 326 | version: "1.1.0" 327 | vector_math: 328 | dependency: transitive 329 | description: 330 | name: vector_math 331 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 332 | url: "https://pub.dev" 333 | source: hosted 334 | version: "2.1.4" 335 | vm_service: 336 | dependency: transitive 337 | description: 338 | name: vm_service 339 | sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" 340 | url: "https://pub.dev" 341 | source: hosted 342 | version: "14.2.5" 343 | web: 344 | dependency: transitive 345 | description: 346 | name: web 347 | sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb 348 | url: "https://pub.dev" 349 | source: hosted 350 | version: "1.1.0" 351 | sdks: 352 | dart: ">=3.5.3 <4.0.0" 353 | flutter: ">=3.22.0" 354 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: phone_auth_handler_demo 2 | description: Demo for the package firebase_phone_auth_handler on pub.dev 3 | 4 | publish_to: 'none' 5 | 6 | version: 1.0.0+1 7 | 8 | environment: 9 | sdk: ^3.5.3 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | 15 | pinput: ^5.0.0 16 | firebase_core: ^3.6.0 17 | easy_container: ^1.0.5 18 | intl_phone_field: ^3.2.0 19 | firebase_phone_auth_handler: 20 | path: "../" 21 | 22 | dev_dependencies: 23 | flutter_lints: ^5.0.0 24 | 25 | 26 | flutter: 27 | uses-material-design: true -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/web/favicon.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rithik-dev/firebase_phone_auth_handler/ede491d2795f38189ea3526da61a3c24b9dbca5d/example/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | FirebasePhoneAuthHandler Demo 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phone_auth_handler_demo", 3 | "short_name": "phone_auth_handler_demo", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "Demo for the package firebase_phone_auth_handler on pub.dev", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /lib/firebase_phone_auth_handler.dart: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | export 'package:firebase_auth/firebase_auth.dart'; 4 | 5 | export 'src/auth_handler.dart'; 6 | export 'src/auth_provider.dart'; 7 | -------------------------------------------------------------------------------- /lib/src/auth_controller.dart: -------------------------------------------------------------------------------- 1 | part of 'auth_handler.dart'; 2 | 3 | class FirebasePhoneAuthController extends ChangeNotifier { 4 | static FirebasePhoneAuthController _of( 5 | BuildContext context, { 6 | bool listen = true, 7 | }) => 8 | Provider.of(context, listen: listen); 9 | 10 | /// {@macro autoRetrievalTimeOutDuration} 11 | static const kAutoRetrievalTimeOutDuration = Duration(minutes: 1); 12 | 13 | /// Firebase auth instance using the default [FirebaseApp]. 14 | static final FirebaseAuth _auth = FirebaseAuth.instance; 15 | 16 | /// Web confirmation result for OTP. 17 | ConfirmationResult? _webConfirmationResult; 18 | 19 | /// {@macro recaptchaVerifierForWeb} 20 | RecaptchaVerifier? _recaptchaVerifierForWeb; 21 | 22 | /// The [_forceResendingToken] obtained from [codeSent] 23 | /// callback to force re-sending another verification SMS before the 24 | /// auto-retrieval timeout. 25 | int? _forceResendingToken; 26 | 27 | /// {@macro phoneNumber} 28 | String? _phoneNumber; 29 | 30 | /// The phone auth verification ID. 31 | String? _verificationId; 32 | 33 | /// Timer object for SMS auto-retrieval. 34 | Timer? _otpAutoRetrievalTimer; 35 | 36 | /// Timer object for OTP expiration. 37 | Timer? _otpExpirationTimer; 38 | 39 | /// Whether OTP to the given phoneNumber is sent or not. 40 | bool codeSent = false; 41 | 42 | /// Whether OTP is being sent to the given phoneNumber. 43 | bool get isSendingCode => !codeSent; 44 | 45 | /// Whether the current platform is web or not; 46 | bool get isWeb => kIsWeb; 47 | 48 | /// {@macro signOutOnSuccessfulVerification} 49 | late bool _signOutOnSuccessfulVerification; 50 | 51 | /// {@macro onCodeSent} 52 | VoidCallback? _onCodeSent; 53 | 54 | /// {@macro onLoginSuccess} 55 | OnLoginSuccess? _onLoginSuccess; 56 | 57 | /// {@macro onLoginFailed} 58 | OnLoginFailed? _onLoginFailed; 59 | 60 | /// {@macro onError} 61 | OnError? _onError; 62 | 63 | /// {@macro linkWithExistingUser} 64 | late bool _linkWithExistingUser; 65 | 66 | /// Set callbacks and other data. (only for internal use) 67 | void _setData({ 68 | required String phoneNumber, 69 | required OnLoginSuccess? onLoginSuccess, 70 | required OnLoginFailed? onLoginFailed, 71 | required OnError? onError, 72 | required VoidCallback? onCodeSent, 73 | required bool signOutOnSuccessfulVerification, 74 | required RecaptchaVerifier? recaptchaVerifierForWeb, 75 | required Duration autoRetrievalTimeOutDuration, 76 | required Duration otpExpirationDuration, 77 | required bool linkWithExistingUser, 78 | }) { 79 | _phoneNumber = phoneNumber; 80 | _signOutOnSuccessfulVerification = signOutOnSuccessfulVerification; 81 | _onLoginSuccess = onLoginSuccess; 82 | _onLoginFailed = onLoginFailed; 83 | _onError = onError; 84 | _onCodeSent = onCodeSent; 85 | _linkWithExistingUser = linkWithExistingUser; 86 | _autoRetrievalTimeOutDuration = autoRetrievalTimeOutDuration; 87 | _otpExpirationDuration = otpExpirationDuration; 88 | if (kIsWeb) _recaptchaVerifierForWeb = recaptchaVerifierForWeb; 89 | } 90 | 91 | /// [otpExpirationTimeLeft] can be used to display a reverse countdown, starting from 92 | /// [_otpExpirationDuration.inSeconds]s till 0, and can show the resend 93 | /// button, to let user request a new OTP. 94 | Duration get otpExpirationTimeLeft { 95 | final otpTickDuration = Duration( 96 | seconds: (_otpExpirationTimer?.tick ?? 0), 97 | ); 98 | return _otpExpirationDuration - otpTickDuration; 99 | } 100 | 101 | /// [autoRetrievalTimeLeft] can be used to display a reverse countdown, starting from 102 | /// [_autoRetrievalTimeOutDuration.inSeconds]s till 0, and can show the 103 | /// the listening for OTP view, and also the time left. 104 | /// 105 | /// After this timer is exhausted, the device no longer tries to auto-fetch 106 | /// the OTP, and requires user to manually enter it. 107 | Duration get autoRetrievalTimeLeft { 108 | final otpTickDuration = Duration( 109 | seconds: (_otpAutoRetrievalTimer?.tick ?? 0), 110 | ); 111 | return _autoRetrievalTimeOutDuration - otpTickDuration; 112 | } 113 | 114 | /// Whether the otp has expired or not. 115 | bool get isOtpExpired => !(_otpExpirationTimer?.isActive ?? false); 116 | 117 | /// Whether the otp retrieval timer is active or not. 118 | bool get isListeningForOtpAutoRetrieve => 119 | _otpAutoRetrievalTimer?.isActive ?? false; 120 | 121 | /// {@macro autoRetrievalTimeOutDuration} 122 | static Duration _autoRetrievalTimeOutDuration = kAutoRetrievalTimeOutDuration; 123 | 124 | /// {@macro otpExpirationDuration} 125 | static Duration _otpExpirationDuration = kAutoRetrievalTimeOutDuration; 126 | 127 | /// Verify the OTP sent to [_phoneNumber] and login user is OTP was correct. 128 | /// 129 | /// Returns true if the [otp] passed was correct and the user was logged in successfully. 130 | /// On login success, [_onLoginSuccess] is called. 131 | /// 132 | /// If the [otp] passed is incorrect, or the [otp] is expired or any other 133 | /// error occurs, the functions returns false. 134 | /// 135 | /// Also, [_onLoginFailed] is called with [FirebaseAuthException] 136 | /// object to handle the error. 137 | Future verifyOtp(String otp) async { 138 | if ((!kIsWeb && _verificationId == null) || 139 | (kIsWeb && _webConfirmationResult == null)) { 140 | return false; 141 | } 142 | 143 | try { 144 | if (kIsWeb) { 145 | final userCredential = await _webConfirmationResult!.confirm(otp); 146 | return await _loginUser( 147 | userCredential: userCredential, 148 | autoVerified: false, 149 | ); 150 | } else { 151 | final credential = PhoneAuthProvider.credential( 152 | verificationId: _verificationId!, 153 | smsCode: otp, 154 | ); 155 | return await _loginUser( 156 | authCredential: credential, 157 | autoVerified: false, 158 | ); 159 | } 160 | } on FirebaseAuthException catch (e, s) { 161 | _onLoginFailed?.call(e, s); 162 | return false; 163 | } catch (e, s) { 164 | _onError?.call(e, s); 165 | return false; 166 | } 167 | } 168 | 169 | /// Send OTP to the given [_phoneNumber]. 170 | /// 171 | /// Returns true if OTP was sent successfully. 172 | /// 173 | /// If for any reason, the OTP is not send, 174 | /// [_onLoginFailed] is called with [FirebaseAuthException] 175 | /// object to handle the error. 176 | /// 177 | /// [shouldAwaitCodeSend] can be used to await the OTP send. 178 | /// The firebase method completes early, and if [shouldAwaitCodeSend] is false, 179 | /// [sendOTP] will complete early, and the OTP will be sent in the background. 180 | /// Whereas, if [shouldAwaitCodeSend] is true, [sendOTP] will wait for the 181 | /// code send callback to be fired, and [sendOTP] will complete only after 182 | /// that callback is fired. Not applicable on web. 183 | Future sendOTP({bool shouldAwaitCodeSend = true}) async { 184 | Completer? codeSendCompleter; 185 | 186 | codeSent = false; 187 | await Future.delayed(Duration.zero, notifyListeners); 188 | 189 | verificationCompletedCallback(AuthCredential authCredential) async { 190 | await _loginUser(authCredential: authCredential, autoVerified: true); 191 | } 192 | 193 | verificationFailedCallback(FirebaseAuthException authException) { 194 | final stackTrace = authException.stackTrace ?? StackTrace.current; 195 | 196 | if (codeSendCompleter != null && !codeSendCompleter.isCompleted) { 197 | codeSendCompleter.completeError(authException, stackTrace); 198 | } 199 | _onLoginFailed?.call(authException, stackTrace); 200 | } 201 | 202 | codeSentCallback( 203 | String verificationId, [ 204 | int? forceResendingToken, 205 | ]) async { 206 | _verificationId = verificationId; 207 | _forceResendingToken = forceResendingToken; 208 | codeSent = true; 209 | _onCodeSent?.call(); 210 | if (codeSendCompleter != null && !codeSendCompleter.isCompleted) { 211 | codeSendCompleter.complete(); 212 | } 213 | _setTimer(); 214 | } 215 | 216 | codeAutoRetrievalTimeoutCallback(String verificationId) { 217 | _verificationId = verificationId; 218 | } 219 | 220 | try { 221 | if (kIsWeb) { 222 | _webConfirmationResult = await _auth.signInWithPhoneNumber( 223 | _phoneNumber!, 224 | _recaptchaVerifierForWeb, 225 | ); 226 | codeSent = true; 227 | _onCodeSent?.call(); 228 | _setTimer(); 229 | } else { 230 | codeSendCompleter = Completer(); 231 | 232 | await _auth.verifyPhoneNumber( 233 | phoneNumber: _phoneNumber!, 234 | verificationCompleted: verificationCompletedCallback, 235 | verificationFailed: verificationFailedCallback, 236 | codeSent: codeSentCallback, 237 | codeAutoRetrievalTimeout: codeAutoRetrievalTimeoutCallback, 238 | timeout: _autoRetrievalTimeOutDuration, 239 | forceResendingToken: _forceResendingToken, 240 | ); 241 | 242 | if (shouldAwaitCodeSend) await codeSendCompleter.future; 243 | } 244 | 245 | return true; 246 | } on FirebaseAuthException catch (e, s) { 247 | if (codeSendCompleter != null && !codeSendCompleter.isCompleted) { 248 | codeSendCompleter.completeError(e, s); 249 | } 250 | _onLoginFailed?.call(e, s); 251 | return false; 252 | } catch (e, s) { 253 | if (codeSendCompleter != null && !codeSendCompleter.isCompleted) { 254 | codeSendCompleter.completeError(e, s); 255 | } 256 | _onError?.call(e, s); 257 | return false; 258 | } 259 | } 260 | 261 | /// Called when the otp is verified either automatically (OTP auto fetched) 262 | /// or [verifyOtp] was called with the correct OTP. 263 | /// 264 | /// If true is returned that means the user was logged in successfully. 265 | /// 266 | /// If for any reason, the user fails to login, 267 | /// [_onLoginFailed] is called with [FirebaseAuthException] 268 | /// object to handle the error and false is returned. 269 | Future _loginUser({ 270 | AuthCredential? authCredential, 271 | UserCredential? userCredential, 272 | required bool autoVerified, 273 | }) async { 274 | if (kIsWeb) { 275 | if (userCredential != null) { 276 | if (_signOutOnSuccessfulVerification) await signOut(); 277 | _onLoginSuccess?.call(userCredential, autoVerified); 278 | return true; 279 | } else { 280 | return false; 281 | } 282 | } 283 | 284 | // Not on web. 285 | try { 286 | late final UserCredential authResult; 287 | 288 | if (_linkWithExistingUser) { 289 | authResult = await _auth.currentUser!.linkWithCredential( 290 | authCredential!, 291 | ); 292 | } else { 293 | authResult = await _auth.signInWithCredential(authCredential!); 294 | } 295 | 296 | if (_signOutOnSuccessfulVerification) await signOut(); 297 | _onLoginSuccess?.call(authResult, autoVerified); 298 | return true; 299 | } on FirebaseAuthException catch (e, s) { 300 | _onLoginFailed?.call(e, s); 301 | return false; 302 | } catch (e, s) { 303 | _onError?.call(e, s); 304 | return false; 305 | } 306 | } 307 | 308 | /// Set timer after code sent. 309 | void _setTimer() { 310 | _otpExpirationTimer = Timer.periodic( 311 | const Duration(seconds: 1), 312 | (timer) { 313 | if (timer.tick == _otpExpirationDuration.inSeconds) { 314 | _otpExpirationTimer?.cancel(); 315 | } 316 | try { 317 | notifyListeners(); 318 | } catch (_) {} 319 | }, 320 | ); 321 | _otpAutoRetrievalTimer = Timer.periodic( 322 | const Duration(seconds: 1), 323 | (timer) { 324 | if (timer.tick == _autoRetrievalTimeOutDuration.inSeconds) { 325 | _otpAutoRetrievalTimer?.cancel(); 326 | } 327 | try { 328 | notifyListeners(); 329 | } catch (_) {} 330 | }, 331 | ); 332 | notifyListeners(); 333 | } 334 | 335 | /// {@macro signOut} 336 | Future signOut() async { 337 | await _auth.signOut(); 338 | // notifyListeners(); 339 | } 340 | 341 | /// Clear all data 342 | void clear() { 343 | if (kIsWeb) { 344 | _recaptchaVerifierForWeb?.clear(); 345 | _recaptchaVerifierForWeb = null; 346 | } 347 | codeSent = false; 348 | _webConfirmationResult = null; 349 | _onLoginSuccess = null; 350 | _onLoginFailed = null; 351 | _onError = null; 352 | _onCodeSent = null; 353 | _signOutOnSuccessfulVerification = false; 354 | // _forceResendingToken = null; 355 | _otpExpirationTimer?.cancel(); 356 | _otpExpirationTimer = null; 357 | _otpAutoRetrievalTimer?.cancel(); 358 | _otpAutoRetrievalTimer = null; 359 | _phoneNumber = null; 360 | _linkWithExistingUser = false; 361 | _autoRetrievalTimeOutDuration = kAutoRetrievalTimeOutDuration; 362 | _otpExpirationDuration = kAutoRetrievalTimeOutDuration; 363 | _verificationId = null; 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /lib/src/auth_handler.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:firebase_auth/firebase_auth.dart'; 4 | import 'package:firebase_phone_auth_handler/src/type_definitions.dart'; 5 | import 'package:flutter/cupertino.dart'; 6 | import 'package:flutter/foundation.dart' show kIsWeb; 7 | import 'package:flutter/material.dart'; 8 | import 'package:provider/provider.dart'; 9 | 10 | part 'auth_controller.dart'; 11 | 12 | class FirebasePhoneAuthHandler extends StatefulWidget { 13 | const FirebasePhoneAuthHandler({ 14 | super.key, 15 | required this.phoneNumber, 16 | required this.builder, 17 | this.onLoginSuccess, 18 | this.onLoginFailed, 19 | this.onError, 20 | this.onCodeSent, 21 | this.signOutOnSuccessfulVerification = false, 22 | this.sendOtpOnInitialize = true, 23 | this.linkWithExistingUser = false, 24 | this.autoRetrievalTimeOutDuration = 25 | FirebasePhoneAuthController.kAutoRetrievalTimeOutDuration, 26 | this.otpExpirationDuration = 27 | FirebasePhoneAuthController.kAutoRetrievalTimeOutDuration, 28 | this.recaptchaVerifierForWebProvider, 29 | }); 30 | 31 | /// {@template phoneNumber} 32 | /// 33 | /// The phone number to which the OTP is to be sent. 34 | /// 35 | /// The phone number should also contain the country code. 36 | /// 37 | /// Example: +919876543210 where +91 is the country code and 9876543210 is the number. 38 | /// 39 | /// {@endtemplate} 40 | final String phoneNumber; 41 | 42 | /// {@template signOutOnSuccessfulVerification} 43 | /// 44 | /// If true, the user is signed out before the [onLoginSuccess] 45 | /// callback is fired when the OTP is verified successfully. 46 | /// 47 | /// Is useful when you only want to verify phone number, 48 | /// and not actually sign in the user. 49 | /// 50 | /// Defaults to false 51 | /// 52 | /// {@endtemplate} 53 | final bool signOutOnSuccessfulVerification; 54 | 55 | /// {@template onCodeSent} 56 | /// 57 | /// Callback called when the code is sent successfully to the phone number 58 | /// 59 | /// {@endtemplate} 60 | final VoidCallback? onCodeSent; 61 | 62 | /// {@template sendOtpOnInitialize} 63 | /// Whether to send OTP as soon as the widget initializes or not 64 | /// {@endtemplate} 65 | final bool sendOtpOnInitialize; 66 | 67 | /// {@template linkWithExistingUser} 68 | /// 69 | /// If true, links the generated credentials to an existing signed in user, 70 | /// and not creating new session. 71 | /// 72 | /// Internally, if true, this calls the linkWithCredential method instead of 73 | /// signInWithCredential. 74 | /// 75 | /// Make sure a user is signed in already, else an error is thrown. 76 | /// 77 | /// NOTE: Does not work on web platforms. 78 | /// 79 | /// Defaults to false 80 | /// 81 | /// {@endtemplate} 82 | final bool linkWithExistingUser; 83 | 84 | /// {@template onLoginSuccess} 85 | /// 86 | /// This callback is triggered when the phone number is verified and the user is 87 | /// signed in successfully. The function provides [UserCredential] which contains 88 | /// essential user information. 89 | /// 90 | /// The boolean provided is whether the OTP was auto verified or 91 | /// verified manually by calling [verifyOtp]. 92 | /// 93 | /// True if auto verified and false is verified manually. 94 | /// 95 | /// {@endtemplate} 96 | final OnLoginSuccess? onLoginSuccess; 97 | 98 | /// {@template onLoginFailed} 99 | /// 100 | /// This callback is triggered if the phone verification fails. The callback provides 101 | /// [FirebaseAuthException] which contains information about the error. 102 | /// 103 | /// {@endtemplate} 104 | final OnLoginFailed? onLoginFailed; 105 | 106 | /// {@template onError} 107 | /// 108 | /// Called when a general error occurs. 109 | /// 110 | /// If the error is a [FirebaseAuthException], then [onLoginFailed] is called. 111 | /// 112 | /// {@endtemplate} 113 | final OnError? onError; 114 | 115 | /// {@template autoRetrievalTimeOutDuration} 116 | /// 117 | /// The maximum amount of time you are willing to wait for SMS 118 | /// auto-retrieval to be completed by the library. 119 | /// 120 | /// Maximum allowed value is 2 minutes. 121 | /// 122 | /// NOTE: The user can still use the OTP to sign in after 123 | /// [autoRetrievalTimeOutDuration] duration, but the device 124 | /// will not try to auto-fetch the OTP after this set duration. 125 | /// 126 | /// Defaults to [FirebasePhoneAuthController.kAutoRetrievalTimeOutDuration]. 127 | /// 128 | /// {@endtemplate} 129 | final Duration autoRetrievalTimeOutDuration; 130 | 131 | /// {@template otpExpirationDuration} 132 | /// 133 | /// The OTP expiration duration, can be used to display a timer, and show 134 | /// a resend button, to resend the OTP. 135 | /// 136 | /// Firebase does not document if the OTP ever expires, or anything 137 | /// about it's validity. Hence, this can be used to show a timer, or force 138 | /// user to request a new otp after a set duration. 139 | /// 140 | /// Defaults to [FirebasePhoneAuthController.kAutoRetrievalTimeOutDuration]. 141 | /// 142 | /// {@endtemplate} 143 | final Duration otpExpirationDuration; 144 | 145 | /// {@template recaptchaVerifierForWeb} 146 | /// 147 | /// Custom reCAPTCHA for web-based authentication. 148 | /// 149 | /// The boolean in the function is provided which can be used to check 150 | /// whether the platform is web or not. 151 | /// 152 | /// NOTE : Only pass a [RecaptchaVerifier] instance if you're on web, else an error occurs. 153 | /// 154 | /// {@endtemplate} 155 | final RecaptchaVerifier? Function(bool)? recaptchaVerifierForWebProvider; 156 | 157 | /// {@template builder} 158 | /// 159 | /// The widget returned by the `builder` is rendered on to the screen and 160 | /// builder is called every time a value changes i.e. either the timerCount or any 161 | /// other value. 162 | /// 163 | /// The builder provides a controller which can be used to render the UI based 164 | /// on the current state. 165 | /// 166 | /// {@endtemplate} 167 | final Widget Function(BuildContext, FirebasePhoneAuthController) builder; 168 | 169 | /// {@template signOut} 170 | /// 171 | /// Signs out the current user. 172 | /// 173 | /// {@endtemplate} 174 | static Future signOut(BuildContext context) => 175 | FirebasePhoneAuthController._of(context, listen: false).signOut(); 176 | 177 | @override 178 | State createState() => 179 | _FirebasePhoneAuthHandlerState(); 180 | } 181 | 182 | class _FirebasePhoneAuthHandlerState extends State { 183 | @override 184 | void initState() { 185 | (() async { 186 | final con = FirebasePhoneAuthController._of(context, listen: false); 187 | 188 | RecaptchaVerifier? captcha; 189 | if (widget.recaptchaVerifierForWebProvider != null) { 190 | captcha = widget.recaptchaVerifierForWebProvider!(kIsWeb); 191 | } 192 | 193 | con._setData( 194 | phoneNumber: widget.phoneNumber, 195 | onLoginSuccess: widget.onLoginSuccess, 196 | onLoginFailed: widget.onLoginFailed, 197 | onError: widget.onError, 198 | autoRetrievalTimeOutDuration: widget.autoRetrievalTimeOutDuration, 199 | otpExpirationDuration: widget.otpExpirationDuration, 200 | onCodeSent: widget.onCodeSent, 201 | linkWithExistingUser: widget.linkWithExistingUser, 202 | signOutOnSuccessfulVerification: widget.signOutOnSuccessfulVerification, 203 | recaptchaVerifierForWeb: captcha, 204 | ); 205 | 206 | if (widget.sendOtpOnInitialize) await con.sendOTP(); 207 | })(); 208 | super.initState(); 209 | } 210 | 211 | @override 212 | void deactivate() { 213 | try { 214 | FirebasePhoneAuthController._of(context, listen: false).clear(); 215 | } catch (_) {} 216 | super.deactivate(); 217 | } 218 | 219 | @override 220 | Widget build(BuildContext context) { 221 | return Consumer( 222 | builder: (context, controller, _) => widget.builder(context, controller), 223 | ); 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /lib/src/auth_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_phone_auth_handler/src/auth_handler.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:provider/provider.dart'; 4 | 5 | /// Wrap the [MaterialApp] with [FirebasePhoneAuthProvider] 6 | /// to enable your application to support phone authentication. 7 | class FirebasePhoneAuthProvider extends StatelessWidget { 8 | const FirebasePhoneAuthProvider({ 9 | super.key, 10 | required this.child, 11 | }); 12 | 13 | /// The child of the widget. 14 | final Widget child; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return ChangeNotifierProvider( 19 | create: (_) => FirebasePhoneAuthController(), 20 | child: child, 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/type_definitions.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:firebase_auth/firebase_auth.dart'; 4 | 5 | typedef OnLoginSuccess = FutureOr Function(UserCredential, bool); 6 | 7 | typedef OnLoginFailed = FutureOr Function( 8 | FirebaseAuthException, 9 | StackTrace, 10 | ); 11 | 12 | typedef OnError = FutureOr Function( 13 | Object, 14 | StackTrace, 15 | ); 16 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _flutterfire_internals: 5 | dependency: transitive 6 | description: 7 | name: _flutterfire_internals 8 | sha256: "5534e701a2c505fed1f0799e652dd6ae23bd4d2c4cf797220e5ced5764a7c1c2" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "1.3.44" 12 | async: 13 | dependency: transitive 14 | description: 15 | name: async 16 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.11.0" 20 | boolean_selector: 21 | dependency: transitive 22 | description: 23 | name: boolean_selector 24 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "2.1.1" 28 | characters: 29 | dependency: transitive 30 | description: 31 | name: characters 32 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.3.0" 36 | charcode: 37 | dependency: transitive 38 | description: 39 | name: charcode 40 | sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.3.1" 44 | clock: 45 | dependency: transitive 46 | description: 47 | name: clock 48 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.1.1" 52 | collection: 53 | dependency: transitive 54 | description: 55 | name: collection 56 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "1.18.0" 60 | fake_async: 61 | dependency: transitive 62 | description: 63 | name: fake_async 64 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "1.3.1" 68 | firebase_auth: 69 | dependency: "direct main" 70 | description: 71 | name: firebase_auth 72 | sha256: d453acec0d958ba0e25d41a9901b32cb77d1535766903dea7a61b2788c304596 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "5.3.1" 76 | firebase_auth_platform_interface: 77 | dependency: transitive 78 | description: 79 | name: firebase_auth_platform_interface 80 | sha256: "78966c2ef774f5bf2a8381a307222867e9ece3509110500f7a138c115926aa65" 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "7.4.7" 84 | firebase_auth_web: 85 | dependency: transitive 86 | description: 87 | name: firebase_auth_web 88 | sha256: "77ad3b252badedd3f08dfa21a4c7fe244be96c6da3a4067f253b13ea5d32424c" 89 | url: "https://pub.dev" 90 | source: hosted 91 | version: "5.13.2" 92 | firebase_core: 93 | dependency: transitive 94 | description: 95 | name: firebase_core 96 | sha256: "51dfe2fbf3a984787a2e7b8592f2f05c986bfedd6fdacea3f9e0a7beb334de96" 97 | url: "https://pub.dev" 98 | source: hosted 99 | version: "3.6.0" 100 | firebase_core_platform_interface: 101 | dependency: transitive 102 | description: 103 | name: firebase_core_platform_interface 104 | sha256: e30da58198a6d4b49d5bce4e852f985c32cb10db329ebef9473db2b9f09ce810 105 | url: "https://pub.dev" 106 | source: hosted 107 | version: "5.3.0" 108 | firebase_core_web: 109 | dependency: transitive 110 | description: 111 | name: firebase_core_web 112 | sha256: f967a7138f5d2ffb1ce15950e2a382924239eaa521150a8f144af34e68b3b3e5 113 | url: "https://pub.dev" 114 | source: hosted 115 | version: "2.18.1" 116 | flutter: 117 | dependency: "direct main" 118 | description: flutter 119 | source: sdk 120 | version: "0.0.0" 121 | flutter_lints: 122 | dependency: "direct dev" 123 | description: 124 | name: flutter_lints 125 | sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" 126 | url: "https://pub.dev" 127 | source: hosted 128 | version: "5.0.0" 129 | flutter_test: 130 | dependency: transitive 131 | description: flutter 132 | source: sdk 133 | version: "0.0.0" 134 | flutter_web_plugins: 135 | dependency: transitive 136 | description: flutter 137 | source: sdk 138 | version: "0.0.0" 139 | http_parser: 140 | dependency: transitive 141 | description: 142 | name: http_parser 143 | sha256: e362d639ba3bc07d5a71faebb98cde68c05bfbcfbbb444b60b6f60bb67719185 144 | url: "https://pub.dev" 145 | source: hosted 146 | version: "4.0.0" 147 | leak_tracker: 148 | dependency: transitive 149 | description: 150 | name: leak_tracker 151 | sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" 152 | url: "https://pub.dev" 153 | source: hosted 154 | version: "10.0.5" 155 | leak_tracker_flutter_testing: 156 | dependency: transitive 157 | description: 158 | name: leak_tracker_flutter_testing 159 | sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" 160 | url: "https://pub.dev" 161 | source: hosted 162 | version: "3.0.5" 163 | leak_tracker_testing: 164 | dependency: transitive 165 | description: 166 | name: leak_tracker_testing 167 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" 168 | url: "https://pub.dev" 169 | source: hosted 170 | version: "3.0.1" 171 | lints: 172 | dependency: transitive 173 | description: 174 | name: lints 175 | sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" 176 | url: "https://pub.dev" 177 | source: hosted 178 | version: "5.0.0" 179 | matcher: 180 | dependency: transitive 181 | description: 182 | name: matcher 183 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb 184 | url: "https://pub.dev" 185 | source: hosted 186 | version: "0.12.16+1" 187 | material_color_utilities: 188 | dependency: transitive 189 | description: 190 | name: material_color_utilities 191 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 192 | url: "https://pub.dev" 193 | source: hosted 194 | version: "0.11.1" 195 | meta: 196 | dependency: transitive 197 | description: 198 | name: meta 199 | sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 200 | url: "https://pub.dev" 201 | source: hosted 202 | version: "1.15.0" 203 | nested: 204 | dependency: transitive 205 | description: 206 | name: nested 207 | sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" 208 | url: "https://pub.dev" 209 | source: hosted 210 | version: "1.0.0" 211 | path: 212 | dependency: transitive 213 | description: 214 | name: path 215 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 216 | url: "https://pub.dev" 217 | source: hosted 218 | version: "1.9.0" 219 | plugin_platform_interface: 220 | dependency: transitive 221 | description: 222 | name: plugin_platform_interface 223 | sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a 224 | url: "https://pub.dev" 225 | source: hosted 226 | version: "2.1.3" 227 | provider: 228 | dependency: "direct main" 229 | description: 230 | name: provider 231 | sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c 232 | url: "https://pub.dev" 233 | source: hosted 234 | version: "6.1.2" 235 | sky_engine: 236 | dependency: transitive 237 | description: flutter 238 | source: sdk 239 | version: "0.0.99" 240 | source_span: 241 | dependency: transitive 242 | description: 243 | name: source_span 244 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 245 | url: "https://pub.dev" 246 | source: hosted 247 | version: "1.10.0" 248 | stack_trace: 249 | dependency: transitive 250 | description: 251 | name: stack_trace 252 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 253 | url: "https://pub.dev" 254 | source: hosted 255 | version: "1.11.1" 256 | stream_channel: 257 | dependency: transitive 258 | description: 259 | name: stream_channel 260 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 261 | url: "https://pub.dev" 262 | source: hosted 263 | version: "2.1.2" 264 | string_scanner: 265 | dependency: transitive 266 | description: 267 | name: string_scanner 268 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 269 | url: "https://pub.dev" 270 | source: hosted 271 | version: "1.2.0" 272 | term_glyph: 273 | dependency: transitive 274 | description: 275 | name: term_glyph 276 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 277 | url: "https://pub.dev" 278 | source: hosted 279 | version: "1.2.1" 280 | test_api: 281 | dependency: transitive 282 | description: 283 | name: test_api 284 | sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" 285 | url: "https://pub.dev" 286 | source: hosted 287 | version: "0.7.2" 288 | typed_data: 289 | dependency: transitive 290 | description: 291 | name: typed_data 292 | sha256: "53bdf7e979cfbf3e28987552fd72f637e63f3c8724c9e56d9246942dc2fa36ee" 293 | url: "https://pub.dev" 294 | source: hosted 295 | version: "1.3.0" 296 | vector_math: 297 | dependency: transitive 298 | description: 299 | name: vector_math 300 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 301 | url: "https://pub.dev" 302 | source: hosted 303 | version: "2.1.4" 304 | vm_service: 305 | dependency: transitive 306 | description: 307 | name: vm_service 308 | sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" 309 | url: "https://pub.dev" 310 | source: hosted 311 | version: "14.2.5" 312 | web: 313 | dependency: transitive 314 | description: 315 | name: web 316 | sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb 317 | url: "https://pub.dev" 318 | source: hosted 319 | version: "1.1.0" 320 | sdks: 321 | dart: ">=3.5.0 <4.0.0" 322 | flutter: ">=3.22.0" 323 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: firebase_phone_auth_handler 2 | description: An easy-to-use firebase phone authentication package to easily send and verify OTP's with auto-fetch OTP support via SMS. Supports web out of the box. 3 | version: 1.1.0+1 4 | homepage: https://github.com/rithik-dev/firebase_phone_auth_handler 5 | repository: https://github.com/rithik-dev/firebase_phone_auth_handler 6 | issue_tracker: https://github.com/rithik-dev/firebase_phone_auth_handler/issues 7 | # documentation: https://github.com/rithik-dev/firebase_phone_auth_handler/blob/master/README.md 8 | topics: 9 | - flutter 10 | - firebase 11 | - widget 12 | - firebase-auth 13 | - authentication 14 | 15 | environment: 16 | # constraints from firebase_auth and provider dependencies 17 | sdk: '>=3.2.0 <4.0.0' 18 | flutter: '>=3.16.0' 19 | 20 | scripts: 21 | pre_publish: dart format .; flutter pub publish --dry-run 22 | publish_skip_validation: flutter pub publish --skip-validation 23 | publish: flutter pub publish 24 | 25 | dependencies: 26 | flutter: 27 | sdk: flutter 28 | 29 | provider: ^6.1.2 30 | firebase_auth: ^5.3.1 31 | 32 | dev_dependencies: 33 | flutter_lints: ^5.0.0 34 | 35 | false_secrets: 36 | - /example/android/app/google-services.json 37 | - /example/ios/firebase_app_id_file.json 38 | - /example/ios/Runner/GoogleService-Info.plist 39 | - /example/lib/firebase_options.dart --------------------------------------------------------------------------------