|
...
|
...
|
@@ -92,66 +92,66 @@ class PhoneLoginView extends GetView<LoginController> { |
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
|
children: [
|
|
|
|
// 手机号输入框
|
|
|
|
Obx(() {
|
|
|
|
final phoneNotEmpty =
|
|
|
|
controller.phoneInput.value.isNotEmpty;
|
|
|
|
return TextField(
|
|
|
|
controller: controller.phoneController,
|
|
|
|
keyboardType: TextInputType.phone,
|
|
|
|
maxLength: 13,
|
|
|
|
contextMenuBuilder: (BuildContext context,
|
|
|
|
EditableTextState editableTextState) {
|
|
|
|
// 获取默认的菜单项列表
|
|
|
|
List<ContextMenuButtonItem> buttonItems =
|
|
|
|
editableTextState.contextMenuButtonItems;
|
|
|
|
TextField(
|
|
|
|
controller: controller.phoneController,
|
|
|
|
keyboardType: TextInputType.phone,
|
|
|
|
maxLength: 13,
|
|
|
|
contextMenuBuilder: (BuildContext context,
|
|
|
|
EditableTextState editableTextState) {
|
|
|
|
// 获取默认的菜单项列表
|
|
|
|
List<ContextMenuButtonItem> buttonItems =
|
|
|
|
editableTextState.contextMenuButtonItems;
|
|
|
|
|
|
|
|
// 例如:只保留“粘贴” (Paste)
|
|
|
|
buttonItems.removeWhere((item) =>
|
|
|
|
item.type != ContextMenuButtonType.paste);
|
|
|
|
return AdaptiveTextSelectionToolbar.buttonItems(
|
|
|
|
anchors: editableTextState.contextMenuAnchors,
|
|
|
|
buttonItems: buttonItems,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
inputFormatters: [
|
|
|
|
FilteringTextInputFormatter.allow(
|
|
|
|
RegExp(r'[0-9\s]')),
|
|
|
|
_PhoneTextInputFormatter(),
|
|
|
|
],
|
|
|
|
style: TextStyle(
|
|
|
|
color: context.colors.textPrimary,
|
|
|
|
// 例如:只保留“粘贴” (Paste)
|
|
|
|
buttonItems.removeWhere((item) =>
|
|
|
|
item.type != ContextMenuButtonType.paste);
|
|
|
|
return AdaptiveTextSelectionToolbar.buttonItems(
|
|
|
|
anchors: editableTextState.contextMenuAnchors,
|
|
|
|
buttonItems: buttonItems,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
inputFormatters: [
|
|
|
|
FilteringTextInputFormatter.allow(
|
|
|
|
RegExp(r'[0-9\s]')),
|
|
|
|
_PhoneTextInputFormatter(),
|
|
|
|
],
|
|
|
|
style: TextStyle(
|
|
|
|
color: context.colors.textPrimary,
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
),
|
|
|
|
decoration: InputDecoration(
|
|
|
|
hintText: l10n.phoneLoginPhoneHint,
|
|
|
|
hintStyle: TextStyle(
|
|
|
|
color: context.colors.textTertiary,
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
),
|
|
|
|
decoration: InputDecoration(
|
|
|
|
hintText: l10n.phoneLoginPhoneHint,
|
|
|
|
hintStyle: TextStyle(
|
|
|
|
color: context.colors.textTertiary,
|
|
|
|
fontSize: 16,
|
|
|
|
),
|
|
|
|
counterText: '',
|
|
|
|
filled: true,
|
|
|
|
fillColor: Color(0xFFF3F3F3),
|
|
|
|
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,
|
|
|
|
),
|
|
|
|
counterText: '',
|
|
|
|
filled: true,
|
|
|
|
fillColor: const Color(0xFFF3F3F3),
|
|
|
|
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,
|
|
|
|
),
|
|
|
|
suffixIcon: phoneNotEmpty
|
|
|
|
),
|
|
|
|
suffixIcon: Obx(() {
|
|
|
|
final phoneNotEmpty =
|
|
|
|
controller.phoneInput.value.isNotEmpty;
|
|
|
|
return phoneNotEmpty
|
|
|
|
? GestureDetector(
|
|
|
|
onTap: controller.phoneController.clear,
|
|
|
|
child: Icon(
|
|
...
|
...
|
@@ -160,82 +160,84 @@ class PhoneLoginView extends GetView<LoginController> { |
|
|
|
size: 18,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
: null,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
: const SizedBox.shrink();
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
|
|
|
|
// 验证码输入框(内嵌发送按钮)
|
|
|
|
Obx(() {
|
|
|
|
final countdown = controller.countdownSeconds.value;
|
|
|
|
final isSending = controller.isSendingCode.value;
|
|
|
|
final isCounting = countdown > 0;
|
|
|
|
final hasSent = controller.hasSentCode.value;
|
|
|
|
|
|
|
|
String btnText = hasSent
|
|
|
|
? l10n.phoneLoginResend
|
|
|
|
: l10n.phoneLoginSendCode;
|
|
|
|
if (isSending) {
|
|
|
|
btnText = l10n.phoneLoginSending;
|
|
|
|
} else if (isCounting) {
|
|
|
|
btnText = l10n.phoneLoginSentCountdown(countdown);
|
|
|
|
}
|
|
|
|
|
|
|
|
return TextField(
|
|
|
|
controller: controller.codeController,
|
|
|
|
focusNode: controller.codeFocusNode,
|
|
|
|
keyboardType: TextInputType.number,
|
|
|
|
maxLength: 6,
|
|
|
|
contextMenuBuilder: (BuildContext context,
|
|
|
|
EditableTextState editableTextState) {
|
|
|
|
// 获取默认的菜单项列表
|
|
|
|
List<ContextMenuButtonItem> buttonItems =
|
|
|
|
editableTextState.contextMenuButtonItems;
|
|
|
|
TextField(
|
|
|
|
controller: controller.codeController,
|
|
|
|
focusNode: controller.codeFocusNode,
|
|
|
|
keyboardType: TextInputType.number,
|
|
|
|
maxLength: 6,
|
|
|
|
contextMenuBuilder: (BuildContext context,
|
|
|
|
EditableTextState editableTextState) {
|
|
|
|
// 获取默认的菜单项列表
|
|
|
|
List<ContextMenuButtonItem> buttonItems =
|
|
|
|
editableTextState.contextMenuButtonItems;
|
|
|
|
|
|
|
|
// 例如:只保留“粘贴” (Paste)
|
|
|
|
buttonItems.removeWhere((item) =>
|
|
|
|
item.type != ContextMenuButtonType.paste);
|
|
|
|
return AdaptiveTextSelectionToolbar.buttonItems(
|
|
|
|
anchors: editableTextState.contextMenuAnchors,
|
|
|
|
buttonItems: buttonItems,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
style: TextStyle(
|
|
|
|
color: context.colors.textPrimary,
|
|
|
|
// 例如:只保留“粘贴” (Paste)
|
|
|
|
buttonItems.removeWhere((item) =>
|
|
|
|
item.type != ContextMenuButtonType.paste);
|
|
|
|
return AdaptiveTextSelectionToolbar.buttonItems(
|
|
|
|
anchors: editableTextState.contextMenuAnchors,
|
|
|
|
buttonItems: buttonItems,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
style: TextStyle(
|
|
|
|
color: context.colors.textPrimary,
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
),
|
|
|
|
decoration: InputDecoration(
|
|
|
|
hintText: l10n.phoneLoginCodeHint,
|
|
|
|
hintStyle: TextStyle(
|
|
|
|
color: context.colors.textTertiary,
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
),
|
|
|
|
decoration: InputDecoration(
|
|
|
|
hintText: l10n.phoneLoginCodeHint,
|
|
|
|
hintStyle: TextStyle(
|
|
|
|
color: context.colors.textTertiary,
|
|
|
|
fontSize: 16,
|
|
|
|
),
|
|
|
|
counterText: '',
|
|
|
|
filled: true,
|
|
|
|
fillColor: Color(0xFFF3F3F3),
|
|
|
|
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,
|
|
|
|
),
|
|
|
|
counterText: '',
|
|
|
|
filled: true,
|
|
|
|
fillColor: const Color(0xFFF3F3F3),
|
|
|
|
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,
|
|
|
|
),
|
|
|
|
suffixIcon: Padding(
|
|
|
|
),
|
|
|
|
suffixIcon: Obx(() {
|
|
|
|
final countdown =
|
|
|
|
controller.countdownSeconds.value;
|
|
|
|
final isSending = controller.isSendingCode.value;
|
|
|
|
final isCounting = countdown > 0;
|
|
|
|
final hasSent = controller.hasSentCode.value;
|
|
|
|
|
|
|
|
String btnText = hasSent
|
|
|
|
? l10n.phoneLoginResend
|
|
|
|
: l10n.phoneLoginSendCode;
|
|
|
|
if (isSending) {
|
|
|
|
btnText = l10n.phoneLoginSending;
|
|
|
|
} else if (isCounting) {
|
|
|
|
btnText =
|
|
|
|
l10n.phoneLoginSentCountdown(countdown);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Padding(
|
|
|
|
padding: const EdgeInsets.only(right: 12),
|
|
|
|
child: TextButton(
|
|
|
|
onPressed: controller.canRequestCode
|
|
...
|
...
|
@@ -257,12 +259,12 @@ class PhoneLoginView extends GetView<LoginController> { |
|
|
|
),
|
|
|
|
child: Text(btnText),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
suffixIconConstraints:
|
|
|
|
const BoxConstraints(minWidth: 0),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
suffixIconConstraints:
|
|
|
|
const BoxConstraints(minWidth: 0),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
|
|
|
|
const SizedBox(height: 56),
|
|
|
|
|
...
|
...
|
|