Commit 17a806898123aa4215fd32331797f156ef980868

Authored by liangchengyou
1 parent f138a761

feat:倒计时功能开发

lib/common/blocs/timerbloc/timer_bloc.dart 0 → 100644
  1 +import 'dart:async';
  2 +
  3 +import 'package:flutter/cupertino.dart';
  4 +import 'package:equatable/equatable.dart';
  5 +import 'package:flutter_bloc/flutter_bloc.dart';
  6 +import 'package:wow_english/common/widgets/timer_ticker.dart';
  7 +
  8 +part 'timer_event.dart';
  9 +part 'timer_state.dart';
  10 +
  11 +class TimerBloc extends Bloc<TimerEvent, TimerState> {
  12 + /// 定时器时间
  13 + static const int _duration = 60;
  14 + /// 定时器数据流
  15 + final TimerTicker _ticker;
  16 + // 流订阅
  17 + StreamSubscription<int>? _tickerSubscription;
  18 + TimerBloc({required TimerTicker ticker})
  19 + :_ticker = ticker,
  20 + super(const TimerInitial(_duration)) {
  21 + on<StartEvent>(_onStarted);
  22 + on<TickEvent>(_onTicked);
  23 + on<PausedEvent>(_onPaused);
  24 + on<ResumedEvent>(_onResumed);
  25 + on<ResetEvent>(_onReset);
  26 + }
  27 +
  28 + @override
  29 + Future<void> close() {
  30 + _tickerSubscription?.cancel();
  31 + return super.close();
  32 + }
  33 +
  34 + /// 开始计时
  35 + void _onStarted(StartEvent event,Emitter<TimerState> emitter) async {
  36 + emitter(RunningState(event.duration));
  37 + _tickerSubscription?.cancel();
  38 + _tickerSubscription = _ticker
  39 + .tick(ticks: event.duration)
  40 + .listen((event) => add(TickEvent(duration: event)));
  41 + }
  42 +
  43 + /// 计时中,判断是否剩余时间
  44 + void _onTicked(TickEvent event,Emitter<TimerState> emitter) async {
  45 + emitter(event.duration>0?RunningState(event.duration):const FinishedState());
  46 + }
  47 +
  48 + ///暂停,判断当时是否state为PausedState类型,之后暂停订阅,发送暂停state
  49 + void _onPaused(PausedEvent event, Emitter<TimerState> emit) async{
  50 + if( state is RunningState) {
  51 + _tickerSubscription?.pause();
  52 + emit(PausedState(state.duration));
  53 + }
  54 + }
  55 +
  56 + /// 恢复,我们判断当前state为暂停状态后,进行恢复
  57 + void _onResumed(ResumedEvent event, Emitter<TimerState> emit) async{
  58 + if( state is PausedState) {
  59 + _tickerSubscription?.resume();
  60 + emit(RunningState(state.duration));
  61 + }
  62 + }
  63 +
  64 + /// 重置,初始化
  65 + void _onReset(ResetEvent event ,Emitter<TimerState> emit) async{
  66 + _tickerSubscription?.cancel();
  67 + emit(const TimerInitial(_duration));
  68 + }
  69 +}
lib/common/blocs/timerbloc/timer_event.dart 0 → 100644
  1 +part of 'timer_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class TimerEvent extends Equatable {
  5 + const TimerEvent();
  6 +
  7 + @override
  8 + List<Object> get props => [];
  9 +}
  10 +
  11 +/// 开始事件
  12 +class StartEvent extends TimerEvent {
  13 + /// 定时器时间
  14 + final int duration;
  15 +
  16 + const StartEvent({required this.duration});
  17 +}
  18 +
  19 +/// 定时器事件
  20 +class TickEvent extends TimerEvent {
  21 + const TickEvent({required this.duration});
  22 + /// 当前时间
  23 + final int duration;
  24 +
  25 + @override
  26 + List<Object> get props => [duration];
  27 +}
  28 +
  29 +/// 暂停事件
  30 +class PausedEvent extends TimerEvent {}
  31 +
  32 +/// 恢复状态
  33 +class ResumedEvent extends TimerEvent {}
  34 +
  35 +/// 重置状态
  36 +class ResetEvent extends TimerEvent {}
0 \ No newline at end of file 37 \ No newline at end of file
lib/common/blocs/timerbloc/timer_state.dart 0 → 100644
  1 +part of 'timer_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class TimerState extends Equatable {
  5 + /// 时间
  6 + final int duration;
  7 + /// 构造方法
  8 + const TimerState(this.duration);
  9 + @override
  10 + List<Object> get props => [duration];
  11 +}
  12 +
  13 +class TimerInitial extends TimerState {
  14 + const TimerInitial(super.duration);
  15 +}
  16 +
  17 +/// 暂停状态
  18 +class PausedState extends TimerState {
  19 + const PausedState(super.duration);
  20 +}
  21 +
  22 +/// 运行状态
  23 +class RunningState extends TimerState {
  24 + const RunningState(super.duration);
  25 +}
  26 +
  27 +/// 完成状态
  28 +class FinishedState extends TimerState{
  29 + const FinishedState() : super(0);
  30 +}
  31 +
  32 +class BtnState extends TimerState {
  33 + const BtnState(super.duration);
  34 +}
