Skip to content

Commit 405bbe7

Browse files
committed
feat(auth): add providerBuilder and DI support for layout customization
1 parent a5da272 commit 405bbe7

35 files changed

Lines changed: 953 additions & 199 deletions

packages/firebase_ui_auth/lib/firebase_ui_auth.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import 'src/oauth_providers.dart';
1414
import 'src/providers/auth_provider.dart';
1515

1616
export 'src/actions.dart';
17-
export 'src/auth_controller.dart' show AuthAction, AuthController;
17+
export 'src/auth_controller.dart'
18+
show AuthAction, AuthController, AuthControllerProvider, FirebaseAuthProvider;
1819
export 'src/auth_flow.dart';
1920
export 'src/auth_state.dart'
2021
show

packages/firebase_ui_auth/lib/src/auth_controller.dart

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,48 @@ class AuthControllerProvider extends InheritedWidget {
8888
return ctrl != oldWidget.ctrl || action != oldWidget.action;
8989
}
9090
}
91+
92+
/// A widget that provides an instance of [fba.FirebaseAuth] down the widget tree.
93+
class FirebaseAuthProvider extends InheritedWidget {
94+
/// An instance of [fba.FirebaseAuth] to provide.
95+
final fba.FirebaseAuth auth;
96+
97+
const FirebaseAuthProvider({
98+
super.key,
99+
required this.auth,
100+
required super.child,
101+
});
102+
103+
@override
104+
bool updateShouldNotify(FirebaseAuthProvider oldWidget) {
105+
return auth != oldWidget.auth;
106+
}
107+
108+
/// Looks up an instance of [fba.FirebaseAuth] in the widget tree.
109+
static fba.FirebaseAuth? maybeOf(BuildContext context) {
110+
return context
111+
.dependOnInheritedWidgetOfExactType<FirebaseAuthProvider>()
112+
?.auth;
113+
}
114+
115+
/// Looks up an instance of [fba.FirebaseAuth] in the widget tree without
116+
/// registering a dependency.
117+
static fba.FirebaseAuth? findAuth(BuildContext context) {
118+
return context.getInheritedWidgetOfExactType<FirebaseAuthProvider>()?.auth;
119+
}
120+
121+
/// Looks up an instance of [fba.FirebaseAuth] in the widget tree.
122+
/// Throws an [Exception] if no [FirebaseAuthProvider] was found.
123+
static fba.FirebaseAuth of(BuildContext context) {
124+
final auth = maybeOf(context);
125+
126+
if (auth == null) {
127+
throw Exception(
128+
'No FirebaseAuthProvider found. '
129+
'Make sure to wrap your code with FirebaseAuthProvider',
130+
);
131+
}
132+
133+
return auth;
134+
}
135+
}

