Ver código fonte

1、login模块移动到comm中

tags/0.0.1
PH2 4 anos atrás
pai
commit
5f7597f47d
29 arquivos alterados com 3081 adições e 24 exclusões
  1. +0
    -13
      lib/pages/login_page.dart
  2. +3
    -0
      lib/pages/login_page/account/bloc/bloc.dart
  3. +63
    -0
      lib/pages/login_page/account/bloc/login_account_bloc.dart
  4. +48
    -0
      lib/pages/login_page/account/bloc/login_account_event.dart
  5. +59
    -0
      lib/pages/login_page/account/bloc/login_account_repository.dart
  6. +61
    -0
      lib/pages/login_page/account/bloc/login_account_state.dart
  7. +589
    -0
      lib/pages/login_page/account/login_account_page.dart
  8. +69
    -0
      lib/pages/login_page/account/login_account_sk.dart
  9. +191
    -0
      lib/pages/login_page/account/widget/slide_verify_widget.dart
  10. +121
    -0
      lib/pages/login_page/account/widget/vcode_widget.dart
  11. +3
    -0
      lib/pages/login_page/bloc/bloc.dart
  12. +38
    -0
      lib/pages/login_page/bloc/login_bloc.dart
  13. +10
    -0
      lib/pages/login_page/bloc/login_event.dart
  14. +26
    -0
      lib/pages/login_page/bloc/login_repository.dart
  15. +31
    -0
      lib/pages/login_page/bloc/login_state.dart
  16. +3
    -0
      lib/pages/login_page/invite/bloc/bloc.dart
  17. +61
    -0
      lib/pages/login_page/invite/bloc/login_invite_bloc.dart
  18. +29
    -0
      lib/pages/login_page/invite/bloc/login_invite_event.dart
  19. +48
    -0
      lib/pages/login_page/invite/bloc/login_invite_repository.dart
  20. +76
    -0
      lib/pages/login_page/invite/bloc/login_invite_state.dart
  21. +341
    -0
      lib/pages/login_page/invite/login_invite_page.dart
  22. +30
    -0
      lib/pages/login_page/invite/model/login_invite_user.dart
  23. +297
    -0
      lib/pages/login_page/login_page.dart
  24. +87
    -0
      lib/pages/login_page/login_page_sk.dart
  25. +53
    -0
      lib/pages/login_page/login_util.dart
  26. +561
    -0
      lib/pages/login_page/model/login_model.dart
  27. +171
    -0
      lib/pages/login_page/quick/login_quick_page.dart
  28. +6
    -10
      lib/util/router_util.dart
  29. +6
    -1
      pubspec.yaml

+ 0
- 13
lib/pages/login_page.dart Ver arquivo

@@ -1,13 +0,0 @@
import 'package:flutter/material.dart';

class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
@override
Widget build(BuildContext context) {
return Material(child: Center(child: Text('登录页'),),);
}
}

+ 3
- 0
lib/pages/login_page/account/bloc/bloc.dart Ver arquivo

@@ -0,0 +1,3 @@
export 'login_account_bloc.dart';
export 'login_account_event.dart';
export 'login_account_state.dart';

+ 63
- 0
lib/pages/login_page/account/bloc/login_account_bloc.dart Ver arquivo

@@ -0,0 +1,63 @@
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.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* {}
}

+ 48
- 0
lib/pages/login_page/account/bloc/login_account_event.dart Ver arquivo

@@ -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];
}

+ 59
- 0
lib/pages/login_page/account/bloc/login_account_repository.dart Ver arquivo

@@ -0,0 +1,59 @@
import 'package:zhiying_comm/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';
import 'package:zhiying_comm/models/user/user_info_model.dart';

import '../../login_util.dart';
import 'bloc.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<UserInfoModel> 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])){
UserInfoModel loginUser = UserInfoModel.fromJson(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]);
if(null != loginUser){
loginUser.mobile = event.mobile;
return loginUser;
}
return null;
}
return null;
}

/// 检查验证码
Future<bool> checkVcode(LoginAccountCheckVcodeEvent event) async{

}

}

+ 61
- 0
lib/pages/login_page/account/bloc/login_account_state.dart Ver arquivo

