Create account field validation

This commit is contained in:
LittleSheep 2024-12-08 15:10:35 +08:00
parent add904cc41
commit 45f61533ee
8 changed files with 137 additions and 65 deletions

View File

@ -84,8 +84,12 @@
"fieldEmail": "Email address", "fieldEmail": "Email address",
"fieldPassword": "Password", "fieldPassword": "Password",
"fieldDescription": "Description", "fieldDescription": "Description",
"fieldUsernameAlphanumOnly": "Username can only contain alphanumeric characters.",
"fieldUsernameLengthLimit": "Username must be between {} and {} characters.",
"fieldUsernameCannotEditHint": "Username cannot be edited after created", "fieldUsernameCannotEditHint": "Username cannot be edited after created",
"fieldUsernameLookupHint": "You can use username, phone number or email to login", "fieldUsernameLookupHint": "You can use username, phone number or email to login",
"fieldNicknameLengthLimit": "Nickname must be between {} and {} characters.",
"fieldEmailAddressMustBeValid": "Email address must be an email address.",
"fieldFirstName": "First name", "fieldFirstName": "First name",
"fieldLastName": "Last name", "fieldLastName": "Last name",
"fieldBirthday": "Birthday", "fieldBirthday": "Birthday",
@ -408,5 +412,6 @@
"accountDeletionSubmitted": "Account deletion request has been sent, you can check your inbox and follow the instructions in the email to complete the deletion operation.", "accountDeletionSubmitted": "Account deletion request has been sent, you can check your inbox and follow the instructions in the email to complete the deletion operation.",
"channelNewChannel": "New Channel", "channelNewChannel": "New Channel",
"channelNewDirectMessage": "New Direct Message", "channelNewDirectMessage": "New Direct Message",
"channelDirectMessageDescription": "Direct Message with {}" "channelDirectMessageDescription": "Direct Message with {}",
"fieldCannotBeEmpty": "This field cannot be empty."
} }

View File

@ -69,8 +69,12 @@
"fieldNickname": "显示名", "fieldNickname": "显示名",
"fieldEmail": "电子邮箱地址", "fieldEmail": "电子邮箱地址",
"fieldPassword": "密码", "fieldPassword": "密码",
"fieldUsernameAlphanumOnly": "用户名只能包含英文大小写字母和数字。",
"fieldUsernameLengthLimit": "用户名必须在 {} 和 {} 之间。",
"fieldUsernameCannotEditHint": "用户名在创建后无法修改", "fieldUsernameCannotEditHint": "用户名在创建后无法修改",
"fieldUsernameLookupHint": "支持用户名、电话号码或邮箱地址", "fieldUsernameLookupHint": "支持用户名、电话号码或邮箱地址",
"fieldNicknameLengthLimit": "昵称必须在 {} 和 {} 之间。",
"fieldEmailAddressMustBeValid": "电子邮箱地址必须是一个电子邮箱地址。",
"fieldFirstName": "名", "fieldFirstName": "名",
"fieldLastName": "姓", "fieldLastName": "姓",
"fieldBirthday": "生日", "fieldBirthday": "生日",
@ -408,5 +412,6 @@
"accountDeletionSubmitted": "帐户删除申请已发出,你可以检查你的收件箱并根据邮件内的指示完成删除操作。", "accountDeletionSubmitted": "帐户删除申请已发出,你可以检查你的收件箱并根据邮件内的指示完成删除操作。",
"channelNewChannel": "新建频道", "channelNewChannel": "新建频道",
"channelNewDirectMessage": "发起私信", "channelNewDirectMessage": "发起私信",
"channelDirectMessageDescription": "与 {} 的私聊" "channelDirectMessageDescription": "与 {} 的私聊",
"fieldCannotBeEmpty": "此字段不能为空。"
} }

View File