packages/firebase_ui_auth/lib/src/screens/email_link_sign_in_screen.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:firebase_auth/firebase_auth.dart' as fba;
56
import 'package:firebase_ui_shared/firebase_ui_shared.dart';
67
import 'package:flutter/widgets.dart';
78
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
@@ -61,6 +62,8 @@ class EmailLinkSignInScreen extends ProviderScreen<EmailLinkAuthProvider> {
6162

6263
@override
6364
Widget build(BuildContext context) {
65+
final auth = this.auth ?? FirebaseAuthProvider.maybeOf(context) ?? fba.FirebaseAuth.instance;
66+
6467
final child = UniversalScaffold(
6568
body: ResponsivePage(
6669
breakpoint: breakpoint,
@@ -76,6 +79,12 @@ class EmailLinkSignInScreen extends ProviderScreen<EmailLinkAuthProvider> {
7679
),
7780
);
7881

79-
return FirebaseUIActions(actions: actions ?? const [], child: child);
82+
return FirebaseAuthProvider(
83+
auth: auth,
84+
child: FirebaseUIActions(
85+
actions: actions ?? const [],
86+
child: child,
87+
),
88+
);
8089
}
8190
}

packages/firebase_ui_auth/lib/src/screens/email_verification_screen.dart

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -84,22 +84,27 @@ class EmailVerificationScreen extends StatelessWidget {
8484

8585
@override
8686
Widget build(BuildContext context) {
87-
return FirebaseUIActions(
88-
actions: actions,
89-
child: UniversalScaffold(
90-
body: ResponsivePage(
91-
breakpoint: breakpoint,
92-
desktopLayoutDirection: desktopLayoutDirection,
93-
headerBuilder: headerBuilder,
94-
headerMaxExtent: headerMaxExtent,
95-
sideBuilder: sideBuilder,
96-
maxWidth: maxWidth,
97-
contentFlex: 2,
98-
child: Padding(
99-
padding: const EdgeInsets.all(32),
100-
child: _EmailVerificationScreenContent(
101-
auth: auth,
102-
actionCodeSettings: actionCodeSettings,
87+
final auth = this.auth ?? FirebaseAuthProvider.maybeOf(context) ?? fba.FirebaseAuth.instance;
88+
89+
return FirebaseAuthProvider(
90+
auth: auth,
91+
child: FirebaseUIActions(
92+
actions: actions,
93+
child: UniversalScaffold(
94+
body: ResponsivePage(
95+
breakpoint: breakpoint,
96+
desktopLayoutDirection: desktopLayoutDirection,
97+
headerBuilder: headerBuilder,
98+
headerMaxExtent: headerMaxExtent,
99+
sideBuilder: sideBuilder,
100+
maxWidth: maxWidth,
101+
contentFlex: 2,
102+
child: Padding(
103+
padding: const EdgeInsets.all(32),
104+
child: _EmailVerificationScreenContent(
105+
auth: auth,
106+
actionCodeSettings: actionCodeSettings,
107+
),
103108
),
104109
),
105110
),

packages/firebase_ui_auth/lib/src/screens/forgot_password_screen.dart

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,24 +61,32 @@ class ForgotPasswordScreen extends StatelessWidget {
6161

6262
@override
6363
Widget build(BuildContext context) {
64+
final auth = this.auth ?? FirebaseAuthProvider.maybeOf(context) ?? fba.FirebaseAuth.instance;
65+
6466
final child = ForgotPasswordView(
6567
auth: auth,
6668
email: email,
6769
footerBuilder: footerBuilder,
6870
subtitleBuilder: subtitleBuilder,
6971
);
7072

71-
return UniversalScaffold(
72-
resizeToAvoidBottomInset: resizeToAvoidBottomInset,
73-
body: ResponsivePage(
74-
desktopLayoutDirection: desktopLayoutDirection,
75-
headerBuilder: headerBuilder,
76-
headerMaxExtent: headerMaxExtent,
77-
sideBuilder: sideBuilder,
78-
breakpoint: breakpoint,
79-
maxWidth: maxWidth,
80-
contentFlex: 1,
81-
child: Padding(padding: const EdgeInsets.all(32), child: child),
73+
return FirebaseAuthProvider(
74+
auth: auth,
75+
child: UniversalScaffold(
76+
resizeToAvoidBottomInset: resizeToAvoidBottomInset,
77+
body: ResponsivePage(
78+
desktopLayoutDirection: desktopLayoutDirection,
79+
headerBuilder: headerBuilder,
80+
headerMaxExtent: headerMaxExtent,
81+
sideBuilder: sideBuilder,
82+
breakpoint: breakpoint,
83+
maxWidth: maxWidth,
84+
contentFlex: 1,
85+
child: Padding(
86+
padding: const EdgeInsets.all(32),
87+
child: child,
88+
),
89+
),
8290
),
8391
);
8492
}

packages/firebase_ui_auth/lib/src/screens/internal/login_screen.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ class LoginScreen extends StatelessWidget {
5757
/// {@macro ui.auth.screens.responsive_page.max_width}
5858
final double? maxWidth;
5959

60+
/// A builder that allows to customize the order and appearance of the providers.
61+
final ProvidersBuilder? providersBuilder;
62+
6063
const LoginScreen({
6164
super.key,
6265
required this.action,
@@ -77,6 +80,7 @@ class LoginScreen extends StatelessWidget {
7780
this.styles,
7881
this.showPasswordVisibilityToggle = false,
7982
this.maxWidth,
83+
this.providersBuilder,
8084
});
8185

8286
@override
@@ -96,6 +100,7 @@ class LoginScreen extends StatelessWidget {
96100
subtitleBuilder: subtitleBuilder,
97101
footerBuilder: footerBuilder,
98102
showPasswordVisibilityToggle: showPasswordVisibilityToggle,
103+
providersBuilder: providersBuilder,
99104
),
100105
),
101106
);

packages/firebase_ui_auth/lib/src/screens/internal/multi_provider_screen.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ class ScreenElement extends ComponentElement {
6161

6262
@override
6363
Widget build() {
64-
return widget.build(this);
64+
return FirebaseAuthProvider(
65+
auth: widget.auth,
66+
child: widget.build(this),
67+
);
6568
}
6669
}

packages/firebase_ui_auth/lib/src/screens/phone_input_screen.dart

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -102,32 +102,36 @@ class PhoneInputScreen extends StatelessWidget {
102102
@override
103103
Widget build(BuildContext context) {
104104
final flowKey = Object();
105-
106-
return FirebaseUIActions(
107-
actions: actions ?? [SMSCodeRequestedAction(_next)],
108-
child: UniversalScaffold(
109-
body: ResponsivePage(
110-
desktopLayoutDirection: desktopLayoutDirection,
111-
sideBuilder: sideBuilder,
112-
headerBuilder: headerBuilder,
113-
headerMaxExtent: headerMaxExtent,
114-
breakpoint: breakpoint,
115-
maxWidth: maxWidth,
116-
child: Padding(
117-
padding: const EdgeInsets.all(20),
118-
child: Column(
119-
crossAxisAlignment: CrossAxisAlignment.stretch,
120-
children: [
121-
PhoneInputView(
122-
auth: auth,
123-
action: action,
124-
subtitleBuilder: subtitleBuilder,
125-
footerBuilder: footerBuilder,
126-
flowKey: flowKey,
127-
multiFactorSession: multiFactorSession,
128-
mfaHint: mfaHint,
129-
),
130-
],
105+
final auth = this.auth ?? FirebaseAuthProvider.maybeOf(context) ?? fba.FirebaseAuth.instance;
106+
107+
return FirebaseAuthProvider(
108+
auth: auth,
109+
child: FirebaseUIActions(
110+
actions: actions ?? [SMSCodeRequestedAction(_next)],
111+
child: UniversalScaffold(
112+
body: ResponsivePage(
113+
desktopLayoutDirection: desktopLayoutDirection,
114+
sideBuilder: sideBuilder,
115+
headerBuilder: headerBuilder,
116+
headerMaxExtent: headerMaxExtent,
117+
breakpoint: breakpoint,
118+
maxWidth: maxWidth,
119+
child: Padding(
120+
padding: const EdgeInsets.all(20),
121+
child: Column(
122+
crossAxisAlignment: CrossAxisAlignment.stretch,
123+
children: [
124+
PhoneInputView(
125+
auth: auth,
126+
action: action,
127+
subtitleBuilder: subtitleBuilder,
128+
footerBuilder: footerBuilder,
129+
flowKey: flowKey,
130+
multiFactorSession: multiFactorSession,
131+
mfaHint: mfaHint,
132+
),
133+
],
134+
),
131135
),
132136
),
133137
),

packages/firebase_ui_auth/lib/src/screens/register_screen.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ class RegisterScreen extends MultiProviderScreen {
9393
/// {@macro ui.auth.screens.responsive_page.max_width}
9494
final double? maxWidth;
9595

96+
/// A builder that allows to customize the order and appearance of the providers.
97+
///
98+
/// If not provided, the default explicit order is used:
99+
/// Email, Phone, Email Link, OAuth.
100+
final ProvidersBuilder? providersBuilder;
101+
96102
const RegisterScreen({
97103
super.key,
98104
super.auth,
@@ -112,6 +118,7 @@ class RegisterScreen extends MultiProviderScreen {
112118
this.styles,
113119
this.showPasswordVisibilityToggle = false,
114120
this.maxWidth,
121+
this.providersBuilder,
115122
});
116123

117124
@override
@@ -136,6 +143,7 @@ class RegisterScreen extends MultiProviderScreen {
136143
breakpoint: breakpoint,
137144
showPasswordVisibilityToggle: showPasswordVisibilityToggle,
138145
maxWidth: maxWidth,
146+
providersBuilder: providersBuilder,
139147
),
140148
);
141149
}

packages/firebase_ui_auth/lib/src/screens/sign_in_screen.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ class SignInScreen extends MultiProviderScreen {
101101
/// {@macro ui.auth.screens.responsive_page.max_width}
102102
final double? maxWidth;
103103

104+
/// A builder that allows to customize the order and appearance of the providers.
105+
///
106+
/// If not provided, the default explicit order is used:
107+
/// Email, Phone, Email Link, OAuth.
108+
final ProvidersBuilder? providersBuilder;
109+
104110
/// {@macro ui.auth.screens.sign_in_screen}
105111
const SignInScreen({
106112
super.key,
@@ -122,6 +128,7 @@ class SignInScreen extends MultiProviderScreen {
122128
this.styles,
123129
this.showPasswordVisibilityToggle = false,
124130
this.maxWidth,
131+
this.providersBuilder,
125132
});
126133

127134
@override
@@ -149,6 +156,7 @@ class SignInScreen extends MultiProviderScreen {
149156
breakpoint: breakpoint,
150157
showPasswordVisibilityToggle: showPasswordVisibilityToggle,
151158
maxWidth: maxWidth,
159+
providersBuilder: providersBuilder,
152160
),
153161
);
154162
}

0 commit comments

Comments
 (0)