@@ -0,0 +1,61 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
import 'package:zhiying_comm/models/user/user_info_model.dart';
import 'package:zhiying_comm/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 UserInfoModel model;

const LoginAccountTypeVcodeLoginSuccessState({@required this.model});

@override
List<Object> get props => [this.model];
}

/// 验证码登陆失败
class LoginAccountTypeVcodeLoginErrorState extends LoginAccountState {
final String msg;

const LoginAccountTypeVcodeLoginErrorState({this.msg});
}

+ 589
- 0
lib/pages/login_page/account/login_account_page.dart Ver arquivo

@@ -0,0 +1,589 @@
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_comm/pages/login_page/invite/login_invite_page.dart';
import 'package:zhiying_comm/pages/login_page/model/login_model.dart';
import 'package:zhiying_comm/zhiying_comm.dart';
import 'bloc/bloc.dart';
import 'bloc/login_account_repository.dart';
import 'login_account_sk.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('跳转到邀请码页面');
Navigator.push(context, MaterialPageRoute(
// builder: (_) => PageFactory.create('login_invite', null)
builder: (_) => LoginInvitePage()
));
}

/// 登陆成功页面
void _openLoginSuccessPage() {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (BuildContext context) => PageFactory.create('homePage', null)),
(Route<dynamic> route) => false,
);
}

/// 返回上一页
void _openPop() {
if (Navigator.canPop(context)) {
Navigator.pop(context);
}
}

/// 登陆
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 = true;

/// 是否显示第三方验证码
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) {
if (state is LoginAccountTypeVcodeLoginSuccessState) {}
},
buildWhen: (prev, current) {
// 验证码登陆失败
if (current is LoginAccountTypeVcodeLoginErrorState) {
Fluttertoast.showToast(msg: '登陆失败');
return false;
}
// 验证码登陆成功
if (current is LoginAccountTypeVcodeLoginSuccessState) {
/// 缓存登陆数据
Provider.of<UserInfoNotifier>(context, listen: false)?.setUserInfo(current.model);
if (current?.model?.registerInviteCodeEnable == '0') {
Fluttertoast.showToast(msg: '登陆成功~');
/// 打开也买
_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 LoginAccountSkeleton();
},
);
}

/// 主页面
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: IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: 22,
color: HexColor.fromHex('#333333'),
),
onPressed: () => _openPop(),
),
);
}

/// 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,
),
),
),
],
),
);
}
}

+ 69
- 0
lib/pages/login_page/account/login_account_sk.dart Ver arquivo

@@ -0,0 +1,69 @@
import 'package:shimmer/shimmer.dart';
import 'package:flutter/material.dart';
import 'package:zhiying_comm/util/color.dart';

///
/// 登陆页面的骨架屏
///
class LoginAccountSkeleton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: HexColor.fromHex('#FFFFFF'),
elevation: 0,
title: Text(
'登录',
style: TextStyle(color: HexColor.fromHex('#333333')),
),
centerTitle: true,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: 22,
color: HexColor.fromHex('#333333'),
),
onPressed: () {},
),
),
body: Container(
padding: const EdgeInsets.symmetric(horizontal: 27.5),
width: double.infinity,
height: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
/// 标题
Padding( padding: const EdgeInsets.only(top: 40), child: _shimmerWidget(width: 175, height: 20),),
Padding( padding: const EdgeInsets.only(top: 30), child: _shimmerWidget(width: 320, height: 42),),
Padding( padding: const EdgeInsets.only(top: 15), child: _shimmerWidget(width: 320, height: 42),),
Align( alignment: Alignment.center, child: Padding( padding: const EdgeInsets.only(top: 15), child: _shimmerWidget(width: 72, height: 13),)),
Padding( padding: const EdgeInsets.only(top: 30), child: _shimmerWidget(width: 320, height: 52.7, radius: 30),),
Align( alignment: Alignment.center, child: Padding( padding: const EdgeInsets.only(top: 12.5), child: _shimmerWidget(width: 220, height: 15),)),
Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(bottom: 25),
child: _shimmerWidget(width: 132, height: 15),
),
),
)
],
),
),
);
}

Widget _shimmerWidget({double width, double height, double radius = 0}) {
return Shimmer.fromColors(
baseColor: Colors.grey[300],
highlightColor: Colors.grey[100],
child: Container(
width: width,
height: height,
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(radius)),
),
);
}
}

