Skip to content

Commit 5df890d

Browse files
committed
fix: refactor the sign up panel build method [#246]
1 parent 03bf379 commit 5df890d

File tree

1 file changed

+110
-87
lines changed

1 file changed

+110
-87
lines changed

packages/clerk_flutter/lib/src/widgets/authentication/clerk_sign_up_panel.dart

Lines changed: 110 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,12 @@ import 'package:clerk_flutter/src/widgets/ui/closeable.dart';
1111
import 'package:clerk_flutter/src/widgets/ui/common.dart';
1212
import 'package:clerk_flutter/src/widgets/ui/style/colors.dart';
1313
import 'package:clerk_flutter/src/widgets/ui/style/text_style.dart';
14+
import 'package:collection/collection.dart';
1415
import 'package:flutter/gestures.dart';
1516
import 'package:flutter/material.dart';
1617
import 'package:phone_input/phone_input_package.dart';
1718
import 'package:url_launcher/url_launcher_string.dart';
1819

19-
typedef _ValueChanger = void Function(String value);
20-
2120
enum _SignUpPanelState {
2221
input,
2322
waiting;
@@ -50,7 +49,6 @@ class _ClerkSignUpPanelState extends State<ClerkSignUpPanel>
5049

5150
_SignUpPanelState _state = _SignUpPanelState.input;
5251
final Map<clerk.UserAttribute, String?> _values = {};
53-
bool _isObscured = true;
5452
bool _needsLegalAcceptance = true;
5553
bool _hasLegalAcceptance = false;
5654
bool _highlightMissing = false;
@@ -166,17 +164,11 @@ class _ClerkSignUpPanelState extends State<ClerkSignUpPanel>
166164
});
167165
}
168166

169-
void _onObscure() => setState(() => _isObscured = !_isObscured);
170-
171167
void _acceptTerms() =>
172168
setState(() => _hasLegalAcceptance = !_hasLegalAcceptance);
173169

174170
void _reset() => setState(() => _state = _SignUpPanelState.input);
175171

176-
_ValueChanger _change(clerk.UserAttribute attr) => (String value) {
177-
_values[attr] = value;
178-
};
179-
180172
@override
181173
Widget build(BuildContext context) {
182174
final authState = ClerkAuth.of(context);
@@ -193,19 +185,19 @@ class _ClerkSignUpPanelState extends State<ClerkSignUpPanel>
193185
case clerk.UserAttributeData data when data.isEnabled) //
194186
_Attribute(attr, data),
195187
];
196-
final lastNameAttr = attributes.any((a) => a.isFirstName)
197-
? attributes.removeFirstOrNull((a) => a.isLastName)
198-
: null;
199188
final isAwaitingCode = (env.supportsEmailCode &&
200189
signUp?.unverified(clerk.Field.emailAddress) == true) ||
201190
(env.supportsPhoneCode &&
202191
signUp?.unverified(clerk.Field.phoneNumber) == true);
203192

204-
bool isMissing(_Attribute attribute) =>
205-
signUp?.missing(clerk.Field.forUserAttribute(attribute.attr)) == true ||
206-
(_highlightMissing &&
207-
attribute.isRequired &&
208-
_valueOrNull(attribute.attr) == null);
193+
// if we have both first and last name, associate them
194+
attributes.firstWhereOrNull((a) => a.isFirstName)?.associated =
195+
attributes.removeFirstOrNull((a) => a.isLastName);
196+
197+
// if we have a password, associate a confirmation
198+
final password = attributes.firstWhereOrNull((a) => a.isPassword);
199+
password?.associated =
200+
_Attribute(clerk.UserAttribute.passwordConfirmation, password.data);
209201

210202
return Column(
211203
crossAxisAlignment: CrossAxisAlignment.stretch,
@@ -257,74 +249,16 @@ class _ClerkSignUpPanelState extends State<ClerkSignUpPanel>
257249
Closeable(
258250
closed: _state.isWaiting,
259251
child: Column(
252+
mainAxisSize: MainAxisSize.min,
260253
children: [
261-
for (final attribute in attributes) ...[
262-
if (attribute.isPhoneNumber) //
263-
ClerkPhoneNumberFormField(
264-
initial: _values[clerk.UserAttribute.phoneNumber],
265-
label: attribute.title(l10ns),
266-
isMissing: isMissing(attribute),
267-
isOptional: attribute.isOptional,
268-
onChanged: _change(clerk.UserAttribute.phoneNumber),
269-
)
270-
else if (attribute.isFirstName) //
271-
Row(
272-
mainAxisSize: MainAxisSize.min,
273-
children: [
274-
Expanded(
275-
child: ClerkTextFormField(
276-
initial: _values[attribute.attr],
277-
label: attribute.title(l10ns),
278-
isMissing: isMissing(attribute),
279-
isOptional: attribute.isOptional,
280-
onChanged: _change(attribute.attr),
281-
),
282-
),
283-
if (lastNameAttr case final lastNameAttr?) ...[
284-
horizontalMargin16,
285-
Expanded(
286-
child: ClerkTextFormField(
287-
initial: _values[lastNameAttr.attr],
288-
label: lastNameAttr.title(l10ns),
289-
isMissing: isMissing(lastNameAttr),
290-
isOptional: lastNameAttr.isOptional,
291-
onChanged: _change(lastNameAttr.attr),
292-
),
293-
),
294-
],
295-
],
296-
)
297-
else if (attribute.isPassword) ...[
298-
ClerkTextFormField(
299-
initial: _values[clerk.UserAttribute.password],
300-
label: attribute.title(l10ns),
301-
isMissing: isMissing(attribute),
302-
isOptional: attribute.isOptional,
303-
obscureText: _isObscured,
304-
onObscure: _onObscure,
305-
onChanged: _change(clerk.UserAttribute.password),
306-
),
307-
verticalMargin16,
308-
ClerkTextFormField(
309-
initial: _values[clerk.UserAttribute.passwordConfirmation],
310-
label: l10ns.grammar.toSentence(l10ns.passwordConfirmation),
311-
isMissing: isMissing(attribute),
312-
isOptional: attribute.isOptional,
313-
obscureText: _isObscured,
314-
onObscure: _onObscure,
315-
onChanged:
316-
_change(clerk.UserAttribute.passwordConfirmation),
317-
),
318-
] else
319-
ClerkTextFormField(
320-
initial: _values[attribute.attr],
321-
label: attribute.title(l10ns),
322-
isMissing: isMissing(attribute),
323-
isOptional: attribute.isOptional,
324-
onChanged: _change(attribute.attr),
325-
),
326-
verticalMargin16,
327-
],
254+
for (final attribute in attributes) //
255+
_FormField(
256+
attribute: attribute,
257+
authState: authState,
258+
localizations: l10ns,
259+
values: _values,
260+
highlight: _highlightMissing,
261+
),
328262
],
329263
),
330264
),
@@ -374,13 +308,97 @@ class _ClerkSignUpPanelState extends State<ClerkSignUpPanel>
374308
}
375309
}
376310

