# Conflicts: # lib/register.dart # lib/widgets/home_slide_banner/bloc/home_slide_banner_repository.darttags/0.0.1
@@ -680,7 +680,7 @@ | |||||
"languageVersion": "2.1" | "languageVersion": "2.1" | ||||
} | } | ||||
], | ], | ||||
"generated": "2020-09-11T04:26:30.812379Z", | |||||
"generated": "2020-09-11T11:58:25.076408Z", | |||||
"generator": "pub", | "generator": "pub", | ||||
"generatorVersion": "2.7.2" | "generatorVersion": "2.7.2" | ||||
} | } |
@@ -1,7 +1,9 @@ | |||||
import 'package:flutter/cupertino.dart'; | import 'package:flutter/cupertino.dart'; | ||||
import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||
import 'package:cached_network_image/cached_network_image.dart'; | import 'package:cached_network_image/cached_network_image.dart'; | ||||
import 'package:provider/provider.dart'; | |||||
import 'package:zhiying_base_widget/pages/home_page/home_page_bloc.dart'; | import 'package:zhiying_base_widget/pages/home_page/home_page_bloc.dart'; | ||||
import 'package:zhiying_base_widget/pages/login_page/notifier/user_info_notifier.dart'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | import 'package:zhiying_comm/zhiying_comm.dart'; | ||||
import 'package:zhiying_comm/util/base_bloc.dart'; | import 'package:zhiying_comm/util/base_bloc.dart'; | ||||
@@ -17,9 +19,14 @@ class _HomePageState extends State<HomePage> { | |||||
@override | @override | ||||
Widget build(BuildContext context) { | Widget build(BuildContext context) { | ||||
print('home_page build'); | print('home_page build'); | ||||
return BlocProvider<HomePageBloc>( | |||||
bloc: HomePageBloc(), | |||||
child: _HomePageContainer(), | |||||
return MultiProvider( | |||||
providers: [ | |||||
ChangeNotifierProvider.value(value: UserInfoNotifier()), | |||||
], | |||||
child: BlocProvider<HomePageBloc>( | |||||
bloc: HomePageBloc(), | |||||
child: _HomePageContainer(), | |||||
), | |||||
); | ); | ||||
} | } | ||||
} | } | ||||
@@ -0,0 +1,3 @@ | |||||
export 'login_account_bloc.dart'; | |||||
export 'login_account_event.dart'; | |||||
export 'login_account_state.dart'; |
@@ -0,0 +1,63 @@ | |||||
import 'dart:async'; | |||||
import 'package:bloc/bloc.dart'; | |||||
import 'package:flutter/cupertino.dart'; | |||||
import 'package:zhiying_comm/util/empty_util.dart'; | |||||
import './bloc.dart'; | |||||
import 'login_account_repository.dart'; | |||||
class LoginAccountBloc extends Bloc<LoginAccountEvent, LoginAccountState> { | |||||
LoginAccountRepository repository; | |||||
LoginAccountBloc({@required this.repository}); | |||||
@override | |||||
LoginAccountState get initialState => LoginAccountInitial(); | |||||
@override | |||||
Stream<LoginAccountState> mapEventToState(LoginAccountEvent event) async* { | |||||
/// 获取数据 | |||||
if (event is LoginAccountInitEvent) { | |||||
yield* _mapInitEventToState(); | |||||
} | |||||
/// 获取验证码 | |||||
if (event is LoginAccountGetVcodeEvent) { | |||||
yield* _mapGetVcodeEventToState(event); | |||||
} | |||||
/// 登陆(验证码) | |||||
if (event is LoginAccountTypeVcodeEvent) { | |||||
yield* _mapLoginTypeVcodeEventToState(event); | |||||
} | |||||
} | |||||
/// 获取数据 | |||||
Stream<LoginAccountState> _mapInitEventToState() async* { | |||||
var data = await repository.fetchData(); | |||||
if (!EmptyUtil.isEmpty(data)) | |||||
yield LoginAccountLoadedState(model: data); | |||||
else | |||||
yield LoginAccountErrorState(); | |||||
} | |||||
/// 获取验证码 | |||||
Stream<LoginAccountState> _mapGetVcodeEventToState(LoginAccountGetVcodeEvent event) async* { | |||||
bool result = await repository.fetchGetVcode(event); | |||||
if (result) | |||||
yield LoginAccountGetVcodeSuccessState(); | |||||
else | |||||
yield LoginAccountGetVcodeErrorState(); | |||||
} | |||||
/// 验证码登陆 | |||||
Stream<LoginAccountState> _mapLoginTypeVcodeEventToState(LoginAccountTypeVcodeEvent event) async* { | |||||
var result = await repository.loginTypeVcode(event); | |||||
if (!EmptyUtil.isEmpty(result)) | |||||
yield LoginAccountTypeVcodeLoginSuccessState(model: result); | |||||
else | |||||
yield LoginAccountTypeVcodeLoginErrorState(); | |||||
} | |||||
/// 密码登陆 | |||||
Stream<LoginAccountState> _mapLoginTypePassEventToState(LoginAccountTypePasswordEvent event) async* {} | |||||
} |
@@ -0,0 +1,48 @@ | |||||
import 'package:equatable/equatable.dart'; | |||||
import 'package:flutter/cupertino.dart'; | |||||
abstract class LoginAccountEvent extends Equatable { | |||||
const LoginAccountEvent(); | |||||
@override | |||||
List<Object> get props => []; | |||||
} | |||||
/// 初始化事件 | |||||
class LoginAccountInitEvent extends LoginAccountEvent {} | |||||
/// 获取验证码 | |||||
class LoginAccountGetVcodeEvent extends LoginAccountEvent{ | |||||
final String mobile; | |||||
const LoginAccountGetVcodeEvent({@required this.mobile}); | |||||
@override | |||||
List<Object> get props => [mobile]; | |||||
} | |||||
/// 核对验证码 | |||||
class LoginAccountCheckVcodeEvent extends LoginAccountEvent{ | |||||
/// 手机号码 | |||||
final String mobile; | |||||
/// 验证码 | |||||
final String captcha; | |||||
const LoginAccountCheckVcodeEvent({@required this.mobile, @required this.captcha}); | |||||
@override | |||||
List<Object> get props => [this.mobile, this.captcha]; | |||||
} | |||||
/// 登陆事件(密码登陆) | |||||
class LoginAccountTypePasswordEvent extends LoginAccountEvent{ | |||||
final String username; | |||||
final String password; | |||||
const LoginAccountTypePasswordEvent({@required this.username, @required this.password}); | |||||
} | |||||
/// 登陆事件(验证码登陆) | |||||
class LoginAccountTypeVcodeEvent extends LoginAccountEvent{ | |||||
final String mobile; | |||||
final String captcha; | |||||
const LoginAccountTypeVcodeEvent({@required this.mobile, @required this.captcha}); | |||||
@override | |||||
List<Object> get props => [this.mobile, this.captcha]; | |||||
} |
@@ -0,0 +1,54 @@ | |||||
import 'package:zhiying_base_widget/pages/login_page/account/bloc/bloc.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/model/login_user.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/login_util.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/model/login_model.dart'; | |||||
import 'package:zhiying_comm/util/empty_util.dart'; | |||||
import 'package:zhiying_comm/util/global_config.dart'; | |||||
import 'package:zhiying_comm/util/net_util.dart'; | |||||
/// | |||||
/// 账户登陆 | |||||
/// | |||||
class LoginAccountRepository{ | |||||
/// 获取数据,如果缓存有,则获取缓存的数据 | |||||
Future<LoginModel> fetchData() async{ | |||||
var result = await LoginUtil.getLoginModel(); | |||||
if(!EmptyUtil.isEmpty(result)){ | |||||
return result; | |||||
} | |||||
return null; | |||||
} | |||||
/// 获取验证码 | |||||
Future<bool> fetchGetVcode(LoginAccountGetVcodeEvent event) async{ | |||||
print('mobile = ${event.mobile}'); | |||||
var result = await NetUtil.post('/api/v1/sign/sms/fast/in', params: {'mobile': event.mobile}); | |||||
if(NetUtil.isSuccess(result)){ | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
/// 密码登陆 | |||||
Future<bool> loginTypePass(LoginAccountTypePasswordEvent event) async{ | |||||
// var result = await NetUtil.post('/api/v1/sign/in', params: {'username'}); | |||||
} | |||||
/// 验证码登陆 | |||||
Future<LoginUser> loginTypeVcode(LoginAccountTypeVcodeEvent event) async{ | |||||
var result = await NetUtil.post('/api/v1/sign/fast/in', params: {'mobile': event.mobile, 'captcha': event.captcha}); | |||||
if(NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])){ | |||||
LoginUser loginUser = LoginUser.fromJson(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); | |||||
return loginUser; | |||||
} | |||||
return null; | |||||
} | |||||
/// 检查验证码 | |||||
Future<bool> checkVcode(LoginAccountCheckVcodeEvent event) async{ | |||||
} | |||||
} |
@@ -0,0 +1,57 @@ | |||||
import 'package:equatable/equatable.dart'; | |||||
import 'package:flutter/cupertino.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/model/login_user.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/model/login_model.dart'; | |||||
abstract class LoginAccountState extends Equatable { | |||||
const LoginAccountState(); | |||||
@override | |||||
List<Object> get props => []; | |||||
} | |||||
/// 初始化状态 | |||||
class LoginAccountInitial extends LoginAccountState {} | |||||
/// 数据加载完毕状态 | |||||
class LoginAccountLoadedState extends LoginAccountState { | |||||
final LoginModel model; | |||||
const LoginAccountLoadedState({@required this.model}); | |||||
@override | |||||
List<Object> get props => [this.model]; | |||||
} | |||||
/// 数据加载出错状态 | |||||
class LoginAccountErrorState extends LoginAccountState {} | |||||
/// 验证码下发成功的状态 | |||||
class LoginAccountGetVcodeSuccessState extends LoginAccountState { | |||||
final String msg; | |||||
const LoginAccountGetVcodeSuccessState({this.msg}); | |||||
@override | |||||
List<Object> get props => [this.msg]; | |||||
} | |||||
/// 验证码下发失败的状态 | |||||
class LoginAccountGetVcodeErrorState extends LoginAccountState { | |||||
final String msg; | |||||
const LoginAccountGetVcodeErrorState({this.msg}); | |||||
} | |||||
/// 验证码登陆成功 | |||||
class LoginAccountTypeVcodeLoginSuccessState extends LoginAccountState { | |||||
final LoginUser model; | |||||
const LoginAccountTypeVcodeLoginSuccessState({@required this.model}); | |||||
@override | |||||
List<Object> get props => [this.model]; | |||||
} | |||||
/// 验证码登陆失败 | |||||
class LoginAccountTypeVcodeLoginErrorState extends LoginAccountState { | |||||
final String msg; | |||||
const LoginAccountTypeVcodeLoginErrorState({this.msg}); | |||||
} |
@@ -0,0 +1,576 @@ | |||||
import 'package:flutter/gestures.dart'; | |||||
import 'package:flutter/material.dart'; | |||||
import 'package:cached_network_image/cached_network_image.dart'; | |||||
import 'package:flutter_bloc/flutter_bloc.dart'; | |||||
import 'package:provider/provider.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/account/bloc/bloc.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/model/login_model.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/notifier/user_info_notifier.dart'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
import 'bloc/login_account_repository.dart'; | |||||
import 'widget/slide_verify_widget.dart'; | |||||
import 'package:zhiying_comm/util/empty_util.dart'; | |||||
import 'package:fluttertoast/fluttertoast.dart'; | |||||
import 'widget/vcode_widget.dart'; | |||||
/// | |||||
/// 账号登陆(手机验证码,密码) | |||||
/// | |||||
class LoginAccountPage extends StatelessWidget { | |||||
final Map<String, dynamic> model; | |||||
const LoginAccountPage(this.model, {Key key}) : super(key: key); | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return Scaffold( | |||||
resizeToAvoidBottomInset: false, | |||||
backgroundColor: HexColor.fromHex('#FFFFFF'), | |||||
body: BlocProvider<LoginAccountBloc>( | |||||
create: (_) => LoginAccountBloc(repository: LoginAccountRepository())..add(LoginAccountInitEvent()), | |||||
child: LoginAccountPageContianer(), | |||||
), | |||||
); | |||||
} | |||||
} | |||||
/// 啦啦啦 | |||||
class LoginAccountPageContianer extends StatefulWidget { | |||||
@override | |||||
_LoginAccountPageContianerState createState() => _LoginAccountPageContianerState(); | |||||
} | |||||
/// | |||||
/// 主体逻辑 | |||||
/// | |||||
class _LoginAccountPageContianerState extends State<LoginAccountPageContianer> implements OnClickListener { | |||||
TextEditingController _phoneEdController; | |||||
TextEditingController _vcodeEdController; | |||||
TextEditingController _passEdController; | |||||
FocusNode _phoneFN; | |||||
FocusNode _passFN; | |||||
FocusNode _vcodeFN; | |||||
/// 跳转到邀请码页面 | |||||
void _openInvitePage(){ | |||||
print('跳转到邀请码页面'); | |||||
} | |||||
/// 登陆成功页面 | |||||
void _openLoginSuccessPage(){ | |||||
Navigator.pushAndRemoveUntil( | |||||
context, | |||||
MaterialPageRoute(builder: (BuildContext context) => PageFactory.create('homePage', null)), | |||||
(Route<dynamic> route) => false, | |||||
); | |||||
} | |||||
/// 登陆 | |||||
void _submitOnClick() { | |||||
print('登陆'); | |||||
if(_checkParam(true)) { | |||||
if (_useVcode) { | |||||
BlocProvider.of<LoginAccountBloc>(context).add( | |||||
LoginAccountTypeVcodeEvent(mobile: _phoneEdController?.text?.toString()?.trim() ?? '', captcha: _vcodeEdController?.text?.toString()?.trim() ?? '')); | |||||
} else { | |||||
BlocProvider.of<LoginAccountBloc>(context).add(LoginAccountTypePasswordEvent(username: _phoneEdController?.text?.toString()?.trim() ?? '', | |||||
password: _passEdController?.text?.toString()?.trim() ?? '')); | |||||
} | |||||
} | |||||
} | |||||
/// 切换登陆方式 | |||||
void _changeLoginTypeOnClick() { | |||||
print('切换登陆'); | |||||
setState(() { | |||||
_useVcode = !_useVcode; | |||||
}); | |||||
// 清除缓存 | |||||
if(_useVcode){ | |||||
_passEdController?.clear(); | |||||
_passFN?.unfocus(); | |||||
}else{ | |||||
_vcodeEdController?.clear(); | |||||
_vcodeFN?.unfocus(); | |||||
} | |||||
} | |||||
/// 同意协议 | |||||
void _agreeOnClick() { | |||||
print('同意协议'); | |||||
setState(() { | |||||
_acceptAgreement = !_acceptAgreement; | |||||
}); | |||||
_checkParam(false); | |||||
} | |||||
/// 打开协议 | |||||
void _openAgreement(String url){ | |||||
if(!EmptyUtil.isEmpty(url)){ | |||||
print('打开协议$url'); | |||||
} | |||||
} | |||||
/// 输入框监听 | |||||
void _onChange(string){ | |||||
print('$string'); | |||||
_checkParam(false); | |||||
} | |||||
/// 校验登陆参数 | |||||
bool _checkParam(bool needToast){ | |||||
// 验证码 | |||||
if(_useVcode){ | |||||
String phone = _phoneEdController?.text?.toString()?.trim() ?? null; | |||||
String vcode = _vcodeEdController?.text?.toString()?.trim() ?? null; | |||||
if(EmptyUtil.isEmpty(phone)){ | |||||
if(needToast) Fluttertoast.showToast(msg: '手机号不能为空!'); | |||||
return false; | |||||
} | |||||
if(phone.length != 11){ | |||||
if(needToast) Fluttertoast.showToast(msg: '手机号格式有误!'); | |||||
return false; | |||||
} | |||||
if(EmptyUtil.isEmpty(vcode)){ | |||||
if(needToast) Fluttertoast.showToast(msg: '验证码不能为空!'); | |||||
return false; | |||||
} | |||||
if(vcode.length < 4){ | |||||
if(needToast) Fluttertoast.showToast(msg: '验证码号格式有误!'); | |||||
return false; | |||||
} | |||||
}else{ | |||||
String phone = _phoneEdController?.text?.toString()?.trim() ?? null; | |||||
String pass = _passEdController?.text?.toString()?.trim() ?? null; | |||||
if(EmptyUtil.isEmpty(phone)){ | |||||
if(needToast) Fluttertoast.showToast(msg: '手机号不能为空!'); | |||||
return false; | |||||
} | |||||
if(phone.length != 11){ | |||||
if(needToast) Fluttertoast.showToast(msg: '手机号格式有误!'); | |||||
return false; | |||||
} | |||||
if(EmptyUtil.isEmpty(pass)){ | |||||
if(needToast) Fluttertoast.showToast(msg: '验证码不能为空!'); | |||||
return false; | |||||
} | |||||
if(pass.length < 4){ | |||||
if(needToast) Fluttertoast.showToast(msg: '验证码号格式有误!'); | |||||
return false; | |||||
} | |||||
} | |||||
if(!_acceptAgreement){ | |||||
if(needToast) Fluttertoast.showToast(msg: '请同意用户协议与隐私政策'); | |||||
return false; | |||||
} | |||||
setState(() { | |||||
_canSubmit = true; | |||||
}); | |||||
return true; | |||||
} | |||||
/// 检测手机号是否合法 | |||||
bool _checkPhoneNumParam(bool needToast){ | |||||
String phone = _phoneEdController?.text?.toString()?.trim() ?? null; | |||||
if(EmptyUtil.isEmpty(phone)){ | |||||
if(needToast) Fluttertoast.showToast(msg: '手机号不能为空!'); | |||||
return false; | |||||
} | |||||
if(phone.length != 11){ | |||||
if(needToast) Fluttertoast.showToast(msg: '手机号格式有误!'); | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
/// 是否使用验证码登陆 默认使用 | |||||
bool _useVcode = true; | |||||
/// 是否可以登陆 | |||||
bool _canSubmit = false; | |||||
/// 是否同意协议 | |||||
bool _acceptAgreement = false; | |||||
/// 是否显示第三方验证码 | |||||
bool _showOtherVcode = false; | |||||
@override | |||||
void initState() { | |||||
_phoneEdController = TextEditingController(); | |||||
_passEdController = TextEditingController(); | |||||
_vcodeEdController = TextEditingController(); | |||||
_vcodeFN = FocusNode(); | |||||
_passFN = FocusNode(); | |||||
_phoneFN = FocusNode(); | |||||
super.initState(); | |||||
} | |||||
@override | |||||
void dispose() { | |||||
_phoneEdController?.dispose(); | |||||
_passEdController?.dispose(); | |||||
_vcodeEdController?.dispose(); | |||||
_phoneFN?.unfocus(); | |||||
_passFN?.unfocus(); | |||||
_vcodeFN?.unfocus(); | |||||
_phoneFN?.dispose(); | |||||
_passFN?.dispose(); | |||||
_vcodeFN?.dispose(); | |||||
super.dispose(); | |||||
} | |||||
@override | |||||
bool onVcodeClick() { | |||||
/// 获取验证码 | |||||
if(_checkPhoneNumParam(true)) { | |||||
BlocProvider.of<LoginAccountBloc>(context).add(LoginAccountGetVcodeEvent(mobile: _phoneEdController?.text?.toString()?.trim() ?? '')); | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return BlocConsumer<LoginAccountBloc, LoginAccountState>( | |||||
listener: (context, state) {}, | |||||
buildWhen: (prev, current) { | |||||
// 验证码登陆失败 | |||||
if(current is LoginAccountTypeVcodeLoginErrorState){ | |||||
Fluttertoast.showToast(msg: '登陆失败'); | |||||
return false; | |||||
} | |||||
// 验证码登陆成功 | |||||
if(current is LoginAccountTypeVcodeLoginSuccessState){ | |||||
if(current?.model?.registerInviteCodeEnable == '0'){ | |||||
Fluttertoast.showToast(msg: '登陆成功~'); | |||||
/// 缓存登陆数据 | |||||
Provider.of<UserInfoNotifier>(context, listen: false).setUserInfo(current.model); | |||||
/// 打开也买 | |||||
_openLoginSuccessPage(); | |||||
}else{ | |||||
/// 打开邀请页面 | |||||
_openInvitePage(); | |||||
} | |||||
return false; | |||||
} | |||||
// 获取验证码成功 | |||||
if (current is LoginAccountGetVcodeSuccessState){ | |||||
Fluttertoast.showToast(msg: '验证码下发成功'); | |||||
return false; | |||||
} | |||||
// 获取验证码失败 | |||||
if(current is LoginAccountGetVcodeErrorState){ | |||||
Fluttertoast.showToast(msg: '验证码获取失败~'); | |||||
return false; | |||||
} | |||||
return true; | |||||
}, | |||||
builder: (context, state) { | |||||
print('state = $state'); | |||||
if (state is LoginAccountLoadedState) { | |||||
return _getMainWidget(state.model); | |||||
} | |||||
// 返回骨架屏 | |||||
return Container(); | |||||
}, | |||||
); | |||||
} | |||||
/// 主页面 | |||||
Widget _getMainWidget(LoginModel model) { | |||||
print(model); | |||||
return Column( | |||||
children: <Widget>[ | |||||
/// appBar | |||||
_getAppBarWidget(model), | |||||
/// title | |||||
Padding(padding: const EdgeInsets.only(left: 27.5, right: 27.5, top: 40), child: _getTitleWidget(model)), | |||||
/// 手机输入框 | |||||
Padding(padding: const EdgeInsets.only(left: 27.5, right: 27.5, top: 30), child: _getPhoneWidget(model)), | |||||
/// 验证码 | |||||
Visibility(visible: _useVcode, child: Padding(padding: const EdgeInsets.only(left: 27.5, right: 27.5, top: 15), child: _getVcodeWidget(model))), | |||||
/// 密码 | |||||
Visibility(visible: !_useVcode, child: Padding(padding: const EdgeInsets.only(left: 27.5, right: 27.5, top: 15), child: _getPassInputWidget(model))), | |||||
/// 第三方验证码 | |||||
Visibility(visible: _showOtherVcode, child: Padding(padding: const EdgeInsets.only(left: 27.5, right: 27.5, top: 15), child: _getOtherVcodeInputWidget(model))), | |||||
/// 切换登陆方式tip | |||||
Padding(padding: const EdgeInsets.only(top: 15), child: _getChangeTipWidget(model)), | |||||
/// 按钮 | |||||
Padding(padding: const EdgeInsets.only(left: 27.5, right: 27.5, top: 30), child: _getSubmiBtnWidget(model)), | |||||
/// 协议 | |||||
Padding(padding: const EdgeInsets.only(top: 15), child: _getProtoclWidget(model)), | |||||
/// 底部提示tip | |||||
Visibility(visible: _useVcode, child: Expanded(child: Align(alignment: Alignment.bottomCenter, child: _getBottomTipWidget(model)))) | |||||
], | |||||
); | |||||
} | |||||
/// appBar | |||||
Widget _getAppBarWidget(LoginModel model) { | |||||
return AppBar( | |||||
backgroundColor: HexColor.fromHex('#FFFFFF'), | |||||
elevation: 0, | |||||
title: Text( | |||||
model?.mobile?.appBarTitle ?? '登录', | |||||
style: TextStyle(color: HexColor.fromHex(model?.mobile?.appBarTitleColor ?? '#333333')), | |||||
), | |||||
centerTitle: true, | |||||
leading: Icon( | |||||
Icons.arrow_back_ios, | |||||
size: 22, | |||||
color: HexColor.fromHex('#333333'), | |||||
), | |||||
); | |||||
} | |||||
/// title | |||||
Widget _getTitleWidget(LoginModel model) { | |||||
return Align( | |||||
alignment: Alignment.centerLeft, | |||||
child: Text( | |||||
model?.mobile?.title ?? '您好,欢迎登录', | |||||
style: TextStyle(color: HexColor.fromHex(model?.mobile?.titleColor ?? '#333333'), fontSize: 25), | |||||
)); | |||||
} | |||||
/// 手机输入框 | |||||
Widget _getPhoneWidget(LoginModel model) { | |||||
return _getCustomInputWidget( hint: model?.mobile?.inputMobileHintText ?? '请输入您的手机号', | |||||
controller: _phoneEdController, | |||||
focusNode: _passFN, | |||||
onChanged: _onChange, | |||||
hintColor: model?.mobile?.inputHintColor ?? '#999999', | |||||
bgColor: model?.mobile?.inputBgColor ?? '#F7F7F7', | |||||
textColor: model?.mobile?.inputTextColor ?? '#333333', | |||||
iconUrl: model?.mobile?.inputMobileIcon?? ''); | |||||
} | |||||
/// 验证码输入框 | |||||
Widget _getVcodeWidget(LoginModel model) { | |||||
return Container( | |||||
height: 42, | |||||
child: Stack( | |||||
children: <Widget>[ | |||||
_getCustomInputWidget( | |||||
controller: _vcodeEdController, | |||||
focusNode: _vcodeFN, | |||||
onChanged: _onChange, | |||||
hintColor: model?.mobile?.inputHintColor ?? '#999999', | |||||
hint: model?.mobile?.inputVcodeHintText ?? '请输入您的验证码', | |||||
bgColor: model?.mobile?.inputBgColor ??'#F7F7F7', | |||||
textColor: model?.mobile?.inputTextColor ?? '#333333', | |||||
iconUrl: model?.mobile?.inputVcodeIcon ?? ''), | |||||
Align(alignment: Alignment.centerRight, child: _getVcodeButtonWidget(model)), | |||||
], | |||||
), | |||||
); | |||||
} | |||||
/// 验证码按钮 | |||||
Widget _getVcodeButtonWidget(LoginModel model) { | |||||
return VcodeWidget( | |||||
onCallBack: this, | |||||
awaitTime: int.parse(model?.mobile?.vcodeTime ?? '60'), | |||||
btnAwaitText: '秒', | |||||
btnText: '获取验证码', | |||||
btnTextColor: model?.mobile?.btnVcodeTextColor ?? '#FFFFFF', | |||||
color: model?.mobile?.btnVcodeBgColor ?? '#FF4343', | |||||
disabledColor: model?.mobile?.btnVcodeBanBgColor ?? '#DDDDDD', | |||||
disabledTextColor: model?.mobile?.btnVcodeBanTextColor ?? '#FFFFFF' | |||||
); | |||||
} | |||||
/// 第三方验证码输入框 | |||||
Widget _getOtherVcodeInputWidget(var model) { | |||||
return Container( | |||||
width: 240, | |||||
height: 42, | |||||
alignment: Alignment.centerLeft, | |||||
child: SlideVerifyWidget( | |||||
width: 240, | |||||
), | |||||
// child: Row( | |||||
// children: <Widget>[ | |||||
// // 输入框 | |||||
// Expanded( | |||||
// child: _getCustomInputWidget(hint: '请输入右方验证码', hintColor: '#999999', textColor: '#333333', bgColor: '#F7F7F7', iconUrl: null, ) | |||||
// ), | |||||
// // 第三方验证码 | |||||
// Container( | |||||
// margin: const EdgeInsets.only(left: 5), | |||||
// width: 100, | |||||
// height: double.infinity, | |||||
// decoration: BoxDecoration( | |||||
// borderRadius: BorderRadius.circular(8), | |||||
// color: Colors.red | |||||
// ), | |||||
// ), | |||||
// | |||||
// ], | |||||
// ), | |||||
); | |||||
} | |||||
/// 密码输入框 | |||||
Widget _getPassInputWidget(LoginModel model) { | |||||
return Container( | |||||
height: 42, | |||||
child: _getCustomInputWidget( | |||||
controller: _passEdController, | |||||
focusNode: _passFN, | |||||
onChanged: _onChange, | |||||
hint: model?.mobile?.inputPassHintText ?? '请输入您的密码', | |||||
iconUrl: model?.mobile?.inputPassIcon ?? '', | |||||
hintColor: model?.mobile?.inputHintColor ?? '#999999', | |||||
textColor: model?.mobile?.inputTextColor ?? '#333333', | |||||
bgColor: model?.mobile?.inputBgColor ?? '#F7F7F7'), | |||||
); | |||||
} | |||||
/// 切换登陆方式tip | |||||
Widget _getChangeTipWidget(LoginModel model) { | |||||
return GestureDetector( | |||||
behavior: HitTestBehavior.opaque, | |||||
onTap: () => _changeLoginTypeOnClick(), | |||||
child: Text( | |||||
_useVcode ? model?.mobile?.textUsePassTip ?? '使用密码登录' : model?.mobile?.textUseVcodeTip ?? '使用验证码登陆', | |||||
style: TextStyle(fontSize: 12, | |||||
color: HexColor.fromHex( | |||||
_useVcode ? | |||||
model?.mobile?.textUsePassTipColor ?? '#999999' : | |||||
model?.mobile?.textUseVcodeTipColor ?? '#999999' | |||||
)), | |||||
), | |||||
); | |||||
} | |||||
/// 登陆按钮 | |||||
Widget _getSubmiBtnWidget(LoginModel model) { | |||||
return Material( | |||||
child: Container( | |||||
height: 52, | |||||
width: double.infinity, | |||||
color: Colors.white, | |||||
child: RaisedButton( | |||||
child: Text( | |||||
model?.mobile?.btnLoginText ?? '立即登录', | |||||
style: TextStyle(fontSize: 15), | |||||
), | |||||
textColor: HexColor.fromHex( model?.mobile?.btnLoginTextColor ?? '#FFFFFF'), | |||||
color: HexColor.fromHex( model?.mobile?.btnLoginBgColor ?? '#FF3939'), | |||||
disabledColor: HexColor.fromHex( model?.mobile?.btnLoginBanBgColor ?? '#F5F5F5'), | |||||
disabledTextColor: HexColor.fromHex( model?.mobile?.btnLoginBanTextColor ?? '#999999'), | |||||
elevation: 5, | |||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(52 / 2)), | |||||
onPressed: _canSubmit ? _submitOnClick : null, | |||||
), | |||||
), | |||||
); | |||||
} | |||||
/// 协议 | |||||
Widget _getProtoclWidget(LoginModel model) { | |||||
// return Text('同意《嗨如意用户协议》 及《营私政策》', style: TextStyle(fontSize: 11, color: HexColor.fromHex('#C0C0C0'))); | |||||
return Row( | |||||
mainAxisAlignment: MainAxisAlignment.center, | |||||
children: <Widget>[ | |||||
/// 图标 | |||||
GestureDetector( | |||||
behavior: HitTestBehavior.opaque, | |||||
onTap: () => _agreeOnClick(), | |||||
child: Padding( | |||||
padding: const EdgeInsets.all(8.0), | |||||
child: CachedNetworkImage( | |||||
imageUrl: _acceptAgreement ? model?.mobile?.protocolSelectIcon ?? '' : model?.mobile?.protocolUnselectIcon ?? '', | |||||
width: 12, | |||||
))), | |||||
/// 协议文字 | |||||
RichText( | |||||
text: TextSpan(text: '', children: model.mobile.protocol.map((item){ | |||||
return TextSpan(text: item?.text, style: TextStyle(color: HexColor.fromHex(item?.textColor), fontSize: 10),recognizer: TapGestureRecognizer()..onTap = (){ | |||||
_openAgreement(item.url); | |||||
}); | |||||
}).toList()), | |||||
) | |||||
], | |||||
); | |||||
} | |||||
/// 底部提示tip | |||||
Widget _getBottomTipWidget(LoginModel model) { | |||||
return Padding( | |||||
padding: const EdgeInsets.only(bottom: 25), | |||||
child: Text( | |||||
model?.mobile?.textBottomTip ?? '未注册过的手机将自动注册', | |||||
style: TextStyle(fontSize: 11, color: HexColor.fromHex( model?.mobile?.textBottomTipColor ?? '#999999')), | |||||
), | |||||
); | |||||
} | |||||
/// 自定义输入框 | |||||
Widget _getCustomInputWidget({String hint, String hintColor, String bgColor, String textColor, String iconUrl, TextEditingController controller, ValueChanged<String> onChanged, FocusNode focusNode}) { | |||||
var border = OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: HexColor.fromHex(bgColor), width: 0)); | |||||
return Container( | |||||
height: 42, | |||||
padding: const EdgeInsets.symmetric(horizontal: 15), | |||||
decoration: BoxDecoration( | |||||
color: HexColor.fromHex(bgColor), | |||||
borderRadius: BorderRadius.circular(8), | |||||
), | |||||
child: Row( | |||||
mainAxisAlignment: MainAxisAlignment.start, | |||||
crossAxisAlignment: CrossAxisAlignment.center, | |||||
children: <Widget>[ | |||||
CachedNetworkImage( | |||||
imageUrl: iconUrl ?? '', | |||||
width: 10, | |||||
), | |||||
Expanded( | |||||
child: TextField( | |||||
controller: controller, | |||||
focusNode: focusNode, | |||||
onChanged: onChanged, | |||||
expands: false, | |||||
style: TextStyle(color: HexColor.fromHex(textColor)), | |||||
maxLines: 1, | |||||
keyboardType: TextInputType.number, | |||||
decoration: InputDecoration( | |||||
contentPadding: EdgeInsets.only(top: 30, left: 7.5), | |||||
hintText: hint, | |||||
hintStyle: TextStyle(fontSize: 13, color: HexColor.fromHex(hintColor)), | |||||
hintMaxLines: 1, | |||||
filled: true, | |||||
fillColor: Colors.transparent, | |||||
border: border, | |||||
focusedBorder: border, | |||||
enabledBorder: border, | |||||
disabledBorder: border, | |||||
errorBorder: border, | |||||
focusedErrorBorder: border, | |||||
), | |||||
), | |||||
), | |||||
], | |||||
), | |||||
); | |||||
} | |||||
} |
@@ -0,0 +1,191 @@ | |||||
import 'package:flutter/material.dart'; | |||||
class SlideVerifyWidget extends StatefulWidget{ | |||||
/// 背景色 | |||||
final Color backgroundColor; | |||||
/// 滑动过的颜色 | |||||
final Color slideColor; | |||||
/// 边框颜色 | |||||
final Color borderColor; | |||||
final double height; | |||||
final double width; | |||||
final VoidCallback verifySuccessListener; | |||||
const SlideVerifyWidget({ | |||||
Key key, | |||||
this.backgroundColor = Colors.blueGrey, | |||||
this.slideColor = Colors.green, | |||||
this.borderColor = Colors.grey, | |||||
this.height = 44, | |||||
this.width = 240, | |||||
this.verifySuccessListener | |||||
}) : super(key: key); | |||||
@override | |||||
State<StatefulWidget> createState() { | |||||
return SlideVerifyState(); | |||||
} | |||||
} | |||||
class SlideVerifyState extends State<SlideVerifyWidget> with TickerProviderStateMixin{ | |||||
double height; | |||||
double width ; | |||||
double sliderDistance = 0; | |||||
double initial = 0.0; | |||||
/// 滑动块宽度 | |||||
double sliderWidth = 64; | |||||
/// 验证是否通过,滑动到最右方为通过 | |||||
bool verifySuccess = false; | |||||
/// 是否允许拖动 | |||||
bool enableSlide = true; | |||||
AnimationController _animationController; | |||||
Animation _curve; | |||||
@override | |||||
void initState() { | |||||
super.initState(); | |||||
this.width = widget.width; | |||||
this.height = widget.height; | |||||
_initAnimation(); | |||||
} | |||||
@override | |||||
void dispose() { | |||||
_animationController?.dispose(); | |||||
super.dispose(); | |||||
} | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return GestureDetector( | |||||
onHorizontalDragStart: (DragStartDetails details){ | |||||
if(!enableSlide){ | |||||
return; | |||||
} | |||||
initial = details.globalPosition.dx; | |||||
}, | |||||
onHorizontalDragUpdate: (DragUpdateDetails details){ | |||||
if(!enableSlide){ | |||||
return; | |||||
} | |||||
sliderDistance = details.globalPosition.dx - initial; | |||||
if(sliderDistance < 0){ | |||||
sliderDistance = 0; | |||||
} | |||||
/// 当滑动到最右边时,通知验证成功,并禁止滑动 | |||||
if(sliderDistance > width - sliderWidth){ | |||||
sliderDistance = width - sliderWidth; | |||||
enableSlide = false; | |||||
verifySuccess = true; | |||||
if(widget.verifySuccessListener != null){ | |||||
widget.verifySuccessListener(); | |||||
} | |||||
} | |||||
setState(() { | |||||
}); | |||||
}, | |||||
onHorizontalDragEnd: (DragEndDetails details){ | |||||
/// 滑动松开时,如果未达到最右边,启动回弹动画 | |||||
if(enableSlide){ | |||||
enableSlide = false; | |||||
_animationController.forward(); | |||||
} | |||||
}, | |||||
child: Container( | |||||
height: height, | |||||
width: width, | |||||
decoration: BoxDecoration( | |||||
color: widget.backgroundColor, | |||||
border: Border.all(color: widget.borderColor), | |||||
/// 圆角实现 | |||||
borderRadius: BorderRadius.all(new Radius.circular(height)) | |||||
), | |||||
child: Stack( | |||||
children: <Widget>[ | |||||
Positioned( | |||||
top: 0, | |||||
left: 0, | |||||
child: Container( | |||||
height: height - 2, | |||||
/// 当slider滑动到距左边只有两三像素距离时,已滑动背景会有一点点渲染出边框范围, | |||||
/// 因此当滑动距离小于1时,直接将宽度设置为0,解决滑动块返回左边时导致的绿色闪动,但如果是缓慢滑动到左边该问题仍没解决 | |||||
width: sliderDistance < 1? 0 : sliderDistance + sliderWidth / 2, | |||||
decoration: BoxDecoration( | |||||
color: widget.slideColor, | |||||
/// 圆角实现 | |||||
borderRadius: BorderRadius.all(new Radius.circular(height / 2)) | |||||
), | |||||
), | |||||
), | |||||
Center( | |||||
child: Text(verifySuccess?"验证成功":"请按住滑块,拖动到最右边", style: TextStyle(color: verifySuccess?Colors.white:Colors.black54, fontSize: 14),), | |||||
), | |||||
Positioned( | |||||
top: 0, | |||||
/// 此处将sliderDistance距离往左偏2是解决当滑动块滑动到最右边时遮挡外部边框 | |||||
left: sliderDistance > sliderWidth ? sliderDistance - 2 : sliderDistance, | |||||
child: Container( | |||||
width: sliderWidth, | |||||
height: height - 2 , | |||||
alignment: Alignment.center, | |||||
decoration: BoxDecoration( | |||||
color: Colors.white, | |||||
border: Border.all(color: widget.borderColor), | |||||
/// 圆角实现 | |||||
borderRadius: BorderRadius.all(new Radius.circular(height)) | |||||
), | |||||
child: Row( | |||||
mainAxisAlignment: MainAxisAlignment.center, | |||||
children: <Widget>[ | |||||
SizedBox(width: 6,), | |||||
Image.asset("assets/images/ic_safety.png", height: 24, width: 24,), | |||||
Image.asset("assets/images/ic_next_primary.png", height: 16, width: 16,), | |||||
/// 因为向右箭头有透明边距导致两个箭头间隔过大,因此将第二个箭头向左偏移,如果切图无边距则不用偏移 | |||||
Transform( | |||||
transform: Matrix4.translationValues(-8, 0, 0), | |||||
child: Image.asset("assets/images/ic_next_primary.png", height: 16, width: 16,), | |||||
), | |||||
], | |||||
), | |||||
), | |||||
) | |||||
], | |||||
), | |||||
), | |||||
); | |||||
} | |||||
/// 回弹动画 | |||||
void _initAnimation(){ | |||||
_animationController = AnimationController( | |||||
duration: const Duration(milliseconds: 300), vsync: this); | |||||
_curve = CurvedAnimation(parent: _animationController, curve: Curves.easeOut); | |||||
_curve.addListener(() { | |||||
setState(() { | |||||
sliderDistance = sliderDistance - sliderDistance * _curve.value; | |||||
if(sliderDistance <= 0){ | |||||
sliderDistance = 0; | |||||
} | |||||
}); | |||||
}); | |||||
_animationController.addStatusListener((status) { | |||||
if(status == AnimationStatus.completed){ | |||||
enableSlide = true; | |||||
_animationController.reset(); | |||||
} | |||||
}); | |||||
} | |||||
} |
@@ -0,0 +1,121 @@ | |||||
import 'dart:async'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
import 'package:flutter/material.dart'; | |||||
class VcodeWidget extends StatefulWidget { | |||||
// 按钮文字 | |||||
final String btnText; | |||||
// 按钮等待文字 | |||||
final String btnAwaitText; | |||||
// 按钮文字的颜色 | |||||
final String btnTextColor; | |||||
// 按钮的背景颜色 | |||||
final String color; | |||||
// 禁用文字的颜色 | |||||
final String disabledTextColor; | |||||
// 禁用按钮的背景颜色 | |||||
final String disabledColor; | |||||
// 等待时长 | |||||
final int awaitTime; | |||||
// 高 | |||||
final double height; | |||||
final double width; | |||||
final double border; | |||||
final OnClickListener onCallBack; | |||||
const VcodeWidget({ | |||||
this.btnText, | |||||
this.btnAwaitText, | |||||
this.btnTextColor, | |||||
this.color, | |||||
this.disabledTextColor, | |||||
this.disabledColor, | |||||
this.awaitTime, | |||||
this.onCallBack, | |||||
this.border = 8, | |||||
this.height = 30, | |||||
this.width = 82}); | |||||
@override | |||||
_VcodeWidgetState createState() => _VcodeWidgetState(); | |||||
} | |||||
class _VcodeWidgetState extends State<VcodeWidget> { | |||||
/// 是否可以获取验证码 | |||||
bool _canGetVcode = true; | |||||
/// 倒计时 | |||||
Timer _timer; | |||||
int _countdownTime = 0; | |||||
/// 获取验证码 | |||||
void _getVcodeOnClick() { | |||||
// print('获取验证码'); | |||||
if(widget.onCallBack.onVcodeClick()) { | |||||
int count = widget?.awaitTime ?? 60; | |||||
startCountdownTimer(count); | |||||
} | |||||
} | |||||
/// 开始倒计时 | |||||
void startCountdownTimer(int countTime) { | |||||
const oneSec = const Duration(seconds: 1); | |||||
setState(() { | |||||
_canGetVcode = false; | |||||
_countdownTime = countTime; | |||||
}); | |||||
var callback = (timer) => { | |||||
setState(() { | |||||
if (_countdownTime < 1) { | |||||
_canGetVcode = true; | |||||
_timer.cancel(); | |||||
_countdownTime = countTime; // 重置倒计时 | |||||
} else { | |||||
_countdownTime = _countdownTime - 1; | |||||
_canGetVcode = false; | |||||
} | |||||
}) | |||||
}; | |||||
_timer = Timer.periodic(oneSec, callback); | |||||
} | |||||
@override | |||||
void dispose() { | |||||
_timer?.cancel(); | |||||
super.dispose(); | |||||
} | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return Material( | |||||
child: Container( | |||||
alignment: Alignment.center, | |||||
height: widget?.height ?? 30, | |||||
width: widget?.width ?? 82, | |||||
margin: const EdgeInsets.only(right: 6), | |||||
child: RaisedButton( | |||||
onPressed: _canGetVcode ? ()=> _getVcodeOnClick() : null, | |||||
padding: EdgeInsets.zero, | |||||
child: Text(_canGetVcode ? ( widget?.btnText ?? '获取验证码') : '${_countdownTime}${widget?.btnAwaitText ?? '秒'}', | |||||
style: TextStyle(color: HexColor.fromHex( widget?.btnTextColor ?? '#FFFFFF'), fontSize: 11)), | |||||
disabledTextColor: HexColor.fromHex( widget?.disabledTextColor ?? '#FFFFFF'), | |||||
disabledColor: HexColor.fromHex( widget?.disabledColor ?? '#DDDDDD'), | |||||
color: HexColor.fromHex( widget?.color ?? '#FF4343'), | |||||
textColor: HexColor.fromHex( widget?.btnTextColor ?? '#FFFFFF'), | |||||
elevation: 4, | |||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(widget?.border ?? 8)), | |||||
), | |||||
), | |||||
); | |||||
} | |||||
} | |||||
abstract class OnClickListener{ | |||||
bool onVcodeClick(); | |||||
} |
@@ -1,8 +1,14 @@ | |||||
import 'dart:async'; | import 'dart:async'; | ||||
import 'package:bloc/bloc.dart'; | import 'package:bloc/bloc.dart'; | ||||
import './bloc.dart'; | import './bloc.dart'; | ||||
import 'login_repository.dart'; | |||||
import 'package:zhiying_comm/util/empty_util.dart'; | |||||
class LoginBloc extends Bloc<LoginEvent, LoginState> { | class LoginBloc extends Bloc<LoginEvent, LoginState> { | ||||
LoginRepository repository; | |||||
LoginBloc({this.repository}); | |||||
@override | @override | ||||
LoginState get initialState => InitialLoginState(); | LoginState get initialState => InitialLoginState(); | ||||
@@ -11,5 +17,22 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> { | |||||
LoginEvent event, | LoginEvent event, | ||||
) async* { | ) async* { | ||||
// TODO: Add Logic | // TODO: Add Logic | ||||
final currentState = state; | |||||
/// 初始化 | |||||
if (event is LoginInitEvent) { | |||||
yield* _mapLoginInitEventToState(event); | |||||
} | |||||
} | |||||
/// 获取页面数据 | |||||
Stream<LoginState> _mapLoginInitEventToState(LoginInitEvent event) async* { | |||||
var cache = await repository.fetchCachePageData(); | |||||
if (!EmptyUtil.isEmpty(cache)) yield LoginCacheState( model: cache); | |||||
var result = await repository.fetchNetPageData(); | |||||
if (!EmptyUtil.isEmpty(result)) | |||||
yield LoginLoadedState(model: result); | |||||
else | |||||
yield LoginErrorState(); | |||||
} | } | ||||
} | } |
@@ -1,19 +1,26 @@ | |||||
import 'package:zhiying_base_widget/pages/login_page/model/login_model.dart'; | |||||
import 'package:zhiying_comm/util/net_util.dart'; | import 'package:zhiying_comm/util/net_util.dart'; | ||||
import 'package:zhiying_comm/util/empty_util.dart'; | import 'package:zhiying_comm/util/empty_util.dart'; | ||||
import 'package:zhiying_comm/util/global_config.dart'; | |||||
class LoginRepository{ | |||||
class LoginRepository { | |||||
/// 获取页面数据 | /// 获取页面数据 | ||||
Future<Map> fetchNetPageData() async{ | |||||
Future<LoginModel> fetchNetPageData() async { | |||||
var result = await NetUtil.post('/api/v1/sign/in', method: NetMethod.GET); | |||||
if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { | |||||
LoginModel model = LoginModel.fromJson(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); | |||||
return model; | |||||
} | |||||
return null; | |||||
} | } | ||||
/// 获取缓存的页面数据 | /// 获取缓存的页面数据 | ||||
Future<Map> fetchCachePageData() async{ | |||||
Future<LoginModel> fetchCachePageData() async { | |||||
var result = await NetUtil.getRequestCachedData('/api/v1/sign/in'); | |||||
if (!EmptyUtil.isEmpty(result)) { | |||||
LoginModel model = LoginModel.fromJson(result); | |||||
return model; | |||||
} | |||||
return null; | |||||
} | } | ||||
} | |||||
} |
@@ -1,10 +1,31 @@ | |||||
import 'package:equatable/equatable.dart'; | import 'package:equatable/equatable.dart'; | ||||
import 'package:zhiying_base_widget/pages/login_page/model/login_model.dart'; | |||||
abstract class LoginState extends Equatable { | abstract class LoginState extends Equatable { | ||||
const LoginState(); | const LoginState(); | ||||
} | |||||
class InitialLoginState extends LoginState { | |||||
@override | @override | ||||
List<Object> get props => []; | List<Object> get props => []; | ||||
} | } | ||||
/// 初始化状态 | |||||
class InitialLoginState extends LoginState {} | |||||
/// 缓存数据状态 | |||||
class LoginCacheState extends LoginState { | |||||
final LoginModel model; | |||||
const LoginCacheState({this.model}); | |||||
@override | |||||
List<Object> get props => [this.model]; | |||||
} | |||||
/// 数据加载完毕状态 | |||||
class LoginLoadedState extends LoginState { | |||||
final LoginModel model; | |||||
const LoginLoadedState({this.model}); | |||||
@override | |||||
List<Object> get props => [this.model]; | |||||
} | |||||
/// 数据加载出错 | |||||
class LoginErrorState extends LoginState {} |
@@ -0,0 +1,3 @@ | |||||
export 'login_invite_bloc.dart'; | |||||
export 'login_invite_event.dart'; | |||||
export 'login_invite_state.dart'; |
@@ -0,0 +1,61 @@ | |||||
import 'dart:async'; | |||||
import 'package:bloc/bloc.dart'; | |||||
import 'package:flutter/cupertino.dart'; | |||||
import './bloc.dart'; | |||||
import 'package:zhiying_comm/util/empty_util.dart'; | |||||
import 'login_invite_repository.dart'; | |||||
class LoginInviteBloc extends Bloc<LoginInviteEvent, LoginInviteState> { | |||||
LoginInviteRepostitory repostitory; | |||||
LoginInviteBloc({@required this.repostitory}); | |||||
@override | |||||
LoginInviteState get initialState => LoginInviteInitial(); | |||||
@override | |||||
Stream<LoginInviteState> mapEventToState( | |||||
LoginInviteEvent event, | |||||
) async* { | |||||
/// 初始化 | |||||
if (event is LoginInviteInitEvent) { | |||||
yield* _mapInitEventToState(event); | |||||
} | |||||
/// 查询邀请人 | |||||
if (event is LoginInviteQueryEvent) { | |||||
yield* _mapQueryEventToState(event); | |||||
} | |||||
/// 提交 | |||||
if (event is LoginInviteSubmitEvent) { | |||||
yield* _mapSubmitEventToState(event); | |||||
} | |||||
} | |||||
/// 获取页面数据 | |||||
Stream<LoginInviteState> _mapInitEventToState(LoginInviteInitEvent event) async* { | |||||
var data = await repostitory.fetchData(); | |||||
if (!EmptyUtil.isEmpty(data)) { | |||||
yield LoginInviteLoadedState(model: data); | |||||
} | |||||
} | |||||
/// 查询邀请人 | |||||
Stream<LoginInviteState> _mapQueryEventToState(LoginInviteQueryEvent event) async* { | |||||
var data = await repostitory.fetchInviteUserInfo(event); | |||||
if (!EmptyUtil.isEmpty(data)) | |||||
yield LoginInviteQuerySuccess(model: data); | |||||
else | |||||
yield LoginInviteQueryError(); | |||||
} | |||||
/// 提交 | |||||
Stream<LoginInviteState> _mapSubmitEventToState(LoginInviteSubmitEvent event) async* { | |||||
var data = await repostitory.submitInvite(event); | |||||
if (!EmptyUtil.isEmpty(data)) | |||||
yield LoginInviteSubmitSuccess(model: data); | |||||
else | |||||
yield LoginInviteSubmitError(); | |||||
} | |||||
} |
@@ -0,0 +1,29 @@ | |||||
import 'package:equatable/equatable.dart'; | |||||
import 'package:flutter/cupertino.dart'; | |||||
abstract class LoginInviteEvent extends Equatable { | |||||
const LoginInviteEvent(); | |||||
@override | |||||
List<Object> get props => []; | |||||
} | |||||
/// 初始化 | |||||
class LoginInviteInitEvent extends LoginInviteEvent {} | |||||
/// 查询邀请人方法 | |||||
class LoginInviteQueryEvent extends LoginInviteEvent { | |||||
final String num; | |||||
const LoginInviteQueryEvent({@required this.num}); | |||||
@override | |||||
List<Object> get props => [this.num]; | |||||
} | |||||
/// 提交方法 | |||||
class LoginInviteSubmitEvent extends LoginInviteEvent { | |||||
final String num; | |||||
final String mobile; | |||||
const LoginInviteSubmitEvent({@required this.num, @required this.mobile}); | |||||
@override | |||||
List<Object> get props => [this.num, this.mobile]; | |||||
} |
@@ -0,0 +1,40 @@ | |||||
import 'package:zhiying_base_widget/pages/login_page/invite/bloc/bloc.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/invite/model/login_invite_user.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/model/login_model.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/model/login_user.dart'; | |||||
import 'package:zhiying_comm/util/empty_util.dart'; | |||||
import 'package:zhiying_comm/util/net_util.dart'; | |||||
import 'package:zhiying_comm/util/global_config.dart'; | |||||
import '../../login_util.dart'; | |||||
class LoginInviteRepostitory { | |||||
/// 获取数据,如果缓存有,则获取缓存的数据 | |||||
Future<LoginModel> fetchData() async { | |||||
var result = await LoginUtil.getLoginModel(); | |||||
if (!EmptyUtil.isEmpty(result)) { | |||||
return result; | |||||
} | |||||
return null; | |||||
} | |||||
/// 获取邀请人信息 | |||||
Future<LoginInviteUser> fetchInviteUserInfo(LoginInviteQueryEvent event) async { | |||||
var result = await NetUtil.post('/api/v1/user/invite/${event.num}'); | |||||
if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { | |||||
LoginInviteUser model = LoginInviteUser.fromJson(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); | |||||
return model; | |||||
} else { | |||||
return null; | |||||
} | |||||
} | |||||
/// 提交 | |||||
Future<LoginUser> submitInvite(LoginInviteSubmitEvent event) async { | |||||
var result = await NetUtil.post('/api/v1/user/invite/ack', params: {'mobile': event.mobile, 'superior_user_id': event.num}); | |||||
if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { | |||||
LoginUser model = LoginUser.fromJson(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); | |||||
return model; | |||||
} | |||||
return null; | |||||
} | |||||
} |
@@ -0,0 +1,75 @@ | |||||
import 'package:equatable/equatable.dart'; | |||||
import 'package:flutter/cupertino.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/invite/model/login_invite_user.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/model/login_model.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/model/login_user.dart'; | |||||
abstract class LoginInviteState extends Equatable { | |||||
const LoginInviteState(); | |||||
@override | |||||
List<Object> get props => []; | |||||
} | |||||
/// 初始化状态 | |||||
class LoginInviteInitial extends LoginInviteState {} | |||||
/// 数据加载完毕状态 | |||||
class LoginInviteLoadedState extends LoginInviteState { | |||||
final LoginModel model; | |||||
const LoginInviteLoadedState({@required this.model}); | |||||
@override | |||||
List<Object> get props => [this.model]; | |||||
} | |||||
/// 数据加载出错 | |||||
class LoginInviteErrorState extends LoginInviteState { | |||||
final String msg; | |||||
const LoginInviteErrorState({this.msg}); | |||||
@override | |||||
List<Object> get props => [this.msg]; | |||||
} | |||||
/// 邀请码成功 | |||||
class LoginInviteSubmitSuccess extends LoginInviteState { | |||||
final LoginUser model; | |||||
const LoginInviteSubmitSuccess({@required this.model}); | |||||
@override | |||||
List<Object> get props => [this.model]; | |||||
} | |||||
/// 邀请码失败 | |||||
class LoginInviteSubmitError extends LoginInviteState { | |||||
final String msg; | |||||
const LoginInviteSubmitError({this.msg}); | |||||
@override | |||||
List<Object> get props => [this.msg]; | |||||
} | |||||
/// 查询邀请人成功 | |||||
class LoginInviteQuerySuccess extends LoginInviteState { | |||||
final LoginInviteUser model; | |||||
const LoginInviteQuerySuccess({@required this.model}); | |||||
@override | |||||
List<Object> get props => [this.model]; | |||||
} | |||||
/// 查询邀请人失败 | |||||
class LoginInviteQueryError extends LoginInviteState { | |||||
final String msg; | |||||
const LoginInviteQueryError({this.msg}); | |||||
@override | |||||
List<Object> get props => [this.msg]; | |||||
} |
@@ -0,0 +1,72 @@ | |||||
import 'package:flutter/material.dart'; | |||||
import 'package:flutter_bloc/flutter_bloc.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/invite/bloc/bloc.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/invite/bloc/login_invite_repository.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/model/login_model.dart'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
import 'model/login_invite_user.dart'; | |||||
/// | |||||
/// 邀请页面 | |||||
/// | |||||
class LoginInvitePage extends StatelessWidget { | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return Scaffold( | |||||
resizeToAvoidBottomInset: false, | |||||
backgroundColor: HexColor.fromHex('#FFFFFF'), | |||||
body: BlocProvider( | |||||
create: (_) => LoginInviteBloc(repostitory: LoginInviteRepostitory())..add(LoginInviteInitEvent()), | |||||
child: LoginInvitePageContainer(), | |||||
), | |||||
); | |||||
} | |||||
} | |||||
/// | |||||
/// 邀请 | |||||
class LoginInvitePageContainer extends StatefulWidget { | |||||
@override | |||||
_LoginInvitePageContainerState createState() => _LoginInvitePageContainerState(); | |||||
} | |||||
class _LoginInvitePageContainerState extends State<LoginInvitePageContainer> { | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return BlocConsumer<LoginInviteBloc, LoginInviteState>( | |||||
listener: (context, state) {}, | |||||
buildWhen: (previous, current) { | |||||
return true; | |||||
}, | |||||
builder: (context, state) { | |||||
/// 骨架屏 | |||||
return Container(); | |||||
}, | |||||
); | |||||
} | |||||
/// 主视图 | |||||
Widget _getMainWidget(LoginModel model) { | |||||
return Column(children: <Widget>[ | |||||
],); | |||||
} | |||||
/// appBar | |||||
Widget _getAppBar(LoginModel model) {} | |||||
/// title | |||||
Widget _getTitleWidget(LoginModel model) {} | |||||
/// 邀请码输入框 | |||||
Widget _getInviteInputWidget(LoginModel model) {} | |||||
/// 邀请人信息 | |||||
Widget _getInviteInfoWidget(LoginInviteUser model) {} | |||||
/// 按钮 | |||||
Widget _getSubmitButtomWidget(LoginModel model) {} | |||||
} |
@@ -0,0 +1,21 @@ | |||||
class LoginInviteUser { | |||||
String userId; | |||||
String nickname; | |||||
String appName; | |||||
LoginInviteUser({this.userId, this.nickname, this.appName}); | |||||
LoginInviteUser.fromJson(Map<String, dynamic> json) { | |||||
userId = json['user_id']; | |||||
nickname = json['nickname']; | |||||
appName = json['app_name']; | |||||
} | |||||
Map<String, dynamic> toJson() { | |||||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
data['user_id'] = this.userId; | |||||
data['nickname'] = this.nickname; | |||||
data['app_name'] = this.appName; | |||||
return data; | |||||
} | |||||
} |
@@ -1,24 +1,30 @@ | |||||
import 'package:flutter/cupertino.dart'; | |||||
import 'package:flutter/gestures.dart'; | |||||
import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||
import 'package:flutter_bloc/flutter_bloc.dart'; | import 'package:flutter_bloc/flutter_bloc.dart'; | ||||
import 'package:zhiying_base_widget/pages/login_page/bloc/bloc.dart'; | import 'package:zhiying_base_widget/pages/login_page/bloc/bloc.dart'; | ||||
import 'package:zhiying_base_widget/pages/login_page/bloc/login_bloc.dart'; | import 'package:zhiying_base_widget/pages/login_page/bloc/login_bloc.dart'; | ||||
import 'package:zhiying_base_widget/pages/login_page/bloc/login_repository.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/model/login_model.dart'; | |||||
import 'package:zhiying_comm/util/empty_util.dart'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
import 'package:cached_network_image/cached_network_image.dart'; | |||||
/// | /// | ||||
/// 登陆页面 | /// 登陆页面 | ||||
/// | /// | ||||
class LoginPage extends StatelessWidget { | class LoginPage extends StatelessWidget { | ||||
final Map<String, dynamic> data; | final Map<String, dynamic> data; | ||||
LoginPage(this.data, {Key key}) : super(key: key); | |||||
const LoginPage(this.data, {Key key}) : super(key: key); | |||||
@override | @override | ||||
Widget build(BuildContext context) { | Widget build(BuildContext context) { | ||||
return Scaffold( | return Scaffold( | ||||
backgroundColor: HexColor.fromHex('#FFFFFF'), | |||||
body: BlocProvider<LoginBloc>( | body: BlocProvider<LoginBloc>( | ||||
create: (_) => LoginBloc()..add(LoginInitEvent()), | |||||
child: SafeArea( | |||||
child: LoginPageContainer(), | |||||
), | |||||
create: (_) => LoginBloc(repository: LoginRepository())..add(LoginInitEvent()), | |||||
child: LoginPageContainer(), | |||||
), | ), | ||||
); | ); | ||||
} | } | ||||
@@ -30,8 +36,250 @@ class LoginPageContainer extends StatefulWidget { | |||||
} | } | ||||
class _LoginPageContainerState extends State<LoginPageContainer> { | class _LoginPageContainerState extends State<LoginPageContainer> { | ||||
/// 微信登陆 | |||||
void _loginClick(String type) { | |||||
print('登陆$type'); | |||||
if(type == 'mobile'){ | |||||
Navigator.push(context, MaterialPageRoute( | |||||
builder: (_) => PageFactory.create('login_account', null) | |||||
)); | |||||
} | |||||
} | |||||
/// 第三方登陆 | |||||
void _otherLoginClick(BottomIcons model) { | |||||
print('第三方登陆${model.type}'); | |||||
} | |||||
/// 跳到用户协议 | |||||
void _jumpUserAgreement(String url) { | |||||
if(!EmptyUtil.isEmpty(url)) { | |||||
print('协议'); | |||||
} | |||||
} | |||||
/// 展开关闭其它登陆 | |||||
void _showOrColoseOtherLogin() { | |||||
setState(() { | |||||
_showOther = !_showOther; | |||||
}); | |||||
} | |||||
final _sizedHeight50 = const SizedBox(height: 50); | |||||
final _sizedHeight9 = const SizedBox(height: 9); | |||||
final _sizedHeight18 = const SizedBox(height: 18); | |||||
final _sizedHeight21 = const SizedBox(height: 21); | |||||
bool _showOther = true; | |||||
@override | @override | ||||
Widget build(BuildContext context) { | Widget build(BuildContext context) { | ||||
return Container(); | |||||
return BlocConsumer<LoginBloc, LoginState>( | |||||
listener: (context, state) { | |||||
// Fluttertoast.showToast(msg: '网络异常'); | |||||
}, | |||||
buildWhen: (prev, current) { | |||||
if (current is LoginErrorState) { | |||||
return false; | |||||
} | |||||
return true; | |||||
}, | |||||
builder: (context, state) { | |||||
if (state is LoginCacheState) { | |||||
return _getMainWidget(state.model); | |||||
} | |||||
if (state is LoginLoadedState) { | |||||
return _getMainWidget(state.model); | |||||
} | |||||
return Container(); | |||||
}, | |||||
); | |||||
} | |||||
/// 主视图 | |||||
Widget _getMainWidget(LoginModel model) { | |||||
return Column( | |||||
children: <Widget>[ | |||||
/// 头部 | |||||
_headWidget(model), | |||||
_sizedHeight50, | |||||
/// 按钮 | |||||
_buttonsWidget(model), | |||||
_sizedHeight9, | |||||
/// 协议 | |||||
_protocolWidget(model), | |||||
/// 其它登陆方式 | |||||
_otherLoginWidget(model), | |||||
], | |||||
); | |||||
} | |||||
/// 头部Widget | |||||
Widget _headWidget(LoginModel model) { | |||||
return Container( | |||||
height: 228 + MediaQuery.of(context).padding.top, | |||||
width: double.infinity, | |||||
decoration: BoxDecoration( | |||||
image: DecorationImage( | |||||
image: CachedNetworkImageProvider(model?.main?.backgroundImg ?? ''), | |||||
fit: BoxFit.fill, | |||||
), | |||||
), | |||||
child: Stack( | |||||
alignment: Alignment.center, | |||||
children: <Widget>[ | |||||
AppBar( | |||||
backgroundColor: Colors.transparent, | |||||
elevation: 0, | |||||
leading: Icon( | |||||
Icons.arrow_back_ios, | |||||
size: 22, | |||||
color: HexColor.fromHex('#333333'), | |||||
), | |||||
), | |||||
Column( | |||||
crossAxisAlignment: CrossAxisAlignment.center, | |||||
mainAxisAlignment: MainAxisAlignment.center, | |||||
children: <Widget>[ | |||||
/// logo | |||||
Container( | |||||
margin: EdgeInsets.only(bottom: 12, top: MediaQuery.of(context).padding.top), | |||||
decoration: BoxDecoration( | |||||
borderRadius: BorderRadius.circular(14), | |||||
boxShadow: [ | |||||
BoxShadow(color: Colors.grey[300], offset: Offset(0.0, 0.0), blurRadius: 10.0, spreadRadius: 1.0), | |||||
BoxShadow(color: Colors.grey[300], offset: Offset(0.0, 0.0)), | |||||
], | |||||
), | |||||
height: 80, | |||||
width: 80, | |||||
child: CachedNetworkImage(imageUrl: model?.logoImg ?? ''), | |||||
), | |||||
/// logo 名字 | |||||
CachedNetworkImage( imageUrl: model?.main?.appNameImg ?? '', width: 90,), | |||||
], | |||||
), | |||||
], | |||||
), | |||||
); | |||||
} | |||||
/// 按钮 | |||||
Widget _buttonsWidget(LoginModel model) { | |||||
return Container( | |||||
padding: const EdgeInsets.symmetric(horizontal: 27.5), | |||||
child: Column( | |||||
children: model.main.importanceLogin.map((item) { | |||||
return Padding( | |||||
padding: const EdgeInsets.only(bottom: 8), | |||||
child: _customButton( | |||||
height: 40, | |||||
text: item?.btnText, | |||||
iconUrl: item?.btnMobileIcon ?? '', | |||||
textColor: item?.btnTextColor, | |||||
bgColor: item?.btnBgColor, | |||||
borderColor: item?.btnBorderColor, | |||||
onTap: () => _loginClick(item?.type)), | |||||
); | |||||
}).toList(), | |||||
), | |||||
); | |||||
} | |||||
/// 协议 | |||||
Widget _protocolWidget(LoginModel model) { | |||||
return RichText( | |||||
text: TextSpan(text: '', children: model.main.agreements.map((item){ | |||||
return TextSpan(text: item?.text, style: TextStyle(color: HexColor.fromHex(item?.textColor), fontSize: 10),recognizer: TapGestureRecognizer()..onTap = (){ | |||||
_jumpUserAgreement(item?.url); | |||||
}); | |||||
}).toList()), | |||||
); | |||||
} | |||||
/// 其它登陆方式 | |||||
Widget _otherLoginWidget(LoginModel model) { | |||||
return Expanded( | |||||
child: Column( | |||||
mainAxisAlignment: MainAxisAlignment.end, | |||||
children: <Widget>[ | |||||
_getOtherLoginTitle(model), | |||||
_sizedHeight18, | |||||
Visibility(visible: _showOther, child: _getOtherLoginIcons(model)), | |||||
Visibility(visible: _showOther, child: _sizedHeight21), | |||||
], | |||||
), | |||||
); | |||||
} | |||||
/// 其它登陆方式的title | |||||
Widget _getOtherLoginTitle(LoginModel model) { | |||||
return GestureDetector( | |||||
behavior: HitTestBehavior.opaque, | |||||
onTap: () => _showOrColoseOtherLogin(), | |||||
child: Row( | |||||
mainAxisAlignment: MainAxisAlignment.center, | |||||
crossAxisAlignment: CrossAxisAlignment.center, | |||||
children: <Widget>[ | |||||
Text('${model?.main?.otherIconsTitle}', style: TextStyle(fontSize: 13, color: HexColor.fromHex(model?.main?.otherIconsTitleColor))), | |||||
SizedBox(width: 5.5), | |||||
RotatedBox( | |||||
quarterTurns: _showOther ? 0 : 2, | |||||
child: CachedNetworkImage(imageUrl: model?.main?.otherExpansionIcon ?? '', width: 12), | |||||
), | |||||
], | |||||
), | |||||
); | |||||
} | |||||
/// 其它登陆方式的按钮 | |||||
Widget _getOtherLoginIcons(LoginModel model) { | |||||
return Row( | |||||
mainAxisAlignment: MainAxisAlignment.center, | |||||
children: model.main.bottomIcons.map((item) { | |||||
return GestureDetector( | |||||
behavior: HitTestBehavior.opaque, | |||||
onTap: () => _otherLoginClick(item), | |||||
child: Container( | |||||
width: 30, | |||||
margin: const EdgeInsets.symmetric(horizontal: 20), | |||||
child: CachedNetworkImage(imageUrl: item?.img ?? ''), | |||||
), | |||||
); | |||||
}).toList(), | |||||
); | |||||
} | |||||
/// 自定义按钮 | |||||
Widget _customButton({double height, String text, String iconUrl, String textColor, String bgColor, String borderColor, GestureTapCallback onTap}) { | |||||
return GestureDetector( | |||||
onTap: onTap, | |||||
child: Container( | |||||
width: double.infinity, | |||||
height: height ?? 0, | |||||
decoration: BoxDecoration( | |||||
border: Border.all(color: HexColor.fromHex(borderColor), width: 0.5), | |||||
borderRadius: BorderRadius.circular(height ?? 0 / 2), | |||||
color: HexColor.fromHex(bgColor), | |||||
), | |||||
child: Row( | |||||
mainAxisAlignment: MainAxisAlignment.center, | |||||
crossAxisAlignment: CrossAxisAlignment.center, | |||||
children: <Widget>[ | |||||
// icon | |||||
CachedNetworkImage(imageUrl: iconUrl, width: 12), | |||||
SizedBox(width: 8), | |||||
// text | |||||
Text(text, style: TextStyle(color: HexColor.fromHex(textColor), fontSize: 15)) | |||||
], | |||||
), | |||||
), | |||||
); | |||||
} | } | ||||
} | } |
@@ -0,0 +1,56 @@ | |||||
import 'package:zhiying_base_widget/pages/login_page/model/login_model.dart'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
import 'package:zhiying_comm/util/shared_prefe_util.dart'; | |||||
import 'package:zhiying_comm/util/empty_util.dart'; | |||||
import 'package:zhiying_comm/util/global_config.dart'; | |||||
/// 登陆数据管理工具类 | |||||
class LoginUtil{ | |||||
static final String _URL = '/api/v1/sign/in'; | |||||
static void init() async{ | |||||
String key = _getCacheKey(); | |||||
/// 清除数据缓存 | |||||
await SharedPreferencesUtil.setNetCacheResult(key, ''); | |||||
} | |||||
/// 获取数据 | |||||
static Future<LoginModel> getLoginModel() async{ | |||||
var cache = await _fetchCachePageData(); | |||||
if (!EmptyUtil.isEmpty(cache)) return cache; | |||||
var result = await _fetchNetPageData(); | |||||
if(!EmptyUtil.isEmpty(result)) return result; | |||||
return null; | |||||
} | |||||
/// 获取缓存的key | |||||
static String _getCacheKey(){ | |||||
return NetUtil.getRequestParamsCachedKey(null, _URL); | |||||
} | |||||
/// 获取缓存的页面数据 | |||||
static Future<LoginModel> _fetchCachePageData() async { | |||||
var result = await NetUtil.getRequestCachedData(_URL); | |||||
if (!EmptyUtil.isEmpty(result)) { | |||||
LoginModel model = LoginModel.fromJson(result); | |||||
return model; | |||||
} | |||||
return null; | |||||
} | |||||
/// 获取页面数据 | |||||
static Future<LoginModel> _fetchNetPageData() async { | |||||
var result = await NetUtil.post(_URL, method: NetMethod.GET); | |||||
if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { | |||||
LoginModel model = LoginModel.fromJson(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); | |||||
return model; | |||||
} | |||||
return null; | |||||
} | |||||
} |
@@ -0,0 +1,561 @@ | |||||
/// back_img : "http://ossq.izhyin.cn/back_up.png" | |||||
/// logo_img : "http://ossq.izhyin.cn/login_logo.png" | |||||
/// main : {"app_bar_title":"","app_bar_title_color":"","app_bar_bg_color":"","app_name_img":"http://ossq.izhyin.cn/login_app_name.png","background_img":"http://ossq.izhyin.cn/login_bg.png","agreements":[{"text":"登录表示您已阅读并同意","text_color":"#C0C0C0","url":""},{"text":"《用户协议》","text_color":"#FF4242","url":"http://www.hairuyi.com/?mod=appapi&act=privacy&ctrl=index&type=protocol"},{"text":"与","text_color":"#C0C0C0","url":""},{"text":"《隐私政策》","text_color":"#FF4242","url":"http://www.hairuyi.com/?mod=appapi&act=privacy&ctrl=index&type=privacyPolicy"}],"importance_login":[{"type":"mobile","btn_text":"手机登录","btn_text_color":"#FFFFFF","btn_border_color":"#FF4242","btn_bg_color":"#FF4242","btn_mobile_icon":"http://ossq.izhyin.cn/login_mobile_icon.png"},{"type":"wechat","btn_text":"微信登录","btn_text_color":"#FF4242","btn_border_color":"#FF4242","btn_bg_color":"#FFFFFF","btn_mobile_icon":"http://ossq.izhyin.cn/login_wechat_icon.png"}],"other_icons_title":"其它方式登录","other_icons_title_color":"#333333","other_expansion_icon":"http://ossq.izhyin.cn/login_other_expansion_icon.png","bottom_icons":[{"type":"qq","img":"http://ossq.izhyin.cn/login_qq_icon.png"},{"type":"taobao","img":"http://ossq.izhyin.cn/login_taobao_icon.png"},{"type":"apple","img":"http://ossq.izhyin.cn/login_apple_icon.png"}]} | |||||
/// mobile : {"vcode_time":"60","app_bar_title":"登录","app_bar_title_color":"#333333","app_bar_bg_color":"#FFFFFF","title":"您好,欢迎登录","title_color":"#333333","input_hint_color":"#999999","input_text_color":"#333333","input_bg_color":"#F7F7F7","input_mobile_icon":"xxxx","input_mobile_hint_text":"请输入您的手机号","input_vcode_icon":"xxxx","input_vcode_hint_text":"请输入您的验证码","input_other_code_icon":"xxx","input_other_code_icon_text":"请输入右方验证码","input_pass_icon":"http://xxx","input_pass_hint_text":"请输入您的密码","btn_login_text":"立即登录","btn_login_text_color":"#FFFFFF","btn_login_bg_color":"#FF3939","btn_login_shadow_color":"#FF0000","btn_login_ban_bg_color":"#F5F5F5","btn_login_ban_text_color":"#999999","btn_vcode_text":"获取验证码","btn_vcode_bg_color":"#FF4343","btn_vcode_ban_text_color":"#FFFFFF","btn_vcode_ban_bg_color":"#DDDDDD","text_use_pass_tip":"使用密码登录","text_use_vcode_tip":"使用验证码登录","text_use_pass_tip_color":"#999999","text_use_vcode_tip_color":"#999999","text_bottom_tip":"未注册过的手机将自动注册","text_bottom_tip_color":"#999999","protocol_select_icon":"http://xxxxx","protocol_unselect_icon":"http://xxxx","protocol":[{"text":"同意","text_color":"#C0C0C0","url":""},{"text":"《智莺生活用户协议》","text_color":"#FF3939","url":"http://www.hairuyi.com/?mod=appapi&act=privacy&ctrl=index&type=protocol"},{"text":"及","text_color":"#C0C0C0","url":""},{"text":"《隐私政策》","text_color":"#FF3939","url":"http://www.hairuyi.com/?mod=appapi&act=privacy&ctrl=index&type=privacyPolicy"}]} | |||||
/// invite : {"app_bar_title":"登陆","app_bar_title_color":"#333333","app_bar_bg_color":"#FFFFFF","title":"输入邀请码","title_color":"#333333","input_hint_color":"#999999","input_bg_color":"#F7F7F7","btn_ban_bg_color":"#F5F5F5","btn_ban_text_color":"#999999","input_invite_icon":"http://xxxx","input_invite_text":"请输入邀请码","input_invite_text_color":"#999999","btn_submit_text":"进入智莺生活","btn_submit_text_color":"#FFFFFF","btn_submit_bg_color":"#FF3939","btn_submit_shadow_color":"#FF0000"} | |||||
/// quick : {"app_bar_title":"","app_bar_bg_color":"","app_bar_title_color":"#FFFFFF","account_color":"#333333","text_tip":"切换账号","text_tip_color":"#FF3939","btn_submit_text":"立即登录","btn_submit_text_color":"#FFFFFF","btn_submit_bg_color":"#FF3939","btn_submit_shadow_color":"#FF0000","protocol_select_icon":"http://xxxx","protocol_unselect_icon":"http://xxxxx","text_bottom_tip":"中国电信提供认证服务","text_bottom_tip_color":"#C0C0C0","protocol":[{"text":"同意","text_color":"#C0C0C0","url":""},{"text":"《智莺生活用户协议》","text_color":"#FF3939","url":"http://www.hairuyi.com/?mod=appapi&act=privacy&ctrl=index&type=protocol"},{"text":"及","text_color":"#C0C0C0","url":""},{"text":"《隐私政策》","text_color":"#FF3939","url":"http://www.hairuyi.com/?mod=appapi&act=privacy&ctrl=index&type=privacyPolicy"}]} | |||||
/// flash_login_enable : "1" | |||||
class LoginModel { | |||||
String backImg; | |||||
String logoImg; | |||||
Main main; | |||||
Mobile mobile; | |||||
Invite invite; | |||||
Quick quick; | |||||
String flashLoginEnable; | |||||
LoginModel( | |||||
{this.backImg, | |||||
this.logoImg, | |||||
this.main, | |||||
this.mobile, | |||||
this.invite, | |||||
this.quick, | |||||
this.flashLoginEnable}); | |||||
LoginModel.fromJson(Map<String, dynamic> json) { | |||||
backImg = json['back_img']; | |||||
logoImg = json['logo_img']; | |||||
main = json['main'] != null ? new Main.fromJson(json['main']) : null; | |||||
mobile = | |||||
json['mobile'] != null ? new Mobile.fromJson(json['mobile']) : null; | |||||
invite = | |||||
json['invite'] != null ? new Invite.fromJson(json['invite']) : null; | |||||
quick = json['quick'] != null ? new Quick.fromJson(json['quick']) : null; | |||||
flashLoginEnable = json['flash_login_enable']; | |||||
} | |||||
Map<String, dynamic> toJson() { | |||||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
data['back_img'] = this.backImg; | |||||
data['logo_img'] = this.logoImg; | |||||
if (this.main != null) { | |||||
data['main'] = this.main.toJson(); | |||||
} | |||||
if (this.mobile != null) { | |||||
data['mobile'] = this.mobile.toJson(); | |||||
} | |||||
if (this.invite != null) { | |||||
data['invite'] = this.invite.toJson(); | |||||
} | |||||
if (this.quick != null) { | |||||
data['quick'] = this.quick.toJson(); | |||||
} | |||||
data['flash_login_enable'] = this.flashLoginEnable; | |||||
return data; | |||||
} | |||||
} | |||||
class Main { | |||||
String appBarTitle; | |||||
String appBarTitleColor; | |||||
String appBarBgColor; | |||||
String appNameImg; | |||||
String backgroundImg; | |||||
List<Agreements> agreements; | |||||
List<ImportanceLogin> importanceLogin; | |||||
String otherIconsTitle; | |||||
String otherIconsTitleColor; | |||||
String otherExpansionIcon; | |||||
List<BottomIcons> bottomIcons; | |||||
Main( | |||||
{this.appBarTitle, | |||||
this.appBarTitleColor, | |||||
this.appBarBgColor, | |||||
this.appNameImg, | |||||
this.backgroundImg, | |||||
this.agreements, | |||||
this.importanceLogin, | |||||
this.otherIconsTitle, | |||||
this.otherIconsTitleColor, | |||||
this.otherExpansionIcon, | |||||
this.bottomIcons}); | |||||
Main.fromJson(Map<String, dynamic> json) { | |||||
appBarTitle = json['app_bar_title']; | |||||
appBarTitleColor = json['app_bar_title_color']; | |||||
appBarBgColor = json['app_bar_bg_color']; | |||||
appNameImg = json['app_name_img']; | |||||
backgroundImg = json['background_img']; | |||||
if (json['agreements'] != null) { | |||||
agreements = new List<Agreements>(); | |||||
json['agreements'].forEach((v) { | |||||
agreements.add(new Agreements.fromJson(v)); | |||||
}); | |||||
} | |||||
if (json['importance_login'] != null) { | |||||
importanceLogin = new List<ImportanceLogin>(); | |||||
json['importance_login'].forEach((v) { | |||||
importanceLogin.add(new ImportanceLogin.fromJson(v)); | |||||
}); | |||||
} | |||||
otherIconsTitle = json['other_icons_title']; | |||||
otherIconsTitleColor = json['other_icons_title_color']; | |||||
otherExpansionIcon = json['other_expansion_icon']; | |||||
if (json['bottom_icons'] != null) { | |||||
bottomIcons = new List<BottomIcons>(); | |||||
json['bottom_icons'].forEach((v) { | |||||
bottomIcons.add(new BottomIcons.fromJson(v)); | |||||
}); | |||||
} | |||||
} | |||||
Map<String, dynamic> toJson() { | |||||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
data['app_bar_title'] = this.appBarTitle; | |||||
data['app_bar_title_color'] = this.appBarTitleColor; | |||||
data['app_bar_bg_color'] = this.appBarBgColor; | |||||
data['app_name_img'] = this.appNameImg; | |||||
data['background_img'] = this.backgroundImg; | |||||
if (this.agreements != null) { | |||||
data['agreements'] = this.agreements.map((v) => v.toJson()).toList(); | |||||
} | |||||
if (this.importanceLogin != null) { | |||||
data['importance_login'] = | |||||
this.importanceLogin.map((v) => v.toJson()).toList(); | |||||
} | |||||
data['other_icons_title'] = this.otherIconsTitle; | |||||
data['other_icons_title_color'] = this.otherIconsTitleColor; | |||||
data['other_expansion_icon'] = this.otherExpansionIcon; | |||||
if (this.bottomIcons != null) { | |||||
data['bottom_icons'] = this.bottomIcons.map((v) => v.toJson()).toList(); | |||||
} | |||||
return data; | |||||
} | |||||
} | |||||
class Agreements { | |||||
String text; | |||||
String textColor; | |||||
String url; | |||||
Agreements({this.text, this.textColor, this.url}); | |||||
Agreements.fromJson(Map<String, dynamic> json) { | |||||
text = json['text']; | |||||
textColor = json['text_color']; | |||||
url = json['url']; | |||||
} | |||||
Map<String, dynamic> toJson() { | |||||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
data['text'] = this.text; | |||||
data['text_color'] = this.textColor; | |||||
data['url'] = this.url; | |||||
return data; | |||||
} | |||||
} | |||||
class ImportanceLogin { | |||||
String type; | |||||
String btnText; | |||||
String btnTextColor; | |||||
String btnBorderColor; | |||||
String btnBgColor; | |||||
String btnMobileIcon; | |||||
ImportanceLogin( | |||||
{this.type, | |||||
this.btnText, | |||||
this.btnTextColor, | |||||
this.btnBorderColor, | |||||
this.btnBgColor, | |||||
this.btnMobileIcon}); | |||||
ImportanceLogin.fromJson(Map<String, dynamic> json) { | |||||
type = json['type']; | |||||
btnText = json['btn_text']; | |||||
btnTextColor = json['btn_text_color']; | |||||
btnBorderColor = json['btn_border_color']; | |||||
btnBgColor = json['btn_bg_color']; | |||||
btnMobileIcon = json['btn_mobile_icon']; | |||||
} | |||||
Map<String, dynamic> toJson() { | |||||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
data['type'] = this.type; | |||||
data['btn_text'] = this.btnText; | |||||
data['btn_text_color'] = this.btnTextColor; | |||||
data['btn_border_color'] = this.btnBorderColor; | |||||
data['btn_bg_color'] = this.btnBgColor; | |||||
data['btn_mobile_icon'] = this.btnMobileIcon; | |||||
return data; | |||||
} | |||||
} | |||||
class BottomIcons { | |||||
String type; | |||||
String img; | |||||
BottomIcons({this.type, this.img}); | |||||
BottomIcons.fromJson(Map<String, dynamic> json) { | |||||
type = json['type']; | |||||
img = json['img']; | |||||
} | |||||
Map<String, dynamic> toJson() { | |||||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
data['type'] = this.type; | |||||
data['img'] = this.img; | |||||
return data; | |||||
} | |||||
} | |||||
class Mobile { | |||||
String vcodeTime; | |||||
String appBarTitle; | |||||
String appBarTitleColor; | |||||
String appBarBgColor; | |||||
String title; | |||||
String titleColor; | |||||
String inputHintColor; | |||||
String inputTextColor; | |||||
String inputBgColor; | |||||
String inputMobileIcon; | |||||
String inputMobileHintText; | |||||
String inputVcodeIcon; | |||||
String inputVcodeHintText; | |||||
String inputOtherCodeIcon; | |||||
String inputOtherCodeIconText; | |||||
String inputPassIcon; | |||||
String inputPassHintText; | |||||
String btnLoginText; | |||||
String btnLoginTextColor; | |||||
String btnLoginBgColor; | |||||
String btnLoginShadowColor; | |||||
String btnLoginBanBgColor; | |||||
String btnLoginBanTextColor; | |||||
String btnVcodeText; | |||||
String btnVcodeTextColor; | |||||
String btnVcodeBgColor; | |||||
String btnVcodeBanTextColor; | |||||
String btnVcodeBanBgColor; | |||||
String textUsePassTip; | |||||
String textUseVcodeTip; | |||||
String textUsePassTipColor; | |||||
String textUseVcodeTipColor; | |||||
String textBottomTip; | |||||
String textBottomTipColor; | |||||
String protocolSelectIcon; | |||||
String protocolUnselectIcon; | |||||
List<Protocol> protocol; | |||||
Mobile( | |||||
{this.vcodeTime, | |||||
this.appBarTitle, | |||||
this.appBarTitleColor, | |||||
this.appBarBgColor, | |||||
this.title, | |||||
this.titleColor, | |||||
this.inputHintColor, | |||||
this.inputTextColor, | |||||
this.inputBgColor, | |||||
this.inputMobileIcon, | |||||
this.inputMobileHintText, | |||||
this.inputVcodeIcon, | |||||
this.inputVcodeHintText, | |||||
this.inputOtherCodeIcon, | |||||
this.inputOtherCodeIconText, | |||||
this.inputPassIcon, | |||||
this.inputPassHintText, | |||||
this.btnLoginText, | |||||
this.btnLoginTextColor, | |||||
this.btnLoginBgColor, | |||||
this.btnLoginShadowColor, | |||||
this.btnLoginBanBgColor, | |||||
this.btnLoginBanTextColor, | |||||
this.btnVcodeText, | |||||
this.btnVcodeTextColor, | |||||
this.btnVcodeBgColor, | |||||
this.btnVcodeBanTextColor, | |||||
this.btnVcodeBanBgColor, | |||||
this.textUsePassTip, | |||||
this.textUseVcodeTip, | |||||
this.textUsePassTipColor, | |||||
this.textUseVcodeTipColor, | |||||
this.textBottomTip, | |||||
this.textBottomTipColor, | |||||
this.protocolSelectIcon, | |||||
this.protocolUnselectIcon, | |||||
this.protocol}); | |||||
Mobile.fromJson(Map<String, dynamic> json) { | |||||
vcodeTime = json['vcode_time']; | |||||
appBarTitle = json['app_bar_title']; | |||||
appBarTitleColor = json['app_bar_title_color']; | |||||
appBarBgColor = json['app_bar_bg_color']; | |||||
title = json['title']; | |||||
titleColor = json['title_color']; | |||||
inputHintColor = json['input_hint_color']; | |||||
inputTextColor = json['input_text_color']; | |||||
inputBgColor = json['input_bg_color']; | |||||
inputMobileIcon = json['input_mobile_icon']; | |||||
inputMobileHintText = json['input_mobile_hint_text']; | |||||
inputVcodeIcon = json['input_vcode_icon']; | |||||
inputVcodeHintText = json['input_vcode_hint_text']; | |||||
inputOtherCodeIcon = json['input_other_code_icon']; | |||||
inputOtherCodeIconText = json['input_other_code_icon_text']; | |||||
inputPassIcon = json['input_pass_icon']; | |||||
inputPassHintText = json['input_pass_hint_text']; | |||||
btnLoginText = json['btn_login_text']; | |||||
btnLoginTextColor = json['btn_login_text_color']; | |||||
btnLoginBgColor = json['btn_login_bg_color']; | |||||
btnLoginShadowColor = json['btn_login_shadow_color']; | |||||
btnLoginBanBgColor = json['btn_login_ban_bg_color']; | |||||
btnLoginBanTextColor = json['btn_login_ban_text_color']; | |||||
btnVcodeText = json['btn_vcode_text']; | |||||
btnVcodeTextColor = json['btnVcodeTextColor']; | |||||
btnVcodeBgColor = json['btn_vcode_bg_color']; | |||||
btnVcodeBanTextColor = json['btn_vcode_ban_text_color']; | |||||
btnVcodeBanBgColor = json['btn_vcode_ban_bg_color']; | |||||
textUsePassTip = json['text_use_pass_tip']; | |||||
textUseVcodeTip = json['text_use_vcode_tip']; | |||||
textUsePassTipColor = json['text_use_pass_tip_color']; | |||||
textUseVcodeTipColor = json['text_use_vcode_tip_color']; | |||||
textBottomTip = json['text_bottom_tip']; | |||||
textBottomTipColor = json['text_bottom_tip_color']; | |||||
protocolSelectIcon = json['protocol_select_icon']; | |||||
protocolUnselectIcon = json['protocol_unselect_icon']; | |||||
if (json['protocol'] != null) { | |||||
protocol = new List<Protocol>(); | |||||
json['protocol'].forEach((v) { | |||||
protocol.add(new Protocol.fromJson(v)); | |||||
}); | |||||
} | |||||
} | |||||
Map<String, dynamic> toJson() { | |||||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
data['vcode_time'] = this.vcodeTime; | |||||
data['app_bar_title'] = this.appBarTitle; | |||||
data['app_bar_title_color'] = this.appBarTitleColor; | |||||
data['app_bar_bg_color'] = this.appBarBgColor; | |||||
data['title'] = this.title; | |||||
data['title_color'] = this.titleColor; | |||||
data['input_hint_color'] = this.inputHintColor; | |||||
data['input_text_color'] = this.inputTextColor; | |||||
data['input_bg_color'] = this.inputBgColor; | |||||
data['input_mobile_icon'] = this.inputMobileIcon; | |||||
data['input_mobile_hint_text'] = this.inputMobileHintText; | |||||
data['input_vcode_icon'] = this.inputVcodeIcon; | |||||
data['input_vcode_hint_text'] = this.inputVcodeHintText; | |||||
data['input_other_code_icon'] = this.inputOtherCodeIcon; | |||||
data['input_other_code_icon_text'] = this.inputOtherCodeIconText; | |||||
data['input_pass_icon'] = this.inputPassIcon; | |||||
data['input_pass_hint_text'] = this.inputPassHintText; | |||||
data['btn_login_text'] = this.btnLoginText; | |||||
data['btn_login_text_color'] = this.btnLoginTextColor; | |||||
data['btn_login_bg_color'] = this.btnLoginBgColor; | |||||
data['btn_login_shadow_color'] = this.btnLoginShadowColor; | |||||
data['btn_login_ban_bg_color'] = this.btnLoginBanBgColor; | |||||
data['btn_login_ban_text_color'] = this.btnLoginBanTextColor; | |||||
data['btn_vcode_text'] = this.btnVcodeText; | |||||
data['btnVcodeTextColor'] = this.btnVcodeTextColor; | |||||
data['btn_vcode_bg_color'] = this.btnVcodeBgColor; | |||||
data['btn_vcode_ban_text_color'] = this.btnVcodeBanTextColor; | |||||
data['btn_vcode_ban_bg_color'] = this.btnVcodeBanBgColor; | |||||
data['text_use_pass_tip'] = this.textUsePassTip; | |||||
data['text_use_vcode_tip'] = this.textUseVcodeTip; | |||||
data['text_use_pass_tip_color'] = this.textUsePassTipColor; | |||||
data['text_use_vcode_tip_color'] = this.textUseVcodeTipColor; | |||||
data['text_bottom_tip'] = this.textBottomTip; | |||||
data['text_bottom_tip_color'] = this.textBottomTipColor; | |||||
data['protocol_select_icon'] = this.protocolSelectIcon; | |||||
data['protocol_unselect_icon'] = this.protocolUnselectIcon; | |||||
if (this.protocol != null) { | |||||
data['protocol'] = this.protocol.map((v) => v.toJson()).toList(); | |||||
} | |||||
return data; | |||||
} | |||||
} | |||||
class Invite { | |||||
String appBarTitle; | |||||
String appBarTitleColor; | |||||
String appBarBgColor; | |||||
String title; | |||||
String titleColor; | |||||
String inputHintColor; | |||||
String inputBgColor; | |||||
String btnBanBgColor; | |||||
String btnBanTextColor; | |||||
String inputInviteIcon; | |||||
String inputInviteText; | |||||
String inputInviteTextColor; | |||||
String btnSubmitText; | |||||
String btnSubmitTextColor; | |||||
String btnSubmitBgColor; | |||||
String btnSubmitShadowColor; | |||||
Invite( | |||||
{this.appBarTitle, | |||||
this.appBarTitleColor, | |||||
this.appBarBgColor, | |||||
this.title, | |||||
this.titleColor, | |||||
this.inputHintColor, | |||||
this.inputBgColor, | |||||
this.btnBanBgColor, | |||||
this.btnBanTextColor, | |||||
this.inputInviteIcon, | |||||
this.inputInviteText, | |||||
this.inputInviteTextColor, | |||||
this.btnSubmitText, | |||||
this.btnSubmitTextColor, | |||||
this.btnSubmitBgColor, | |||||
this.btnSubmitShadowColor}); | |||||
Invite.fromJson(Map<String, dynamic> json) { | |||||
appBarTitle = json['app_bar_title']; | |||||
appBarTitleColor = json['app_bar_title_color']; | |||||
appBarBgColor = json['app_bar_bg_color']; | |||||
title = json['title']; | |||||
titleColor = json['title_color']; | |||||
inputHintColor = json['input_hint_color']; | |||||
inputBgColor = json['input_bg_color']; | |||||
btnBanBgColor = json['btn_ban_bg_color']; | |||||
btnBanTextColor = json['btn_ban_text_color']; | |||||
inputInviteIcon = json['input_invite_icon']; | |||||
inputInviteText = json['input_invite_text']; | |||||
inputInviteTextColor = json['input_invite_text_color']; | |||||
btnSubmitText = json['btn_submit_text']; | |||||
btnSubmitTextColor = json['btn_submit_text_color']; | |||||
btnSubmitBgColor = json['btn_submit_bg_color']; | |||||
btnSubmitShadowColor = json['btn_submit_shadow_color']; | |||||
} | |||||
Map<String, dynamic> toJson() { | |||||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
data['app_bar_title'] = this.appBarTitle; | |||||
data['app_bar_title_color'] = this.appBarTitleColor; | |||||
data['app_bar_bg_color'] = this.appBarBgColor; | |||||
data['title'] = this.title; | |||||
data['title_color'] = this.titleColor; | |||||
data['input_hint_color'] = this.inputHintColor; | |||||
data['input_bg_color'] = this.inputBgColor; | |||||
data['btn_ban_bg_color'] = this.btnBanBgColor; | |||||
data['btn_ban_text_color'] = this.btnBanTextColor; | |||||
data['input_invite_icon'] = this.inputInviteIcon; | |||||
data['input_invite_text'] = this.inputInviteText; | |||||
data['input_invite_text_color'] = this.inputInviteTextColor; | |||||
data['btn_submit_text'] = this.btnSubmitText; | |||||
data['btn_submit_text_color'] = this.btnSubmitTextColor; | |||||
data['btn_submit_bg_color'] = this.btnSubmitBgColor; | |||||
data['btn_submit_shadow_color'] = this.btnSubmitShadowColor; | |||||
return data; | |||||
} | |||||
} | |||||
class Quick { | |||||
String appBarTitle; | |||||
String appBarBgColor; | |||||
String appBarTitleColor; | |||||
String accountColor; | |||||
String textTip; | |||||
String textTipColor; | |||||
String btnSubmitText; | |||||
String btnSubmitTextColor; | |||||
String btnSubmitBgColor; | |||||
String btnSubmitShadowColor; | |||||
String protocolSelectIcon; | |||||
String protocolUnselectIcon; | |||||
String textBottomTip; | |||||
String textBottomTipColor; | |||||
List<Protocol> protocol; | |||||
Quick( | |||||
{this.appBarTitle, | |||||
this.appBarBgColor, | |||||
this.appBarTitleColor, | |||||
this.accountColor, | |||||
this.textTip, | |||||
this.textTipColor, | |||||
this.btnSubmitText, | |||||
this.btnSubmitTextColor, | |||||
this.btnSubmitBgColor, | |||||
this.btnSubmitShadowColor, | |||||
this.protocolSelectIcon, | |||||
this.protocolUnselectIcon, | |||||
this.textBottomTip, | |||||
this.textBottomTipColor, | |||||
this.protocol}); | |||||
Quick.fromJson(Map<String, dynamic> json) { | |||||
appBarTitle = json['app_bar_title']; | |||||
appBarBgColor = json['app_bar_bg_color']; | |||||
appBarTitleColor = json['app_bar_title_color']; | |||||
accountColor = json['account_color']; | |||||
textTip = json['text_tip']; | |||||
textTipColor = json['text_tip_color']; | |||||
btnSubmitText = json['btn_submit_text']; | |||||
btnSubmitTextColor = json['btn_submit_text_color']; | |||||
btnSubmitBgColor = json['btn_submit_bg_color']; | |||||
btnSubmitShadowColor = json['btn_submit_shadow_color']; | |||||
protocolSelectIcon = json['protocol_select_icon']; | |||||
protocolUnselectIcon = json['protocol_unselect_icon']; | |||||
textBottomTip = json['text_bottom_tip']; | |||||
textBottomTipColor = json['text_bottom_tip_color']; | |||||
if (json['protocol'] != null) { | |||||
protocol = new List<Protocol>(); | |||||
json['protocol'].forEach((v) { | |||||
protocol.add(new Protocol.fromJson(v)); | |||||
}); | |||||
} | |||||
} | |||||
Map<String, dynamic> toJson() { | |||||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
data['app_bar_title'] = this.appBarTitle; | |||||
data['app_bar_bg_color'] = this.appBarBgColor; | |||||
data['app_bar_title_color'] = this.appBarTitleColor; | |||||
data['account_color'] = this.accountColor; | |||||
data['text_tip'] = this.textTip; | |||||
data['text_tip_color'] = this.textTipColor; | |||||
data['btn_submit_text'] = this.btnSubmitText; | |||||
data['btn_submit_text_color'] = this.btnSubmitTextColor; | |||||
data['btn_submit_bg_color'] = this.btnSubmitBgColor; | |||||
data['btn_submit_shadow_color'] = this.btnSubmitShadowColor; | |||||
data['protocol_select_icon'] = this.protocolSelectIcon; | |||||
data['protocol_unselect_icon'] = this.protocolUnselectIcon; | |||||
data['text_bottom_tip'] = this.textBottomTip; | |||||
data['text_bottom_tip_color'] = this.textBottomTipColor; | |||||
if (this.protocol != null) { | |||||
data['protocol'] = this.protocol.map((v) => v.toJson()).toList(); | |||||
} | |||||
return data; | |||||
} | |||||
} | |||||
class Protocol { | |||||
String text; | |||||
String textColor; | |||||
String url; | |||||
Protocol({this.text, this.textColor, this.url}); | |||||
Protocol.fromJson(Map<String, dynamic> json) { | |||||
text = json['text']; | |||||
textColor = json['text_color']; | |||||
url = json['url']; | |||||
} | |||||
Map<String, dynamic> toJson() { | |||||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
data['text'] = this.text; | |||||
data['text_color'] = this.textColor; | |||||
data['url'] = this.url; | |||||
return data; | |||||
} | |||||
} |
@@ -0,0 +1,43 @@ | |||||
/// token : "6209c60befba0f34c3ade079409337713ddab9c5" | |||||
/// user_id : "55" | |||||
/// username : "1328603811x" | |||||
/// perms : ["app_zhiyingshenghuo_access"] | |||||
/// register_invite_code_enable : "0" | |||||
class LoginUser { | |||||
String token; | |||||
String userId; | |||||
String username; | |||||
List<String> perms; | |||||
String registerInviteCodeEnable; | |||||
LoginUser( | |||||
{this.token, | |||||
this.userId, | |||||
this.username, | |||||
this.perms, | |||||
this.registerInviteCodeEnable}); | |||||
LoginUser.fromJson(Map<String, dynamic> json) { | |||||
token = json['token']; | |||||
userId = json['user_id']; | |||||
username = json['username']; | |||||
perms = json['perms'].cast<String>(); | |||||
registerInviteCodeEnable = json['register_invite_code_enable']; | |||||
} | |||||
Map<String, dynamic> toJson() { | |||||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
data['token'] = this.token; | |||||
data['user_id'] = this.userId; | |||||
data['username'] = this.username; | |||||
data['perms'] = this.perms; | |||||
data['register_invite_code_enable'] = this.registerInviteCodeEnable; | |||||
return data; | |||||
} | |||||
@override | |||||
String toString() { | |||||
return 'LoginUser{token: $token, userId: $userId, username: $username, perms: $perms, registerInviteCodeEnable: $registerInviteCodeEnable}'; | |||||
} | |||||
} |
@@ -0,0 +1,35 @@ | |||||
import 'package:flutter/material.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/model/login_user.dart'; | |||||
import 'package:zhiying_comm/util/empty_util.dart'; | |||||
/// | |||||
/// 用户信息 | |||||
/// | |||||
class UserInfoNotifier with ChangeNotifier { | |||||
LoginUser userInfo; | |||||
/// 更新用户数据 | |||||
void setUserInfo(LoginUser loginUser) { | |||||
print('${loginUser.toString()}'); | |||||
this.userInfo = loginUser; | |||||
// 缓存数据 TODO | |||||
} | |||||
/// 退出登陆 | |||||
void unLogin(){ | |||||
this.userInfo = null; | |||||
// 清除缓存数据 TODO | |||||
} | |||||
/// 获取登陆数据 | |||||
LoginUser getLoginUserInfo(){ | |||||
if(null != userInfo){ | |||||
return userInfo; | |||||
} | |||||
// TODO 需要读取缓存的数据? | |||||
return null; | |||||
} | |||||
} |
@@ -0,0 +1,171 @@ | |||||
import 'package:flutter/material.dart'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
import 'package:cached_network_image/cached_network_image.dart'; | |||||
/// | |||||
/// 快速登陆 | |||||
/// | |||||
class LoginQuickPage extends StatelessWidget { | |||||
final Map<String, dynamic> model; | |||||
const LoginQuickPage(this.model, {Key key}) : super(key: key); | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return Scaffold( | |||||
backgroundColor: Colors.white, | |||||
body: LoginQuickContainerPage(), | |||||
); | |||||
} | |||||
} | |||||
class LoginQuickContainerPage extends StatefulWidget { | |||||
@override | |||||
_LoginQuickContainerPageState createState() => _LoginQuickContainerPageState(); | |||||
} | |||||
class _LoginQuickContainerPageState extends State<LoginQuickContainerPage> { | |||||
/// 登陆事件 | |||||
void _submitOnClick() { | |||||
print('点击登陆'); | |||||
} | |||||
/// 切换账号 | |||||
void _changeAccount() { | |||||
print('切换账号'); | |||||
} | |||||
/// 同意or取消用户协议 | |||||
void _agree(){ | |||||
} | |||||
final _sizedBoxHeight30 = const SizedBox(height: 30); | |||||
final _sizedBoxHeight35 = const SizedBox(height: 35); | |||||
final _sizedBoxHeight20 = const SizedBox(height: 20); | |||||
final _sizedBoxHeight16 = const SizedBox(height: 16); | |||||
final _sizedBoxHeight28 = const SizedBox(height: 28); | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return Column( | |||||
mainAxisAlignment: MainAxisAlignment.center, | |||||
children: <Widget>[ | |||||
/// appbar | |||||
_getAppBarWidget(), | |||||
_sizedBoxHeight30, | |||||
/// logo | |||||
_getLogoWidget(null), | |||||
_sizedBoxHeight35, | |||||
/// 账号 | |||||
_getAccountWidget(null), | |||||
// _sizedBoxHeight20, | |||||
/// 切换账号提示 | |||||
_changeAccountTipWidget(null), | |||||
// _sizedBoxHeight20, | |||||
/// 登陆按钮 | |||||
_submitButton(null), | |||||
_sizedBoxHeight16, | |||||
/// 协议 | |||||
_protocolWidget(null), | |||||
/// 底部tip | |||||
Expanded( | |||||
child: Align( | |||||
child: _bottomTipWidget(null), | |||||
alignment: Alignment.bottomCenter, | |||||
), | |||||
) | |||||
], | |||||
); | |||||
} | |||||
/// 底部提示 | |||||
Widget _bottomTipWidget(var model) { | |||||
return Container( | |||||
margin: const EdgeInsets.only(bottom: 28), child: Text('中国电信提供认证服务', style: TextStyle(fontSize: 11, color: HexColor.fromHex('#C0C0C0')))); | |||||
} | |||||
/// 协议 | |||||
Widget _protocolWidget(var model) { | |||||
return Container( | |||||
child: Text('《嗨如意用户协议》', style: TextStyle(fontSize: 11, color: HexColor.fromHex('#C0C0C0'))), | |||||
); | |||||
} | |||||
/// 立即登陆按钮 | |||||
Widget _submitButton(var model) { | |||||
return Material( | |||||
child: Container( | |||||
height: 52, | |||||
width: double.infinity, | |||||
color: Colors.white, | |||||
padding: const EdgeInsets.symmetric(horizontal: 27.5), | |||||
child: RaisedButton( | |||||
child: Text( | |||||
'立即登录', | |||||
style: TextStyle(fontSize: 15), | |||||
), | |||||
textColor: HexColor.fromHex('#FFFFFF'), | |||||
color: HexColor.fromHex('#FF3939'), | |||||
disabledColor: HexColor.fromHex('#F5F5F5'), | |||||
disabledTextColor: HexColor.fromHex('#999999'), | |||||
elevation: 5, | |||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(52 / 2)), | |||||
onPressed: _submitOnClick, | |||||
), | |||||
), | |||||
); | |||||
} | |||||
/// 切换账号提示 | |||||
Widget _changeAccountTipWidget(var model) { | |||||
return GestureDetector( | |||||
behavior: HitTestBehavior.opaque, | |||||
onTap: () => _changeAccount(), child: Container( margin: const EdgeInsets.symmetric(vertical: 20), child: Text('切换账号', style: TextStyle(fontSize: 13, color: HexColor.fromHex('#FF3939'))))); | |||||
} | |||||
/// 账号 | |||||
Widget _getAccountWidget(var model) { | |||||
return Text('158****3158', style: TextStyle(fontSize: 25, color: HexColor.fromHex('#333333'))); | |||||
} | |||||
/// login | |||||
Widget _getLogoWidget(var model) { | |||||
return Container( | |||||
margin: EdgeInsets.only(bottom: 12, top: MediaQuery.of(context).padding.top), | |||||
decoration: BoxDecoration( | |||||
borderRadius: BorderRadius.circular(14), | |||||
boxShadow: [ | |||||
BoxShadow(color: Colors.grey[300], offset: Offset(0.0, 0.0), blurRadius: 10.0, spreadRadius: 1.0), | |||||
BoxShadow(color: Colors.grey[300], offset: Offset(0.0, 0.0)), | |||||
], | |||||
), | |||||
height: 80, | |||||
width: 80, | |||||
child: CachedNetworkImage( | |||||
imageUrl: model?.logoImg ?? '', | |||||
fit: BoxFit.fill, | |||||
), | |||||
); | |||||
} | |||||
/// appBar | |||||
Widget _getAppBarWidget() { | |||||
return AppBar( | |||||
backgroundColor: Colors.transparent, | |||||
elevation: 0, | |||||
leading: Icon( | |||||
Icons.arrow_back_ios, | |||||
size: 22, | |||||
color: HexColor.fromHex('#333333'), | |||||
), | |||||
); | |||||
} | |||||
} |
@@ -1,4 +1,6 @@ | |||||
import 'package:zhiying_base_widget/pages/home_page/home_page.dart'; | import 'package:zhiying_base_widget/pages/home_page/home_page.dart'; | ||||
import 'package:zhiying_base_widget/pages/login_page/login_page.dart'; | |||||
import 'package:zhiying_base_widget/pages/login_page/quick/login_quick_page.dart'; | |||||
import 'package:zhiying_base_widget/pages/main_page/main_page.dart'; | import 'package:zhiying_base_widget/pages/main_page/main_page.dart'; | ||||
import 'package:zhiying_base_widget/pages/wallet_page/wallet_page.dart'; | import 'package:zhiying_base_widget/pages/wallet_page/wallet_page.dart'; | ||||
import 'package:zhiying_base_widget/widgets/home/home_banner/home_banner_creater.dart'; | import 'package:zhiying_base_widget/widgets/home/home_banner/home_banner_creater.dart'; | ||||
@@ -17,6 +19,9 @@ import 'package:zhiying_base_widget/widgets/wallet/wallet_detail/wallet_detail.d | |||||
import 'package:zhiying_base_widget/widgets/wallet/wallet_income/wallet_income.dart'; | import 'package:zhiying_base_widget/widgets/wallet/wallet_income/wallet_income.dart'; | ||||
import 'package:zhiying_comm/zhiying_comm.dart'; | import 'package:zhiying_comm/zhiying_comm.dart'; | ||||
import 'pages/login_page/account/login_account_page.dart'; | |||||
import 'pages/login_page/invite/login_invite_page.dart'; | |||||
class BaseWidgetRegister { | class BaseWidgetRegister { | ||||
/// 初始化方法 | /// 初始化方法 | ||||
static void init() { | static void init() { | ||||
@@ -30,6 +35,10 @@ class BaseWidgetRegister { | |||||
PageFactory.regist('index', (model) => MainPage(model)); | PageFactory.regist('index', (model) => MainPage(model)); | ||||
PageFactory.regist('profile', (model) => MainPage(model)); | PageFactory.regist('profile', (model) => MainPage(model)); | ||||
PageFactory.regist('category', (model) => WalletPage()); | PageFactory.regist('category', (model) => WalletPage()); | ||||
PageFactory.regist('login', (model) => LoginPage(model)); | |||||
PageFactory.regist('login_quick', (model) => LoginQuickPage(model)); | |||||
PageFactory.regist('login_account', (model) => LoginAccountPage(model)); | |||||
PageFactory.regist('login_invite', (model) => LoginInvitePage()); | |||||
} | } | ||||
// 注册控件 | // 注册控件 | ||||
@@ -168,14 +168,19 @@ class HomeQuickEntryItem extends StatelessWidget { | |||||
HomeQuickEntryItem({this.data}); | HomeQuickEntryItem({this.data}); | ||||
_itemOnClick(){ | |||||
/// 子图标的点击 | |||||
_itemOnClick(context){ | |||||
Navigator.push(context, MaterialPageRoute( | |||||
builder: (context){ | |||||
return PageFactory.create('login', null); | |||||
} | |||||
)); | |||||
} | } | ||||
@override | @override | ||||
Widget build(BuildContext context) { | Widget build(BuildContext context) { | ||||
return GestureDetector( | return GestureDetector( | ||||
onTap: () => _itemOnClick(), | |||||
onTap: () => _itemOnClick(context), | |||||
child: Column( | child: Column( | ||||
crossAxisAlignment: CrossAxisAlignment.center, | crossAxisAlignment: CrossAxisAlignment.center, | ||||
mainAxisAlignment: MainAxisAlignment.center, | mainAxisAlignment: MainAxisAlignment.center, | ||||
@@ -0,0 +1,34 @@ | |||||
import 'package:flutter/cupertino.dart'; | |||||
import 'package:zhiying_base_widget/widgets/home_slide_banner/model/home_slide_banner_model.dart'; | |||||
import 'package:zhiying_comm/util/net_util.dart'; | |||||
import 'package:zhiying_comm/util/empty_util.dart'; | |||||
import 'package:zhiying_comm/util/global_config.dart'; | |||||
class HomeSlideBannerRepository{ | |||||
/// 获取缓存数据 | |||||
Future<List<HomeSlideBannerModelItems>> fetchCachedDate({@required int id}) async{ | |||||
var cached = await NetUtil.getRequestCachedData('/api/v1/mod', params: {'ids': [id]}); | |||||
if(!EmptyUtil.isEmpty(cached)){ | |||||
HomeSlideBannerModel model = HomeSlideBannerModel.fromJson(cached); | |||||
if(null != model && !EmptyUtil.isEmpty(model.items)){ | |||||
return model.items; | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
/// 获取数据 | |||||
Future<List<HomeSlideBannerModelItems>> fetchData({@required int id}) async{ | |||||
var params = await NetUtil.post('/api/v1/mod', params: {'ids': [id]}); | |||||
if(NetUtil.isSuccess(params)){ | |||||
HomeSlideBannerModel model = HomeSlideBannerModel.fromJson(params[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); | |||||
if(null != model && !EmptyUtil.isEmpty(model.items)){ | |||||
return model.items; | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
} |