+ 191
- 0
lib/pages/login_page/account/widget/slide_verify_widget.dart Ver arquivo

@@ -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();
}
});
}

}

+ 121
- 0
lib/pages/login_page/account/widget/vcode_widget.dart Ver arquivo

@@ -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();
}

+ 3
- 0
lib/pages/login_page/bloc/bloc.dart Ver arquivo

@@ -0,0 +1,3 @@
export 'login_bloc.dart';
export 'login_event.dart';
export 'login_state.dart';

+ 38
- 0
lib/pages/login_page/bloc/login_bloc.dart Ver arquivo

@@ -0,0 +1,38 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import './bloc.dart';
import 'login_repository.dart';
import 'package:zhiying_comm/util/empty_util.dart';

class LoginBloc extends Bloc<LoginEvent, LoginState> {
LoginRepository repository;

LoginBloc({this.repository});

@override
LoginState get initialState => InitialLoginState();

@override
Stream<LoginState> mapEventToState(
LoginEvent event,
) async* {
// 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();
}
}

+ 10
- 0
lib/pages/login_page/bloc/login_event.dart Ver arquivo

@@ -0,0 +1,10 @@
import 'package:equatable/equatable.dart';

abstract class LoginEvent extends Equatable {
const LoginEvent();
@override
List<Object> get props => [];
}

/// 初始化
class LoginInitEvent extends LoginEvent{}

+ 26
- 0
lib/pages/login_page/bloc/login_repository.dart Ver arquivo

@@ -0,0 +1,26 @@
import 'package:zhiying_comm/pages/login_page/model/login_model.dart';
import '../login_util.dart';

class LoginRepository {
/// 获取页面数据
Future<LoginModel> fetchNetPageData() async {
// var result = await NetUtil.post('/api/v1/sign/in', method: NetMethod.GET, cache: true);
// 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;
return await LoginUtil.fetchNetPageData();
}

/// 获取缓存的页面数据
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;
return await LoginUtil.fetchCachePageData();
}
}

+ 31
- 0
lib/pages/login_page/bloc/login_state.dart Ver arquivo

@@ -0,0 +1,31 @@
import 'package:equatable/equatable.dart';
import 'package:zhiying_comm/pages/login_page/model/login_model.dart';

abstract class LoginState extends Equatable {
const LoginState();

@override
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 {}

+ 3
- 0
lib/pages/login_page/invite/bloc/bloc.dart Ver arquivo

@@ -0,0 +1,3 @@
export 'login_invite_bloc.dart';
export 'login_invite_event.dart';
export 'login_invite_state.dart';

+ 61
- 0
lib/pages/login_page/invite/bloc/login_invite_bloc.dart Ver arquivo

@@ -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> {
LoginInviteRepository 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 LoginInviteQuerySuccessState(model: data, pageMdel: repostitory?.pageModel);
else
yield LoginInviteQueryErrorState();
}

/// 提交
Stream<LoginInviteState> _mapSubmitEventToState(LoginInviteSubmitEvent event) async* {
var data = await repostitory.submitInvite(event);
if (!EmptyUtil.isEmpty(data))
yield LoginInviteSubmitSuccess(model: data);
else
yield LoginInviteSubmitErrorState();
}
}

+ 29
- 0
lib/pages/login_page/invite/bloc/login_invite_event.dart Ver arquivo

@@ -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];
}

+ 48
- 0
lib/pages/login_page/invite/bloc/login_invite_repository.dart Ver arquivo

@@ -0,0 +1,48 @@
import 'package:zhiying_comm/pages/login_page/invite/model/login_invite_user.dart';
import 'package:zhiying_comm/pages/login_page/model/login_model.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 'package:zhiying_comm/models/user/user_info_model.dart';
import '../../login_util.dart';
import 'bloc.dart';