0 \ No newline at end of file 35 \ No newline at end of file
lib/common/widgets/timer_ticker.dart 0 → 100644
  1 +///定时器数据源
  2 +class TimerTicker {
  3 + const TimerTicker();
  4 + Stream<int> tick({required int ticks}) {
  5 + return Stream.periodic(const Duration(seconds: 1), (x) => ticks - x - 1)
  6 + .take(ticks);
  7 + }
  8 +}
0 \ No newline at end of file 9 \ No newline at end of file
lib/login/loginpage/bloc/login_bloc.dart
1 -import 'dart:async';  
2 -  
3 import 'package:flutter/cupertino.dart'; 1 import 'package:flutter/cupertino.dart';
4 -import 'package:flutter/foundation.dart';  
5 import 'package:flutter_bloc/flutter_bloc.dart'; 2 import 'package:flutter_bloc/flutter_bloc.dart';
6 3
7 part 'login_event.dart'; 4 part 'login_event.dart';
@@ -35,7 +32,6 @@ class LoginBloc extends Bloc&lt;LoginEvent, LoginState&gt; { @@ -35,7 +32,6 @@ class LoginBloc extends Bloc&lt;LoginEvent, LoginState&gt; {
35 bool get agreement => _agreement; 32 bool get agreement => _agreement;
36 LoginType get loginType => _loginType; 33 LoginType get loginType => _loginType;
37 bool get canSendSms => _canSendSms; 34 bool get canSendSms => _canSendSms;
38 - int get countDown => _countDown;  
39 bool get sendSmsIng => _sendSmsIng; 35 bool get sendSmsIng => _sendSmsIng;
40 36
41 LoginBloc() : super(LoginInitial()) { 37 LoginBloc() : super(LoginInitial()) {
@@ -45,8 +41,6 @@ class LoginBloc extends Bloc&lt;LoginEvent, LoginState&gt; { @@ -45,8 +41,6 @@ class LoginBloc extends Bloc&lt;LoginEvent, LoginState&gt; {
45 on<AgreementChangeEvent>(_agreementTypeChange); 41 on<AgreementChangeEvent>(_agreementTypeChange);
46 on<ForgetPasswordEvent>(_forgetPassword); 42 on<ForgetPasswordEvent>(_forgetPassword);
47 on<CheckFieldChangeEvent>(_checkFiledChange); 43 on<CheckFieldChangeEvent>(_checkFiledChange);
48 - on<CountDownEvent>(_countDownChange);  
49 - on<CancelCountDownEvent>(_cancelTimer);  
50 } 44 }
51 45
52 46
@@ -111,16 +105,6 @@ class LoginBloc extends Bloc&lt;LoginEvent, LoginState&gt; { @@ -111,16 +105,6 @@ class LoginBloc extends Bloc&lt;LoginEvent, LoginState&gt; {
111 105
112 } 106 }
113 107
114 - ///倒计时  
115 - void _countDownChange(CountDownEvent event, Emitter<LoginState> emitter) {  
116 - _sendSmsIng = !_sendSmsIng;  
117 - emitter(CountDownNumChangeState());  
118 - }  
119 -  
120 - void _cancelTimer(CancelCountDownEvent event,Emitter<LoginState> emitter) {  
121 -  
122 - }  
123 -  
124 ///登陆状态判断 108 ///登陆状态判断
125 bool _loginStateChange() { 109 bool _loginStateChange() {
126 if (_agreement) { 110 if (_agreement) {
lib/login/loginpage/login_page.dart
@@ -5,6 +5,7 @@ import &#39;package:flutter_screenutil/flutter_screenutil.dart&#39;; @@ -5,6 +5,7 @@ import &#39;package:flutter_screenutil/flutter_screenutil.dart&#39;;
5 import 'package:wow_english/common/extension/string_extension.dart'; 5 import 'package:wow_english/common/extension/string_extension.dart';
6 import 'package:wow_english/common/widgets/textfield_customer_widget.dart'; 6 import 'package:wow_english/common/widgets/textfield_customer_widget.dart';
7 import 'package:wow_english/login/loginpage/bloc/login_bloc.dart'; 7 import 'package:wow_english/login/loginpage/bloc/login_bloc.dart';
  8 +import 'package:wow_english/login/loginpage/time_widget.dart';
8 import 'package:wow_english/route/route.dart'; 9 import 'package:wow_english/route/route.dart';
9 10
10 class LoginPage extends StatelessWidget { 11 class LoginPage extends StatelessWidget {
@@ -163,7 +164,7 @@ class LoginPage extends StatelessWidget { @@ -163,7 +164,7 @@ class LoginPage extends StatelessWidget {
163 ); 164 );
164 165
165 Widget _buildSmsViewWidget()=> BlocBuilder<LoginBloc,LoginState>( 166 Widget _buildSmsViewWidget()=> BlocBuilder<LoginBloc,LoginState>(
166 - builder: (context,state){ 167 + builder: (context,state) {
167 final bloc = BlocProvider.of<LoginBloc>(context); 168 final bloc = BlocProvider.of<LoginBloc>(context);
168 return Padding( 169 return Padding(
169 padding: EdgeInsets.symmetric(horizontal: 135.w), 170 padding: EdgeInsets.symmetric(horizontal: 135.w),
@@ -198,27 +199,8 @@ class LoginPage extends StatelessWidget { @@ -198,27 +199,8 @@ class LoginPage extends StatelessWidget {
198 controller: bloc.checkNumController, 199 controller: bloc.checkNumController,
199 ) 200 )
200 ), 201 ),
201 - GestureDetector(  
202 - onTap: () {  
203 - if (bloc.canSendSms) {  
204 - bloc.add(CountDownEvent());  
205 - }  
206 - },  
207 - child: Container(  
208 - decoration: BoxDecoration(  
209 - image: DecorationImage(  
210 - image: AssetImage(  
211 - bloc.canSendSms?'securitycode'.assetPng:'securitycode_dis'.assetPng  
212 - ),  
213 - fit: BoxFit.fill  
214 - ),  
215 - ),  
216 - padding: const EdgeInsets.symmetric(horizontal:12.0,vertical: 15.0),  
217 - child: Text(  
218 - !bloc.sendSmsIng ? '获取验证码':'${bloc.countDown}s后在次获取'  
219 - ),  
220 - ),  
221 - ) 202 + TimerWidget(
  203 + canSendSms: bloc.canSendSms)
222 ], 204 ],
223 ) 205 )
224 ], 206 ],
lib/login/loginpage/time_widget.dart 0 → 100644
  1 +import 'package:flutter/cupertino.dart';
  2 +import 'package:flutter_bloc/flutter_bloc.dart';
  3 +import 'package:wow_english/common/blocs/timerbloc/timer_bloc.dart';
  4 +import 'package:wow_english/common/extension/string_extension.dart';
  5 +import 'package:wow_english/common/widgets/timer_ticker.dart';
  6 +
  7 +class TimerWidget extends StatefulWidget {
  8 + const TimerWidget({super.key, required this.canSendSms});
  9 +
  10 + final bool canSendSms;
  11 +
  12 + @override
  13 + State<StatefulWidget> createState() {
  14 + return _TimerWidgetState();
  15 + }
  16 +}
  17 +
  18 +class _TimerWidgetState extends State<TimerWidget> {
  19 + late bool sendSmsIng;
  20 +
  21 + @override
  22 + void initState() {
  23 + super.initState();
  24 + sendSmsIng = false;
  25 + }
  26 +
  27 + @override
  28 + Widget build(BuildContext context) {
  29 + return BlocProvider(
  30 + create: (_) => TimerBloc(ticker: const TimerTicker()),
  31 + child: _buildCountdownWidget(),
  32 + );
  33 + }
  34 +
  35 + Widget _buildCountdownWidget() => BlocBuilder<TimerBloc,TimerState>(
  36 + buildWhen: (prev, state) => prev.runtimeType != state.runtimeType,
  37 + builder: (context,state) {
  38 + final bloc = BlocProvider.of<TimerBloc>(context);
  39 + final duration = bloc.state.duration;
  40 + final secondsStr = (duration % 60).floor().toString().padLeft(2, '0');
  41 + if (state is TimerInitial) {
  42 + sendSmsIng = true;
  43 + } else {
  44 + sendSmsIng = false;
  45 + }
  46 + return GestureDetector(
  47 + onTap: () {
  48 + if (widget.canSendSms && !sendSmsIng) {
  49 + bloc.add(ResetEvent());
  50 + bloc.add(StartEvent(duration: bloc.state.duration));
  51 + }
  52 + },
  53 + child: Container(
  54 + decoration: BoxDecoration(
  55 + image: DecorationImage(
  56 + image: AssetImage(
  57 + widget.canSendSms?'securitycode'.assetPng:'securitycode_dis'.assetPng
  58 + ),
  59 + fit: BoxFit.fill
  60 + ),
  61 + ),
  62 + padding: const EdgeInsets.symmetric(horizontal:12.0,vertical: 15.0),
  63 + child: Row(
  64 + children: [
  65 + if (state is TimerInitial)...[
  66 + const Text('获取验证码')
  67 + ],
  68 + if (state is RunningState)...[
  69 + Text('${secondsStr}s倒计时')
  70 + ],
  71 + if (state is FinishedState) ...[
  72 + const Text('获取验证码')
  73 + ]
  74 + ],
  75 + ),
  76 + ),
  77 + );
  78 + },
  79 + );
  80 +}
0 \ No newline at end of file 81 \ No newline at end of file