Commit 062f0df28f7d79534512dfcd9a5e991842af9f96

Authored by liangchengyou
1 parent 4847e3dc

feat:登录模块代码提交

assets/images/Input_layer_down.png 0 → 100644

29.5 KB

assets/images/Input_layer_up.png 0 → 100644

38.8 KB

assets/images/lock.png 0 → 100644

9.46 KB

assets/images/login_enter.png 0 → 100644

15.2 KB

assets/images/login_enter_dis.png 0 → 100644

15.7 KB

assets/images/login_logo.png 0 → 100644

15.6 KB

assets/images/phone.png 0 → 100644

13.5 KB

assets/images/securitycode.png 0 → 100644

23.4 KB

assets/images/securitycode_dis.png 0 → 100644

22.3 KB

assets/images/splash.png 0 → 100644

439 KB

assets/images/wow_logo.png 0 → 100644

45 KB

lib/app/splash_page.dart
  1 +import 'dart:async';
  2 +
1 import 'package:flutter/material.dart'; 3 import 'package:flutter/material.dart';
  4 +import 'package:wow_english/common/extension/string_extension.dart';
2 import 'package:wow_english/network/basic_configuration.dart'; 5 import 'package:wow_english/network/basic_configuration.dart';
3 import 'package:wow_english/route/route.dart'; 6 import 'package:wow_english/route/route.dart';
4 -import 'package:wow_english/widgets/we_app_bar.dart'; 7 +
5 8
6 class SplashPage extends StatelessWidget { 9 class SplashPage extends StatelessWidget {
7 const SplashPage({super.key}); 10 const SplashPage({super.key});
@@ -22,23 +25,36 @@ class TransitionView extends StatefulWidget { @@ -22,23 +25,36 @@ class TransitionView extends StatefulWidget {
22 } 25 }
23 26
24 class _TransitionViewState extends State<TransitionView> { 27 class _TransitionViewState extends State<TransitionView> {
  28 +
  29 + Future startTime() async {
  30 + Timer(const Duration(seconds: 1),() {
  31 + if(BasicConfigurationManager().sessionId!.isNotEmpty) {
  32 + Navigator.of(context).pushNamedAndRemoveUntil(AppRouteName.tab, (route) => false);
  33 + } else {
  34 + Navigator.of(context).pushNamedAndRemoveUntil(AppRouteName.login,(route) => false);
  35 + }
  36 + });
  37 + }
  38 +
  39 + @override
  40 + void initState() {
  41 + super.initState();
  42 + startTime();
  43 + }
  44 +
25 @override 45 @override
26 Widget build(BuildContext context) { 46 Widget build(BuildContext context) {
27 return Scaffold( 47 return Scaffold(
28 - appBar: WEAppBar(  
29 - titleText: '首页',  
30 - backgroundColor: Theme.of(context).colorScheme.inversePrimary  
31 - ),  
32 body: Center( 48 body: Center(
33 - child: GestureDetector(  
34 - onTap: (){  
35 - if(BasicConfigurationManager().sessionId!.isNotEmpty) {  
36 - Navigator.of(context).pushNamedAndRemoveUntil(AppRouteName.tab, (route) => false);  
37 - } else {  
38 - Navigator.of(context).pushNamed(AppRouteName.login,arguments: {'title':'登陆'});  
39 - }  
40 - },  
41 - child: const Text('登陆'), 49 + child: Container(
  50 + decoration: BoxDecoration(
  51 + image: DecorationImage(
  52 + image: AssetImage(
  53 + 'splash'.assetPng,
  54 + ),
  55 + fit: BoxFit.fill
  56 + )
  57 + ),
42 ), 58 ),
43 ), 59 ),
44 ); 60 );
lib/common/extension/string_extension.dart 0 → 100644
  1 +const String _assetImagePrefix = "assets/images/";
  2 +
  3 +/// 资源类扩展方法
  4 +extension AssetExtension on String {
  5 + /// 图片url
  6 + String get assetImg => _assetImagePrefix + this;
  7 + String get assetPng => 'assets/images/$this.png';
  8 +}
0 \ No newline at end of file 9 \ No newline at end of file
lib/login/blocs/login_bloc.dart
  1 +import 'package:flutter/cupertino.dart';
1 import 'package:flutter/foundation.dart'; 2 import 'package:flutter/foundation.dart';
2 import 'package:flutter_bloc/flutter_bloc.dart'; 3 import 'package:flutter_bloc/flutter_bloc.dart';
3 -import 'package:flutter_easyloading/flutter_easyloading.dart';  
4 -import 'package:wow_english/network/api.dart';  
5 -import 'package:wow_english/network/network_manager.dart';  
6 4
7 part 'login_event.dart'; 5 part 'login_event.dart';
8 part 'login_state.dart'; 6 part 'login_state.dart';
9 7
  8 +enum LoginType {
  9 + ///密码登陆
  10 + pwd,
  11 + ///验证码登陆
  12 + sms,
  13 +}
  14 +
10 class LoginBloc extends Bloc<LoginEvent, LoginState> { 15 class LoginBloc extends Bloc<LoginEvent, LoginState> {
11 16
12 - bool _isLogin = false; 17 + bool _canLogin = false;
  18 + ///是否可以发送验证码
  19 + bool _canSendSms = false;
  20 + ///是否阅读协议
  21 + bool _agreement = false;
  22 + ///登陆方式
  23 + LoginType _loginType = LoginType.sms;
  24 +
  25 + final TextEditingController phoneNumController = TextEditingController();
  26 + final TextEditingController checkNumController = TextEditingController();
13 27
14 - bool get isLogin => _isLogin; 28 + bool get canLogin => _canLogin;
  29 + bool get agreement => _agreement;
  30 + LoginType get loginType => _loginType;
  31 + bool get canSendSms => _canSendSms;
15 32
16 LoginBloc() : super(LoginInitial()) { 33 LoginBloc() : super(LoginInitial()) {
17 on<RequestLoginEvent>(_requestLoginApi); 34 on<RequestLoginEvent>(_requestLoginApi);
  35 + on<ChangeLoginTypeEvent>(_changeLoginType);
  36 + on<PhoneNumChangeEvent>(_changePhoneNumber);
  37 + on<AgreementChangeEvent>(_agreementTypeChange);
  38 + on<ForgetPasswordEvent>(_forgetPassword);
  39 + on<CheckFieldChangeEvent>(_checkFiledChange);
18 } 40 }
19 41
20 42
  43 + ///请求登陆
21 void _requestLoginApi(RequestLoginEvent event, Emitter<LoginState> emitter) async { 44 void _requestLoginApi(RequestLoginEvent event, Emitter<LoginState> emitter) async {
22 - EasyLoading.show(status: 'loading');  
23 - await DioUtil().requestData(  
24 - Api.testApi,  
25 - successCallBack: (dynamic data){  
26 - EasyLoading.dismiss();  
27 - if (kDebugMode) {  
28 - print(data);  
29 - }  
30 - _isLogin = true;  
31 - emitter(LoginEventChangeState());  
32 - },  
33 - errorCallBack: (dynamic error){  
34 - EasyLoading.dismiss();  
35 - if (kDebugMode) {  
36 - print(error);  
37 - }  
38 - _isLogin = false;  
39 - emitter(LoginEventChangeState());  
40 - }); 45 +
  46 + }
  47 +
  48 + ///切换登陆方式
  49 + void _changeLoginType(ChangeLoginTypeEvent event, Emitter<LoginState> emitter) async {
  50 + if (_loginType == LoginType.sms) {
  51 + _loginType = LoginType.pwd;
  52 + } else {
  53 + _loginType = LoginType.sms;
  54 + }
  55 + checkNumController.clear();
  56 + if (_loginStateChange()) {
  57 + emitter(LoginEventChangeState());
  58 + }
  59 + emitter(LoginTypeChangeState());
  60 + }
  61 +
  62 + ///手机号输入
  63 + void _changePhoneNumber(PhoneNumChangeEvent event, Emitter<LoginState> emitter) async {
  64 + if(phoneNumController.text.isNotEmpty) {
  65 + if (!_canSendSms) {
  66 + _canSendSms = true;
  67 + emitter(SmsSendTypeChangeState());
  68 + }
  69 + if (_loginStateChange()) {
  70 + emitter(LoginEventChangeState());
  71 + }
  72 + } else {
  73 + if (_canSendSms) {
  74 + _canSendSms = false;
  75 + emitter(SmsSendTypeChangeState());
  76 + }
  77 + if (_loginStateChange()) {
  78 + emitter(LoginEventChangeState());
  79 + }
  80 + }
  81 + }
  82 +
  83 + ///验证码/密码输入变化
  84 + void _checkFiledChange(CheckFieldChangeEvent event,Emitter<LoginState> emitter) async {
  85 + if (_loginStateChange()) {
  86 + emitter(LoginEventChangeState());
  87 + }
  88 + }
  89 +
  90 + ///是否阅读协议
  91 + void _agreementTypeChange(AgreementChangeEvent event, Emitter<LoginState> emitter) async {
  92 + _agreement = !_agreement;
  93 + emitter(AgreementTypeChangeState());
  94 + if (_loginStateChange()) {
  95 + emitter(LoginEventChangeState());
  96 + }
  97 + }
  98 +
  99 + ///忘记密码
  100 + void _forgetPassword(ForgetPasswordEvent event, Emitter<LoginState> emitter) async {
  101 +
  102 + }
  103 +
  104 + bool _loginStateChange() {
  105 + if (_agreement) {
  106 + if (phoneNumController.text.isNotEmpty && checkNumController.text.isNotEmpty) {
  107 + if (!_canLogin) {
  108 + _canLogin = true;
  109 + return true;
  110 + }
  111 + } else {
  112 + if (_canLogin) {
  113 + _canLogin = false;
  114 + return true;
  115 + }
  116 + }
  117 + } else {
  118 + if (_canLogin) {
  119 + _canLogin = false;
  120 + return true;
  121 + }
  122 + }
  123 + return false;
41 } 124 }
42 } 125 }
lib/login/blocs/login_event.dart
@@ -4,3 +4,13 @@ part of &#39;login_bloc.dart&#39;; @@ -4,3 +4,13 @@ part of &#39;login_bloc.dart&#39;;
4 abstract class LoginEvent {} 4 abstract class LoginEvent {}
5 5
6 class RequestLoginEvent extends LoginEvent {} 6 class RequestLoginEvent extends LoginEvent {}
  7 +
  8 +class ChangeLoginTypeEvent extends LoginEvent {}
  9 +
  10 +class PhoneNumChangeEvent extends LoginEvent {}
  11 +
  12 +class CheckFieldChangeEvent extends LoginEvent {}
  13 +
  14 +class AgreementChangeEvent extends LoginEvent {}
  15 +
  16 +class ForgetPasswordEvent extends LoginEvent {}
lib/login/blocs/login_state.dart
@@ -6,3 +6,9 @@ abstract class LoginState {} @@ -6,3 +6,9 @@ abstract class LoginState {}
6 class LoginInitial extends LoginState {} 6 class LoginInitial extends LoginState {}
7 7
8 class LoginEventChangeState extends LoginState {} 8 class LoginEventChangeState extends LoginState {}
  9 +
  10 +class LoginTypeChangeState extends LoginState {}
  11 +
  12 +class SmsSendTypeChangeState extends LoginState {}
  13 +
  14 +class AgreementTypeChangeState extends LoginState {}
lib/login/login_page.dart
1 -import 'package:flutter/foundation.dart';  
2 import 'package:flutter/material.dart'; 1 import 'package:flutter/material.dart';
3 import 'package:flutter_bloc/flutter_bloc.dart'; 2 import 'package:flutter_bloc/flutter_bloc.dart';
  3 +import 'package:flutter_screenutil/flutter_screenutil.dart';
  4 +import 'package:wow_english/common/extension/string_extension.dart';
4 import 'package:wow_english/login/blocs/login_bloc.dart'; 5 import 'package:wow_english/login/blocs/login_bloc.dart';
5 -import 'package:wow_english/modes/test_model.dart';  
6 -import 'package:wow_english/route/route.dart';  
7 -import 'package:wow_english/widgets/we_app_bar.dart';  
8 6
9 class LoginPage extends StatelessWidget { 7 class LoginPage extends StatelessWidget {
10 - const LoginPage({super.key, this.title});  
11 -  
12 - final String? title; 8 + const LoginPage({super.key});
13 9
14 @override 10 @override
15 Widget build(BuildContext context) { 11 Widget build(BuildContext context) {
@@ -23,46 +19,87 @@ class LoginPage extends StatelessWidget { @@ -23,46 +19,87 @@ class LoginPage extends StatelessWidget {
23 builder: (context, state) { 19 builder: (context, state) {
24 final bloc = BlocProvider.of<LoginBloc>(context); 20 final bloc = BlocProvider.of<LoginBloc>(context);
25 return Scaffold( 21 return Scaffold(
26 - appBar: WEAppBar(  
27 - titleText: title??'',  
28 - ),  
29 - body: Center(  
30 - child: Column(  
31 - mainAxisAlignment: MainAxisAlignment.spaceAround, 22 + body: SafeArea(
  23 + child: ListView(
32 children: [ 24 children: [
33 - GestureDetector(  
34 - onTap: () {  
35 - Navigator.of(context).pushNamedAndRemoveUntil(AppRouteName.tab, (route) => false);  
36 - },  
37 - child: const Text(  
38 - '进入首页'  
39 - ),  
40 - ),  
41 - Text(  
42 - bloc.isLogin?'登陆成功':'还未登陆'  
43 - ),  
44 - GestureDetector(  
45 - onTap: () {  
46 - TestModel testModel = TestModel(name: 'lcy',age: 31,sex: '男');  
47 - Map<String,dynamic> jsonMap = testModel.toJson();  
48 - if (kDebugMode) {  
49 - print(jsonMap);  
50 - }  
51 - TestModel testModel2 = TestModel.fromJson(jsonMap);  
52 - if (kDebugMode) {  
53 - print(testModel2.name);  
54 - }  
55 - },  
56 - child: const Text(  
57 - '数据转换'  
58 - ),  
59 - ),  
60 - GestureDetector(  
61 - onTap: (){  
62 - bloc.add(RequestLoginEvent());  
63 - },  
64 - child: const Text(  
65 - '发起网络请求' 25 + Container(
  26 + padding: EdgeInsets.only(top: 25.h),
  27 + child: Stack(
  28 + children: [
  29 + Positioned(
  30 + right: 29.w,
  31 + child: GestureDetector(
  32 + onTap: () => bloc.add(ChangeLoginTypeEvent()),
  33 + child: Container(
  34 + decoration: BoxDecoration(
  35 + image: DecorationImage(
  36 + image: AssetImage(
  37 + 'login_logo'.assetPng
  38 + ),
  39 + fit: BoxFit.fill
  40 + ),
  41 + ),
  42 + padding: const EdgeInsets.symmetric(horizontal: 18.0),
  43 + child: Text(
  44 + bloc.loginType == LoginType.sms?'密码登陆':'验证码密码'
  45 + ),
  46 + ),
  47 + )
  48 + ),
  49 + Center(
  50 + child: Column(
  51 + children: [
  52 + Image.asset(
  53 + 'wow_logo'.assetPng,
  54 + height: 81.h,
  55 + width: 139.w,),
  56 + Offstage(
  57 + offstage: bloc.loginType == LoginType.pwd,
  58 + child: _buildSmsViewWidget(),
  59 + ),
  60 + Offstage(
  61 + offstage: bloc.loginType == LoginType.sms,
  62 + child: _buildPwdViewWidget(),
  63 + ),
  64 + 20.verticalSpace,
  65 + Row(
  66 + mainAxisAlignment: MainAxisAlignment.center,
  67 + children: [
  68 + GestureDetector(
  69 + onTap: () => bloc.add(AgreementChangeEvent()),
  70 + child: Icon(
  71 + bloc.agreement ? Icons.check_circle_outlined:Icons.circle_outlined,
  72 + color:bloc.agreement ? Colors.green:Colors.black),
  73 + ),
  74 + 6.horizontalSpace,
  75 + const Text('我已阅读并同意《用户隐私协议》,《儿童隐私策略》')
  76 + ],
  77 + ),
  78 + 14.5.verticalSpace,
  79 + GestureDetector(
  80 + onTap: () => bloc.add(ChangeLoginTypeEvent()),
  81 + child: Container(
  82 + decoration: BoxDecoration(
  83 + image: DecorationImage(
  84 + image: AssetImage(
  85 + bloc.canLogin?'login_enter'.assetPng:'login_enter_dis'.assetPng
  86 + ),
  87 + fit: BoxFit.fill
  88 + ),
  89 + ),
  90 + padding: const EdgeInsets.symmetric(
  91 + horizontal: 36.0,
  92 + vertical: 20.0
  93 + ),
  94 + child: const Text(
  95 + '登录'
  96 + ),
  97 + ),
  98 + )
  99 + ],
  100 + ),
  101 + )
  102 + ],
66 ), 103 ),
67 ) 104 )
68 ], 105 ],
@@ -71,4 +108,202 @@ class LoginPage extends StatelessWidget { @@ -71,4 +108,202 @@ class LoginPage extends StatelessWidget {
71 ); 108 );
72 }, 109 },
73 ); 110 );
  111 +
  112 + Widget _buildSmsViewWidget()=> BlocBuilder<LoginBloc,LoginState>(
  113 + builder: (context,state){
  114 + final bloc = BlocProvider.of<LoginBloc>(context);
  115 + return Column(
  116 + children: [
  117 + 15.verticalSpace,
  118 + Container(
  119 + padding: EdgeInsets.symmetric(horizontal: 135.w),
  120 + width: double.infinity,
  121 + height: 55,
  122 + alignment: Alignment.center,
  123 + decoration: BoxDecoration(
  124 + image: DecorationImage(
  125 + image: AssetImage(
  126 + 'Input_layer_up'.assetPng
  127 + )
  128 + )
  129 + ),
  130 + child: TextField(
  131 + controller: bloc.phoneNumController,
  132 + textAlign: TextAlign.center,
  133 + textInputAction: TextInputAction.done,
  134 + keyboardType: TextInputType.phone,
  135 + decoration: const InputDecoration(
  136 + hintText: '请输入手机号',
  137 + border: InputBorder.none,
  138 + ),
  139 + onChanged: (String value) {
  140 + bloc.add(PhoneNumChangeEvent());
  141 + },
  142 + )
  143 + ),
  144 + 6.5.verticalSpace,
  145 + const Text('未注册用户登录默认注册'),
  146 + 4.5.verticalSpace,
  147 + Container(
  148 + padding: EdgeInsets.symmetric(horizontal: 205.w),
  149 + child: Row(
  150 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
  151 + children: [
  152 + Container(
  153 + width: 257,
  154 + height: 50,
  155 + alignment: Alignment.center,
  156 + decoration: BoxDecoration(
  157 + image: DecorationImage(
  158 + image: AssetImage(
  159 + 'Input_layer_down'.assetPng
  160 + )
  161 + )
  162 + ),
  163 + child: TextField(
  164 + controller: bloc.checkNumController,
  165 + textAlign: TextAlign.center,
  166 + textInputAction: TextInputAction.done,
  167 + keyboardType: TextInputType.number,
  168 + decoration: const InputDecoration(
  169 + hintText: '请输入验证码',
  170 + border: InputBorder.none,
  171 + ),
  172 + onChanged: (String value) {
  173 + bloc.add(CheckFieldChangeEvent());
  174 + },
  175 + )
  176 + ),
  177 + GestureDetector(
  178 + onTap: () {
  179 + if (bloc.canSendSms) {
  180 + bloc.add(ChangeLoginTypeEvent());
  181 + }
  182 + },
  183 + child: Container(
  184 + decoration: BoxDecoration(
  185 + image: DecorationImage(
  186 + image: AssetImage(
  187 + bloc.canSendSms?'securitycode'.assetPng:'securitycode_dis'.assetPng
  188 + ),
  189 + fit: BoxFit.fill
  190 + ),
  191 + ),
  192 + padding: const EdgeInsets.symmetric(horizontal:12.0,vertical: 15.0),
  193 + child: const Text(
  194 + '获取验证码'
  195 + ),
  196 + ),
  197 + )
  198 + ],
  199 + ),
  200 + )
  201 + ],
  202 + );
  203 + });
  204 +
  205 + Widget _buildPwdViewWidget()=> BlocBuilder<LoginBloc,LoginState>(
  206 + builder: (context,state){
  207 + final bloc = BlocProvider.of<LoginBloc>(context);
  208 + return Container(
  209 + padding: EdgeInsets.symmetric(horizontal: 135.w),
  210 + child: Column(
  211 + children: [
  212 + 15.verticalSpace,
  213 + Row(
  214 + mainAxisAlignment: MainAxisAlignment.center,
  215 + children: [
  216 + Image.asset(
  217 + 'phone'.assetPng,
  218 + height: 45,
  219 + width: 35,
  220 + ),
  221 + 10.5.horizontalSpace,
  222 + Container(
  223 + width: 397.5,
  224 + height: 55,
  225 + alignment: Alignment.center,
  226 + decoration: BoxDecoration(
  227 + image: DecorationImage(
  228 + image: AssetImage(
  229 + 'Input_layer_up'.assetPng
  230 + ),
  231 + fit: BoxFit.fill,
  232 + )
  233 + ),
  234 + child: TextField(
  235 + controller: bloc.phoneNumController,
  236 + textAlign: TextAlign.center,
  237 + textInputAction: TextInputAction.done,
  238 + decoration: const InputDecoration(
  239 + hintText: '请输入手机号',
  240 + border: InputBorder.none,
  241 + ),
  242 + keyboardType: TextInputType.phone,
  243 + onChanged: (String value) {
  244 + bloc.add(PhoneNumChangeEvent());
  245 + },
  246 + )
  247 + ),
  248 + 5.horizontalSpace,
  249 + const SizedBox(
  250 + width: 100,
  251 + height: 55.0,
  252 + )
  253 + ],
  254 + ),
  255 + 12.verticalSpace,
  256 + Row(
  257 + mainAxisAlignment: MainAxisAlignment.center,
  258 + children: [
  259 + Image.asset(
  260 + 'lock'.assetPng,
  261 + height: 34,
  262 + width: 31,
  263 + ),
  264 + 10.5.horizontalSpace,
  265 + Container(
  266 + width: 397.5,
  267 + height: 55,
  268 + alignment: Alignment.center,
  269 + decoration: BoxDecoration(
  270 + image: DecorationImage(
  271 + image: AssetImage(
  272 + 'Input_layer_down'.assetPng
  273 + ),
  274 + fit: BoxFit.fill,
  275 + )
  276 + ),
  277 + child: TextField(
  278 + controller: bloc.checkNumController,
  279 + textAlign: TextAlign.center,
  280 + textInputAction: TextInputAction.done,
  281 + decoration: const InputDecoration(
  282 + hintText: '请输入密码',
  283 + border: InputBorder.none,
  284 + ),
  285 + onChanged: (String value) {
  286 + bloc.add(CheckFieldChangeEvent());
  287 + },
  288 + )
  289 + ),
  290 + 5.horizontalSpace,
  291 + GestureDetector(
  292 + onTap: () => bloc.add(ForgetPasswordEvent()),
  293 + child: Container(
  294 + width: 100,
  295 + height: 55.0,
  296 + alignment: Alignment.centerLeft,
  297 + child: const Text(
  298 + '忘记密码 ?'
  299 + ),
  300 + ),
  301 + )
  302 + ],
  303 + )
  304 + ],
  305 + ),
  306 + );
  307 + });
  308 +
74 } 309 }
lib/route/route.dart
@@ -25,8 +25,7 @@ class AppRouter { @@ -25,8 +25,7 @@ class AppRouter {
25 transitionDuration: Duration.zero, 25 transitionDuration: Duration.zero,
26 transitionsBuilder: (_, __, ___, child) => child); 26 transitionsBuilder: (_, __, ___, child) => child);
27 case AppRouteName.login: 27 case AppRouteName.login:
28 - final title = (settings.arguments as Map)['title'] as String;  
29 - return CupertinoPageRoute(builder: (_) => LoginPage(title: title)); 28 + return CupertinoPageRoute(builder: (_) => const LoginPage());
30 case AppRouteName.tab: 29 case AppRouteName.tab:
31 return PageRouteBuilder( 30 return PageRouteBuilder(
32 opaque: false, 31 opaque: false,
pubspec.yaml
@@ -108,6 +108,9 @@ flutter: @@ -108,6 +108,9 @@ flutter:
108 # the material Icons class. 108 # the material Icons class.
109 uses-material-design: true 109 uses-material-design: true
110 110
  111 + assets:
  112 + - assets/images/
  113 +
111 # To add assets to your application, add an assets section, like this: 114 # To add assets to your application, add an assets section, like this:
112 # assets: 115 # assets:
113 # - images/a_dot_burr.jpeg 116 # - images/a_dot_burr.jpeg