diff --git a/.gitignore b/.gitignore
index d520d057..219ee230 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,7 +40,9 @@ GeneratedPluginRegistrant.m
GeneratedPluginRegistrant.java
GeneratedPluginRegistrant.swift
generated_plugin_registrant.dart
+generated_plugin_registrant.h
generated_plugin_registrant.cc
+generated_plugins.cmake
build/
.flutter-plugins
.flutter-plugins-dependencies
diff --git a/docs/firebase-ui-auth/README.md b/docs/firebase-ui-auth/README.md
index b21d729b..66cabc83 100644
--- a/docs/firebase-ui-auth/README.md
+++ b/docs/firebase-ui-auth/README.md
@@ -17,8 +17,6 @@ Install dependencies
```sh
flutter pub add firebase_core
flutter pub add firebase_auth
-# required for email link sign in and email verification
-flutter pub add firebase_dynamic_links
flutter pub add firebase_ui_auth
```
diff --git a/packages/firebase_ui_auth/example/.gitignore b/packages/firebase_ui_auth/example/.gitignore
index a8e938c0..221b4225 100644
--- a/packages/firebase_ui_auth/example/.gitignore
+++ b/packages/firebase_ui_auth/example/.gitignore
@@ -5,9 +5,11 @@
*.swp
.DS_Store
.atom/
+.build/
.buildlog/
.history
.svn/
+.swiftpm/
migrate_working_dir/
# IntelliJ related
diff --git a/packages/firebase_ui_auth/example/android/app/build.gradle b/packages/firebase_ui_auth/example/android/app/build.gradle
index 07d4638f..5f864f3e 100644
--- a/packages/firebase_ui_auth/example/android/app/build.gradle
+++ b/packages/firebase_ui_auth/example/android/app/build.gradle
@@ -1,3 +1,10 @@
+plugins {
+ id "com.android.application"
+ id "kotlin-android"
+ id "dev.flutter.flutter-gradle-plugin"
+ id "com.google.gms.google-services"
+}
+
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
@@ -6,11 +13,6 @@ if (localPropertiesFile.exists()) {
}
}
-def flutterRoot = localProperties.getProperty('flutter.sdk')
-if (flutterRoot == null) {
- throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
-}
-
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
@@ -21,25 +23,18 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
-apply plugin: 'com.android.application'
-// START: FlutterFire Configuration
-apply plugin: 'com.google.gms.google-services'
-// END: FlutterFire Configuration
-apply plugin: 'kotlin-android'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
-
android {
namespace 'io.flutter.plugins.firebase_ui_example'
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
- jvmTarget = '1.8'
+ jvmTarget = '17'
}
sourceSets {
@@ -66,7 +61,3 @@ android {
flutter {
source '../..'
}
-
-dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
-}
diff --git a/packages/firebase_ui_auth/example/android/app/src/main/AndroidManifest.xml b/packages/firebase_ui_auth/example/android/app/src/main/AndroidManifest.xml
index 36f5b767..dcc81eac 100644
--- a/packages/firebase_ui_auth/example/android/app/src/main/AndroidManifest.xml
+++ b/packages/firebase_ui_auth/example/android/app/src/main/AndroidManifest.xml
@@ -5,7 +5,7 @@
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true">
-
+
@@ -33,6 +33,14 @@
+
+
+
+
+
+
diff --git a/packages/firebase_ui_auth/example/android/build.gradle b/packages/firebase_ui_auth/example/android/build.gradle
index 2551d225..bc157bd1 100644
--- a/packages/firebase_ui_auth/example/android/build.gradle
+++ b/packages/firebase_ui_auth/example/android/build.gradle
@@ -1,19 +1,3 @@
-buildscript {
- ext.kotlin_version = '1.7.10'
- repositories {
- google()
- mavenCentral()
- }
-
- dependencies {
- // START: FlutterFire Configuration
- classpath 'com.google.gms:google-services:4.3.15'
- // END: FlutterFire Configuration
- classpath 'com.android.tools.build:gradle:7.4.2'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- }
-}
-
allprojects {
repositories {
google()
diff --git a/packages/firebase_ui_auth/example/android/gradle.properties b/packages/firebase_ui_auth/example/android/gradle.properties
index 0ccde7ee..ef8d51b1 100644
--- a/packages/firebase_ui_auth/example/android/gradle.properties
+++ b/packages/firebase_ui_auth/example/android/gradle.properties
@@ -2,4 +2,7 @@ org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
-android.nonFinalResIds=false
\ No newline at end of file
+android.nonFinalResIds=false
+
+# Java 17 settings to suppress Java 8 warnings
+kotlin.jvm.target.validation.mode=warning
\ No newline at end of file
diff --git a/packages/firebase_ui_auth/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/firebase_ui_auth/example/android/gradle/wrapper/gradle-wrapper.properties
index da1db5f0..fce403e4 100644
--- a/packages/firebase_ui_auth/example/android/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/firebase_ui_auth/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/packages/firebase_ui_auth/example/android/settings.gradle b/packages/firebase_ui_auth/example/android/settings.gradle
index 44e62bcf..0cfdd0fb 100644
--- a/packages/firebase_ui_auth/example/android/settings.gradle
+++ b/packages/firebase_ui_auth/example/android/settings.gradle
@@ -1,11 +1,26 @@
-include ':app'
+pluginManagement {
+ def flutterSdkPath = {
+ def properties = new Properties()
+ file("local.properties").withInputStream { properties.load(it) }
+ def flutterSdkPath = properties.getProperty("flutter.sdk")
+ assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+ return flutterSdkPath
+ }()
-def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
-def properties = new Properties()
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
-assert localPropertiesFile.exists()
-localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
-def flutterSdkPath = properties.getProperty("flutter.sdk")
-assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
-apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
+plugins {
+ id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+ id "com.android.application" version "8.3.0" apply false
+ id "org.jetbrains.kotlin.android" version "1.9.25" apply false
+ id "com.google.gms.google-services" version "4.4.2" apply false
+}
+
+include ":app"
\ No newline at end of file
diff --git a/packages/firebase_ui_auth/example/ios/Flutter/AppFrameworkInfo.plist b/packages/firebase_ui_auth/example/ios/Flutter/AppFrameworkInfo.plist
index 9625e105..7c569640 100644
--- a/packages/firebase_ui_auth/example/ios/Flutter/AppFrameworkInfo.plist
+++ b/packages/firebase_ui_auth/example/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
CFBundleVersion
1.0
MinimumOSVersion
- 11.0
+ 12.0
diff --git a/packages/firebase_ui_auth/example/ios/Podfile b/packages/firebase_ui_auth/example/ios/Podfile
index 2c068c40..fe628cb8 100644
--- a/packages/firebase_ui_auth/example/ios/Podfile
+++ b/packages/firebase_ui_auth/example/ios/Podfile
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
-platform :ios, '12.0'
+platform :ios, '17.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
diff --git a/packages/firebase_ui_auth/example/ios/Runner.xcodeproj/project.pbxproj b/packages/firebase_ui_auth/example/ios/Runner.xcodeproj/project.pbxproj
index ac92c860..175cb443 100644
--- a/packages/firebase_ui_auth/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/firebase_ui_auth/example/ios/Runner.xcodeproj/project.pbxproj
@@ -11,6 +11,7 @@
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
615AB19345F2CB9C4AFB55AE /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5CBE04B4787B566D8CAE0579 /* GoogleService-Info.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
83A0F86F233458219B2DC55F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 416FB58C991096C1726F437F /* Pods_Runner.framework */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
@@ -36,6 +37,7 @@
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
416FB58C991096C1726F437F /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 467989B22DF71C4F0093BE6F /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; };
5CBE04B4787B566D8CAE0579 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; };
6DF47F8C0940BEBB4259C546 /* 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 = ""; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
@@ -56,6 +58,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
83A0F86F233458219B2DC55F /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -97,6 +100,7 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
+ 467989B22DF71C4F0093BE6F /* Runner.entitlements */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
@@ -149,6 +153,9 @@
dependencies = (
);
name = Runner;
+ packageProductDependencies = (
+ 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
+ );
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
@@ -177,6 +184,9 @@
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
+ packageReferences = (
+ 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
+ );
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
@@ -379,6 +389,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = YYX2P3XVJ7;
ENABLE_BITCODE = NO;
@@ -508,6 +519,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = YYX2P3XVJ7;
ENABLE_BITCODE = NO;
@@ -531,6 +543,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = YYX2P3XVJ7;
ENABLE_BITCODE = NO;
@@ -571,6 +584,20 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
+
+/* Begin XCLocalSwiftPackageReference section */
+ 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
+ isa = XCLocalSwiftPackageReference;
+ relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
+ };
+/* End XCLocalSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = FlutterGeneratedPluginSwiftPackage;
+ };
+/* End XCSwiftPackageProductDependency section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
diff --git a/packages/firebase_ui_auth/example/ios/Runner/AppDelegate.swift b/packages/firebase_ui_auth/example/ios/Runner/AppDelegate.swift
index 70693e4a..75bd5cea 100644
--- a/packages/firebase_ui_auth/example/ios/Runner/AppDelegate.swift
+++ b/packages/firebase_ui_auth/example/ios/Runner/AppDelegate.swift
@@ -1,13 +1,22 @@
import UIKit
import Flutter
+import app_links
-@UIApplicationMain
+@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
+
+ // Retrieve the link from parameters
+ if let url = AppLinks.shared.getLink(launchOptions: launchOptions) {
+ // We have a link, propagate it to your Flutter app or not
+ AppLinks.shared.handleLink(url: url)
+ return true // Returning true will stop the propagation to other packages
+ }
+
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
diff --git a/packages/firebase_ui_auth/example/ios/Runner/Info.plist b/packages/firebase_ui_auth/example/ios/Runner/Info.plist
index accb0a4c..5139a1be 100644
--- a/packages/firebase_ui_auth/example/ios/Runner/Info.plist
+++ b/packages/firebase_ui_auth/example/ios/Runner/Info.plist
@@ -47,6 +47,8 @@
UIApplicationSupportsIndirectInputEvents
+ FlutterDeepLinkingEnabled
+
CFBundleURLTypes
diff --git a/packages/firebase_ui_auth/example/ios/Runner/Runner.entitlements b/packages/firebase_ui_auth/example/ios/Runner/Runner.entitlements
new file mode 100644
index 00000000..77423e3e
--- /dev/null
+++ b/packages/firebase_ui_auth/example/ios/Runner/Runner.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.developer.associated-domains
+
+ applinks:flutterfire-e2e-tests.firebaseapp.com
+
+
+
diff --git a/packages/firebase_ui_auth/example/lib/main.dart b/packages/firebase_ui_auth/example/lib/main.dart
index f53ceee0..faffd75b 100644
--- a/packages/firebase_ui_auth/example/lib/main.dart
+++ b/packages/firebase_ui_auth/example/lib/main.dart
@@ -23,7 +23,7 @@ final actionCodeSettings = ActionCodeSettings(
url: 'https://flutterfire-e2e-tests.firebaseapp.com',
handleCodeInApp: true,
androidMinimumVersion: '1',
- androidPackageName: 'io.flutter.plugins.firebase_ui.firebase_ui_example',
+ androidPackageName: 'io.flutter.plugins.firebase_ui_example',
iOSBundleId: 'io.flutter.plugins.fireabaseUiExample',
);
final emailLinkProviderConfig = EmailLinkAuthProvider(
@@ -245,7 +245,7 @@ class FirebaseAuthUIExample extends StatelessWidget {
return EmailLinkSignInScreen(
actions: [
AuthStateChangeAction((context, state) {
- Navigator.pushReplacementNamed(context, '/');
+ Navigator.pushReplacementNamed(context, '/profile');
}),
],
provider: emailLinkProviderConfig,
diff --git a/packages/firebase_ui_auth/example/pubspec.yaml b/packages/firebase_ui_auth/example/pubspec.yaml
index de72b0f1..af6b582d 100644
--- a/packages/firebase_ui_auth/example/pubspec.yaml
+++ b/packages/firebase_ui_auth/example/pubspec.yaml
@@ -35,6 +35,8 @@ dependencies:
firebase_ui_oauth_facebook: ^1.3.2
firebase_ui_oauth_google: ^1.4.2
firebase_ui_oauth_twitter: ^1.3.2
+ # This and twitter oauth package need to depend on git main directly due to namespace build error on android.
+ twitter_login: ^4.4.2
dev_dependencies:
drive: ^1.0.0-1.0.nullsafety.5
firebase_ui_shared: ^1.4.1
@@ -48,7 +50,7 @@ dev_dependencies:
http: ^1.1.2
integration_test:
sdk: flutter
- twitter_login: ^4.4.2
+
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
diff --git a/packages/firebase_ui_auth/example/windows/flutter/generated_plugin_registrant.h b/packages/firebase_ui_auth/example/windows/flutter/generated_plugin_registrant.h
deleted file mode 100644
index dc139d85..00000000
--- a/packages/firebase_ui_auth/example/windows/flutter/generated_plugin_registrant.h
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-// Generated file. Do not edit.
-//
-
-// clang-format off
-
-#ifndef GENERATED_PLUGIN_REGISTRANT_
-#define GENERATED_PLUGIN_REGISTRANT_
-
-#include
-
-// Registers Flutter plugins.
-void RegisterPlugins(flutter::PluginRegistry* registry);
-
-#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/packages/firebase_ui_auth/example/windows/flutter/generated_plugins.cmake b/packages/firebase_ui_auth/example/windows/flutter/generated_plugins.cmake
deleted file mode 100644
index 73d6fa1c..00000000
--- a/packages/firebase_ui_auth/example/windows/flutter/generated_plugins.cmake
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# Generated file, do not edit.
-#
-
-list(APPEND FLUTTER_PLUGIN_LIST
- desktop_webview_auth
- firebase_auth
- firebase_core
- flutter_secure_storage_windows
-)
-
-list(APPEND FLUTTER_FFI_PLUGIN_LIST
-)
-
-set(PLUGIN_BUNDLED_LIBRARIES)
-
-foreach(plugin ${FLUTTER_PLUGIN_LIST})
- add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
- target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
- list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
- list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
-endforeach(plugin)
-
-foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
- add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
- list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
-endforeach(ffi_plugin)
diff --git a/packages/firebase_ui_auth/lib/src/email_verification.dart b/packages/firebase_ui_auth/lib/src/email_verification.dart
index f20497ba..62126e44 100644
--- a/packages/firebase_ui_auth/lib/src/email_verification.dart
+++ b/packages/firebase_ui_auth/lib/src/email_verification.dart
@@ -3,8 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:firebase_auth/firebase_auth.dart' as fba;
-import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';
+import 'package:app_links/app_links.dart';
import 'package:flutter/material.dart';
+import 'dart:async';
/// All possible states of the email verification process.
enum EmailVerificationState {
@@ -22,7 +23,7 @@ enum EmailVerificationState {
sending,
/// A state that indicates that user needs to follow the link and the app
- /// awaits a valid dynamic link.
+ /// awaits a valid deep link.
pending,
/// A state that indicates that the verification email was successfully sent.
@@ -42,8 +43,12 @@ class EmailVerificationController extends ValueNotifier
/// {@macro ui.auth.auth_controller.auth}
final fba.FirebaseAuth auth;
- EmailVerificationController(this.auth)
- : super(EmailVerificationState.unresolved) {
+ final AppLinks _appLinks;
+ StreamSubscription? _linkSubscription;
+
+ EmailVerificationController(this.auth, {AppLinks? appLinks})
+ : _appLinks = appLinks ?? AppLinks(),
+ super(EmailVerificationState.unresolved) {
final user = auth.currentUser;
if (user != null) {
@@ -57,6 +62,13 @@ class EmailVerificationController extends ValueNotifier
WidgetsBinding.instance.addObserver(this);
}
+ @override
+ void dispose() {
+ _linkSubscription?.cancel();
+ WidgetsBinding.instance.removeObserver(this);
+ super.dispose();
+ }
+
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
@@ -93,6 +105,7 @@ class EmailVerificationController extends ValueNotifier
/// Indicates that email verification process was cancelled.
void dismiss() {
value = EmailVerificationState.dismissed;
+ _linkSubscription?.cancel();
}
/// Sends an email with a link to verify the user's email address.
@@ -111,19 +124,32 @@ class EmailVerificationController extends ValueNotifier
if (_isMobile(platform)) {
value = EmailVerificationState.pending;
- // ignore: deprecated_member_use
- final linkData = await FirebaseDynamicLinks.instance.onLink.first;
-
- try {
- final code = linkData.link.queryParameters['oobCode']!;
- await auth.checkActionCode(code);
- await auth.applyActionCode(code);
- await user.reload();
- value = EmailVerificationState.verified;
- } on Exception catch (err) {
- error = err;
- value = EmailVerificationState.failed;
- }
+
+ _linkSubscription?.cancel();
+
+ _linkSubscription = _appLinks.uriLinkStream.listen(
+ (Uri uri) async {
+ try {
+ final code = uri.queryParameters['oobCode'];
+ if (code != null) {
+ await auth.checkActionCode(code);
+ await auth.applyActionCode(code);
+ await user.reload();
+ value = EmailVerificationState.verified;
+ _linkSubscription?.cancel();
+ }
+ } on Exception catch (err) {
+ error = err;
+ value = EmailVerificationState.failed;
+ _linkSubscription?.cancel();
+ }
+ },
+ onError: (error) {
+ this.error = error is Exception ? error : Exception(error.toString());
+ value = EmailVerificationState.failed;
+ _linkSubscription?.cancel();
+ },
+ );
} else {
value = EmailVerificationState.sent;
}
diff --git a/packages/firebase_ui_auth/lib/src/providers/email_link_auth_provider.dart b/packages/firebase_ui_auth/lib/src/providers/email_link_auth_provider.dart
index a57f9673..092cfa25 100644
--- a/packages/firebase_ui_auth/lib/src/providers/email_link_auth_provider.dart
+++ b/packages/firebase_ui_auth/lib/src/providers/email_link_auth_provider.dart
@@ -4,9 +4,10 @@
// ignore_for_file: deprecated_member_use
import 'package:firebase_auth/firebase_auth.dart' as fba;
-import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';
import 'package:flutter/foundation.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
+import 'package:app_links/app_links.dart';
+import 'dart:async';
/// A listener of the [EmailLinkFlow] lifecycle.
abstract class EmailLinkAuthListener extends AuthListener {
@@ -26,7 +27,8 @@ class EmailLinkAuthProvider
/// A configuration of the dynamic link.
final fba.ActionCodeSettings actionCodeSettings;
- final FirebaseDynamicLinks _dynamicLinks;
+ final AppLinks _appLinks;
+ StreamSubscription? _linkSubscription;
@override
late EmailLinkAuthListener authListener;
@@ -41,13 +43,13 @@ class EmailLinkAuthProvider
}
/// {@macro ui.auth.providers.email_link_auth_provider}
- EmailLinkAuthProvider(
- {required this.actionCodeSettings,
+ EmailLinkAuthProvider({
+ required this.actionCodeSettings,
- /// An instance of the [FirebaseDynamicLinks] that should be used to handle
- /// the link. By default [FirebaseDynamicLinks.instance] is used.
- FirebaseDynamicLinks? dynamicLinks})
- : _dynamicLinks = dynamicLinks ?? FirebaseDynamicLinks.instance;
+ /// An instance of the [AppLinks] that should be used to handle
+ /// the link. By default [AppLinks()] is used.
+ AppLinks? appLinks,
+ }) : _appLinks = appLinks ?? AppLinks();
/// Sends a link to the [email].
void sendLink(String email) {
@@ -63,8 +65,8 @@ class EmailLinkAuthProvider
.catchError(authListener.onError);
}
- void _onLinkReceived(String email, PendingDynamicLinkData linkData) {
- final link = linkData.link.toString();
+ void _onLinkReceived(String email, Uri uri) {
+ final link = uri.toString();
if (auth.isSignInWithEmailLink(link)) {
authListener.onBeforeSignIn();
@@ -79,12 +81,20 @@ class EmailLinkAuthProvider
}
}
- /// Calls [FirebaseDynamicLinks] to receive the link and perform a sign in.
+ /// Listens for incoming app links and handles email authentication.
/// Should be called after [EmailLinkAuthListener.onLinkSent] was called.
void awaitLink(String email) {
- _dynamicLinks.onLink.first
- .then((linkData) => _onLinkReceived(email, linkData))
- .catchError(authListener.onError);
+ _linkSubscription?.cancel();
+
+ _linkSubscription = _appLinks.uriLinkStream.listen(
+ (Uri uri) => _onLinkReceived(email, uri),
+ onError: (error) => authListener.onError(error),
+ );
+ }
+
+ void dispose() {
+ _linkSubscription?.cancel();
+ _linkSubscription = null;
}
void _signInWithEmailLink(String email, String link) {
diff --git a/packages/firebase_ui_auth/lib/src/screens/email_link_sign_in_screen.dart b/packages/firebase_ui_auth/lib/src/screens/email_link_sign_in_screen.dart
index 92450fd5..1052f575 100644
--- a/packages/firebase_ui_auth/lib/src/screens/email_link_sign_in_screen.dart
+++ b/packages/firebase_ui_auth/lib/src/screens/email_link_sign_in_screen.dart
@@ -61,7 +61,7 @@ class EmailLinkSignInScreen extends ProviderScreen {
@override
Widget build(BuildContext context) {
- return UniversalScaffold(
+ final child = UniversalScaffold(
body: ResponsivePage(
breakpoint: breakpoint,
headerBuilder: headerBuilder,
@@ -78,5 +78,10 @@ class EmailLinkSignInScreen extends ProviderScreen {
),
),
);
+
+ return FirebaseUIActions(
+ actions: actions ?? const [],
+ child: child,
+ );
}
}
diff --git a/packages/firebase_ui_auth/pubspec.yaml b/packages/firebase_ui_auth/pubspec.yaml
index 4fb4bde0..6c2b73d3 100644
--- a/packages/firebase_ui_auth/pubspec.yaml
+++ b/packages/firebase_ui_auth/pubspec.yaml
@@ -9,10 +9,10 @@ environment:
flutter: ">=3.3.0"
dependencies:
+ app_links: ^6.4.0
email_validator: ^2.1.17
firebase_auth: ^5.6.0
firebase_core: ^3.14.0
- firebase_dynamic_links: ^6.1.7
firebase_ui_localizations: ^1.14.0
firebase_ui_oauth: ^1.7.0
firebase_ui_shared: ^1.4.1
diff --git a/packages/firebase_ui_auth/test/flows/email_link_flow_test.dart b/packages/firebase_ui_auth/test/flows/email_link_flow_test.dart
index f0fcc619..517c441e 100644
--- a/packages/firebase_ui_auth/test/flows/email_link_flow_test.dart
+++ b/packages/firebase_ui_auth/test/flows/email_link_flow_test.dart
@@ -14,7 +14,7 @@ void main() {
late EmailLinkAuthProvider provider;
late MockListener listener;
late MockAuth auth;
- late MockDynamicLinks dynamicLinks;
+ late MockAppLinks appLinks;
late EmailLinkFlow flow;
late EmailLinkAuthController ctrl;
@@ -27,11 +27,11 @@ void main() {
setUp(() {
auth = MockAuth();
listener = MockListener();
- dynamicLinks = MockDynamicLinks();
+ appLinks = MockAppLinks();
provider = EmailLinkAuthProvider(
actionCodeSettings: actionCodeSettings,
- dynamicLinks: dynamicLinks,
+ appLinks: appLinks,
);
flow = EmailLinkFlow(
@@ -114,11 +114,14 @@ void main() {
group('#awaitLink', () {
test(
- 'waits for a link from dynamic links and calls onBeforeSignIn',
+ 'waits for a link from app links and calls onBeforeSignIn',
() async {
provider.authListener = listener;
provider.awaitLink('test@test.com');
+ // Simulate receiving an app link
+ MockUriStream.addLink(Uri.parse('https://test.com'));
+
await untilCalled(listener.onBeforeSignIn());
verify(listener.onBeforeSignIn()).called(1);
@@ -131,6 +134,9 @@ void main() {
when(auth.isSignInWithEmailLink(any)).thenReturn(false);
+ // Simulate receiving an invalid app link
+ MockUriStream.addLink(Uri.parse('https://invalid-link.com'));
+
await untilCalled(listener.onError(any));
final result = verify(listener.onError(captureAny));
@@ -145,6 +151,9 @@ void main() {
provider.authListener = listener;
provider.awaitLink('test@test.com');
+ // Simulate receiving a valid app link
+ MockUriStream.addLink(Uri.parse('https://test.com'));
+
await untilCalled(listener.onBeforeSignIn());
final result = verify(
@@ -165,6 +174,9 @@ void main() {
provider.authListener = listener;
provider.awaitLink('test@test.com');
+ // Simulate receiving a valid app link
+ MockUriStream.addLink(Uri.parse('https://test.com'));
+
await untilCalled(listener.onSignedIn(any));
final result = verify(listener.onSignedIn(captureAny));
@@ -185,6 +197,9 @@ void main() {
provider.awaitLink('test@test.com');
+ // Simulate receiving a valid app link
+ MockUriStream.addLink(Uri.parse('https://test.com'));
+
await untilCalled(listener.onError(any));
final result = verify(listener.onError(captureAny));
diff --git a/packages/firebase_ui_auth/test/test_utils.dart b/packages/firebase_ui_auth/test/test_utils.dart
index 4485e56e..bd1bb10e 100644
--- a/packages/firebase_ui_auth/test/test_utils.dart
+++ b/packages/firebase_ui_auth/test/test_utils.dart
@@ -4,9 +4,10 @@
import 'package:firebase_auth/firebase_auth.dart' as fba;
import 'package:firebase_core/firebase_core.dart';
-import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';
+import 'package:app_links/app_links.dart';
import 'package:flutter/material.dart';
import 'package:mockito/mockito.dart';
+import 'dart:async';
class TestMaterialApp extends StatelessWidget {
final Widget child;
@@ -53,27 +54,52 @@ class MockUser extends Mock implements fba.User {
}
}
-class MockLinksStream extends Mock implements Stream {
+class MockUriStream extends Mock implements Stream {
+ static final StreamController _controller =
+ StreamController.broadcast();
+
@override
- Future get first async {
- return super.noSuchMethod(
- Invocation.getter(#first),
- returnValue: PendingDynamicLinkData(
- link: Uri.parse('https://test.com'),
- ),
- returnValueForMissingStub: PendingDynamicLinkData(
- link: Uri.parse('https://test.com'),
- ),
+ StreamSubscription listen(
+ void Function(Uri)? onData, {
+ Function? onError,
+ void Function()? onDone,
+ bool? cancelOnError,
+ }) {
+ return _controller.stream.listen(
+ onData,
+ onError: onError,
+ onDone: onDone,
+ cancelOnError: cancelOnError,
);
}
+
+ static void addLink(Uri uri) {
+ _controller.add(uri);
+ }
+
+ static void addError(Object error) {
+ _controller.addError(error);
+ }
+
+ static void close() {
+ _controller.close();
+ }
}
-// ignore: deprecated_member_use
-class MockDynamicLinks extends Mock implements FirebaseDynamicLinks {
- static final _linkStream = MockLinksStream();
+class MockAppLinks extends Mock implements AppLinks {
+ static final _uriStream = MockUriStream();
+
+ @override
+ Stream get uriLinkStream => _uriStream;
@override
- Stream get onLink => _linkStream;
+ Future getInitialLink() async {
+ return super.noSuchMethod(
+ Invocation.method(#getInitialLink, []),
+ returnValue: null,
+ returnValueForMissingStub: null,
+ );
+ }
}
class MockApp extends Mock implements FirebaseApp {}
diff --git a/packages/firebase_ui_auth/test/views/email_link_sign_in_view_test.dart b/packages/firebase_ui_auth/test/views/email_link_sign_in_view_test.dart
index db8705f0..04c844fd 100644
--- a/packages/firebase_ui_auth/test/views/email_link_sign_in_view_test.dart
+++ b/packages/firebase_ui_auth/test/views/email_link_sign_in_view_test.dart
@@ -13,18 +13,18 @@ import '../test_utils.dart';
void main() {
const labels = DefaultLocalizations();
late MockAuth auth;
- late MockDynamicLinks dynamicLinks;
+ late MockAppLinks appLinks;
late EmailLinkAuthProvider emailLinkProvider;
setUp(() {
auth = MockAuth();
- dynamicLinks = MockDynamicLinks();
+ appLinks = MockAppLinks();
final actionCodeSettings = fba.ActionCodeSettings(
url: 'https://example.com',
);
emailLinkProvider = EmailLinkAuthProvider(
actionCodeSettings: actionCodeSettings,
- dynamicLinks: dynamicLinks,
+ appLinks: appLinks,
);
});
diff --git a/tests/ios/Podfile b/tests/ios/Podfile
index 93badd10..7741e5eb 100644
--- a/tests/ios/Podfile
+++ b/tests/ios/Podfile
@@ -40,5 +40,12 @@ end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
+
+ # Enable Swift 6 access level on imports feature
+ target.build_configurations.each do |config|
+ config.build_settings['OTHER_SWIFT_FLAGS'] ||= []
+ config.build_settings['OTHER_SWIFT_FLAGS'] << '-enable-experimental-feature'
+ config.build_settings['OTHER_SWIFT_FLAGS'] << 'AccessLevelOnImport'
+ end
end
end
diff --git a/tests/linux/flutter/generated_plugin_registrant.h b/tests/linux/flutter/generated_plugin_registrant.h
deleted file mode 100644
index e0f0a47b..00000000
--- a/tests/linux/flutter/generated_plugin_registrant.h
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-// Generated file. Do not edit.
-//
-
-// clang-format off
-
-#ifndef GENERATED_PLUGIN_REGISTRANT_
-#define GENERATED_PLUGIN_REGISTRANT_
-
-#include
-
-// Registers Flutter plugins.
-void fl_register_plugins(FlPluginRegistry* registry);
-
-#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/tests/macos/Podfile b/tests/macos/Podfile
index 6176a934..9741269d 100644
--- a/tests/macos/Podfile
+++ b/tests/macos/Podfile
@@ -41,5 +41,12 @@ end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_macos_build_settings(target)
+
+ # Enable Swift 6 access level on imports feature
+ target.build_configurations.each do |config|
+ config.build_settings['OTHER_SWIFT_FLAGS'] ||= []
+ config.build_settings['OTHER_SWIFT_FLAGS'] << '-enable-experimental-feature'
+ config.build_settings['OTHER_SWIFT_FLAGS'] << 'AccessLevelOnImport'
+ end
end
end
diff --git a/tests/windows/flutter/generated_plugin_registrant.h b/tests/windows/flutter/generated_plugin_registrant.h
deleted file mode 100644
index dc139d85..00000000
--- a/tests/windows/flutter/generated_plugin_registrant.h
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-// Generated file. Do not edit.
-//
-
-// clang-format off
-
-#ifndef GENERATED_PLUGIN_REGISTRANT_
-#define GENERATED_PLUGIN_REGISTRANT_
-
-#include
-
-// Registers Flutter plugins.
-void RegisterPlugins(flutter::PluginRegistry* registry);
-
-#endif // GENERATED_PLUGIN_REGISTRANT_