基础库
 
 
 
 
 

616 line
20 KiB

  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:cached_network_image/cached_network_image.dart';
  4. import 'package:flutter/cupertino.dart';
  5. import 'package:flutter/gestures.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:flutter_alibc/alibc_model.dart';
  8. import 'package:flutter_alibc/flutter_alibc.dart';
  9. import 'package:flutter_bloc/flutter_bloc.dart';
  10. import 'package:fluttertoast/fluttertoast.dart';
  11. import 'package:sharesdk_plugin/sharesdk_plugin.dart';
  12. import 'package:zhiying_comm/pages/login_page/account/login_account_page.dart';
  13. import 'package:zhiying_comm/pages/login_page/bind/login_bind_phone_page.dart';
  14. import 'package:zhiying_comm/util/dialog/loading/loading.dart';
  15. import 'package:zhiying_comm/util/empty_util.dart';
  16. import 'package:zhiying_comm/util/mob_util/mob_util.dart';
  17. import 'package:zhiying_comm/zhiying_comm.dart';
  18. import 'package:provider/provider.dart';
  19. import 'package:ali_auth_wbq/ali_auth_wbq.dart';
  20. import 'bloc/bloc.dart';
  21. import 'bloc/login_repository.dart';
  22. import 'login_page_sk.dart';
  23. import 'model/login_style_model.dart';
  24. ///
  25. /// 登陆页面
  26. ///
  27. class LoginPage extends StatelessWidget {
  28. const LoginPage({Key key}) : super(key: key);
  29. @override
  30. Widget build(BuildContext context) {
  31. return Scaffold(
  32. backgroundColor: HexColor.fromHex('#FFFFFF'),
  33. body: BlocProvider<LoginBloc>(
  34. create: (_) => LoginBloc(repository: LoginRepository())..add(LoginInitEvent()),
  35. child: LoginPageContainer(),
  36. ),
  37. );
  38. }
  39. }
  40. class LoginPageContainer extends StatefulWidget {
  41. @override
  42. _LoginPageContainerState createState() => _LoginPageContainerState();
  43. }
  44. class _LoginPageContainerState extends State<LoginPageContainer> {
  45. LoginModel _taoBao;
  46. var _qqUserData;
  47. var _wxUserData;
  48. String _appleData;
  49. bool checkBool = false;
  50. LoginStyleModel loginStyleModel;
  51. @override
  52. void initState() {
  53. // 登录监听
  54. AliAuthPlugin.loginListen(type: false, onEvent: _onAppleLoginEvent);
  55. super.initState();
  56. }
  57. // 返回授权信息
  58. // returnCode: 200成功 500失败
  59. // returnMsg
  60. // user
  61. // familyName //可能为 @""
  62. // givenName //可能为 @""
  63. // email //可能为 @""
  64. // identityTokenStr //需要使用的参数
  65. // authorizationCodeStr
  66. void _onAppleLoginEvent(event) {
  67. var stateCode = event['returnCode'];
  68. String stateMsg = event['returnMsg'];
  69. // _appleData = event['identityTokenStr'];
  70. _appleData = event['user'];
  71. Logger.debug('event == ' + event?.toString());
  72. if (stateCode == 200 && !EmptyUtil.isEmpty(_appleData)) {
  73. Loading.dismiss();
  74. print("成功------$event");
  75. BlocProvider.of<LoginBloc>(context).add(LoginThirdAppleEvent(identityToken: _appleData));
  76. } else {
  77. Loading.dismiss();
  78. print("失败------$stateMsg");
  79. }
  80. }
  81. /// 微信or手机登陆
  82. void _loginClick(String type, LoginStyleModel model) {
  83. if (!isCheck()) {
  84. return;
  85. }
  86. print('登陆$type');
  87. RouterUtil.hideKeyboard(context);
  88. if ('mobile' == type) {
  89. if (model?.flashLoginEnable == '1') {
  90. // mob 一键登录
  91. // MobUtil.openQuickLoginPage(context, model?.quick);
  92. Navigator.push(context, CupertinoPageRoute(builder: (_) => LoginAccountPage(null)));
  93. } else {
  94. Navigator.push(context, CupertinoPageRoute(builder: (_) => LoginAccountPage(null)));
  95. }
  96. } else if ('wechat' == type) {
  97. /// 微信登录
  98. Loading.show(context);
  99. SharesdkPlugin.getUserInfo(ShareSDKPlatforms.wechatSession, (SSDKResponseState state, Map userdata, SSDKError error) {
  100. Logger.log('state = ${state?.toString()}, userInfo = ${userdata?.toString()} , error = ${error?.code}');
  101. if (state == SSDKResponseState.Success) {
  102. try {
  103. if (Platform.isIOS) {
  104. Map credentialDic = userdata['credential'];
  105. Map rawDatalDic = userdata['rawData'];
  106. var name = userdata['nickname'];
  107. var gender = userdata['gender'];
  108. var icon = userdata['icon'];
  109. var userID = userdata['uid'];
  110. var unionid = rawDatalDic['unionid'];
  111. var token = credentialDic['token'];
  112. Map dbInfoDic = {
  113. 'nickname': name,
  114. 'gender': gender,
  115. 'icon': icon,
  116. 'userID': userID,
  117. 'unionid': unionid,
  118. 'token': token,
  119. };
  120. Map tempUserData = {'dbInfo': jsonEncode(dbInfoDic)};
  121. _wxUserData = jsonDecode(tempUserData['dbInfo']);
  122. BlocProvider.of<LoginBloc>(context).add(LoginThirdWeChatEvent(model: tempUserData));
  123. } else if (Platform.isAndroid) {
  124. _wxUserData = jsonDecode(userdata['dbInfo']);
  125. BlocProvider.of<LoginBloc>(context).add(LoginThirdWeChatEvent(model: userdata));
  126. }
  127. Loading.dismiss();
  128. } catch (e) {
  129. Logger.error(e?.toString());
  130. Fluttertoast.showToast(msg: '登录失败');
  131. Loading.dismiss();
  132. }
  133. } else {
  134. Loading.dismiss();
  135. }
  136. });
  137. }
  138. }
  139. /// 第三方登陆
  140. void _otherLoginClick(BottomIcons model) async {
  141. if (!isCheck()) {
  142. return;
  143. }
  144. print('第三方登陆${model.type}');
  145. if (EmptyUtil.isEmpty(model) || EmptyUtil.isEmpty(model.type)) {
  146. Fluttertoast.showToast(msg: '暂不支持~');
  147. return;
  148. }
  149. /// 淘宝登录
  150. if ('taobao' == model.type) {
  151. Loading.show(context);
  152. _taoBao = await FlutterAlibc.loginTaoBao();
  153. if (!EmptyUtil.isEmpty(_taoBao) && !EmptyUtil.isEmpty(_taoBao?.errorCode) && _taoBao.errorCode == '0') {
  154. BlocProvider.of<LoginBloc>(context).add(LoginThirdAliEvent(
  155. nick: _taoBao?.data?.nick,
  156. avatarUrl: _taoBao?.data?.avatarUrl,
  157. openId: _taoBao?.data?.openId,
  158. openSid: _taoBao?.data?.openSid,
  159. topAccessToken: _taoBao?.data?.topAccessToken,
  160. topAuthCode: _taoBao?.data?.topAuthCode));
  161. // Logger.warn(' tao login = ${_taoBao?.errorCode} , msg = ${_taoBao?.errorMessage}, nick = ${_taoBao?.data?.nick}, '
  162. // 'avatar = ${_taoBao?.data?.avatarUrl}, openId = ${_taoBao?.data?.openId}, openSid = ${_taoBao?.data?.openSid}, '
  163. // 'topAccessToken = ${_taoBao?.data?.topAccessToken}, topAuthCode = ${_taoBao?.data?.topAuthCode}');
  164. }
  165. Loading.dismiss();
  166. }
  167. /// QQ登录
  168. if ('qq' == model.type) {
  169. Loading.show(context);
  170. SharesdkPlugin.getUserInfo(ShareSDKPlatforms.qq, (SSDKResponseState state, Map userdata, SSDKError error) {
  171. Logger.log('state = ${state?.toString()}\nuserData = ${userdata?.toString()}');
  172. if (state == SSDKResponseState.Success && !EmptyUtil.isEmpty(userdata)) {
  173. try {
  174. _qqUserData = jsonDecode(userdata['dbInfo']);
  175. BlocProvider.of<LoginBloc>(context).add(LoginThirdQQEvent(model: userdata));
  176. Loading.dismiss();
  177. } catch (e) {
  178. Fluttertoast.showToast(msg: '登录失败');
  179. Loading.dismiss();
  180. }
  181. } else {
  182. Loading.dismiss();
  183. }
  184. });
  185. // Fluttertoast.showToast(msg: '暂不支持~');
  186. }
  187. /// 苹果登录
  188. if ('apple' == model.type) {
  189. if (Platform.isIOS) {
  190. Loading.show(context);
  191. await AliAuthPlugin.appleLogin;
  192. // final appleLogin = await AliAuthPlugin.appleLogin;
  193. } else {
  194. Fluttertoast.showToast(msg: '暂不支持~');
  195. }
  196. }
  197. }
  198. /// 跳到用户协议
  199. void _jumpUserAgreement(String url) {
  200. if (!EmptyUtil.isEmpty(url)) {
  201. print('协议');
  202. RouterUtil.openWebview(url, context);
  203. }
  204. }
  205. /// 跳到绑定手机号
  206. void _jumpBindPhonePage(String thirdType) {
  207. /// 如果是苹果登录
  208. if (GlobalConfig.LOGIN_THIRD_APPLE == thirdType) {
  209. Navigator.push(
  210. context,
  211. CupertinoPageRoute(
  212. builder: (_) => LoginBindPhonePage({
  213. 'thirdType': thirdType,
  214. 'identityTokenStr': _appleData,
  215. })));
  216. }
  217. /// 如果是淘宝登录
  218. if (GlobalConfig.LOGIN_THIRD_ALI == thirdType) {
  219. Navigator.push(
  220. context,
  221. CupertinoPageRoute(
  222. builder: (_) => LoginBindPhonePage({
  223. 'thirdType': thirdType,
  224. 'nick': _taoBao?.data?.nick,
  225. 'avatarUrl': _taoBao?.data?.avatarUrl,
  226. 'openId': _taoBao?.data?.openId,
  227. 'openSid': _taoBao?.data?.openSid,
  228. 'topAccessToken': _taoBao?.data?.topAccessToken,
  229. 'topAuthCode': _taoBao?.data?.topAuthCode,
  230. })));
  231. }
  232. /// 如果是QQ登录
  233. if (GlobalConfig.LOGIN_THIRD_QQ == thirdType) {
  234. if (!EmptyUtil.isEmpty(_qqUserData)) {
  235. Navigator.push(
  236. context,
  237. CupertinoPageRoute(
  238. builder: (_) => LoginBindPhonePage({
  239. 'thirdType': thirdType,
  240. 'nickname': _qqUserData['nickname']?.toString(),
  241. 'avatar_url': _qqUserData['icon']?.toString(),
  242. 'open_id': _qqUserData['userID']?.toString(),
  243. 'gender': _qqUserData['gender']?.toString() == '0' ? '1' : '2',
  244. 'unionid': _qqUserData['unionid']?.toString(),
  245. 'token': _qqUserData['token']?.toString(),
  246. })));
  247. }
  248. }
  249. /// 微信登录
  250. if (GlobalConfig.LOGIN_THIRD_WECHAT == thirdType) {
  251. if (!EmptyUtil.isEmpty(_wxUserData)) {
  252. Navigator.push(
  253. context,
  254. CupertinoPageRoute(
  255. builder: (_) => LoginBindPhonePage({
  256. 'thirdType': thirdType,
  257. 'nickname': _wxUserData['nickname']?.toString(),
  258. 'avatar_url': _wxUserData['icon']?.toString(),
  259. 'open_id': _wxUserData['userID']?.toString(),
  260. 'gender': _wxUserData['gender']?.toString() == '0' ? '1' : '2',
  261. 'unionid': _wxUserData['unionid']?.toString(),
  262. 'token': _wxUserData['token']?.toString(),
  263. })));
  264. }
  265. }
  266. }
  267. /// 跳到首页
  268. void _jumpHomePage() {
  269. RouterUtil.goBackHomePage(context);
  270. }
  271. /// 展开关闭其它登陆
  272. void _showOrCloseOtherLogin() {
  273. setState(() {
  274. _showOther = !_showOther;
  275. });
  276. }
  277. final _sizedHeight50 = const SizedBox(height: 50);
  278. final _sizedHeight9 = const SizedBox(height: 9);
  279. final _sizedHeight18 = const SizedBox(height: 18);
  280. final _sizedHeight21 = const SizedBox(height: 21);
  281. bool _showOther = true;
  282. @override
  283. Widget build(BuildContext context) {
  284. return BlocConsumer<LoginBloc, LoginState>(
  285. listener: (context, state) {
  286. // Fluttertoast.showToast(msg: '网络异常');
  287. },
  288. buildWhen: (prev, current) {
  289. if (current is LoginErrorState) {
  290. return false;
  291. }
  292. /// 登录失败
  293. if (current is LoginThirdLoginErrorState) {
  294. return false;
  295. }
  296. /// 登录成功
  297. if (current is LoginThirdLoginSuccessState) {
  298. // 需要绑定手机号
  299. if (current.model.bindPhoneEnable == '1') {
  300. // 打开绑定手机号页面
  301. _jumpBindPhonePage(current.thirdType);
  302. } else {
  303. // 更新登录数据
  304. Provider.of<UserInfoNotifier>(context, listen: false)?.setUserInfo(current.model);
  305. // 直接打开首页
  306. _jumpHomePage();
  307. Fluttertoast.showToast(msg: '登录成功~');
  308. }
  309. return false;
  310. }
  311. return true;
  312. },
  313. builder: (context, state) {
  314. if (state is LoginCacheState) {
  315. return _getMainWidget(state.model);
  316. }
  317. if (state is LoginLoadedState) {
  318. return _getMainWidget(state.model);
  319. }
  320. return LoginPageSkeleton(); // 骨架屏幕
  321. },
  322. );
  323. }
  324. /// 主视图
  325. Widget _getMainWidget(LoginStyleModel model) {
  326. return Column(
  327. children: <Widget>[
  328. /// 头部
  329. _headWidget(model),
  330. _sizedHeight50,
  331. /// 按钮
  332. _buttonsWidget(model),
  333. _sizedHeight9,
  334. ///账号密码登录
  335. _buildOrderLogin(),
  336. _sizedHeight9,
  337. /// 协议
  338. _protocolWidget(model),
  339. /// 其它登陆方式
  340. _otherLoginWidget(model),
  341. ],
  342. );
  343. }
  344. /// 头部Widget
  345. Widget _headWidget(LoginStyleModel model) {
  346. return Container(
  347. height: 228 + MediaQuery.of(context).padding.top,
  348. width: double.infinity,
  349. decoration: BoxDecoration(
  350. image: DecorationImage(
  351. image: CachedNetworkImageProvider(model?.main?.backgroundImg ?? ''),
  352. fit: BoxFit.fill,
  353. ),
  354. ),
  355. child: Stack(
  356. alignment: Alignment.center,
  357. children: <Widget>[
  358. AppBar(
  359. backgroundColor: Colors.transparent,
  360. elevation: 0,
  361. leading: IconButton(
  362. icon: Icon(
  363. Icons.arrow_back_ios,
  364. size: 22,
  365. color: HexColor.fromHex('#333333'),
  366. ),
  367. onPressed: () => Navigator.maybePop(context),
  368. ),
  369. ),
  370. Column(
  371. crossAxisAlignment: CrossAxisAlignment.center,
  372. mainAxisAlignment: MainAxisAlignment.center,
  373. children: <Widget>[
  374. /// logo
  375. Container(
  376. margin: EdgeInsets.only(bottom: 12, top: MediaQuery.of(context).padding.top),
  377. decoration: BoxDecoration(
  378. borderRadius: BorderRadius.circular(14),
  379. boxShadow: [
  380. BoxShadow(color: Colors.grey[300], offset: Offset(0.0, 0.0), blurRadius: 10.0, spreadRadius: 1.0),
  381. BoxShadow(color: Colors.grey[300], offset: Offset(0.0, 0.0)),
  382. ],
  383. ),
  384. height: 80,
  385. width: 80,
  386. child: CachedNetworkImage(imageUrl: model?.logoImg ?? ''),
  387. ),
  388. /// logo 名字
  389. // CachedNetworkImage(
  390. // imageUrl: model?.main?.appNameImg ?? '',
  391. // width: 90,
  392. // ),
  393. Container(
  394. margin: EdgeInsets.only(top: 0, bottom: 6),
  395. child: Center(
  396. child: Text(
  397. model?.main?.appName ?? "",
  398. style: TextStyle(color: HexColor.fromHex("#FF333333"), fontSize: 20,fontWeight: FontWeight.bold),
  399. )),
  400. )
  401. ],
  402. ),
  403. ],
  404. ),
  405. );
  406. }
  407. /// 按钮
  408. Widget _buttonsWidget(LoginStyleModel model) {
  409. return Container(
  410. padding: const EdgeInsets.symmetric(horizontal: 27.5),
  411. child: Column(
  412. children: model.main.importanceLogin.map((item) {
  413. if(item?.type=="mobile"){
  414. loginStyleModel=model;
  415. }
  416. return Padding(
  417. padding: const EdgeInsets.only(bottom: 8),
  418. child: _customButton(
  419. height: 40,
  420. text: item?.btnText,
  421. iconUrl: item?.btnMobileIcon ?? '',
  422. textColor: item?.btnTextColor,
  423. bgColor: item?.btnBgColor,
  424. borderColor: item?.btnBorderColor,
  425. onTap: () => _loginClick(item?.type, model)),
  426. );
  427. }).toList(),
  428. ),
  429. );
  430. }
  431. /// 协议
  432. Widget _protocolWidget(LoginStyleModel model) {
  433. return Row(
  434. mainAxisAlignment: MainAxisAlignment.center,
  435. children: <Widget>[
  436. InkWell(
  437. borderRadius: BorderRadius.circular(20),
  438. onTap: () {
  439. checkBool = !checkBool;
  440. setState(() {});
  441. },
  442. child: Container(
  443. width: 32,
  444. height: 32,
  445. child: Icon(
  446. checkBool ? Icons.check_box : Icons.check_box_outline_blank,
  447. size: 16,
  448. color: checkBool ? Colors.red : Colors.black12,
  449. ),
  450. ),
  451. ),
  452. RichText(
  453. text: TextSpan(
  454. text: '',
  455. children: model.main.agreements.map((item) {
  456. return TextSpan(
  457. text: item?.text,
  458. style: TextStyle(color: HexColor.fromHex(item?.textColor), fontSize: 10),
  459. recognizer: TapGestureRecognizer()
  460. ..onTap = () {
  461. _jumpUserAgreement(item?.url);
  462. });
  463. }).toList()),
  464. ),
  465. ],
  466. );
  467. }
  468. /// 其它登陆方式
  469. Widget _otherLoginWidget(LoginStyleModel model) {
  470. return Expanded(
  471. child: Column(
  472. mainAxisAlignment: MainAxisAlignment.end,
  473. children: <Widget>[
  474. _getOtherLoginTitle(model),
  475. _sizedHeight18,
  476. Visibility(visible: _showOther, child: _getOtherLoginIcons(model)),
  477. Visibility(visible: _showOther, child: _sizedHeight21),
  478. ],
  479. ),
  480. );
  481. }
  482. /// 其它登陆方式的title
  483. Widget _getOtherLoginTitle(LoginStyleModel model) {
  484. return GestureDetector(
  485. behavior: HitTestBehavior.opaque,
  486. onTap: () => _showOrCloseOtherLogin(),
  487. child: Row(
  488. mainAxisAlignment: MainAxisAlignment.center,
  489. crossAxisAlignment: CrossAxisAlignment.center,
  490. children: <Widget>[
  491. Text('${model?.main?.otherIconsTitle}', style: TextStyle(fontSize: 13, color: HexColor.fromHex(model?.main?.otherIconsTitleColor))),
  492. SizedBox(width: 5.5),
  493. RotatedBox(
  494. quarterTurns: _showOther ? 0 : 2,
  495. child: CachedNetworkImage(imageUrl: model?.main?.otherExpansionIcon ?? '', width: 12),
  496. ),
  497. ],
  498. ),
  499. );
  500. }
  501. /// 其它登陆方式的按钮
  502. Widget _getOtherLoginIcons(LoginStyleModel model) {
  503. return Row(
  504. mainAxisAlignment: MainAxisAlignment.center,
  505. children: model.main.bottomIcons.map((item) {
  506. return GestureDetector(
  507. behavior: HitTestBehavior.opaque,
  508. onTap: () => _otherLoginClick(item),
  509. child: Container(
  510. width: 30,
  511. margin: const EdgeInsets.symmetric(horizontal: 20),
  512. child: CachedNetworkImage(imageUrl: item?.img ?? ''),
  513. ),
  514. );
  515. }).toList(),
  516. );
  517. }
  518. /// 自定义按钮
  519. Widget _customButton({double height, String text, String iconUrl, String textColor, String bgColor, String borderColor, GestureTapCallback onTap}) {
  520. return GestureDetector(
  521. onTap: onTap,
  522. child: Container(
  523. width: double.infinity,
  524. height: height ?? 0,
  525. decoration: BoxDecoration(
  526. border: Border.all(color: HexColor.fromHex(borderColor), width: 0.5),
  527. borderRadius: BorderRadius.circular(height ?? 0 / 2),
  528. color: HexColor.fromHex(bgColor),
  529. ),
  530. child: Row(
  531. mainAxisAlignment: MainAxisAlignment.center,
  532. crossAxisAlignment: CrossAxisAlignment.center,
  533. children: <Widget>[
  534. // icon
  535. CachedNetworkImage(imageUrl: iconUrl, width: 12),
  536. SizedBox(width: 8),
  537. // text
  538. Text(text, style: TextStyle(color: HexColor.fromHex(textColor), fontSize: 15))
  539. ],
  540. ),
  541. ),
  542. );
  543. }
  544. ///账号密码登录 update:与手机一键登录互换
  545. _buildOrderLogin() {
  546. return Center(
  547. child: GestureDetector(
  548. behavior: HitTestBehavior.opaque,
  549. onTap: () {
  550. if (isCheck()) {
  551. //Navigator.push(context, CupertinoPageRoute(builder: (_) => LoginAccountPage(null)));
  552. MobUtil.openQuickLoginPage(context, loginStyleModel?.quick);
  553. }
  554. },
  555. child: Text(
  556. "本机一键登录",
  557. style: TextStyle(color: HexColor.fromHex("#FFFF4242"), fontSize: 14),
  558. ),
  559. ),
  560. );
  561. }
  562. bool isCheck() {
  563. if (!checkBool) {
  564. Fluttertoast.showToast(msg: "请阅读并勾选同意用户协议选项");
  565. return false;
  566. } else {
  567. return true;
  568. }
  569. }
  570. }