基础库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

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