✨ Create account field validation
This commit is contained in:
parent
add904cc41
commit
45f61533ee
@ -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."
|
||||||
}
|
}
|
||||||
|
@ -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": "此字段不能为空。"
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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"))
|
||||||
|
24
pubspec.lock
24
pubspec.lock
@ -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:
|
||||||
|
@ -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:
|
||||||
|
@ -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"));
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user