Browse Source

Merge branch 'master' of http://192.168.0.138:3000/FnuoOS_ZhiYing/zhiying_base_widget

# Conflicts:
#	lib/register.dart
#	lib/widgets/home_slide_banner/bloc/home_slide_banner_repository.dart
tags/0.0.1
Weller 4 years ago
parent
commit
e8808a5c03
29 changed files with 2661 additions and 27 deletions
  1. +1
    -1
      .dart_tool/package_config.json
  2. +10
    -3
      lib/pages/home_page/home_page.dart
  3. +3
    -0
      lib/pages/login_page/account/bloc/bloc.dart
  4. +63
    -0
      lib/pages/login_page/account/bloc/login_account_bloc.dart
  5. +48
    -0
      lib/pages/login_page/account/bloc/login_account_event.dart
  6. +54
    -0
      lib/pages/login_page/account/bloc/login_account_repository.dart
  7. +57
    -0
      lib/pages/login_page/account/bloc/login_account_state.dart
  8. +576
    -0
      lib/pages/login_page/account/login_account_page.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. +23
    -0
      lib/pages/login_page/bloc/login_bloc.dart
  12. +18
    -11
      lib/pages/login_page/bloc/login_repository.dart
  13. +23
    -2
      lib/pages/login_page/bloc/login_state.dart
  14. +3
    -0
      lib/pages/login_page/invite/bloc/bloc.dart
  15. +61
    -0
      lib/pages/login_page/invite/bloc/login_invite_bloc.dart
  16. +29
    -0
      lib/pages/login_page/invite/bloc/login_invite_event.dart
  17. +40
    -0
      lib/pages/login_page/invite/bloc/login_invite_repository.dart
  18. +75
    -0
      lib/pages/login_page/invite/bloc/login_invite_state.dart
  19. +72
    -0
      lib/pages/login_page/invite/login_invite_page.dart
  20. +21
    -0
      lib/pages/login_page/invite/model/login_invite_user.dart
  21. +255
    -7
      lib/pages/login_page/login_page.dart
  22. +56
    -0
      lib/pages/login_page/login_util.dart
  23. +561
    -0
      lib/pages/login_page/model/login_model.dart
  24. +43
    -0
      lib/pages/login_page/model/login_user.dart
  25. +35
    -0
      lib/pages/login_page/notifier/user_info_notifier.dart
  26. +171
    -0
      lib/pages/login_page/quick/login_quick_page.dart
  27. +9
    -0
      lib/register.dart
  28. +8
    -3
      lib/widgets/home/home_quick_entry/home_quick_entry_widget.dart
  29. +34
    -0
      lib/widgets/home_slide_banner/bloc/home_slide_banner_repository.dart

+ 1
- 1
.dart_tool/package_config.json View File

@@ -680,7 +680,7 @@
"languageVersion": "2.1"
}
],
"generated": "2020-09-11T04:26:30.812379Z",
"generated": "2020-09-11T11:58:25.076408Z",
"generator": "pub",
"generatorVersion": "2.7.2"
}

+ 10
- 3
lib/pages/home_page/home_page.dart View File

@@ -1,7 +1,9 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.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/login_page/notifier/user_info_notifier.dart';
import 'package:zhiying_comm/zhiying_comm.dart';
import 'package:zhiying_comm/util/base_bloc.dart';

@@ -17,9 +19,14 @@ class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
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(),
),
);
}
}


+ 3
- 0
lib/pages/login_page/account/bloc/bloc.dart View File

@@ -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 View File

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

+ 48
- 0
lib/pages/login_page/account/bloc/login_account_event.dart View File

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

+ 54
- 0
lib/pages/login_page/account/bloc/login_account_repository.dart View File

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

}

}

+ 57
- 0
lib/pages/login_page/account/bloc/login_account_state.dart View File

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

+ 576
- 0
lib/pages/login_page/account/login_account_page.dart View File

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

+ 191
- 0
lib/pages/login_page/account/widget/slide_verify_widget.dart View File

@@ -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 View File

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

+ 23
- 0
lib/pages/login_page/bloc/login_bloc.dart View File

@@ -1,8 +1,14 @@
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();

@@ -11,5 +17,22 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
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();
}
}

+ 18
- 11
lib/pages/login_page/bloc/login_repository.dart View File

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



}
}

+ 23
- 2
lib/pages/login_page/bloc/login_state.dart View File

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

abstract class LoginState extends Equatable {
const LoginState();
}

class InitialLoginState extends 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 View File

@@ -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 View File

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

+ 29
- 0
lib/pages/login_page/invite/bloc/login_invite_event.dart View File

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

+ 40
- 0
lib/pages/login_page/invite/bloc/login_invite_repository.dart View File

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

+ 75
- 0
lib/pages/login_page/invite/bloc/login_invite_state.dart View File

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

+ 72
- 0
lib/pages/login_page/invite/login_invite_page.dart View File

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

+ 21
- 0
lib/pages/login_page/invite/model/login_invite_user.dart View File

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

+ 255
- 7
lib/pages/login_page/login_page.dart View File

@@ -1,24 +1,30 @@
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_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_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 {

final Map<String, dynamic> data;
LoginPage(this.data, {Key key}) : super(key: key);

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()..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> {

/// 微信登陆
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
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))
],
),
),
);
}
}

+ 56
- 0
lib/pages/login_page/login_util.dart View File

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

}

+ 561
- 0
lib/pages/login_page/model/login_model.dart View File

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

+ 43
- 0
lib/pages/login_page/model/login_user.dart View File

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

+ 35
- 0
lib/pages/login_page/notifier/user_info_notifier.dart View File

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


}

+ 171
- 0
lib/pages/login_page/quick/login_quick_page.dart View File

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

+ 9
- 0
lib/register.dart View File

@@ -1,4 +1,6 @@
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/wallet_page/wallet_page.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_comm/zhiying_comm.dart';

import 'pages/login_page/account/login_account_page.dart';
import 'pages/login_page/invite/login_invite_page.dart';

class BaseWidgetRegister {
/// 初始化方法
static void init() {
@@ -30,6 +35,10 @@ class BaseWidgetRegister {
PageFactory.regist('index', (model) => MainPage(model));
PageFactory.regist('profile', (model) => MainPage(model));
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());
}

// 注册控件


+ 8
- 3
lib/widgets/home/home_quick_entry/home_quick_entry_widget.dart View File

@@ -168,14 +168,19 @@ class HomeQuickEntryItem extends StatelessWidget {

HomeQuickEntryItem({this.data});

_itemOnClick(){

/// 子图标的点击
_itemOnClick(context){
Navigator.push(context, MaterialPageRoute(
builder: (context){
return PageFactory.create('login', null);
}
));
}

@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => _itemOnClick(),
onTap: () => _itemOnClick(context),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,


+ 34
- 0
lib/widgets/home_slide_banner/bloc/home_slide_banner_repository.dart View File

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

}

Loading…
Cancel
Save