Skip to content

Commit 0816bfd

Browse files
authored
feat(ui_auth): add a way to get an AuthController for AuthState (#207)
* feat(ui_auth): add a way to get an AuthController for AuthState * add license header
1 parent a3ffc36 commit 0816bfd

File tree

5 files changed

+129
-3
lines changed

5 files changed

+129
-3
lines changed

Diff for: packages/firebase_ui_auth/lib/src/actions.dart

+37
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,9 @@ class _FlutterfireUIAuthActionsElement extends InheritedElement {
180180
listener: (oldState, newState, controller) {
181181
for (final action in widget.actions) {
182182
if (action is AuthStateChangeAction && action.matches(newState)) {
183+
_controllerRegistry[newState] = controller;
183184
action.invoke(this, newState);
185+
_controllerRegistry.remove(newState);
184186
}
185187
}
186188

@@ -190,3 +192,38 @@ class _FlutterfireUIAuthActionsElement extends InheritedElement {
190192
);
191193
}
192194
}
195+
196+
final _controllerRegistry = <AuthState, AuthController>{};
197+
198+
/// Allows getting an [AuthController] that caused an [AuthState] transition.
199+
/// Calling [getControllerForState] is only allowed from a [FirebaseUIAction]
200+
/// callback:
201+
///
202+
/// ```dart
203+
/// SignInScreen(
204+
/// actions: [
205+
/// AuthStateChangeAction<SignedIn>((context, state) {
206+
/// final ctrl = getControllerForState(state);
207+
///
208+
/// if (ctrl is EmailAuthController) {
209+
/// print('email was used for authentication');
210+
/// }
211+
///
212+
///
213+
/// Navigator.of(context).pushReplacementNamed('/profile');
214+
/// }
215+
/// ]
216+
/// );
217+
/// ```
218+
AuthController getControllerForState(AuthState state) {
219+
final ctrl = _controllerRegistry[state];
220+
221+
if (ctrl == null) {
222+
throw StateError(
223+
'Quering controller for an auth state is only allowed '
224+
'from FirebaseUIAction callback',
225+
);
226+
}
227+
228+
return ctrl;
229+
}
+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2023, the Chromium project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
6+
import 'package:flutter/material.dart';
7+
import 'package:flutter_test/flutter_test.dart';
8+
9+
import '../utils.dart';
10+
11+
void main() {
12+
setUpTests();
13+
group('getControllerForType', () {
14+
setUp(() async {
15+
await auth.createUserWithEmailAndPassword(
16+
17+
password: '123456',
18+
);
19+
});
20+
21+
Future<void> authenticate(WidgetTester tester) async {
22+
final inputs = find.byType(TextFormField);
23+
24+
await tester.enterText(inputs.at(0), '[email protected]');
25+
await tester.testTextInput.receiveAction(TextInputAction.done);
26+
await tester.pumpAndSettle();
27+
28+
await tester.enterText(inputs.at(1), '123456');
29+
await tester.testTextInput.receiveAction(TextInputAction.done);
30+
await tester.pumpAndSettle();
31+
}
32+
33+
testWidgets(
34+
'getControllerForState returns correct controller type',
35+
(tester) async {
36+
late AuthController controller;
37+
38+
await render(
39+
tester,
40+
FirebaseUIActions(
41+
actions: [
42+
AuthStateChangeAction<SignedIn>((context, state) {
43+
controller = getControllerForState(state);
44+
})
45+
],
46+
child: const EmailForm(action: AuthAction.signIn),
47+
),
48+
);
49+
50+
await authenticate(tester);
51+
52+
expect(controller, isA<EmailAuthController>());
53+
},
54+
);
55+
56+
testWidgets(
57+
'throws a StateError if used outside of the FirebaseUIAction',
58+
(tester) async {
59+
late AuthState state;
60+
61+
await render(
62+
tester,
63+
AuthStateListener(
64+
listener: (oldState, newState, _) {
65+
state = newState;
66+
return null;
67+
},
68+
child: const EmailForm(action: AuthAction.signIn),
69+
),
70+
);
71+
72+
await authenticate(tester);
73+
74+
expect(() => getControllerForState(state), throwsStateError);
75+
},
76+
);
77+
});
78+
}

Diff for: tests/integration_test/firebase_ui_auth/firebase_ui_auth_e2e.dart

+2
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import 'universal_email_sign_in_screen_test.dart'
1111
as universal_email_sign_in_screen;
1212
import 'phone_verification_test.dart' as phone_verification;
1313
import 'layout_test.dart' as layout;
14+
import 'actions_test.dart' as actions;
1415

1516
Future<void> main() async {
1617
group('Auth', () {
1718
email_form.main();
1819
email_link_sign_in_view.main();
1920
universal_email_sign_in_screen.main();
21+
actions.main();
2022

2123
switch (defaultTargetPlatform) {
2224
case TargetPlatform.iOS:

Diff for: tests/integration_test/firebase_ui_test.dart

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
66
import 'package:flutter_test/flutter_test.dart';
77

88
import './firebase_ui_auth/firebase_ui_auth_e2e.dart' as firebase_ui_auth_e2e;
9+
910
import './firebase_ui_firestore/firebase_ui_firestore_e2e.dart'
1011
as firebase_ui_firestore_e2e;
1112
import './firebase_ui_oauth_apple/firebase_ui_oauth_apple_e2e.dart'
@@ -20,10 +21,8 @@ import './firebase_ui_oauth_twitter/firebase_ui_oauth_twitter_e2e.dart'
2021
import 'utils.dart';
2122

2223
void main() {
24+
setUpTests();
2325
group('Firebase UI', () {
24-
setUpAll(prepare);
25-
tearDown(authCleanup);
26-
2726
firebase_ui_auth_e2e.main();
2827

2928
if (defaultTargetPlatform != TargetPlatform.macOS) {

Diff for: tests/integration_test/utils.dart

+10
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ Future<void> authCleanup() async {
5858
await deleteAllAccounts();
5959
}
6060

61+
bool _testsSetUp = false;
62+
63+
void setUpTests() {
64+
if (_testsSetUp) return;
65+
66+
setUpAll(prepare);
67+
tearDown(authCleanup);
68+
_testsSetUp = true;
69+
}
70+
6171
Future<void> render(WidgetTester tester, Widget widget) async {
6272
await tester.pumpWidget(
6373
MaterialApp(

0 commit comments

Comments
 (0)