|
|
|
import 'dart:async';
|
|
|
|
|
|
|
|
import 'package:doublefeel_flutter/app/actions/dialog_action.dart';
|
|
|
|
import 'package:doublefeel_flutter/app/models/dialog_meta_data.dart';
|
|
|
|
import 'package:doublefeel_flutter/app/modules/home/widgets/my/security_email_controller.dart';
|
|
|
|
import 'package:doublefeel_flutter/app/utils/dialog_utils.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/theme/app_theme.dart';
|
|
|
|
import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
|
|
|
|
const _securityEmailPageColor = Color(0xFFF5F2FF);
|
|
|
|
const _securityEmailTitleColor = Color(0xFF0F0F11);
|
|
|
|
const _securityEmailDescriptionColor = Color(0xFF78787D);
|
|
|
|
const _securityEmailBrandColor = Color(0xFF845EEE);
|
|
|
|
|
|
|
|
class AddSecurityEmailView extends StatefulWidget {
|
|
|
|
class AddSecurityEmailView extends GetView<AddSecurityEmailController> {
|
|
|
|
const AddSecurityEmailView({super.key});
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<AddSecurityEmailView> createState() => _AddSecurityEmailViewState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _AddSecurityEmailViewState extends State<AddSecurityEmailView> {
|
|
|
|
static final _emailPattern = RegExp(
|
|
|
|
r'^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$',
|
|
|
|
);
|
|
|
|
|
|
|
|
final _emailController = TextEditingController();
|
|
|
|
bool _hasSubmitted = false;
|
|
|
|
|
|
|
|
bool get _isEmailValid =>
|
|
|
|
_emailPattern.hasMatch(_emailController.text.trim());
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
_emailController.dispose();
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
void _sendVerificationEmail() {
|
|
|
|
setState(() => _hasSubmitted = true);
|
|
|
|
if (!_isEmailValid) return;
|
|
|
|
|
|
|
|
Get.to(
|
|
|
|
() => SecurityEmailVerificationView(
|
|
|
|
email: _emailController.text.trim(),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
final c = Get.isRegistered<AddSecurityEmailController>()
|
|
|
|
? controller
|
|
|
|
: Get.put(AddSecurityEmailController());
|
|
|
|
final l10n = context.l10n;
|
|
|
|
final showError = _hasSubmitted && !_isEmailValid;
|
|
|
|
final colors = context.colors;
|
|
|
|
|
|
|
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
|
|
|
value: const SystemUiOverlayStyle(
|
|
|
|
value: SystemUiOverlayStyle(
|
|
|
|
statusBarColor: Colors.transparent,
|
|
|
|
statusBarIconBrightness: Brightness.dark,
|
|
|
|
statusBarBrightness: Brightness.light,
|
|
|
|
systemNavigationBarColor: _securityEmailPageColor,
|
|
|
|
systemNavigationBarColor: colors.backgroundPage,
|
|
|
|
systemNavigationBarIconBrightness: Brightness.dark,
|
|
|
|
),
|
|
|
|
child: Scaffold(
|
|
|
|
backgroundColor: _securityEmailPageColor,
|
|
|
|
backgroundColor: colors.backgroundPage,
|
|
|
|
appBar: const _SecurityEmailAppBar(),
|
|
|
|
body: SafeArea(
|
|
|
|
top: false,
|
|
...
|
...
|
@@ -70,8 +39,8 @@ class _AddSecurityEmailViewState extends State<AddSecurityEmailView> { |
|
|
|
const SizedBox(height: 16),
|
|
|
|
Text(
|
|
|
|
l10n.addSecurityEmail,
|
|
|
|
style: const TextStyle(
|
|
|
|
color: _securityEmailTitleColor,
|
|
|
|
style: TextStyle(
|
|
|
|
color: colors.textPrimary,
|
|
|
|
fontSize: 24,
|
|
|
|
fontWeight: FontWeight.w600,
|
|
|
|
height: 1.25,
|
|
...
|
...
|
@@ -81,8 +50,8 @@ class _AddSecurityEmailViewState extends State<AddSecurityEmailView> { |
|
|
|
Text(
|
|
|
|
l10n.securityEmailDescription,
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
style: const TextStyle(
|
|
|
|
color: _securityEmailDescriptionColor,
|
|
|
|
style: TextStyle(
|
|
|
|
color: colors.textSecondary,
|
|
|
|
fontSize: 14,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
height: 1.43,
|
|
...
|
...
|
@@ -90,16 +59,12 @@ class _AddSecurityEmailViewState extends State<AddSecurityEmailView> { |
|
|
|
),
|
|
|
|
const SizedBox(height: 32),
|
|
|
|
TextField(
|
|
|
|
controller: _emailController,
|
|
|
|
controller: c.emailController,
|
|
|
|
keyboardType: TextInputType.emailAddress,
|
|
|
|
autofillHints: const [AutofillHints.email],
|
|
|
|
inputFormatters: [
|
|
|
|
FilteringTextInputFormatter.deny(RegExp(r'\s')),
|
|
|
|
],
|
|
|
|
onChanged: (_) => setState(() {}),
|
|
|
|
onSubmitted: (_) => _sendVerificationEmail(),
|
|
|
|
style: const TextStyle(
|
|
|
|
color: _securityEmailTitleColor,
|
|
|
|
onSubmitted: (_) => c.sendVerificationEmail(),
|
|
|
|
style: TextStyle(
|
|
|
|
color: colors.textPrimary,
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
),
|
|
...
|
...
|
@@ -114,35 +79,64 @@ class _AddSecurityEmailViewState extends State<AddSecurityEmailView> { |
|
|
|
contentPadding: const EdgeInsets.symmetric(
|
|
|
|
horizontal: 20,
|
|
|
|
),
|
|
|
|
errorText: showError ? l10n.invalidEmailFormat : null,
|
|
|
|
errorStyle: const TextStyle(fontSize: 12),
|
|
|
|
border: _inputBorder(),
|
|
|
|
enabledBorder: _inputBorder(),
|
|
|
|
focusedBorder: _inputBorder(
|
|
|
|
color: _securityEmailBrandColor,
|
|
|
|
color: colors.primary,
|
|
|
|
),
|
|
|
|
suffixIcon: Obx(() {
|
|
|
|
return c.emailInput.value.isNotEmpty
|
|
|
|
? GestureDetector(
|
|
|
|
onTap: c.emailController.clear,
|
|
|
|
child: Icon(
|
|
|
|
Icons.cancel,
|
|
|
|
color: context.colors.textTertiary,
|
|
|
|
size: 18,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
: SizedBox();
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Obx(() {
|
|
|
|
if (!c.showError) return const SizedBox(height: 0);
|
|
|
|
return Padding(
|
|
|
|
padding: const EdgeInsets.only(top: 8, left: 20),
|
|
|
|
child: Align(
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
child: Text(
|
|
|
|
l10n.invalidEmailFormat,
|
|
|
|
style: TextStyle(
|
|
|
|
color: Theme.of(context).colorScheme.error,
|
|
|
|
fontSize: 12,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
const SizedBox(height: 32),
|
|
|
|
SizedBox(
|
|
|
|
width: double.infinity,
|
|
|
|
height: 48,
|
|
|
|
child: ElevatedButton(
|
|
|
|
onPressed: _isEmailValid ? _sendVerificationEmail : null,
|
|
|
|
style: ElevatedButton.styleFrom(
|
|
|
|
backgroundColor: _securityEmailBrandColor,
|
|
|
|
disabledBackgroundColor:
|
|
|
|
_securityEmailBrandColor.withValues(alpha: 0.4),
|
|
|
|
foregroundColor: Colors.white,
|
|
|
|
disabledForegroundColor: Colors.white,
|
|
|
|
elevation: 0,
|
|
|
|
shape: const StadiumBorder(),
|
|
|
|
textStyle: const TextStyle(
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: FontWeight.w600,
|
|
|
|
child: Obx(
|
|
|
|
() => ElevatedButton(
|
|
|
|
onPressed:
|
|
|
|
c.isEmailValid ? c.sendVerificationEmail : null,
|
|
|
|
style: ElevatedButton.styleFrom(
|
|
|
|
backgroundColor: colors.primary,
|
|
|
|
disabledBackgroundColor:
|
|
|
|
colors.primary.withValues(alpha: 0.4),
|
|
|
|
foregroundColor: Colors.white,
|
|
|
|
disabledForegroundColor: Colors.white,
|
|
|
|
elevation: 0,
|
|
|
|
shape: const StadiumBorder(),
|
|
|
|
textStyle: const TextStyle(
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: FontWeight.w600,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
child: Text(l10n.watchThemeNext),
|
|
|
|
),
|
|
|
|
child: Text(l10n.sendVerificationEmail),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
...
|
...
|
@@ -154,74 +148,25 @@ class _AddSecurityEmailViewState extends State<AddSecurityEmailView> { |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class SecurityEmailVerificationView extends StatefulWidget {
|
|
|
|
const SecurityEmailVerificationView({
|
|
|
|
super.key,
|
|
|
|
required this.email,
|
|
|
|
});
|
|
|
|
|
|
|
|
final String email;
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<SecurityEmailVerificationView> createState() =>
|
|
|
|
_SecurityEmailVerificationViewState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _SecurityEmailVerificationViewState
|
|
|
|
extends State<SecurityEmailVerificationView> {
|
|
|
|
static const _resendSeconds = 60;
|
|
|
|
|
|
|
|
final _codeController = TextEditingController();
|
|
|
|
Timer? _resendTimer;
|
|
|
|
int _secondsRemaining = _resendSeconds;
|
|
|
|
|
|
|
|
bool get _canSubmit => _codeController.text.trim().isNotEmpty;
|
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
_startResendCountdown();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
_resendTimer?.cancel();
|
|
|
|
_codeController.dispose();
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
void _startResendCountdown() {
|
|
|
|
_resendTimer?.cancel();
|
|
|
|
setState(() => _secondsRemaining = _resendSeconds);
|
|
|
|
_resendTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
|
|
|
if (_secondsRemaining <= 1) {
|
|
|
|
timer.cancel();
|
|
|
|
setState(() => _secondsRemaining = 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
setState(() => _secondsRemaining -= 1);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void _submitCode() {
|
|
|
|
if (!_canSubmit) return;
|
|
|
|
FocusScope.of(context).unfocus();
|
|
|
|
}
|
|
|
|
class SecurityEmailVerificationView
|
|
|
|
extends GetView<AddSecurityEmailController> {
|
|
|
|
const SecurityEmailVerificationView({super.key});
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
final l10n = context.l10n;
|
|
|
|
final colors = context.colors;
|
|
|
|
|
|
|
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
|
|
|
value: const SystemUiOverlayStyle(
|
|
|
|
value: SystemUiOverlayStyle(
|
|
|
|
statusBarColor: Colors.transparent,
|
|
|
|
statusBarIconBrightness: Brightness.dark,
|
|
|
|
statusBarBrightness: Brightness.light,
|
|
|
|
systemNavigationBarColor: _securityEmailPageColor,
|
|
|
|
systemNavigationBarColor: colors.backgroundPage,
|
|
|
|
systemNavigationBarIconBrightness: Brightness.dark,
|
|
|
|
),
|
|
|
|
child: Scaffold(
|
|
|
|
backgroundColor: _securityEmailPageColor,
|
|
|
|
backgroundColor: colors.backgroundPage,
|
|
|
|
appBar: const _SecurityEmailAppBar(),
|
|
|
|
body: SafeArea(
|
|
|
|
top: false,
|
|
...
|
...
|
@@ -232,20 +177,23 @@ class _SecurityEmailVerificationViewState |
|
|
|
const SizedBox(height: 16),
|
|
|
|
Text(
|
|
|
|
l10n.confirmYourEmail,
|
|
|
|
style: const TextStyle(
|
|
|
|
color: _securityEmailTitleColor,
|
|
|
|
style: TextStyle(
|
|
|
|
color: colors.textPrimary,
|
|
|
|
fontSize: 24,
|
|
|
|
fontWeight: FontWeight.w600,
|
|
|
|
height: 1.25,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const SizedBox(height: 7),
|
|
|
|
Text.rich(
|
|
|
|
_verificationInstruction(
|
|
|
|
l10n.enterCodeSentTo(widget.email),
|
|
|
|
widget.email,
|
|
|
|
Obx(
|
|
|
|
() => Text.rich(
|
|
|
|
_verificationInstruction(
|
|
|
|
context,
|
|
|
|
l10n.enterCodeSentTo(controller.emailInput.value),
|
|
|
|
controller.emailInput.value,
|
|
|
|
),
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
),
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
),
|
|
|
|
const SizedBox(height: 47),
|
|
|
|
Container(
|
|
...
|
...
|
@@ -257,17 +205,15 @@ class _SecurityEmailVerificationViewState |
|
|
|
child: Stack(
|
|
|
|
children: [
|
|
|
|
TextField(
|
|
|
|
controller: _codeController,
|
|
|
|
keyboardType: TextInputType.number,
|
|
|
|
controller: controller.codeController,
|
|
|
|
// keyboardType: TextInputType.number,
|
|
|
|
textInputAction: TextInputAction.done,
|
|
|
|
maxLength: 6,
|
|
|
|
inputFormatters: [
|
|
|
|
FilteringTextInputFormatter.digitsOnly
|
|
|
|
],
|
|
|
|
onChanged: (_) => setState(() {}),
|
|
|
|
onSubmitted: (_) => _submitCode(),
|
|
|
|
style: const TextStyle(
|
|
|
|
color: _securityEmailTitleColor,
|
|
|
|
// inputFormatters: const [
|
|
|
|
// _DigitsOnlyFormatter(),
|
|
|
|
// ],
|
|
|
|
onSubmitted: (_) => controller.submitCode(context),
|
|
|
|
style: TextStyle(
|
|
|
|
color: colors.textPrimary,
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
),
|
|
...
|
...
|
@@ -289,60 +235,71 @@ class _SecurityEmailVerificationViewState |
|
|
|
top: 0,
|
|
|
|
right: 0,
|
|
|
|
bottom: 0,
|
|
|
|
child: TextButton(
|
|
|
|
onPressed: _secondsRemaining == 0
|
|
|
|
? _startResendCountdown
|
|
|
|
: null,
|
|
|
|
style: TextButton.styleFrom(
|
|
|
|
foregroundColor: _securityEmailBrandColor,
|
|
|
|
disabledForegroundColor: const Color(0xFFD9D9D9),
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
|
|
|
textStyle: const TextStyle(
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
child: Text(
|
|
|
|
_secondsRemaining == 0
|
|
|
|
? l10n.resend
|
|
|
|
: l10n.resendWithSeconds(_secondsRemaining),
|
|
|
|
),
|
|
|
|
child: Obx(
|
|
|
|
() {
|
|
|
|
final seconds = controller.secondsRemaining.value;
|
|
|
|
return TextButton(
|
|
|
|
onPressed: seconds == 0
|
|
|
|
? controller.startResendCountdown
|
|
|
|
: null,
|
|
|
|
style: TextButton.styleFrom(
|
|
|
|
foregroundColor: colors.primary,
|
|
|
|
disabledForegroundColor:
|
|
|
|
const Color(0xFFD9D9D9),
|
|
|
|
padding:
|
|
|
|
const EdgeInsets.symmetric(horizontal: 20),
|
|
|
|
textStyle: const TextStyle(
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
child: Text(
|
|
|
|
seconds == 0
|
|
|
|
? l10n.resend
|
|
|
|
: l10n.resendWithSeconds(seconds),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
const SizedBox(height: 60),
|
|
|
|
Text(
|
|
|
|
l10n.emailVerificationHelp,
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
style: const TextStyle(
|
|
|
|
color: _securityEmailDescriptionColor,
|
|
|
|
style: TextStyle(
|
|
|
|
color: colors.textSecondary,
|
|
|
|
fontSize: 14,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
height: 1.2,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const SizedBox(height: 64),
|
|
|
|
const SizedBox(height: 11),
|
|
|
|
SizedBox(
|
|
|
|
width: double.infinity,
|
|
|
|
height: 48,
|
|
|
|
child: ElevatedButton(
|
|
|
|
onPressed: _canSubmit ? _submitCode : null,
|
|
|
|
style: ElevatedButton.styleFrom(
|
|
|
|
backgroundColor: _securityEmailBrandColor,
|
|
|
|
disabledBackgroundColor:
|
|
|
|
_securityEmailBrandColor.withValues(alpha: 0.4),
|
|
|
|
foregroundColor: Colors.white,
|
|
|
|
disabledForegroundColor: Colors.white,
|
|
|
|
elevation: 0,
|
|
|
|
shape: const StadiumBorder(),
|
|
|
|
textStyle: const TextStyle(
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: FontWeight.w600,
|
|
|
|
child: Obx(
|
|
|
|
() => ElevatedButton(
|
|
|
|
onPressed: controller.canSubmitCode
|
|
|
|
? () => controller.submitCode(context)
|
|
|
|
: null,
|
|
|
|
style: ElevatedButton.styleFrom(
|
|
|
|
backgroundColor: colors.primary,
|
|
|
|
disabledBackgroundColor:
|
|
|
|
colors.primary.withValues(alpha: 0.4),
|
|
|
|
foregroundColor: Colors.white,
|
|
|
|
disabledForegroundColor: Colors.white,
|
|
|
|
elevation: 0,
|
|
|
|
shape: const StadiumBorder(),
|
|
|
|
textStyle: const TextStyle(
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: FontWeight.w600,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
child: Text(l10n.submit),
|
|
|
|
),
|
|
|
|
child: Text(l10n.submit),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
...
|
...
|
@@ -354,9 +311,14 @@ class _SecurityEmailVerificationViewState |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TextSpan _verificationInstruction(String instruction, String email) {
|
|
|
|
const textStyle = TextStyle(
|
|
|
|
color: _securityEmailDescriptionColor,
|
|
|
|
TextSpan _verificationInstruction(
|
|
|
|
BuildContext context,
|
|
|
|
String instruction,
|
|
|
|
String email,
|
|
|
|
) {
|
|
|
|
final colors = context.colors;
|
|
|
|
final textStyle = TextStyle(
|
|
|
|
color: colors.textSecondary,
|
|
|
|
fontSize: 14,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
height: 1.43,
|
|
...
|
...
|
@@ -371,7 +333,7 @@ TextSpan _verificationInstruction(String instruction, String email) { |
|
|
|
TextSpan(text: instruction.substring(0, emailStart)),
|
|
|
|
TextSpan(
|
|
|
|
text: instruction.substring(emailStart, emailEnd),
|
|
|
|
style: const TextStyle(color: _securityEmailTitleColor),
|
|
|
|
style: TextStyle(color: colors.textPrimary),
|
|
|
|
),
|
|
|
|
TextSpan(text: instruction.substring(emailEnd)),
|
|
|
|
],
|
|
...
|
...
|
@@ -380,7 +342,10 @@ TextSpan _verificationInstruction(String instruction, String email) { |
|
|
|
|
|
|
|
class _SecurityEmailAppBar extends StatelessWidget
|
|
|
|
implements PreferredSizeWidget {
|
|
|
|
const _SecurityEmailAppBar();
|
|
|
|
const _SecurityEmailAppBar({
|
|
|
|
this.onPressed,
|
|
|
|
});
|
|
|
|
final VoidCallback? onPressed;
|
|
|
|
|
|
|
|
@override
|
|
|
|
Size get preferredSize => const Size.fromHeight(44);
|
|
...
|
...
|
@@ -388,13 +353,13 @@ class _SecurityEmailAppBar extends StatelessWidget |
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return AppBar(
|
|
|
|
backgroundColor: _securityEmailPageColor,
|
|
|
|
backgroundColor: context.colors.backgroundPage,
|
|
|
|
elevation: 0,
|
|
|
|
scrolledUnderElevation: 0,
|
|
|
|
toolbarHeight: 44,
|
|
|
|
leadingWidth: 60,
|
|
|
|
leading: IconButton(
|
|
|
|
onPressed: Get.back,
|
|
|
|
onPressed: onPressed ?? Get.back,
|
|
|
|
splashColor: Colors.transparent,
|
|
|
|
highlightColor: Colors.transparent,
|
|
|
|
icon: Image.asset(
|
|
...
|
...
|
@@ -413,3 +378,340 @@ OutlineInputBorder _inputBorder({Color color = Colors.transparent}) { |
|
|
|
borderSide: BorderSide(color: color),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
class SetSecurityEmailPasswordView extends GetView<AddSecurityEmailController> {
|
|
|
|
const SetSecurityEmailPasswordView({super.key});
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
final c = Get.isRegistered<AddSecurityEmailController>()
|
|
|
|
? controller
|
|
|
|
: Get.put(AddSecurityEmailController());
|
|
|
|
final l10n = context.l10n;
|
|
|
|
final colors = context.colors;
|
|
|
|
|
|
|
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
|
|
|
value: SystemUiOverlayStyle(
|
|
|
|
statusBarColor: Colors.transparent,
|
|
|
|
statusBarIconBrightness: Brightness.dark,
|
|
|
|
statusBarBrightness: Brightness.light,
|
|
|
|
systemNavigationBarColor: colors.backgroundPage,
|
|
|
|
systemNavigationBarIconBrightness: Brightness.dark,
|
|
|
|
),
|
|
|
|
child: Scaffold(
|
|
|
|
backgroundColor: colors.backgroundPage,
|
|
|
|
appBar: _SecurityEmailAppBar(onPressed: () async {
|
|
|
|
final result = await DialogUtils.showCommonDialog(DialogMetaData(
|
|
|
|
title: context.l10n.setupIncomplete,
|
|
|
|
message: context.l10n
|
|
|
|
.setAPasswordToAddThisEmailSuccessfullyLeavingNowWillCancelThisSetup,
|
|
|
|
confirmText: context.l10n.setPassword,
|
|
|
|
cancelText: context.l10n.leave,
|
|
|
|
iconAsset: 'assets/images/common/ic_warning.png',
|
|
|
|
));
|
|
|
|
if (result.action == DialogAction.cancel) {
|
|
|
|
Get.back();
|
|
|
|
Get.back();
|
|
|
|
Get.back();
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
body: SafeArea(
|
|
|
|
top: false,
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 28),
|
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
Text(
|
|
|
|
l10n.setAPassword,
|
|
|
|
style: TextStyle(
|
|
|
|
color: colors.textPrimary,
|
|
|
|
fontSize: 24,
|
|
|
|
fontWeight: FontWeight.w600,
|
|
|
|
height: 1.25,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
Text(
|
|
|
|
l10n.setAPasswordToSignInWithYourEmail,
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
style: TextStyle(
|
|
|
|
color: colors.textSecondary,
|
|
|
|
fontSize: 14,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
height: 1.43,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const SizedBox(height: 48),
|
|
|
|
Row(
|
|
|
|
children: [
|
|
|
|
const SizedBox(width: 16),
|
|
|
|
Text(
|
|
|
|
context.l10n.enterYourPassword,
|
|
|
|
style: TextStyle(
|
|
|
|
color: const Color(0xFF141414),
|
|
|
|
fontSize: 14,
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
const SizedBox(height: 15),
|
|
|
|
AutofillGroup(
|
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
Offstage(
|
|
|
|
offstage: true,
|
|
|
|
child: TextField(
|
|
|
|
focusNode: FocusNode(canRequestFocus: false),
|
|
|
|
controller: c.emailController,
|
|
|
|
autofillHints: const [
|
|
|
|
AutofillHints.username,
|
|
|
|
AutofillHints.email
|
|
|
|
],
|
|
|
|
// readOnly: true,
|
|
|
|
// enabled: false,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Obx(() {
|
|
|
|
final isObscured = c.isPasswordObscured.value;
|
|
|
|
return TextField(
|
|
|
|
textInputAction: TextInputAction.next,
|
|
|
|
controller: c.passwordController,
|
|
|
|
// keyboardType: TextInputType.visiblePassword,
|
|
|
|
obscureText: isObscured,
|
|
|
|
autofillHints: const [AutofillHints.newPassword],
|
|
|
|
obscuringCharacter: '*',
|
|
|
|
contextMenuBuilder: (BuildContext context,
|
|
|
|
EditableTextState editableTextState) {
|
|
|
|
List<ContextMenuButtonItem> buttonItems =
|
|
|
|
editableTextState.contextMenuButtonItems;
|
|
|
|
buttonItems.removeWhere((item) =>
|
|
|
|
item.type != ContextMenuButtonType.paste);
|
|
|
|
return AdaptiveTextSelectionToolbar.buttonItems(
|
|
|
|
anchors: editableTextState.contextMenuAnchors,
|
|
|
|
buttonItems: buttonItems,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
inputFormatters: [
|
|
|
|
FilteringTextInputFormatter.deny(RegExp(r'\s')),
|
|
|
|
FilteringTextInputFormatter.allow(
|
|
|
|
RegExp(r'[\x21-\x7E]'),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
style: TextStyle(
|
|
|
|
color: context.colors.textPrimary,
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
),
|
|
|
|
decoration: InputDecoration(
|
|
|
|
hintText: l10n.emailLoginYourPassword,
|
|
|
|
hintStyle: TextStyle(
|
|
|
|
color: context.colors.textTertiary,
|
|
|
|
fontSize: 16,
|
|
|
|
),
|
|
|
|
counterText: '',
|
|
|
|
filled: true,
|
|
|
|
fillColor: Colors.white,
|
|
|
|
contentPadding: const EdgeInsets.symmetric(
|
|
|
|
horizontal: 20,
|
|
|
|
vertical: 16,
|
|
|
|
),
|
|
|
|
border: OutlineInputBorder(
|
|
|
|
borderRadius: BorderRadius.circular(27),
|
|
|
|
borderSide: BorderSide.none,
|
|
|
|
),
|
|
|
|
enabledBorder: OutlineInputBorder(
|
|
|
|
borderRadius: BorderRadius.circular(27),
|
|
|
|
borderSide: BorderSide.none,
|
|
|
|
),
|
|
|
|
focusedBorder: OutlineInputBorder(
|
|
|
|
borderRadius: BorderRadius.circular(27),
|
|
|
|
borderSide: BorderSide(
|
|
|
|
color: context.colors.primary,
|
|
|
|
width: 1,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
suffixIconConstraints: const BoxConstraints(
|
|
|
|
minWidth: 24,
|
|
|
|
minHeight: 24,
|
|
|
|
),
|
|
|
|
suffixIcon: GestureDetector(
|
|
|
|
onTap: c.togglePasswordVisibility,
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.only(right: 16),
|
|
|
|
child: Image.asset(
|
|
|
|
isObscured
|
|
|
|
? 'assets/images/common/ic_hide_password.png'
|
|
|
|
: 'assets/images/common/ic_show_password.webp',
|
|
|
|
color: context.colors.textTertiary,
|
|
|
|
width: 24,
|
|
|
|
height: 24,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
Padding(
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
|
|
child: Text(
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
context.l10n
|
|
|
|
.passwordMustBeAtLeast6CharactersAndInclude1NumberAnd1UppercaseLetter,
|
|
|
|
style: TextStyle(
|
|
|
|
color: context.colors.textTertiary,
|
|
|
|
fontSize: 12,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const SizedBox(height: 32),
|
|
|
|
Row(
|
|
|
|
children: [
|
|
|
|
const SizedBox(width: 16),
|
|
|
|
Text(
|
|
|
|
context.l10n.confirmNewPassword,
|
|
|
|
style: TextStyle(
|
|
|
|
color: const Color(0xFF141414),
|
|
|
|
fontSize: 14,
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
const SizedBox(height: 15),
|
|
|
|
Obx(() {
|
|
|
|
final isObscured = c.isConfirmPasswordObscured.value;
|
|
|
|
return TextField(
|
|
|
|
controller: c.confirmPasswordController,
|
|
|
|
// keyboardType: TextInputType.visiblePassword,
|
|
|
|
obscureText: isObscured,
|
|
|
|
autofillHints: const [AutofillHints.newPassword],
|
|
|
|
obscuringCharacter: '*',
|
|
|
|
contextMenuBuilder: (BuildContext context,
|
|
|
|
EditableTextState editableTextState) {
|
|
|
|
List<ContextMenuButtonItem> buttonItems =
|
|
|
|
editableTextState.contextMenuButtonItems;
|
|
|
|
buttonItems.removeWhere((item) =>
|
|
|
|
item.type != ContextMenuButtonType.paste);
|
|
|
|
return AdaptiveTextSelectionToolbar.buttonItems(
|
|
|
|
anchors: editableTextState.contextMenuAnchors,
|
|
|
|
buttonItems: buttonItems,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
onSubmitted: (_) {
|
|
|
|
controller.setPassword(context);
|
|
|
|
},
|
|
|
|
inputFormatters: [
|
|
|
|
FilteringTextInputFormatter.deny(RegExp(r'\s')),
|
|
|
|
FilteringTextInputFormatter.allow(
|
|
|
|
RegExp(r'[\x21-\x7E]'),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
style: TextStyle(
|
|
|
|
color: context.colors.textPrimary,
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
),
|
|
|
|
decoration: InputDecoration(
|
|
|
|
hintText: l10n.emailLoginYourPassword,
|
|
|
|
hintStyle: TextStyle(
|
|
|
|
color: context.colors.textTertiary,
|
|
|
|
fontSize: 16,
|
|
|
|
),
|
|
|
|
counterText: '',
|
|
|
|
filled: true,
|
|
|
|
fillColor: Colors.white,
|
|
|
|
contentPadding: const EdgeInsets.symmetric(
|
|
|
|
horizontal: 20,
|
|
|
|
vertical: 16,
|
|
|
|
),
|
|
|
|
border: OutlineInputBorder(
|
|
|
|
borderRadius: BorderRadius.circular(27),
|
|
|
|
borderSide: BorderSide.none,
|
|
|
|
),
|
|
|
|
enabledBorder: OutlineInputBorder(
|
|
|
|
borderRadius: BorderRadius.circular(27),
|
|
|
|
borderSide: BorderSide.none,
|
|
|
|
),
|
|
|
|
focusedBorder: OutlineInputBorder(
|
|
|
|
borderRadius: BorderRadius.circular(27),
|
|
|
|
borderSide: BorderSide(
|
|
|
|
color: context.colors.primary,
|
|
|
|
width: 1,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
suffixIconConstraints: const BoxConstraints(
|
|
|
|
minWidth: 24,
|
|
|
|
minHeight: 24,
|
|
|
|
),
|
|
|
|
suffixIcon: GestureDetector(
|
|
|
|
onTap: c.toggleConfirmPasswordVisibility,
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.only(right: 16),
|
|
|
|
child: Image.asset(
|
|
|
|
isObscured
|
|
|
|
? 'assets/images/common/ic_hide_password.png'
|
|
|
|
: 'assets/images/common/ic_show_password.webp',
|
|
|
|
color: context.colors.textTertiary,
|
|
|
|
width: 24,
|
|
|
|
height: 24,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
Padding(
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
|
|
child: Text(
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
context.l10n
|
|
|
|
.passwordMustBeAtLeast6CharactersAndInclude1NumberAnd1UppercaseLetter,
|
|
|
|
style: TextStyle(
|
|
|
|
color: context.colors.textTertiary,
|
|
|
|
fontSize: 12,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const SizedBox(height: 32),
|
|
|
|
SizedBox(
|
|
|
|
width: double.infinity,
|
|
|
|
height: 48,
|
|
|
|
child: Obx(
|
|
|
|
() => ElevatedButton(
|
|
|
|
onPressed: c.canSetPassword
|
|
|
|
? () => c.setPassword(context)
|
|
|
|
: null,
|
|
|
|
style: ElevatedButton.styleFrom(
|
|
|
|
backgroundColor: colors.primary,
|
|
|
|
disabledBackgroundColor:
|
|
|
|
colors.primary.withValues(alpha: 0.4),
|
|
|
|
foregroundColor: Colors.white,
|
|
|
|
disabledForegroundColor: Colors.white,
|
|
|
|
elevation: 0,
|
|
|
|
shape: const StadiumBorder(),
|
|
|
|
textStyle: const TextStyle(
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: FontWeight.w600,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
child: Text(context.l10n.setPassword),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} |
...
|
...
|
|