Skip to content

Commit

Permalink
feat: Sign up form: try to highlight the issue (openfoodfacts#2535)
Browse files Browse the repository at this point in the history
* Sign up form: try to highlight the issue

* oOOO
Dart format acts really weirdly on this one…

* Checkboxes: the whole item (including text) is clickable, not only the checkbox itself

* Sign up: Highlight the faulting field
  • Loading branch information
g123k authored Aug 16, 2022
1 parent c52327e commit d08b1bd
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class SmoothTextFormField extends StatefulWidget {
this.onChanged,
this.onFieldSubmitted,
this.autofocus,
this.focusNode,
});

final TextFieldTypes type;
Expand All @@ -37,6 +38,7 @@ class SmoothTextFormField extends StatefulWidget {
final void Function(String?)? onChanged;
final ValueChanged<String>? onFieldSubmitted;
final bool? autofocus;
final FocusNode? focusNode;

@override
State<SmoothTextFormField> createState() => _SmoothTextFormFieldState();
Expand Down Expand Up @@ -68,6 +70,7 @@ class _SmoothTextFormFieldState extends State<SmoothTextFormField> {
obscureText: _obscureText,
enableSuggestions: enableSuggestions,
autocorrect: autocorrect,
focusNode: widget.focusNode,
autofillHints: widget.autofillHints,
autofocus: widget.autofocus ?? false,
autovalidateMode: AutovalidateMode.onUserInteraction,
Expand Down
87 changes: 62 additions & 25 deletions packages/smooth_app/lib/pages/user_management/sign_up_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:matomo_tracker/matomo_tracker.dart';
import 'package:openfoodfacts/model/SignUpStatus.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/data_models/user_management_provider.dart';
Expand Down Expand Up @@ -34,6 +35,10 @@ class _SignUpPageState extends State<SignUpPage> with TraceableClientMixin {
final TextEditingController _password2Controller = TextEditingController();
final TextEditingController _brandController = TextEditingController();

final FocusNode _userFocusNode = FocusNode();
final FocusNode _emailFocusNode = FocusNode();
final FocusNode _password1FocusNode = FocusNode();

bool _foodProducer = false;
bool _agree = false;
bool _subscribe = false;
Expand Down Expand Up @@ -103,6 +108,7 @@ class _SignUpPageState extends State<SignUpPage> with TraceableClientMixin {
textInputType: TextInputType.emailAddress,
type: TextFieldTypes.PLAIN_TEXT,
controller: _emailController,
focusNode: _emailFocusNode,
textInputAction: TextInputAction.next,
hintText: appLocalizations.sign_up_page_email_hint,
prefixIcon: const Icon(Icons.person),
Expand All @@ -124,6 +130,7 @@ class _SignUpPageState extends State<SignUpPage> with TraceableClientMixin {
SmoothTextFormField(
type: TextFieldTypes.PLAIN_TEXT,
controller: _userController,
focusNode: _userFocusNode,
textInputAction: TextInputAction.next,
hintText: appLocalizations.sign_up_page_username_hint,
prefixIcon: const Icon(Icons.person),
Expand Down Expand Up @@ -153,6 +160,7 @@ class _SignUpPageState extends State<SignUpPage> with TraceableClientMixin {
SmoothTextFormField(
type: TextFieldTypes.PASSWORD,
controller: _password1Controller,
focusNode: _password1FocusNode,
textInputAction: TextInputAction.next,
hintText: appLocalizations.sign_up_page_password_hint,
prefixIcon: const Icon(Icons.vpn_key),
Expand Down Expand Up @@ -197,14 +205,18 @@ class _SignUpPageState extends State<SignUpPage> with TraceableClientMixin {
// careful with CheckboxListTile and hyperlinks
// cf. https://github.com/flutter/flutter/issues/31437
ListTile(
leading: Checkbox(
value: _agree,
fillColor: MaterialStateProperty.resolveWith(getCheckBoxColor),
onChanged: (final bool? value) {
if (value != null) {
setState(() => _agree = value);
}
},
onTap: () {
setState(() => _agree = !_agree);
},
contentPadding: EdgeInsets.zero,
leading: IgnorePointer(
ignoring: true,
child: Checkbox(
value: _agree,
fillColor:
MaterialStateProperty.resolveWith(getCheckBoxColor),
onChanged: (_) {},
),
),
title: RichText(
text: TextSpan(
Expand Down Expand Up @@ -243,14 +255,18 @@ class _SignUpPageState extends State<SignUpPage> with TraceableClientMixin {
),
const SizedBox(height: space),
ListTile(
leading: Checkbox(
value: _foodProducer,
fillColor: MaterialStateProperty.resolveWith(getCheckBoxColor),
onChanged: (final bool? value) {
if (value != null) {
setState(() => _foodProducer = value);
}
},
onTap: () {
setState(() => _foodProducer = !_foodProducer);
},
contentPadding: EdgeInsets.zero,
leading: IgnorePointer(
ignoring: true,
child: Checkbox(
value: _foodProducer,
fillColor:
MaterialStateProperty.resolveWith(getCheckBoxColor),
onChanged: (_) {},
),
),
title: Text(
appLocalizations.sign_up_page_producer_checkbox,
Expand All @@ -277,14 +293,18 @@ class _SignUpPageState extends State<SignUpPage> with TraceableClientMixin {
],
const SizedBox(height: space),
ListTile(
leading: Checkbox(
value: _subscribe,
fillColor: MaterialStateProperty.resolveWith(getCheckBoxColor),
onChanged: (final bool? value) {
if (value != null) {
setState(() => _subscribe = value);
}
},
onTap: () {
setState(() => _subscribe = !_subscribe);
},
contentPadding: EdgeInsets.zero,
leading: IgnorePointer(
ignoring: true,
child: Checkbox(
value: _subscribe,
fillColor:
MaterialStateProperty.resolveWith(getCheckBoxColor),
onChanged: (_) {},
),
),
title: Text(
appLocalizations.sign_up_page_subscribe_checkbox,
Expand Down Expand Up @@ -331,7 +351,7 @@ class _SignUpPageState extends State<SignUpPage> with TraceableClientMixin {
userId: _userController.trimmedText,
password: _password1Controller.text,
);
final Status? status = await LoadingDialog.run<Status>(
final SignUpStatus? status = await LoadingDialog.run<SignUpStatus>(
context: context,
future: OpenFoodAPIClient.register(
user: user,
Expand All @@ -348,6 +368,23 @@ class _SignUpPageState extends State<SignUpPage> with TraceableClientMixin {
}
if (status.error != null) {
await LoadingDialog.error(context: context, title: status.error);

// Highlight the field with the error
if (status.statusErrors?.isNotEmpty == true) {
if (status.statusErrors!
.contains(SignUpStatusError.EMAIL_ALREADY_USED)) {
_emailFocusNode.requestFocus();
} else if (status.statusErrors!
.contains(SignUpStatusError.INVALID_PASSWORD)) {
_password1FocusNode.requestFocus();
} else if (status.statusErrors!
.contains(SignUpStatusError.INVALID_USERNAME) ||
status.statusErrors!
.contains(SignUpStatusError.USERNAME_ALREADY_USED)) {
_userFocusNode.requestFocus();
}
}

return;
}
AnalyticsHelper.trackRegister();
Expand Down

0 comments on commit d08b1bd

Please sign in to comment.