class LoginInviteRepository {
LoginModel pageModel;

/// 获取数据,如果缓存有,则获取缓存的数据
Future<LoginModel> fetchData() async {
pageModel = await LoginUtil.getLoginModel();
if (!EmptyUtil.isEmpty(pageModel)) {
return pageModel;
}
return null;
}

/// 获取邀请人信息
Future<LoginInviteUser> fetchInviteUserInfo(LoginInviteQueryEvent event) async {
var result = await NetUtil.post('/api/v1/user/invite/${event.num}', method: NetMethod.GET);
if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) {
LoginInviteUser model = LoginInviteUser.fromJson(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]);
if(null != model)
return model;
else return null;
} else {
return null;
}
}

/// 提交
Future<UserInfoModel> submitInvite(LoginInviteSubmitEvent event) async {
var result = await NetUtil.post('/api/v1/user/invite/ack', params: {'mobile': event.mobile, 'parent_uid': event.num});
if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) {
UserInfoModel model = UserInfoModel.fromJson(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]);
if (null != model) {
model.mobile = event.mobile;
return model;
}
return null;
}
return null;
}
}

+ 76
- 0
lib/pages/login_page/invite/bloc/login_invite_state.dart Ver arquivo

@@ -0,0 +1,76 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
import 'package:zhiying_comm/models/user/user_info_model.dart';
import 'package:zhiying_comm/pages/login_page/invite/model/login_invite_user.dart';
import 'package:zhiying_comm/pages/login_page/model/login_model.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 UserInfoModel model;

const LoginInviteSubmitSuccess({@required this.model});

@override
List<Object> get props => [this.model];
}

/// 邀请码失败
class LoginInviteSubmitErrorState extends LoginInviteState {
final String msg;

const LoginInviteSubmitErrorState({this.msg});

@override
List<Object> get props => [this.msg];
}

/// 查询邀请人成功
class LoginInviteQuerySuccessState extends LoginInviteState {
final LoginInviteUser model;
final LoginModel pageMdel;

const LoginInviteQuerySuccessState({@required this.model, @required this.pageMdel});

@override
List<Object> get props => [this.model];
}

/// 查询邀请人失败
class LoginInviteQueryErrorState extends LoginInviteState {
final String msg;

const LoginInviteQueryErrorState({this.msg});

@override
List<Object> get props => [this.msg];
}

+ 341
- 0
lib/pages/login_page/invite/login_invite_page.dart Ver arquivo

@@ -0,0 +1,341 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart';
import 'package:zhiying_comm/pages/login_page/model/login_model.dart';
import 'package:zhiying_comm/zhiying_comm.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:zhiying_comm/util/empty_util.dart';

import 'bloc/bloc.dart';
import 'bloc/login_invite_repository.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: LoginInviteRepository())..add(LoginInviteInitEvent()),
child: LoginInvitePageContainer(),
),
);
}
}

///
/// 邀请
class LoginInvitePageContainer extends StatefulWidget {
@override
_LoginInvitePageContainerState createState() => _LoginInvitePageContainerState();
}

class _LoginInvitePageContainerState extends State<LoginInvitePageContainer> {
TextEditingController _editingController;
FocusNode _focusNode;
bool _showInviteInfo = false;

/// 返回上一页
void _openPop() {
if (Navigator.canPop(context)) {
Navigator.pop(context);
}
}

/// 注册成功跳转
void _successJump(){
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (BuildContext context) => PageFactory.create('homePage', null)),
(Route<dynamic> route) => false,
);
}

/// 输入框输入变化
void _onChange(String string) {
setState(() {
_showInviteInfo = false;
});
print('$string');
if (!EmptyUtil.isEmpty(string) && string.length >= 3) {
_queryInviteInfo(string);
}
}

/// 查询邀请人
void _queryInviteInfo(String inviteNum) {
if (!EmptyUtil.isEmpty(inviteNum) && inviteNum.length < 3) {
return;
}
BlocProvider.of<LoginInviteBloc>(context).add(LoginInviteQueryEvent(num: inviteNum));
}

/// 填写邀请啊吗
void _submitOnClick(LoginInviteUser inviteUser) async {
_focusNode.unfocus();

/// 邀请码
String inviteNum = inviteUser?.userId ?? '';

/// 手机号
UserInfoModel model = await Provider.of<UserInfoNotifier>(context, listen: false)?.getUserInfoModel();
String mobile = model?.mobile ?? '';
if (!EmptyUtil.isEmpty(inviteNum) && !EmptyUtil.isEmpty(mobile)) {
BlocProvider.of<LoginInviteBloc>(context).add(LoginInviteSubmitEvent(mobile: mobile, num: inviteNum));
}
}