@ -1,4 +1,5 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:email_validator/email_validator.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
@ -16,20 +17,21 @@ class RegisterScreen extends StatefulWidget {
} }
class _RegisterScreenState extends State<RegisterScreen> { class _RegisterScreenState extends State<RegisterScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController(); final _emailController = TextEditingController();
final _usernameController = TextEditingController(); final _usernameController = TextEditingController();
final _nicknameController = TextEditingController(); final _nicknameController = TextEditingController();
final _passwordController = TextEditingController(); final _passwordController = TextEditingController();
void _performAction(BuildContext context) async { void _performAction(BuildContext context) async {
if (!_formKey.currentState!.validate()) return;
final email = _emailController.value.text; final email = _emailController.value.text;
final username = _usernameController.value.text; final username = _usernameController.value.text;
final nickname = _nicknameController.value.text; final nickname = _nicknameController.value.text;
final password = _passwordController.value.text; final password = _passwordController.value.text;
if (email.isEmpty || if (email.isEmpty || username.isEmpty || nickname.isEmpty || password.isEmpty) {
username.isEmpty ||
nickname.isEmpty ||
password.isEmpty) {
return; return;
} }
@ -42,8 +44,7 @@ class _RegisterScreenState extends State<RegisterScreen> {
'password': password, 'password': password,
}); });
if (!mounted) return; if (!context.mounted) return;
GoRouter.of(context).replaceNamed("authLogin"); GoRouter.of(context).replaceNamed("authLogin");
} catch (err) { } catch (err) {
context.showErrorDialog(err); context.showErrorDialog(err);
@ -75,67 +76,96 @@ class _RegisterScreenState extends State<RegisterScreen> {
fontWeight: FontWeight.w900, fontWeight: FontWeight.w900,
), ),
).tr().padding(left: 4, bottom: 16), ).tr().padding(left: 4, bottom: 16),
Column( Form(
children: [ key: _formKey,
TextField( autovalidateMode: AutovalidateMode.onUserInteraction,
autocorrect: false, child: Column(
enableSuggestions: false, children: [
controller: _usernameController, TextFormField(
autofillHints: const [AutofillHints.username], validator: (value) {
decoration: InputDecoration( if (value == null || value.length < 4 || value.length > 32) {
isDense: true, return 'fieldUsernameLengthLimit'.tr(args: [4.toString(), 32.toString()]);
border: const UnderlineInputBorder(), }
labelText: 'fieldUsername'.tr(), if (!RegExp(r'^[a-zA-Z0-9_]+$').hasMatch(value)) {
return 'fieldUsernameAlphanumOnly'.tr();
}
return null;
},
autocorrect: false,
enableSuggestions: false,
controller: _usernameController,
autofillHints: const [AutofillHints.username],
decoration: InputDecoration(
isDense: true,
border: const UnderlineInputBorder(),
labelText: 'fieldUsername'.tr(),
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
), ),
onTapOutside: (_) => const Gap(12),
FocusManager.instance.primaryFocus?.unfocus(), TextFormField(
), validator: (value) {
const Gap(12), if (value == null || value.length < 4 || value.length > 32) {
TextField( return 'fieldNicknameLengthLimit'.tr(args: [4.toString(), 32.toString()]);
autocorrect: false, }
enableSuggestions: false, return null;
controller: _nicknameController, },
autofillHints: const [AutofillHints.nickname], autocorrect: false,
decoration: InputDecoration( enableSuggestions: false,
isDense: true, controller: _nicknameController,
border: const UnderlineInputBorder(), autofillHints: const [AutofillHints.nickname],
labelText: 'fieldNickname'.tr(), decoration: InputDecoration(
isDense: true,
border: const UnderlineInputBorder(),
labelText: 'fieldNickname'.tr(),
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
), ),
onTapOutside: (_) => const Gap(12),
FocusManager.instance.primaryFocus?.unfocus(), TextFormField(
), validator: (value) {
const Gap(12), if (value == null || value.isEmpty) {
TextField( return 'fieldCannotBeEmpty'.tr();
autocorrect: false, }
enableSuggestions: false, if (!EmailValidator.validate(value)) {
controller: _emailController, return 'fieldEmailAddressMustBeValid'.tr();
autofillHints: const [AutofillHints.email], }
decoration: InputDecoration( return null;
isDense: true, },
border: const UnderlineInputBorder(), autocorrect: false,
labelText: 'fieldEmail'.tr(), enableSuggestions: false,
controller: _emailController,
autofillHints: const [AutofillHints.email],
decoration: InputDecoration(
isDense: true,
border: const UnderlineInputBorder(),
labelText: 'fieldEmail'.tr(),
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
), ),
onTapOutside: (_) => const Gap(12),
FocusManager.instance.primaryFocus?.unfocus(), TextFormField(
), validator: (value) {
const Gap(12), if (value == null || value.isEmpty) {
TextField( return 'fieldCannotBeEmpty'.tr();
obscureText: true, }
autocorrect: false, return null;
enableSuggestions: false, },
autofillHints: const [AutofillHints.password], obscureText: true,
controller: _passwordController, autocorrect: false,
decoration: InputDecoration( enableSuggestions: false,
isDense: true, autofillHints: const [AutofillHints.password],
border: const UnderlineInputBorder(), controller: _passwordController,
labelText: 'fieldPassword'.tr(), decoration: InputDecoration(
isDense: true,
border: const UnderlineInputBorder(),
labelText: 'fieldPassword'.tr(),
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
), ),
onTapOutside: (_) => ],
FocusManager.instance.primaryFocus?.unfocus(), ).padding(horizontal: 7),
onSubmitted: (_) => _performAction(context), ),
),
],
).padding(horizontal: 7),
const Gap(16), const Gap(16),
Align( Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,

View File

@ -23,6 +23,7 @@ import pasteboard
import path_provider_foundation import path_provider_foundation
import screen_brightness_macos import screen_brightness_macos
import sentry_flutter import sentry_flutter
import share_plus
import shared_preferences_foundation import shared_preferences_foundation
import sqflite_darwin import sqflite_darwin
import url_launcher_macos import url_launcher_macos
@ -47,6 +48,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin")) ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin"))
SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin")) SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))

View File

@ -454,6 +454,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.0.2" version: "0.0.2"
email_validator:
dependency: "direct main"
description:
name: email_validator
sha256: b19aa5d92fdd76fbc65112060c94d45ba855105a28bb6e462de7ff03b12fa1fb
url: "https://pub.dev"
source: hosted
version: "3.0.0"
equatable: equatable:
dependency: transitive dependency: transitive
description: description:
@ -1538,6 +1546,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.10.1" version: "8.10.1"
share_plus:
dependency: "direct main"
description:
name: share_plus
sha256: "9c9bafd4060728d7cdb2464c341743adbd79d327cb067ec7afb64583540b47c8"
url: "https://pub.dev"
source: hosted
version: "10.1.2"
share_plus_platform_interface:
dependency: transitive
description:
name: share_plus_platform_interface
sha256: c57c0bbfec7142e3a0f55633be504b796af72e60e3c791b44d5a017b985f7a48
url: "https://pub.dev"
source: hosted
version: "5.0.1"
shared_preferences: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -96,6 +96,8 @@ dependencies:
sliver_tools: ^0.2.12 sliver_tools: ^0.2.12
bitsdojo_window: ^0.1.6 bitsdojo_window: ^0.1.6
gal: ^2.3.0 gal: ^2.3.0
share_plus: ^10.1.2
email_validator: ^3.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@ -20,6 +20,7 @@
#include <permission_handler_windows/permission_handler_windows_plugin.h> #include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <screen_brightness_windows/screen_brightness_windows_plugin.h> #include <screen_brightness_windows/screen_brightness_windows_plugin.h>
#include <sentry_flutter/sentry_flutter_plugin.h> #include <sentry_flutter/sentry_flutter_plugin.h>
#include <share_plus/share_plus_windows_plugin_c_api.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
@ -51,6 +52,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin"));
SentryFlutterPluginRegisterWithRegistrar( SentryFlutterPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SentryFlutterPlugin")); registry->GetRegistrarForPlugin("SentryFlutterPlugin"));
SharePlusWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
UrlLauncherWindowsRegisterWithRegistrar( UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows")); registry->GetRegistrarForPlugin("UrlLauncherWindows"));
} }

View File

@ -17,6 +17,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
permission_handler_windows permission_handler_windows
screen_brightness_windows screen_brightness_windows
sentry_flutter sentry_flutter
share_plus
url_launcher_windows url_launcher_windows
) )