Commit 17a806898123aa4215fd32331797f156ef980868
1 parent
f138a761
feat:倒计时功能开发
Showing
7 changed files
with
231 additions
and
38 deletions
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 | 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 | 35 | \ No newline at end of file | ... | ... |
lib/common/widgets/timer_ticker.dart
0 → 100644
lib/login/loginpage/bloc/login_bloc.dart
1 | -import 'dart:async'; | |
2 | - | |
3 | 1 | import 'package:flutter/cupertino.dart'; |
4 | -import 'package:flutter/foundation.dart'; | |
5 | 2 | import 'package:flutter_bloc/flutter_bloc.dart'; |
6 | 3 | |
7 | 4 | part 'login_event.dart'; |
... | ... | @@ -35,7 +32,6 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> { |
35 | 32 | bool get agreement => _agreement; |
36 | 33 | LoginType get loginType => _loginType; |
37 | 34 | bool get canSendSms => _canSendSms; |
38 | - int get countDown => _countDown; | |
39 | 35 | bool get sendSmsIng => _sendSmsIng; |
40 | 36 | |
41 | 37 | LoginBloc() : super(LoginInitial()) { |
... | ... | @@ -45,8 +41,6 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> { |
45 | 41 | on<AgreementChangeEvent>(_agreementTypeChange); |
46 | 42 | on<ForgetPasswordEvent>(_forgetPassword); |
47 | 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<LoginEvent, LoginState> { |
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 | 109 | bool _loginStateChange() { |
126 | 110 | if (_agreement) { | ... | ... |
lib/login/loginpage/login_page.dart
... | ... | @@ -5,6 +5,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; |
5 | 5 | import 'package:wow_english/common/extension/string_extension.dart'; |
6 | 6 | import 'package:wow_english/common/widgets/textfield_customer_widget.dart'; |
7 | 7 | import 'package:wow_english/login/loginpage/bloc/login_bloc.dart'; |
8 | +import 'package:wow_english/login/loginpage/time_widget.dart'; | |
8 | 9 | import 'package:wow_english/route/route.dart'; |
9 | 10 | |
10 | 11 | class LoginPage extends StatelessWidget { |
... | ... | @@ -163,7 +164,7 @@ class LoginPage extends StatelessWidget { |
163 | 164 | ); |
164 | 165 | |
165 | 166 | Widget _buildSmsViewWidget()=> BlocBuilder<LoginBloc,LoginState>( |
166 | - builder: (context,state){ | |
167 | + builder: (context,state) { | |
167 | 168 | final bloc = BlocProvider.of<LoginBloc>(context); |
168 | 169 | return Padding( |
169 | 170 | padding: EdgeInsets.symmetric(horizontal: 135.w), |
... | ... | @@ -198,27 +199,8 @@ class LoginPage extends StatelessWidget { |
198 | 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 | 81 | \ No newline at end of file | ... | ... |