Skip to content

Commit 6e3735d

Browse files
authored
feat(ui_auth): allow to toggle password visibility (firebase#104)
1 parent e05ae75 commit 6e3735d

10 files changed

+217
-59
lines changed

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
@@ -51,6 +51,9 @@ class LoginScreen extends StatelessWidget {
5151
final double breakpoint;
5252
final Set<FirebaseUIStyle>? styles;
5353

54+
/// {@macro ui.auth.widgets.email_form.showPasswordVisibilityToggle}
55+
final bool showPasswordVisibilityToggle;
56+
5457
const LoginScreen({
5558
super.key,
5659
required this.action,
@@ -69,6 +72,7 @@ class LoginScreen extends StatelessWidget {
6972
this.loginViewKey,
7073
this.breakpoint = 800,
7174
this.styles,
75+
this.showPasswordVisibilityToggle = false,
7276
});
7377

7478
@override
@@ -87,6 +91,7 @@ class LoginScreen extends StatelessWidget {
8791
showAuthActionSwitch: showAuthActionSwitch,
8892
subtitleBuilder: subtitleBuilder,
8993
footerBuilder: footerBuilder,
94+
showPasswordVisibilityToggle: showPasswordVisibilityToggle,
9095
),
9196
),
9297
);

packages/firebase_ui_auth/lib/src/views/different_method_sign_in_view.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ class DifferentMethodSignInView extends StatelessWidget {
2424
/// the [availableProviders].
2525
final VoidCallback? onSignedIn;
2626

27+
/// {@macro ui.auth.widgets.email_from.showPasswordVisibilityToggle}
28+
final bool showPasswordVisibilityToggle;
29+
2730
/// {@macro ui.auth.views.different_method_sign_in_view}
2831
const DifferentMethodSignInView({
2932
super.key,
3033
required this.availableProviders,
3134
required this.providers,
3235
this.auth,
3336
this.onSignedIn,
37+
this.showPasswordVisibilityToggle = false,
3438
});
3539

3640
@override
@@ -59,6 +63,7 @@ class DifferentMethodSignInView extends StatelessWidget {
5963
action: AuthAction.signIn,
6064
providers: providers,
6165
showTitle: false,
66+
showPasswordVisibilityToggle: showPasswordVisibilityToggle,
6267
),
6368
listener: (oldState, newState, ctrl) {
6469
if (newState is SignedIn) {

packages/firebase_ui_auth/lib/src/views/login_view.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ class LoginView extends StatefulWidget {
5555
/// A label that would be used for the "Sign in" button.
5656
final String? actionButtonLabelOverride;
5757

58+
/// {@macro ui.auth.widgets.email_from.showPasswordVisibilityToggle}
59+
final bool showPasswordVisibilityToggle;
60+
5861
/// {@macro ui.auth.views.login_view}
5962
const LoginView({
6063
super.key,
@@ -68,6 +71,7 @@ class LoginView extends StatefulWidget {
6871
this.footerBuilder,
6972
this.subtitleBuilder,
7073
this.actionButtonLabelOverride,
74+
this.showPasswordVisibilityToggle = false,
7175
});
7276

7377
@override
@@ -222,6 +226,8 @@ class _LoginViewState extends State<LoginView> {
222226
provider: provider,
223227
email: widget.email,
224228
actionButtonLabelOverride: widget.actionButtonLabelOverride,
229+
showPasswordVisibilityToggle:
230+
widget.showPasswordVisibilityToggle,
225231
)
226232
] else if (provider is PhoneAuthProvider) ...[
227233
const SizedBox(height: 8),

packages/firebase_ui_auth/lib/src/views/reauthenticate_view.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,17 @@ class ReauthenticateView extends StatelessWidget {
2222
/// A label that would be used for the "Sign in" button.
2323
final String? actionButtonLabelOverride;
2424

25+
/// {@macro ui.auth.widgets.email_from.showPasswordVisibilityToggle}
26+
final bool showPasswordVisibilityToggle;
27+
2528
/// {@macro ui.auth.views.reauthenticate_view}
2629
const ReauthenticateView({
2730
super.key,
2831
required this.providers,
2932
this.auth,
3033
this.onSignedIn,
3134
this.actionButtonLabelOverride,
35+
this.showPasswordVisibilityToggle = false,
3236
});
3337

3438
@override
@@ -63,6 +67,7 @@ class ReauthenticateView extends StatelessWidget {
6367
showTitle: false,
6468
showAuthActionSwitch: false,
6569
actionButtonLabelOverride: actionButtonLabelOverride,
70+
showPasswordVisibilityToggle: showPasswordVisibilityToggle,
6671
),
6772
listener: (oldState, newState, ctrl) {
6873
if (newState is SignedIn) {

packages/firebase_ui_auth/lib/src/widgets/email_form.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ class EmailForm extends StatelessWidget {
131131
/// ```
132132
final EmailFormStyle? style;
133133

134+
/// {@template ui.auth.widgets.email_from.showPasswordVisibilityToggle}
135+
/// Whether to show the password visibility toggle button.
136+
/// {@endtemplate}
137+
final bool showPasswordVisibilityToggle;
138+
134139
/// {@macro ui.auth.widgets.email_form}
135140
const EmailForm({
136141
super.key,
@@ -141,6 +146,7 @@ class EmailForm extends StatelessWidget {
141146
this.email,
142147
this.actionButtonLabelOverride,
143148
this.style,
149+
this.showPasswordVisibilityToggle = false,
144150
});
145151

146152
@override
@@ -153,6 +159,7 @@ class EmailForm extends StatelessWidget {
153159
onSubmit: onSubmit,
154160
actionButtonLabelOverride: actionButtonLabelOverride,
155161
style: style,
162+
showPasswordVisibilityToggle: showPasswordVisibilityToggle,
156163
);
157164

158165
return AuthFlowBuilder<EmailAuthController>(
@@ -175,6 +182,7 @@ class _SignInFormContent extends StatefulWidget {
175182
final EmailAuthProvider? provider;
176183

177184
final String? actionButtonLabelOverride;
185+
final bool showPasswordVisibilityToggle;
178186

179187
final EmailFormStyle? style;
180188

@@ -186,6 +194,7 @@ class _SignInFormContent extends StatefulWidget {
186194
this.provider,
187195
this.actionButtonLabelOverride,
188196
this.style,
197+
this.showPasswordVisibilityToggle = false,
189198
});
190199

191200
@override
@@ -258,6 +267,7 @@ class _SignInFormContentState extends State<_SignInFormContent> {
258267
controller: passwordCtrl,
259268
onSubmit: _submit,
260269
placeholder: l.passwordInputLabel,
270+
showVisibilityToggle: widget.showPasswordVisibilityToggle,
261271
),
262272
if (widget.action == AuthAction.signIn) ...[
263273
const SizedBox(height: 8),
@@ -297,6 +307,7 @@ class _SignInFormContentState extends State<_SignInFormContent> {
297307
)
298308
]),
299309
placeholder: l.confirmPasswordInputLabel,
310+
showVisibilityToggle: widget.showPasswordVisibilityToggle,
300311
),
301312
const SizedBox(height: 8),
302313
],

packages/firebase_ui_auth/lib/src/widgets/email_sign_up_dialog.dart

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,50 +23,74 @@ class EmailSignUpDialog extends StatelessWidget {
2323
/// An instance of [EmailAuthProvider] that should be used to authenticate.
2424
final EmailAuthProvider provider;
2525

26+
/// {@macro ui.auth.widgets.email_from.showPasswordVisibilityToggle}
27+
final bool showPasswordVisibilityToggle;
28+
2629
/// {@macro ui.auth.widget.email_sign_up_dialog}
2730
const EmailSignUpDialog({
2831
super.key,
2932
this.auth,
3033
required this.provider,
3134
required this.action,
35+
this.showPasswordVisibilityToggle = false,
3236
});
3337

38+
AuthStateListenerCallback<EmailAuthController> onAuthStateChanged(
39+
BuildContext context,
40+
) {
41+
return (AuthState oldState, AuthState newState, _) {
42+
if (newState is CredentialLinked) {
43+
Navigator.of(context).pop();
44+
}
45+
46+
return null;
47+
};
48+
}
49+
3450
@override
3551
Widget build(BuildContext context) {
3652
final l = FirebaseUILocalizations.labelsOf(context);
3753

54+
return _DialogWrapper(
55+
child: Dialog(
56+
child: AuthStateListener<EmailAuthController>(
57+
listener: onAuthStateChanged(context),
58+
child: Padding(
59+
padding: const EdgeInsets.all(16),
60+
child: Column(
61+
crossAxisAlignment: CrossAxisAlignment.stretch,
62+
mainAxisSize: MainAxisSize.min,
63+
children: [
64+
const SizedBox(height: 16),
65+
Title(text: l.provideEmail),
66+
const SizedBox(height: 32),
67+
EmailForm(
68+
auth: auth,
69+
action: action,
70+
provider: provider,
71+
showPasswordVisibilityToggle: showPasswordVisibilityToggle,
72+
),
73+
],
74+
),
75+
),
76+
),
77+
),
78+
);
79+
}
80+
}
81+
82+
class _DialogWrapper extends StatelessWidget {
83+
final Widget child;
84+
85+
const _DialogWrapper({required this.child});
86+
87+
@override
88+
Widget build(BuildContext context) {
3889
return Center(
3990
child: SingleChildScrollView(
4091
child: ConstrainedBox(
4192
constraints: const BoxConstraints(maxWidth: 500),
42-
child: Dialog(
43-
child: AuthStateListener<EmailAuthController>(
44-
listener: (oldState, newState, ctrl) {
45-
if (newState is CredentialLinked) {
46-
Navigator.of(context).pop();
47-
}
48-
49-
return null;
50-
},
51-
child: Padding(
52-
padding: const EdgeInsets.all(16),
53-
child: Column(
54-
crossAxisAlignment: CrossAxisAlignment.stretch,
55-
mainAxisSize: MainAxisSize.min,
56-
children: [
57-
const SizedBox(height: 16),
58-
Title(text: l.provideEmail),
59-
const SizedBox(height: 32),
60-
EmailForm(
61-
auth: auth,
62-
action: action,
63-
provider: provider,
64-
),
65-
],
66-
),
67-
),
68-
),
69-
),
93+
child: child,
7094
),
7195
),
7296
);

packages/firebase_ui_auth/lib/src/widgets/internal/universal_icon_button.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class UniversalIconButton extends PlatformWidget {
2626
Widget buildCupertino(BuildContext context) {
2727
return CupertinoButton(
2828
onPressed: onPressed,
29+
padding: EdgeInsets.zero,
2930
child: Icon(cupertinoIcon, size: size),
3031
);
3132
}

packages/firebase_ui_auth/lib/src/widgets/internal/universal_text_form_field.dart

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class UniversalTextFormField extends PlatformWidget {
2424
final bool autocorrect;
2525
final Widget? prefix;
2626
final Iterable<String>? autofillHints;
27+
final Widget? suffixIcon;
2728

2829
const UniversalTextFormField({
2930
super.key,
@@ -40,34 +41,49 @@ class UniversalTextFormField extends PlatformWidget {
4041
this.enableSuggestions,
4142
this.autocorrect = false,
4243
this.autofillHints,
44+
this.suffixIcon,
4345
});
4446

4547
@override
4648
Widget buildCupertino(BuildContext context) {
47-
return Container(
48-
padding: const EdgeInsets.only(bottom: 8),
49-
decoration: const BoxDecoration(
50-
border: Border(
51-
bottom: BorderSide(
52-
color: CupertinoColors.inactiveGray,
49+
return Stack(
50+
children: [
51+
Container(
52+
padding: const EdgeInsets.only(bottom: 8),
53+
decoration: const BoxDecoration(
54+
border: Border(
55+
bottom: BorderSide(
56+
color: CupertinoColors.inactiveGray,
57+
),
58+
),
59+
),
60+
child: CupertinoTextFormFieldRow(
61+
autocorrect: autocorrect,
62+
autofillHints: autofillHints,
63+
focusNode: focusNode,
64+
padding: EdgeInsets.zero,
65+
controller: controller,
66+
placeholder: placeholder,
67+
validator: validator,
68+
onFieldSubmitted: onSubmitted,
69+
autofocus: autofocus,
70+
inputFormatters: inputFormatters,
71+
keyboardType: keyboardType,
72+
obscureText: obscureText,
73+
prefix: prefix,
5374
),
5475
),
55-
),
56-
child: CupertinoTextFormFieldRow(
57-
autocorrect: autocorrect,
58-
autofillHints: autofillHints,
59-
focusNode: focusNode,
60-
padding: EdgeInsets.zero,
61-
controller: controller,
62-
placeholder: placeholder,
63-
validator: validator,
64-
onFieldSubmitted: onSubmitted,
65-
autofocus: autofocus,
66-
inputFormatters: inputFormatters,
67-
keyboardType: keyboardType,
68-
obscureText: obscureText,
69-
prefix: prefix,
70-
),
76+
if (suffixIcon != null)
77+
Positioned.fill(
78+
child: Align(
79+
alignment: Alignment.topRight,
80+
child: Padding(
81+
padding: const EdgeInsets.only(bottom: 8),
82+
child: suffixIcon,
83+
),
84+
),
85+
),
86+
],
7187
);
7288
}
7389

@@ -82,6 +98,7 @@ class UniversalTextFormField extends PlatformWidget {
8298
decoration: InputDecoration(
8399
labelText: placeholder,
84100
prefix: prefix,
101+
suffixIcon: suffixIcon,
85102
),
86103
validator: validator,
87104
onFieldSubmitted: onSubmitted,

0 commit comments

Comments
 (0)