@override
void initState() {
_editingController = TextEditingController();
_focusNode = FocusNode();
super.initState();
}

@override
void didChangeDependencies() {

super.didChangeDependencies();
}

@override
void dispose() {
_editingController?.dispose();
_focusNode?.unfocus();
_focusNode?.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return BlocConsumer<LoginInviteBloc, LoginInviteState>(
listener: (context, state) {},
buildWhen: (previous, current) {
/// 提交失败
if (current is LoginInviteSubmitErrorState) {
return false;
}

/// 数据加载出错
if (current is LoginInviteErrorState) {
return false;
}
/// 查询邀请人失败
if(current is LoginInviteQueryErrorState){
return false;
}
/// 邀请人查询成功
if(current is LoginInviteQuerySuccessState){
_showInviteInfo = true;
return true;
}
/// 邀请码成功 跳转
if (current is LoginInviteSubmitSuccess) {
// 缓存数据
Provider.of<UserInfoNotifier>(context, listen: false).setUserInfo(current.model);
_successJump();
return false;
}
return true;
},
builder: (context, state) {
print(state);
if (state is LoginInviteLoadedState) {
return _getMainWidget(state.model, null);
}
if (state is LoginInviteQuerySuccessState) {
return _getMainWidget(state.pageMdel, state.model);
}

/// 骨架屏
return Container();
},
);
}

/// 主视图
Widget _getMainWidget(LoginModel model, LoginInviteUser inviteUser) {
return Column(
children: <Widget>[
/// appbar
_getAppBar(model),

/// 标题
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: _getInviteInputWidget(model)),

/// 邀请人信息
Visibility(
visible: inviteUser != null && _showInviteInfo,
child: Padding(padding: const EdgeInsets.only(left: 27.5, right: 27.5, top: 8), child: _getInviteInfoWidget(inviteUser))),

/// 按钮
Padding(padding: const EdgeInsets.only(left: 27.5, right: 27.5, top: 30), child: _getSubmiBtnWidget(model, inviteUser)),
],
);
}

/// appBar
Widget _getAppBar(LoginModel model) {
return AppBar(
backgroundColor: HexColor.fromHex('#FFFFFF'),
elevation: 0,
title: Text(
model?.invite?.appBarTitle ?? '登录',
style: TextStyle(color: HexColor.fromHex(model?.invite?.appBarTitleColor ?? '#333333')),
),
centerTitle: true,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: 22,
color: HexColor.fromHex('#333333'),
),
onPressed: () => _openPop(),
),
);
}

/// title
Widget _getTitleWidget(LoginModel model) {
return Align(
alignment: Alignment.centerLeft,
child: Text(
model?.invite?.title ?? '输入邀请码',
style: TextStyle(color: HexColor.fromHex(model?.invite?.titleColor ?? '#333333'), fontSize: 25),
));
}

/// 邀请码输入框
Widget _getInviteInputWidget(LoginModel model) {
return _getCustomInputWidget(
hint: model?.invite?.inputInviteText ?? '请输入邀请码',
controller: _editingController,
focusNode: _focusNode,
onChanged: _onChange,
hintColor: model?.invite?.inputHintColor ?? '#999999',
bgColor: model?.invite?.inputBgColor ?? '#F7F7F7',
textColor: model?.invite?.inputInviteTextColor ?? '#333333',
iconUrl: model?.invite?.inputInviteIcon ?? '');
}

/// 邀请人信息
Widget _getInviteInfoWidget(LoginInviteUser model) {
return Container(
// height: 77.5,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8), border: Border.all(color: HexColor.fromHex('#E8E8E8'), width: 0.5)),
padding: const EdgeInsets.all(15),
child: Row(
children: <Widget>[
/// 头像
CircleAvatar(
radius: 23.5,
backgroundImage: CachedNetworkImageProvider(model?.avatar ?? ''),
),
const SizedBox(width: 13),
Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
/// 名字
Text(
'${model?.nickname}',
style: TextStyle(color: HexColor.fromHex(model?.nickNameColor), fontSize: 13),
),

/// 邀请
RichText(
text: TextSpan(text: '邀请您进入', style: TextStyle(fontSize: 11, color: HexColor.fromHex(model?.nickNameColor)), children: [
TextSpan(
text: '${model?.appName}',
style: TextStyle(fontSize: 11, color: HexColor.fromHex(model?.appNameColor)),
),
]),
)
],
)
],
),
);
}