311+
class _FormField extends StatelessWidget {
312+
const _FormField({
313+
required this.attribute,
314+
required this.authState,
315+
required this.localizations,
316+
required this.values,
317+
required this.highlight,
318+
});
319+
320+
final _Attribute attribute;
321+
322+
final ClerkAuthState authState;
323+
324+
final ClerkSdkLocalizations localizations;
325+
326+
final Map<clerk.UserAttribute, String?> values;
327+
328+
final bool highlight;
329+
330+
static final _obscure = ValueNotifier(true);
331+
332+
bool _isMissing(ClerkAuthState authState, _Attribute attribute) =>
333+
authState.signUp?.missing(clerk.Field.forUserAttribute(attribute.attr)) ==
334+
true ||
335+
(highlight &&
336+
attribute.isRequired &&
337+
(values[attribute.attr]?.trim() ?? '').isEmpty);
338+
339+
Widget _formField(_Attribute attribute) {
340+
if (attribute.needsObscuring) {
341+
return ValueListenableBuilder(
342+
valueListenable: _obscure,
343+
builder: (context, obscure, _) {
344+
return ClerkTextFormField(
345+
initial: values[attribute.attr],
346+
label: attribute.title(localizations),
347+
obscureText: obscure,
348+
onObscure: () => _obscure.value = !obscure,
349+
isMissing: _isMissing(authState, attribute),
350+
onChanged: (value) => values[attribute.attr] = value,
351+
);
352+
},
353+
);
354+
}
355+
356+
return ClerkTextFormField(
357+
initial: values[attribute.attr],
358+
label: attribute.title(localizations),
359+
isMissing: _isMissing(authState, attribute),
360+
onChanged: (value) => values[attribute.attr] = value,
361+
);
362+
}
363+
364+
@override
365+
Widget build(BuildContext context) {
366+
return Padding(
367+
padding: bottomPadding16,
368+
child: switch (attribute) {
369+
_Attribute attribute when attribute.isPhoneNumber =>
370+
ClerkPhoneNumberFormField(
371+
initial: values[attribute.attr],
372+
label: attribute.title(localizations),
373+
isMissing: _isMissing(authState, attribute),
374+
isOptional: attribute.isOptional,
375+
onChanged: (value) => values[attribute.attr] = value,
376+
),
377+
_Attribute attribute when attribute.associated is _Attribute => Flex(
378+
direction: attribute.isFirstName ? Axis.horizontal : Axis.vertical,
379+
mainAxisSize: MainAxisSize.min,
380+
children: [
381+
Flexible(fit: FlexFit.loose, child: _formField(attribute)),
382+
const SizedBox.square(dimension: 16),
383+
Flexible(
384+
fit: FlexFit.loose,
385+
child: _formField(attribute.associated!),
386+
),
387+
],
388+
),
389+
_Attribute attribute => _formField(attribute),
390+
},
391+
);
392+
}
393+
}
394+
377395
class _LegalAcceptanceConfirmation extends StatelessWidget {
378396
const _LegalAcceptanceConfirmation();
379397

380398
List<TextSpan> _subSpans(String text, String target, String? url) {
381-
if (url case String url) {
399+
if (url case String url when url.isNotEmpty) {
382400
final segments = text.split(target);
383-
final spans = <TextSpan>[TextSpan(text: segments.first)];
401+
final spans = [TextSpan(text: segments.first)];
384402

385403
for (final segmentText in segments.skip(1)) {
386404
spans.add(
@@ -507,12 +525,14 @@ class _CodeInputBoxState extends State<_CodeInputBox> {
507525
}
508526

509527
class _Attribute {
510-
const _Attribute(this.attr, this.data);
528+
_Attribute(this.attr, this.data);
511529

512530
final clerk.UserAttribute attr;
513531

514532
final clerk.UserAttributeData data;
515533

534+
_Attribute? associated;
535+
516536
bool get isPhoneNumber => attr == clerk.UserAttribute.phoneNumber;
517537

518538
bool get isPassword => attr == clerk.UserAttribute.password;
@@ -525,6 +545,9 @@ class _Attribute {
525545

526546
bool get isOptional => isRequired == false;
527547

548+
bool get needsObscuring =>
549+
isPassword || attr == clerk.UserAttribute.passwordConfirmation;
550+
528551
String title(ClerkSdkLocalizations l10ns) =>
529552
l10ns.grammar.toSentence(attr.localizedMessage(l10ns));
530553
}

0 commit comments

Comments
 (0)