提交 1cc1304a authored 作者: 祁增奎's avatar 祁增奎

组件重构逻辑,待验证,后续切换需要通知测试验证

上级 59255731
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"fluttertoast","path":"/Users/mrqi/.pub-cache/hosted/pub.flutter-io.cn/fluttertoast-8.2.8/","native_build":true,"dependencies":[]}],"android":[{"name":"fluttertoast","path":"/Users/mrqi/.pub-cache/hosted/pub.flutter-io.cn/fluttertoast-8.2.8/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[{"name":"fluttertoast","path":"/Users/mrqi/.pub-cache/hosted/pub.flutter-io.cn/fluttertoast-8.2.8/","dependencies":[]}]},"dependencyGraph":[{"name":"fluttertoast","dependencies":[]}],"date_created":"2025-04-24 14:46:53.353732","version":"3.19.6"}
\ No newline at end of file
<!-- # CLX Verification Code
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for 一个功能强大、高度可定制的 Flutter 验证码业务组件。支持短信验证码、语音验证码、倒计时管理、自动状态流转(如短信未收到自动提示语音验证码)以及完善的错误处理。
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
For general information about developing packages, see the Dart guide for ## 🚀 快速开始
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/developing-packages).
-->
TODO: Put a short description of the package here that helps potential users ### 1. 实现服务接口
know whether this package might be useful for them.
## Features 首先,你需要实现 `IVerificationService` 接口,对接你的后端 API。
TODO: List what your package can do. Maybe include images, gifs, or videos. ```dart
import 'package:clx_verification_code/clx_verification_code.dart';
class MyVerificationService implements IVerificationService {
@override
Future<bool> sendSmsCode() async {
// TODO: 调用发送短信 API
// return await api.sendSms();
return true;
}
## Getting started @override
Future<bool> sendVoiceCode() async {
// TODO: 调用发送语音 API
return true;
}
TODO: List prerequisites and provide or point to information on how to @override
start using the package. Future<String?> verifyCode(String code) async {
// TODO: 调用校验 API
// return null; // 校验通过
return "验证码错误"; // 返回错误信息
}
## Usage @override
Future<void> contactCustomerService() async {
// TODO: 处理联系客服逻辑
}
}
```
TODO: Include short and useful examples for package users. Add longer examples ### 2. 使用组件
to `/example` folder.
在你的页面中使用 `CLXLineCodeManageView``CLXPageCodeManageView`
```dart ```dart
const like = 'sample'; import 'package:flutter/material.dart';
import 'package:clx_verification_code/clx_verification_code.dart';
class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
late VerificationCodeViewModel _viewModel;
@override
void initState() {
super.initState();
// 初始化 ViewModel
_viewModel = VerificationCodeViewModel(
service: MyVerificationService(),
maxLength: 6,
onShowToast: (msg) {
// 处理 Toast 提示
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg)));
},
);
}
@override
void dispose() {
_viewModel.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("验证码示例")),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
// 使用行内样式组件
CLXLineCodeManageView(
viewModel: _viewModel,
config: LineCodeManageConfig(
hintText: "请输入验证码",
// 更多样式配置...
),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
// 可以在这里获取输入内容
print("输入内容: ${_viewModel.codeInputCtrl.text}");
},
child: Text("提交"),
)
],
),
),
);
}
}
``` ```
## Additional information ## 📚 API 文档
### VerificationCodeViewModel
| 参数 | 类型 | 说明 | 默认值 |
| --- | --- | --- | --- |
| `service` | `IVerificationService` | **必传**。业务逻辑实现接口。 | - |
| `maxLength` | `int` | 验证码长度。 | `6` |
| `isFirstResponsed` | `bool` | 是否自动获取焦点。 | `false` |
| `isAccessOutbound` | `bool` | 是否开启语音验证码/外呼功能。 | `true` |
| `onShowToast` | `Function(String)` | Toast 提示回调。 | `null` |
### IVerificationService
| 方法 | 返回值 | 说明 |
| --- | --- | --- |
| `sendSmsCode()` | `Future<bool>` | 发送短信验证码。返回 `true` 表示成功。 |
| `sendVoiceCode()` | `Future<bool>` | 发送语音验证码。返回 `true` 表示成功。 |
| `verifyCode(String code)` | `Future<String?>` | 校验验证码。返回 `null` 表示通过,否则返回错误文案。 |
| `contactCustomerService()` | `Future<void>` | 联系客服的回调。 |
### 组件视图
#### CLXLineCodeManageView
适用于表单行内的验证码输入(通常是左侧输入框,右侧发送按钮)。
| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `viewModel` | `VerificationCodeViewModel` | ViewModel 实例。 |
| `config` | `LineCodeManageConfig` | 样式配置对象。 |
| `disabled` | `bool` | 是否禁用输入。 |
#### CLXPageCodeManageView
适用于独立的验证码输入页面(通常是分格输入框)。
| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `viewModel` | `VerificationCodeViewModel` | ViewModel 实例。 |
| `config` | `PageCodeManageConfig` | 样式配置对象。 |
### 配置对象
#### LineCodeManageConfig / PageCodeManageConfig
### 1.0.1 支持配置 `margin`, `padding`, `decoration`, `hintText`, `hintStyle`, `style`, `countdownStyle`, `errorStyle` 等几乎所有视觉元素。详情请参考源码注释。
- 增加拦截,防止直接粘贴提示文案导致触发多次输入变化
library clx_verification_code; library clx_verification_code;
/// 公共控制器 /// 公共控制器
export 'code_manage/clx_code_manage_logic.dart'; export 'code_manage/verification_code_view_model.dart';
/// -------公共View--------- /// -------公共View---------
export 'views/clx_line_code_manage_view.dart'; export 'views/clx_line_code_manage_view.dart';
export 'views/clx_page_code_manage_view.dart'; export 'views/clx_page_code_manage_view.dart';
/// -------公共Package---------
export 'package:get/get.dart';
export 'package:fluttertoast/fluttertoast.dart';
/// -------公共Widget--------- /// -------公共Widget---------
export 'widgets/input_widget/code_input_paint.dart'; export 'widgets/input_widget/code_input_paint.dart';
export 'widgets/input_widget/code_input_view.dart'; export 'widgets/input_widget/code_input_view.dart';
...@@ -22,3 +18,8 @@ export 'widgets/tips_widget/no_receive_voice_view.dart'; ...@@ -22,3 +18,8 @@ export 'widgets/tips_widget/no_receive_voice_view.dart';
export 'utils/line_code_manage_config.dart'; export 'utils/line_code_manage_config.dart';
export 'utils/page_code_manage_config.dart'; export 'utils/page_code_manage_config.dart';
export 'utils/code_send_enum.dart'; export 'utils/code_send_enum.dart';
/// -------Core---------
export 'core/verification_service_interface.dart';
export 'core/countdown_controller.dart';
export 'core/verification_state_machine.dart';
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:fluttertoast/fluttertoast.dart';
import '../utils/code_send_enum.dart';
class CLXCodeManageLogic extends GetxController {
CLXCodeManageLogic(
{this.maxLength = 6,
this.isFirstResponsed = false,
this.isAccessOutbound = true});
/// 验证码长度
final int maxLength;
/// 是否是成为第一响应者
final bool isFirstResponsed;
/// 是否接入外呼功能
final bool isAccessOutbound;
/// 验证码输入框焦点
final FocusNode codeInoutFocus = FocusNode();
/// 验证码输入框控制器
final TextEditingController codeInoutCtrl = TextEditingController();
/// 倒计时
RxInt countdown = 60.obs;
/// 定时器
Timer? timer;
/// 获取短信验证码
Future<bool?> Function()? getSmsVerificationCode;
/// 获取语音验证码
Future<bool?> Function()? obtainVoiceVerificationCode;
/// 校验验证码输入是否有误
Future<String?> Function(String code)? verifyCode;
/// 联系客服点击事件
Future Function()? contactCustomerService;
/// 验证码校验错误信息
RxString verifyCodeError = ''.obs;
/// 获取验证码按钮文案
RxString codeSendBtn = '获取验证码'.obs;
/// 验证码发送状态
Rx<CodeSendType> codeSendType = CodeSendType.none.obs;
/// 是否展示提示信息模块
RxBool isShowTip = false.obs;
/// 刷新页面需要
RxString refreshPage = ''.obs;
/// 是否允许发送验证码
bool canSend = true;
@override
void onInit() {
super.onInit();
codeInoutCtrl.addListener(_verifyCodeInput);
if (isFirstResponsed) {
codeInoutFocus.requestFocus();
}
}
/// 校验验证码
_verifyCodeInput() async {
/// 处理多次发送校验间隔问题
if (refreshPage.value == codeInoutCtrl.text) {
return;
}
handleTipInfo();
update();
refreshPage.value = codeInoutCtrl.text;
if (codeInoutCtrl.text.length != maxLength) {
return;
}
final result = await verifyCode?.call(codeInoutCtrl.text) ?? '';
verifyCodeError.value = result;
handleTipInfo();
}
/// 外部调用开启定时器
startTimerAndChangeType() {
if (!canSend) {
return;
}
canSend = false;
_startCountdown();
handleCodeSendType();
}
/// 发送短信验证码
sendSmsCode() async {
if (!canSend) {
return;
}
canSend = false;
if (countdown.value != 60) {
return;
}
final result = await getSmsVerificationCode?.call();
if (result == true) {
handleCodeSendType();
_startCountdown();
} else {
canSend = true;
}
}
/// 发送语音验证码
sendVoiceCode() async {
if (!canSend) {
return;
}
canSend = false;
if (countdown.value != 60) {
return;
}
final result = await obtainVoiceVerificationCode?.call();
if (result == true) {
handleCodeSendType(isSmsSend: false);
_startCountdown(isSmsSend: false);
Fluttertoast.showToast(
msg: '即将向您电话播报',
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 2,
);
} else {
canSend = true;
}
}
/// 联系客服
contactService() async {
await contactCustomerService?.call();
}
/// 开启倒计时
_startCountdown({bool isSmsSend = true}) {
if (countdown.value != 60) {
return;
}
timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (countdown.value == 0) {
timer.cancel();
codeSendBtn.value = '';
countdown.value = 60;
canSend = true;
handleCodeSendType(isSmsSend: isSmsSend);
handleTipInfo();
} else {
countdown.value--;
codeSendBtn.value = '已发送${countdown.value}s';
}
});
}
/// 处理验证码发送状态
handleCodeSendType({bool isSmsSend = true}) {
switch (codeSendType.value) {
case CodeSendType.none:
codeSendType.value = CodeSendType.firstSendSmsCode;
break;
case CodeSendType.firstSendSmsCode:
codeSendType.value = CodeSendType.notReceiveSmsCode;
break;
case CodeSendType.notReceiveSmsCode:
if (isSmsSend) {
codeSendType.value = CodeSendType.resendSmsCode;
} else {
codeSendType.value = CodeSendType.firstSendVoiceCode;
}
break;
case CodeSendType.resendSmsCode:
if (isAccessOutbound) {
codeSendType.value = CodeSendType.notReceiveResendSmsCode;
}
break;
case CodeSendType.notReceiveResendSmsCode:
codeSendType.value = CodeSendType.firstSendVoiceCode;
break;
case CodeSendType.firstSendVoiceCode:
codeSendType.value = CodeSendType.notReceiveVoiceCode;
break;
case CodeSendType.notReceiveVoiceCode:
codeSendType.value = CodeSendType.resendVoiceCode;
break;
case CodeSendType.resendVoiceCode:
codeSendType.value = CodeSendType.notReceiveResendVoiceCode;
break;
case CodeSendType.notReceiveResendVoiceCode:
codeSendType.value = CodeSendType.resendVoiceCode;
break;
default:
codeSendType.value = CodeSendType.notReceiveResendVoiceCode;
break;
}
}
/// 处理提示信息
handleTipInfo() {
/// 如果输入框内容不为空,则不展示提示信息
isShowTip.value = verifyCodeError.value.isNotEmpty ||
codeInoutCtrl.text.isEmpty && countdown.value == 60;
}
}
import 'package:flutter/widgets.dart';
import '../core/verification_service_interface.dart';
import '../core/countdown_controller.dart';
import '../core/verification_state_machine.dart';
import '../utils/code_send_enum.dart';
/// 验证码 ViewModel
class VerificationCodeViewModel extends ChangeNotifier {
VerificationCodeViewModel({
required this.service,
this.maxLength = 6,
this.isFirstResponsed = false,
this.isAccessOutbound = true,
this.onShowToast,
}) : _countdownController = CountdownController(),
_stateMachine = VerificationStateMachine(isAccessOutbound: isAccessOutbound) {
codeInputCtrl.addListener(_verifyCodeInput);
_countdownController.addListener(_onCountdownChanged);
if (isFirstResponsed) {
codeInputFocus.requestFocus();
}
}
/// 外部注入的服务接口
final IVerificationService service;
/// 倒计时控制器
final CountdownController _countdownController;
/// 状态机
final VerificationStateMachine _stateMachine;
/// 验证码长度
final int maxLength;
/// 是否是成为第一响应者
final bool isFirstResponsed;
/// 是否接入外呼功能
final bool isAccessOutbound;
/// Toast 回调
final Function(String msg)? onShowToast;
/// 验证码输入框焦点
final FocusNode codeInputFocus = FocusNode();
/// 验证码输入框控制器
final TextEditingController codeInputCtrl = TextEditingController();
/// 验证码校验错误信息
String _verifyCodeError = '';
String get verifyCodeError => _verifyCodeError;
/// 获取验证码按钮文案
String _codeSendBtn = '获取验证码';
String get codeSendBtn => _codeSendBtn;
/// 验证码发送状态
CodeSendType _codeSendType = CodeSendType.none;
CodeSendType get codeSendType => _codeSendType;
/// 是否展示提示信息模块
bool _isShowTip = false;
bool get isShowTip => _isShowTip;
/// 记录上一次输入,防止重复校验
String _lastInput = '';
/// 是否允许发送验证码
bool _canSend = true;
@override
void dispose() {
codeInputCtrl.removeListener(_verifyCodeInput);
_countdownController.removeListener(_onCountdownChanged);
codeInputCtrl.dispose();
codeInputFocus.dispose();
_countdownController.dispose();
super.dispose();
}
/// 倒计时变化回调
void _onCountdownChanged() {
if (_countdownController.isRunning) {
_codeSendBtn = '已发送${_countdownController.currentCount}s';
} else {
// 倒计时结束
_codeSendBtn = '';
_canSend = true;
// 在 startTimer 时记录类型,或者直接根据当前状态判断
if (_codeSendType.isSend) {
_codeSendType = _stateMachine.nextState(_codeSendType, isSmsSend: _codeSendType.isSms);
_handleTipInfo();
}
}
notifyListeners();
}
/// 校验验证码
void _verifyCodeInput() async {
if (_lastInput == codeInputCtrl.text) return;
_lastInput = codeInputCtrl.text;
_handleTipInfo();
notifyListeners();
if (codeInputCtrl.text.length != maxLength) return;
final result = await service.verifyCode(codeInputCtrl.text);
_verifyCodeError = result ?? '';
_handleTipInfo();
notifyListeners();
}
/// 外部调用开启定时器
void startTimerAndChangeType() {
if (!_canSend) return;
_canSend = false;
_codeSendType = _stateMachine.nextState(_codeSendType);
_countdownController.start();
notifyListeners();
}
/// 发送短信验证码
Future<void> sendSmsCode() async {
if (!_canSend) return;
_canSend = false;
if (_countdownController.isRunning) return;
final success = await service.sendSmsCode();
if (success) {
_codeSendType = _stateMachine.nextState(_codeSendType, isSmsSend: true);
_countdownController.start();
} else {
_canSend = true;
}
notifyListeners();
}
/// 发送语音验证码
Future<void> sendVoiceCode() async {
if (!_canSend) return;
_canSend = false;
if (_countdownController.isRunning) return;
final success = await service.sendVoiceCode();
if (success) {
_codeSendType = _stateMachine.nextState(_codeSendType, isSmsSend: false);
_countdownController.start();
onShowToast?.call('即将向您电话播报');
} else {
_canSend = true;
}
notifyListeners();
}
/// 联系客服
Future<void> contactService() async {
await service.contactCustomerService();
}
/// 处理提示信息
void _handleTipInfo() {
_isShowTip = _verifyCodeError.isNotEmpty ||
(codeInputCtrl.text.isEmpty && !_countdownController.isRunning && _codeSendType != CodeSendType.none);
}
}
import 'dart:async';
import 'package:flutter/foundation.dart';
/// 倒计时控制器
class CountdownController extends ChangeNotifier {
CountdownController({this.duration = 60});
final int duration;
Timer? _timer;
int _currentCount = 0;
bool _isRunning = false;
int get currentCount => _currentCount;
bool get isRunning => _isRunning;
/// 开始倒计时
void start() {
if (_isRunning) return;
_currentCount = duration;
_isRunning = true;
notifyListeners();
_timer?.cancel();
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (_currentCount <= 0) {
stop();
} else {
_currentCount--;
notifyListeners();
}
});
}
/// 停止倒计时
void stop() {
_timer?.cancel();
_timer = null;
_isRunning = false;
_currentCount = duration; // 重置为初始值
notifyListeners();
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
}
/// 验证码服务接口
abstract class IVerificationService {
/// 获取短信验证码
/// 返回 true 表示发送成功,false 表示失败
Future<bool> sendSmsCode();
/// 获取语音验证码
/// 返回 true 表示发送成功,false 表示失败
Future<bool> sendVoiceCode();
/// 校验验证码
/// 返回 null 表示校验通过,否则返回错误信息
Future<String?> verifyCode(String code);
/// 联系客服
Future<void> contactCustomerService();
}
import '../utils/code_send_enum.dart';
/// 验证码状态机
class VerificationStateMachine {
final bool isAccessOutbound;
VerificationStateMachine({required this.isAccessOutbound});
/// 根据当前状态和触发动作计算下一个状态
CodeSendType nextState(CodeSendType currentState, {bool isSmsSend = true}) {
switch (currentState) {
case CodeSendType.none:
return CodeSendType.firstSendSmsCode;
case CodeSendType.firstSendSmsCode:
return CodeSendType.notReceiveSmsCode;
case CodeSendType.notReceiveSmsCode:
return isSmsSend
? CodeSendType.resendSmsCode
: CodeSendType.firstSendVoiceCode;
case CodeSendType.resendSmsCode:
return isAccessOutbound
? CodeSendType.notReceiveResendSmsCode
: CodeSendType.resendSmsCode; // 如果没有外呼,可能保持原状或进入其他逻辑,这里保持原逻辑
case CodeSendType.notReceiveResendSmsCode:
return CodeSendType.firstSendVoiceCode;
case CodeSendType.firstSendVoiceCode:
return CodeSendType.notReceiveVoiceCode;
case CodeSendType.notReceiveVoiceCode:
return CodeSendType.resendVoiceCode;
case CodeSendType.resendVoiceCode:
return CodeSendType.notReceiveResendVoiceCode;
case CodeSendType.notReceiveResendVoiceCode:
return CodeSendType.resendVoiceCode;
default:
return CodeSendType.notReceiveResendVoiceCode;
}
}
}
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../clx_verification_code.dart'; import '../clx_verification_code.dart';
import '../widgets/tips_widget/status_tips_widget.dart';
class CLXLineCodeManageView extends StatelessWidget { class CLXLineCodeManageView extends StatelessWidget {
const CLXLineCodeManageView({ const CLXLineCodeManageView({
super.key, super.key,
required this.loigc, required this.viewModel,
required this.config, required this.config,
this.disabled = false, this.disabled = false,
}); });
/// 页面状态变化处理控制器 /// 页面状态变化处理控制器
final CLXCodeManageLogic loigc; final VerificationCodeViewModel viewModel;
/// 是否禁用 /// 是否禁用
final bool disabled; final bool disabled;
...@@ -20,113 +21,82 @@ class CLXLineCodeManageView extends StatelessWidget { ...@@ -20,113 +21,82 @@ class CLXLineCodeManageView extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return AnimatedBuilder(
margin: config.margin, animation: viewModel,
padding: config.padding, builder: (context, child) {
decoration: config.decoration, return Container(
child: Column( margin: config.margin,
crossAxisAlignment: CrossAxisAlignment.start, padding: config.padding,
children: [ decoration: config.decoration,
Row( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Expanded( Row(
child: TextField( crossAxisAlignment: CrossAxisAlignment.center,
controller: loigc.codeInoutCtrl, children: [
keyboardType: TextInputType.number, Expanded(
maxLength: loigc.maxLength, child: TextField(
style: config.style, controller: viewModel.codeInputCtrl,
enabled: !disabled, keyboardType: TextInputType.number,
enableInteractiveSelection: false, maxLength: viewModel.maxLength,
decoration: InputDecoration( style: config.style,
isCollapsed: config.isCollapsed, enabled: !disabled,
counterText: '', enableInteractiveSelection: false,
hintText: config.hintText, decoration: InputDecoration(
hintStyle: config.hintStyle, isCollapsed: config.isCollapsed,
border: InputBorder.none, counterText: '',
), hintText: config.hintText,
), hintStyle: config.hintStyle,
), border: InputBorder.none,
const SizedBox(width: 10), ),
Obx(
() => Visibility(
visible: loigc.codeSendBtn.value.isNotEmpty,
child: GestureDetector(
onTap: loigc.sendSmsCode,
child: Text(
loigc.codeSendBtn.value,
style: loigc.codeSendType.value.isUnSend
? config.countdownStyle
: config.codeSendStyle,
), ),
), ),
), const SizedBox(width: 10),
), Visibility(
], visible: viewModel.codeSendBtn.isNotEmpty,
), child: GestureDetector(
Obx( onTap: viewModel.sendSmsCode,
() => Visibility( child: Text(
visible: loigc.verifyCodeError.value.isNotEmpty, viewModel.codeSendBtn,
child: Column( style: viewModel.codeSendType.isUnSend
children: [ ? config.countdownStyle
const SizedBox(height: 5), : config.codeSendStyle,
Text( ),
loigc.verifyCodeError.value, ),
style: config.errorStyle,
), ),
], ],
), ),
), Visibility(
), visible: viewModel.verifyCodeError.isNotEmpty,
Obx( child: Column(
() => Visibility( children: [
visible: loigc.isShowTip.value, const SizedBox(height: 5),
child: Column( Text(
children: [ viewModel.verifyCodeError,
/// 展示重新发送验证码按钮以及获取语音验证码按钮 style: config.errorStyle,
if (loigc.codeSendType.value ==
CodeSendType.notReceiveSmsCode)
NoReceiveSmsView(
isAccessOutCall: loigc.isAccessOutbound,
tipsStyle: config.tipsStyle,
reSendStyle: config.reSendStyle,
voiceSendStyle: config.voiceSendStyle,
isSmallWindow: config.isSmallWindow,
sendSmsCode: loigc.sendSmsCode,
sendVoiceCode: loigc.sendVoiceCode,
),
/// 展示没有收到二次发送的验证码的提示
if (loigc.codeSendType.value ==
CodeSendType.notReceiveResendSmsCode)
NoReceiveResendSmsView(
isAccessOutCall: loigc.isAccessOutbound,
tipsStyle: config.tipsStyle,
reSendStyle: config.reSendStyle,
sendVoiceCode: loigc.sendVoiceCode,
),
/// 展示没有收到首次发送的语音验证码的提示
if (loigc.codeSendType.value ==
CodeSendType.notReceiveVoiceCode ||
loigc.codeSendType.value ==
CodeSendType.notReceiveResendVoiceCode)
NoReceiveVoiceView(
isSmallWindow: config.isSmallWindow,
tipsStyle: config.tipsStyle,
reSendStyle: config.reSendStyle,
contactStyle: config.contactStyle,
sendVoiceCode: loigc.sendVoiceCode,
contactService: loigc.contactService,
isShowContact: loigc.codeSendType.value ==
CodeSendType.notReceiveResendVoiceCode,
), ),
], ],
),
), ),
), Visibility(
visible: viewModel.isShowTip,
child: StatusTipsWidget(
codeSendType: viewModel.codeSendType,
isAccessOutbound: viewModel.isAccessOutbound,
onSendSms: viewModel.sendSmsCode,
onSendVoice: viewModel.sendVoiceCode,
onContactService: viewModel.contactService,
tipsStyle: config.tipsStyle,
reSendStyle: config.reSendStyle,
voiceSendStyle: config.voiceSendStyle,
contactStyle: config.contactStyle,
isSmallWindow: config.isSmallWindow,
),
),
],
), ),
], );
), },
); );
} }
} }
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import '../clx_verification_code.dart'; import '../clx_verification_code.dart';
import '../widgets/tips_widget/status_tips_widget.dart';
class CLXPageCodeManageView extends StatelessWidget { class CLXPageCodeManageView extends StatelessWidget {
const CLXPageCodeManageView({ const CLXPageCodeManageView({
super.key, super.key,
required this.loigc, required this.viewModel,
required this.config, required this.config,
}); });
/// 页面状态变化处理控制器 /// 页面状态变化处理控制器
final CLXCodeManageLogic loigc; final VerificationCodeViewModel viewModel;
/// 是否隐藏输入内容 /// 是否隐藏输入内容
final PageCodeManageConfig config; final PageCodeManageConfig config;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return AnimatedBuilder(
margin: config.margin, animation: viewModel,
padding: config.padding, builder: (context, child) {
decoration: config.decoration, return Container(
child: Column( margin: config.margin,
crossAxisAlignment: CrossAxisAlignment.start, padding: config.padding,
children: [ decoration: config.decoration,
GetBuilder( child: Column(
init: loigc, crossAxisAlignment: CrossAxisAlignment.start,
builder: (controller) { children: [
return CodeInputView( CodeInputView(
controller: loigc.codeInoutCtrl, controller: viewModel.codeInputCtrl,
focusNode: loigc.codeInoutFocus, focusNode: viewModel.codeInputFocus,
obscureText: config.obscureText, obscureText: config.obscureText,
maxLength: loigc.maxLength, maxLength: viewModel.maxLength,
inputRectBg: config.inputRectBg, inputRectBg: config.inputRectBg,
runSpance: config.runSpance, runSpance: config.runSpance,
radius: config.radius, radius: config.radius,
);
},
),
Obx(
() => Visibility(
visible: loigc.verifyCodeError.value.isNotEmpty,
child: Column(
children: [
const SizedBox(height: 5),
Text(
loigc.verifyCodeError.value,
style: config.errorStyle,
),
],
), ),
), Visibility(
), visible: viewModel.verifyCodeError.isNotEmpty,
Obx( child: Column(
() => Visibility( children: [
visible: loigc.isShowTip.value, const SizedBox(height: 5),
child: Column( Text(
children: [ viewModel.verifyCodeError,
/// 展示重新发送验证码按钮以及获取语音验证码按钮 style: config.errorStyle,
if (loigc.codeSendType.value ==
CodeSendType.notReceiveSmsCode)
NoReceiveSmsView(
isAccessOutCall: loigc.isAccessOutbound,
tipsStyle: config.tipsStyle,
reSendStyle: config.reSendStyle,
voiceSendStyle: config.voiceSendStyle,
isSmallWindow: false,
sendSmsCode: loigc.sendSmsCode,
sendVoiceCode: loigc.sendVoiceCode,
),
/// 展示没有收到二次发送的验证码的提示
if (loigc.codeSendType.value ==
CodeSendType.notReceiveResendSmsCode)
NoReceiveResendSmsView(
isAccessOutCall: loigc.isAccessOutbound,
tipsStyle: config.tipsStyle,
reSendStyle: config.reSendStyle,
sendVoiceCode: loigc.sendVoiceCode,
), ),
],
/// 展示没有收到首次发送的语音验证码的提示 ),
if (loigc.codeSendType.value == ),
CodeSendType.notReceiveVoiceCode || Visibility(
loigc.codeSendType.value == visible: viewModel.isShowTip,
CodeSendType.notReceiveResendVoiceCode) child: StatusTipsWidget(
NoReceiveVoiceView( codeSendType: viewModel.codeSendType,
tipsStyle: config.tipsStyle, isAccessOutbound: viewModel.isAccessOutbound,
reSendStyle: config.reSendStyle, onSendSms: viewModel.sendSmsCode,
contactStyle: config.contactStyle, onSendVoice: viewModel.sendVoiceCode,
isSmallWindow: false, onContactService: viewModel.contactService,
sendVoiceCode: loigc.sendVoiceCode, tipsStyle: config.tipsStyle,
contactService: loigc.contactService, reSendStyle: config.reSendStyle,
isShowContact: loigc.codeSendType.value == voiceSendStyle: config.voiceSendStyle,
CodeSendType.notReceiveResendVoiceCode, contactStyle: config.contactStyle,
), isSmallWindow: false,
], ),
), ),
), ],
), ),
], );
), },
); );
} }
} }
import 'package:flutter/widgets.dart';
import '../../utils/code_send_enum.dart';
import 'no_receive_sms_view.dart';
import 'no_receive_resend_sms_view.dart';
import 'no_receive_voice_view.dart';
/// 状态提示组件
class StatusTipsWidget extends StatelessWidget {
const StatusTipsWidget({
super.key,
required this.codeSendType,
required this.isAccessOutbound,
required this.onSendSms,
required this.onSendVoice,
required this.onContactService,
this.tipsStyle,
this.reSendStyle,
this.voiceSendStyle,
this.contactStyle,
this.isSmallWindow = false,
});
final CodeSendType codeSendType;
final bool isAccessOutbound;
final VoidCallback onSendSms;
final VoidCallback onSendVoice;
final VoidCallback onContactService;
final TextStyle? tipsStyle;
final TextStyle? reSendStyle;
final TextStyle? voiceSendStyle;
final TextStyle? contactStyle;
final bool isSmallWindow;
@override
Widget build(BuildContext context) {
return Column(
children: [
/// 展示重新发送验证码按钮以及获取语音验证码按钮
if (codeSendType == CodeSendType.notReceiveSmsCode)
NoReceiveSmsView(
isAccessOutCall: isAccessOutbound,
tipsStyle: tipsStyle,
reSendStyle: reSendStyle,
voiceSendStyle: voiceSendStyle,
isSmallWindow: isSmallWindow,
sendSmsCode: onSendSms,
sendVoiceCode: onSendVoice,
),
/// 展示没有收到二次发送的验证码的提示
if (codeSendType == CodeSendType.notReceiveResendSmsCode)
NoReceiveResendSmsView(
isAccessOutCall: isAccessOutbound,
tipsStyle: tipsStyle,
reSendStyle: reSendStyle,
sendVoiceCode: onSendVoice,
),
/// 展示没有收到首次发送的语音验证码的提示
if (codeSendType == CodeSendType.notReceiveVoiceCode ||
codeSendType == CodeSendType.notReceiveResendVoiceCode)
NoReceiveVoiceView(
isSmallWindow: isSmallWindow,
tipsStyle: tipsStyle,
reSendStyle: reSendStyle,
contactStyle: contactStyle,
sendVoiceCode: onSendVoice,
contactService: onContactService,
isShowContact: codeSendType == CodeSendType.notReceiveResendVoiceCode,
),
],
);
}
}
...@@ -11,11 +11,6 @@ dependencies: ...@@ -11,11 +11,6 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
# 状态管理
get: ^4.3.7
# 吐司管理
fluttertoast: ^8.2.4
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论