/// 按钮
Widget _getSubmiBtnWidget(LoginModel model, LoginInviteUser inviteUser) {
return Material(
child: Container(
height: 52,
width: double.infinity,
color: Colors.white,
child: RaisedButton(
child: Text(
model?.invite?.btnSubmitText ?? '进入智莺生活',
style: TextStyle(fontSize: 15),
),
textColor: HexColor.fromHex(model?.invite?.btnSubmitTextColor ?? '#FFFFFF'),
color: HexColor.fromHex(model?.invite?.btnSubmitBgColor ?? '#FF3939'),
disabledColor: HexColor.fromHex(model?.invite?.btnBanBgColor ?? '#F5F5F5'),
disabledTextColor: HexColor.fromHex(model?.invite?.btnBanTextColor ?? '#999999'),
elevation: 5,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(52 / 2)),
onPressed: _showInviteInfo && inviteUser != null ? ()=> _submitOnClick(inviteUser) : null,
),
),
);
}

/// 自定义输入框
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,
),
),
),
],
),
);
}
}

+ 30
- 0
lib/pages/login_page/invite/model/login_invite_user.dart Ver arquivo

@@ -0,0 +1,30 @@
class LoginInviteUser {
String userId;
String nickname;
String appName;
String nickNameColor;
String appNameColor;
String avatar;

LoginInviteUser({this.userId, this.nickname, this.appName});

LoginInviteUser.fromJson(Map<String, dynamic> json) {
userId = json['user_id']?.toString();
nickname = json['nickname']?.toString();
appName = json['app_name']?.toString();
avatar = json['avatar']?.toString();
appNameColor = json['app_name_color']?.toString();
nickNameColor = json['nickname_color']?.toString();
}

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;
data['nickname_color'] = this.nickNameColor;
data['app_name_color'] = this.appNameColor;
data['avatar'] = this.avatar;
return data;
}
}

+ 297
- 0
lib/pages/login_page/login_page.dart Ver arquivo

@@ -0,0 +1,297 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:zhiying_comm/pages/login_page/account/login_account_page.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';

import 'bloc/bloc.dart';
import 'bloc/login_repository.dart';
import 'login_page_sk.dart';
import 'model/login_model.dart';

///
/// 登陆页面
///
class LoginPage extends StatelessWidget {
final Map<String, dynamic> data;

const LoginPage(this.data, {Key key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: HexColor.fromHex('#FFFFFF'),
body: BlocProvider<LoginBloc>(
create: (_) => LoginBloc(repository: LoginRepository())..add(LoginInitEvent()),
child: LoginPageContainer(),
),
);
}
}

class LoginPageContainer extends StatefulWidget {
@override
_LoginPageContainerState createState() => _LoginPageContainerState();
}

class _LoginPageContainerState extends State<LoginPageContainer> {

/// 微信登陆
void _loginClick(String type) {
print('登陆$type');
if(type == 'mobile'){
Navigator.push(context, MaterialPageRoute(
builder: (_) => LoginAccountPage(null)
));
}
}

/// 返回上一页
void _openPop(){
if(Navigator.canPop(context)){
Navigator.pop(context);
}
}

/// 第三方登陆
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
Widget build(BuildContext context) {
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 LoginPageSkeleton(); // 骨架屏幕
},
);
}

/// 主视图
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: IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: 22,
color: HexColor.fromHex('#333333'),
),
onPressed: ()=> _openPop(),
),
),
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))
],
),
),
);
}
}

+ 87
- 0
lib/pages/login_page/login_page_sk.dart Ver arquivo

@@ -0,0 +1,87 @@
import 'package:shimmer/shimmer.dart';
import 'package:flutter/material.dart';

///
/// 登陆页面的骨架屏
///
class LoginPageSkeleton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
width: double.infinity,
height: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
height: 230,
child: Column(
children: <Widget>[
/// logn
Padding(padding: const EdgeInsets.only(top: 60), child: _logo()),

/// appName
Padding(padding: const EdgeInsets.only(top: 12), child: _appName()),
],
),
),

/// btn
Padding(padding: const EdgeInsets.only(top: 50, left: 28, right: 28), child: _shimmerWidget(width: 320, height: 40, radius: 20)),
Padding(padding: const EdgeInsets.only(top: 8, left: 28, right: 28), child: _shimmerWidget(width: 320, height: 40, radius: 20)),

/// 协议
Padding(padding: const EdgeInsets.only(top: 9, left: 28, right: 28), child: _shimmerWidget(width: 250, height: 14, radius: 0)),

Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(padding: const EdgeInsets.only(bottom: 18), child: _shimmerWidget(width: 78, height: 18, radius: 0)),
],
),
),
],
),
),
);
}

Widget _logo() {
return Shimmer.fromColors(
baseColor: Colors.grey[300],
highlightColor: Colors.grey[100],
child: Container(
height: 80,
width: 80,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: Colors.white),
),
);
}

Widget _appName() {
return Shimmer.fromColors(
baseColor: Colors.grey[300],
highlightColor: Colors.grey[100],
child: Container(
width: 90,
height: 22.5,
color: Colors.white,
),
);
}

Widget _shimmerWidget({double width, double height, double radius = 0}) {
return Shimmer.fromColors(
baseColor: Colors.grey[300],
highlightColor: Colors.grey[100],
child: Container(
width: width,
height: height,
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(radius)),
),
);
}
}

+ 53
- 0
lib/pages/login_page/login_util.dart Ver arquivo

@@ -0,0 +1,53 @@
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';

import 'model/login_model.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, cache: true);
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;
}
}

+ 561
- 0
lib/pages/login_page/model/login_model.dart Ver arquivo

@@ -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;
}
}

+ 171
- 0
lib/pages/login_page/quick/login_quick_page.dart Ver arquivo

@@ -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'),
),
);
}
}

+ 6
- 10
lib/util/router_util.dart Ver arquivo

@@ -1,11 +1,10 @@
import 'package:flutter/material.dart';
import 'package:zhiying_comm/pages/login_page.dart';
import 'package:zhiying_comm/pages/login_page/login_page.dart';
import 'package:zhiying_comm/zhiying_comm.dart';
import 'package:provider/provider.dart';

class RouterUtil {

static Future route(Map<String, dynamic> model, BuildContext context) {
static Future route(Map<String, dynamic> model, BuildContext context) async {
// 唯一跳转标识
String skipIdentifier = model['skip_identifier'].toString();
// 需要登录
@@ -13,21 +12,18 @@ class RouterUtil {
// 需要淘宝授权
String requiredTaobaoAuth = model['required_taobao_auth'].toString();


if (requiredLogin == '1') {
UserInfoModel user = Provider.of<UserInfoNotifier>(context, listen: false).getUserInfoModel();
UserInfoModel user = await Provider.of<UserInfoNotifier>(context, listen: false).getUserInfoModel();
print(user.toString());
if (user?.token == null || user.token == '') {
print('need login...');
return Navigator.of(context)
.push(MaterialPageRoute(builder: (BuildContext context) {
return LoginPage();
return Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
return LoginPage(model);
}));
}
}

return Navigator.of(context)
.push(MaterialPageRoute(builder: (BuildContext context) {
return Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
if (PageFactory.hasRegisted(skipIdentifier)) {
return PageFactory.create(skipIdentifier, model);
}


+ 6
- 1
pubspec.yaml Ver arquivo

@@ -16,7 +16,9 @@ dependencies:
package_info: ^0.4.0+17
device_info: ^0.4.0+1
flutter_native_image: ^0.0.5
# toast提示
fluttertoast: 4.0.1
# 图片缓存
cached_network_image: ^2.2.0+1
equatable: ^1.2.0
json_serializable: ^3.3.0
@@ -25,7 +27,10 @@ dependencies:
flutter_screenutil: ^1.1.0
# 缓存
shared_preferences: ^0.5.10

# bloc
flutter_bloc: ^4.0.1
# 骨架屏
shimmer: ^1.1.1


dev_dependencies:


Carregando…
Cancelar
Salvar