setting.dart 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  1. import 'dart:async';
  2. import 'dart:io';
  3. import 'dart:ui';
  4. import 'package:dio/dio.dart';
  5. import 'package:dio/dio.dart' as adio;
  6. import 'dart:convert' as convert;
  7. import 'package:fast/constants.dart';
  8. import 'package:fast/model/model.dart';
  9. import 'package:fast/utils/api.dart';
  10. import 'package:fast/utils/global.dart';
  11. import 'package:fast/utils/http_utils.dart';
  12. import 'package:fast/utils/size_fit.dart';
  13. import 'package:fast/utils/storage.dart';
  14. import 'package:fast/view/component/navi_bar.dart';
  15. import 'package:fast/view/feedback.dart';
  16. import 'package:fast/view/nickname.dart';
  17. import 'package:fast/view/phone.dart';
  18. import 'package:fast/view/web.dart';
  19. import 'package:flutter/cupertino.dart';
  20. import 'package:flutter/foundation.dart';
  21. import 'package:flutter/material.dart';
  22. import 'package:flutter/services.dart';
  23. import 'package:flutter/widgets.dart';
  24. import 'package:fluwx/fluwx.dart';
  25. import 'package:get/get.dart';
  26. import 'package:image_picker/image_picker.dart';
  27. import 'package:shared_preferences/shared_preferences.dart';
  28. import 'component/alert_widget.dart';
  29. import 'component/share.dart';
  30. import 'component/toast.dart';
  31. class LifecycleEventHandler extends WidgetsBindingObserver {
  32. AsyncCallback resumeCallback;
  33. AsyncCallback suspendingCallback;
  34. LifecycleEventHandler({
  35. required this.resumeCallback,
  36. required this.suspendingCallback,
  37. });
  38. @override
  39. void didChangeAppLifecycleState(AppLifecycleState state) {
  40. switch (state) {
  41. case AppLifecycleState.resumed:
  42. resumeCallback();
  43. break;
  44. case AppLifecycleState.inactive:
  45. break;
  46. case AppLifecycleState.paused:
  47. break;
  48. case AppLifecycleState.detached:
  49. break;
  50. }
  51. super.didChangeAppLifecycleState(state);
  52. }
  53. }
  54. class Setting extends StatefulWidget {
  55. const Setting({Key? key}) : super(key: key);
  56. @override
  57. State<Setting> createState() => _SettingState();
  58. }
  59. class _SettingState extends State<Setting>
  60. with WidgetsBindingObserver, RouteAware {
  61. late UserBean userBean;
  62. StreamSubscription? subscription;
  63. String agreeUrl = '';
  64. String aboutUrl = '';
  65. String privacyUrl = '';
  66. // final RouteObserver<ModalRoute<void>> routeObserver = RouteObserver<ModalRoute<void>>();
  67. @override
  68. void initState() {
  69. userBean = Global().userBean!;
  70. super.initState();
  71. initWxApi();
  72. WidgetsBinding widgetsBinding = WidgetsBinding.instance!;
  73. widgetsBinding.addObserver(this);
  74. getUserInfo();
  75. // WidgetsBinding.instance?.addObserver(LifecycleEventHandler(
  76. // resumeCallback: () async {}, suspendingCallback: () async {}));
  77. // SystemChannels.lifecycle.setMessageHandler((msg) async {
  78. // debugPrint('SystemChannels> $msg');
  79. // if (msg == AppLifecycleState.resumed.toString()) setState(() {});
  80. // });
  81. }
  82. @override
  83. // ignore: unnecessary_overrides
  84. void didChangeAppLifecycleState(AppLifecycleState state) {
  85. super.didChangeAppLifecycleState(state);
  86. }
  87. @override
  88. void didChangeDependencies() {
  89. super.didChangeDependencies();
  90. Global().routeObserver!.subscribe(this, ModalRoute.of(context)!);
  91. }
  92. @override
  93. void didPopNext() {
  94. getUserInfo();
  95. super.didPopNext();
  96. }
  97. Future initWxApi() async {
  98. bool isSuccess = await registerWxApi(
  99. appId: 'wxa8557217acf4f532',
  100. universalLink: 'https://api.fast.liveplus.fun/');
  101. if (isSuccess) {
  102. subscription = weChatResponseEventHandler.listen((event) {
  103. if (event is WeChatPaymentResponse) {
  104. } else if (event is WeChatAuthResponse) {
  105. print(event.code);
  106. bindwx(event.code);
  107. } else if (event is WeChatShareResponse) {}
  108. });
  109. }
  110. }
  111. Future bindwx(code) async {
  112. try {
  113. var data = await HttpUtils.post(Api.outhBind, data: {
  114. "app_version": "1.0",
  115. "client_type": Platform.isAndroid ? "ANDROID" : "IOS",
  116. "client_version": "1.0",
  117. "type": "WX",
  118. "code": code
  119. });
  120. print(data.toString());
  121. // ignore: unused_catch_clause
  122. } on DioError catch (e) {
  123. showDialog(
  124. context: context,
  125. barrierDismissible: false,
  126. barrierColor: Colors.transparent,
  127. builder: (BuildContext context) {
  128. return Toast(
  129. title: e.response?.data['error_message'],
  130. showError: true,
  131. content: const SizedBox(
  132. width: 0,
  133. ),
  134. );
  135. });
  136. // if (e.response?.data['error_code'] == 'INVITE_CODE_NOT_EXIST') {
  137. // }
  138. }
  139. getUserInfo();
  140. getUrls();
  141. }
  142. Future getUserInfo() async {
  143. Map<String, dynamic> data = await HttpUtils.get(Api.userInfo);
  144. UserBean bean = UserBean.fromJson(data);
  145. Global().userBean = bean;
  146. final prefs = await SharedPreferences.getInstance();
  147. await prefs.setString('userInfo', convert.jsonEncode(data));
  148. if (mounted) {
  149. setState(() {
  150. userBean = bean;
  151. });
  152. }
  153. }
  154. Future getUrls() async {
  155. List urls = await HttpUtils.get(Api.resourcesUrls);
  156. setState(() {
  157. urls.forEach((element) {
  158. if (element['code'] == 'user_agreement') {
  159. agreeUrl = element['url'];
  160. }
  161. if (element['code'] == 'privacy') {
  162. privacyUrl = element['url'];
  163. }
  164. if (element['code'] == 'about') {
  165. aboutUrl = element['url'];
  166. }
  167. });
  168. });
  169. }
  170. Future deleteAccount() async {
  171. await HttpUtils.post(Api.delAccount);
  172. Global().token = "";
  173. StorageUtil().prefs!.clear();
  174. Navigator.of(context).pop();
  175. Get.back();
  176. Global().mainPage!.logout();
  177. }
  178. Future clearFast() async {
  179. await HttpUtils.post(Api.clearFast);
  180. Navigator.of(context).pop();
  181. Get.back();
  182. }
  183. @override
  184. void dispose() {
  185. subscription!.cancel();
  186. Global().routeObserver!.unsubscribe(this);
  187. super.dispose();
  188. }
  189. // Future initWxApi() async {
  190. // bool isSuccess = await registerWxApi(
  191. // appId: 'wxa8557217acf4f532',
  192. // universalLink: 'https://api.fast.liveplus.fun/');
  193. // if (isSuccess) {
  194. // weChatResponseEventHandler.listen((event) {
  195. // if (event is WeChatPaymentResponse) {
  196. // } else if (event is WeChatAuthResponse) {
  197. // print(event.code);
  198. // login(event.code);
  199. // } else if (event is WeChatShareResponse) {}
  200. // });
  201. // }
  202. // }
  203. showShare() {
  204. showDialog(
  205. context: context,
  206. barrierDismissible: false,
  207. barrierColor: const Color(0xF2000D1F),
  208. builder: (BuildContext context) {
  209. return Share(
  210. type: 'me',
  211. );
  212. });
  213. }
  214. Future share() async {
  215. // bool isSuccess = await registerWxApi(
  216. // appId: 'wxa8557217acf4f532',
  217. // universalLink: 'https://api.fast.liveplus.fun/');
  218. // if (isSuccess) {
  219. // weChatResponseEventHandler.listen((event) {
  220. // if (event is WeChatPaymentResponse) {
  221. // } else if (event is WeChatAuthResponse) {
  222. // print(event.code);
  223. // } else if (event is WeChatShareResponse) {
  224. // print(event.toString());
  225. // }
  226. // });
  227. // }
  228. shareToWeChat(
  229. // WeChatShareTextModel("hello world")
  230. WeChatShareImageModel(WeChatImage.asset("assets/images/3days_bg.png")));
  231. }
  232. @override
  233. Widget build(BuildContext context) {
  234. SizeFit.initialize(context);
  235. EdgeInsets safePadding = MediaQuery.of(context).padding;
  236. return Material(
  237. color: kBgColor,
  238. child: Stack(
  239. children: [
  240. SizedBox(
  241. height: MediaQuery.of(context).size.height,
  242. child: SingleChildScrollView(
  243. child: Column(children: [
  244. Container(
  245. margin: EdgeInsets.only(top: safePadding.top + 40.px),
  246. ),
  247. Container(
  248. margin: EdgeInsets.fromLTRB(14.px, 8.px, 14.px, 8.px),
  249. padding: EdgeInsets.only(left: 18.px, right: 18.px),
  250. decoration: groupDecoration(),
  251. child: Column(
  252. children: [
  253. GestureDetector(
  254. onTap: () {
  255. showPicker();
  256. },
  257. child: Container(
  258. height: 60.px,
  259. alignment: Alignment.center,
  260. decoration: borderBottomDecoration(),
  261. child: Row(
  262. children: [
  263. Expanded(
  264. child: Text(
  265. '头像',
  266. style: titleStyle(),
  267. ),
  268. ),
  269. ClipOval(
  270. child: Image.network(
  271. userBean.avatar!,
  272. width: 40.px,
  273. height: 40.px,
  274. fit: BoxFit.cover,
  275. ),
  276. )
  277. ],
  278. ),
  279. ),
  280. ),
  281. GestureDetector(
  282. onTap: () {
  283. Get.to(() => const Nickname());
  284. },
  285. child: Container(
  286. height: 54.px,
  287. alignment: Alignment.center,
  288. child: Row(
  289. children: [
  290. Expanded(
  291. child: Text(
  292. '昵称',
  293. style: titleStyle(),
  294. )),
  295. Text(
  296. userBean.nickname!,
  297. style: subTitleStyle(),
  298. )
  299. ],
  300. ),
  301. ),
  302. )
  303. ],
  304. ),
  305. ),
  306. Container(
  307. margin: EdgeInsets.fromLTRB(14.px, 8.px, 14.px, 8.px),
  308. padding: EdgeInsets.only(left: 18.px, right: 18.px),
  309. decoration: groupDecoration(),
  310. child: Column(
  311. children: [
  312. GestureDetector(
  313. onTap: () {
  314. showShare();
  315. },
  316. child: Container(
  317. height: 60.px,
  318. alignment: Alignment.center,
  319. decoration: borderBottomDecoration(),
  320. child: Row(
  321. children: [
  322. Text(
  323. '邀请码',
  324. style: titleStyle(),
  325. ),
  326. SizedBox(
  327. width: 15.px,
  328. ),
  329. Expanded(
  330. child: Text(
  331. userBean.inviteCode!,
  332. style: subTitleStyle(),
  333. )),
  334. Text(
  335. '分享给好友',
  336. style: TextStyle(
  337. color: kThemeColor, fontSize: 14.px),
  338. ),
  339. SizedBox(
  340. width: 6.px,
  341. ),
  342. Text(
  343. '+3',
  344. style: TextStyle(
  345. color: const Color(0x99FFFFFF),
  346. fontSize: 12.px,
  347. fontFamily: 'Exo2',
  348. ),
  349. ),
  350. SizedBox(
  351. width: 2.px,
  352. ),
  353. Image.asset(
  354. 'assets/images/stone.png',
  355. width: 14.px,
  356. height: 14.px,
  357. )
  358. ],
  359. ),
  360. ),
  361. ),
  362. GestureDetector(
  363. child: Container(
  364. height: 54.px,
  365. alignment: Alignment.center,
  366. child: Row(
  367. children: [
  368. Text(
  369. '受邀码',
  370. style: titleStyle(),
  371. ),
  372. SizedBox(
  373. width: 15.px,
  374. ),
  375. Expanded(
  376. child: Text(
  377. '还没有哦',
  378. style: subTitleStyle(),
  379. ))
  380. ],
  381. ),
  382. ),
  383. )
  384. ],
  385. ),
  386. ),
  387. Container(
  388. margin: EdgeInsets.fromLTRB(14.px, 8.px, 14.px, 8.px),
  389. padding: EdgeInsets.only(left: 18.px, right: 18.px),
  390. decoration: groupDecoration(),
  391. child: Column(
  392. children: [
  393. GestureDetector(
  394. onTap: () {
  395. if (userBean.mobieBind) {
  396. return;
  397. }
  398. Get.to(() => Phone(
  399. type: 2,
  400. ));
  401. },
  402. child: Container(
  403. height: 54.px,
  404. alignment: Alignment.center,
  405. decoration: borderBottomDecoration(),
  406. child: Row(
  407. children: [
  408. Expanded(
  409. child: Text(
  410. '手机号',
  411. style: titleStyle(),
  412. )),
  413. Text(
  414. userBean.mobieBind ? '已绑定' : '立即绑定',
  415. style: subTitleStyle(),
  416. )
  417. ],
  418. ),
  419. ),
  420. ),
  421. GestureDetector(
  422. onTap: () {
  423. if (userBean.wxBind) {
  424. return;
  425. }
  426. sendWeChatAuth(scope: 'snsapi_userinfo');
  427. },
  428. child: Container(
  429. height: 54.px,
  430. alignment: Alignment.center,
  431. child: Row(
  432. children: [
  433. Expanded(
  434. child: Text(
  435. '绑定微信',
  436. style: titleStyle(),
  437. )),
  438. Text(
  439. userBean.wxBind ? '已绑定' : '立即绑定',
  440. style: subTitleStyle(),
  441. )
  442. ],
  443. ),
  444. ),
  445. )
  446. ],
  447. ),
  448. ),
  449. Container(
  450. margin: EdgeInsets.fromLTRB(14.px, 8.px, 14.px, 8.px),
  451. padding: EdgeInsets.only(left: 18.px, right: 18.px),
  452. decoration: groupDecoration(),
  453. child: Column(
  454. children: [
  455. GestureDetector(
  456. onTap: () {
  457. Get.to(() => const FeedbackPage());
  458. },
  459. child: Container(
  460. height: 54.px,
  461. alignment: Alignment.center,
  462. decoration: borderBottomDecoration(),
  463. child: Row(
  464. children: [
  465. Expanded(
  466. child: Text(
  467. '意见反馈',
  468. style: titleStyle(),
  469. )),
  470. Image.asset(
  471. 'assets/images/next.png',
  472. width: 24.px,
  473. height: 24.px,
  474. )
  475. ],
  476. ),
  477. ),
  478. ),
  479. GestureDetector(
  480. onTap: () {
  481. Get.to(() => Web(
  482. title: '用户协议',
  483. url: agreeUrl,
  484. ));
  485. },
  486. child: Container(
  487. height: 54.px,
  488. alignment: Alignment.center,
  489. decoration: borderBottomDecoration(),
  490. child: Row(
  491. children: [
  492. Expanded(
  493. child: Text(
  494. '用户协议',
  495. style: titleStyle(),
  496. )),
  497. Image.asset(
  498. 'assets/images/next.png',
  499. width: 24.px,
  500. height: 24.px,
  501. )
  502. ],
  503. ),
  504. ),
  505. ),
  506. GestureDetector(
  507. onTap: () {
  508. Get.to(() => Web(
  509. title: '隐私政策',
  510. url: privacyUrl,
  511. ));
  512. },
  513. child: Container(
  514. height: 54.px,
  515. alignment: Alignment.center,
  516. decoration: borderBottomDecoration(),
  517. child: Row(
  518. children: [
  519. Expanded(
  520. child: Text(
  521. '隐私政策',
  522. style: titleStyle(),
  523. )),
  524. Image.asset(
  525. 'assets/images/next.png',
  526. width: 24.px,
  527. height: 24.px,
  528. )
  529. ],
  530. ),
  531. ),
  532. ),
  533. GestureDetector(
  534. onTap: () {
  535. Get.to(() => Web(
  536. title: '关于间歇性断食',
  537. url: aboutUrl,
  538. ));
  539. },
  540. child: Container(
  541. height: 54.px,
  542. alignment: Alignment.center,
  543. child: Row(
  544. children: [
  545. Expanded(
  546. child: Text(
  547. '关于间歇性断食',
  548. style: titleStyle(),
  549. )),
  550. Image.asset(
  551. 'assets/images/next.png',
  552. width: 24.px,
  553. height: 24.px,
  554. )
  555. ],
  556. ),
  557. ),
  558. )
  559. ],
  560. ),
  561. ),
  562. GestureDetector(
  563. onTap: () {
  564. showDialog(
  565. context: context,
  566. barrierDismissible: false,
  567. barrierColor: const Color(0xF2000D1F),
  568. builder: (BuildContext context) {
  569. return AlertWidget(
  570. title: '确定要退出吗?',
  571. confirm: '确定',
  572. confirmCallback: () {
  573. Global().token = "";
  574. StorageUtil().prefs!.clear();
  575. Navigator.of(context).pop();
  576. Get.back();
  577. Global().mainPage!.logout();
  578. });
  579. });
  580. },
  581. child: Container(
  582. width: 347.px,
  583. height: 50.px,
  584. margin: EdgeInsets.only(top: 8.px, bottom: 0.px),
  585. decoration: BoxDecoration(
  586. border: Border.all(color: const Color(0x66AAFF00)),
  587. borderRadius: BorderRadius.all(Radius.circular(25.px))),
  588. alignment: Alignment.center,
  589. child: Text(
  590. '安全退出账号',
  591. style: TextStyle(
  592. color: kThemeColor,
  593. fontSize: 16.px,
  594. fontWeight: FontWeight.bold),
  595. ),
  596. ),
  597. ),
  598. SizedBox(
  599. height: 28.px,
  600. ),
  601. GestureDetector(
  602. onTap: () {
  603. showDialog(
  604. context: context,
  605. barrierDismissible: false,
  606. barrierColor: const Color(0xF2000D1F),
  607. builder: (BuildContext context) {
  608. return AlertWidget(
  609. title: '确定要注销此账号吗?',
  610. confirm: '确定',
  611. confirmCallback: () {
  612. deleteAccount();
  613. });
  614. });
  615. },
  616. child: Text(
  617. '注销账号',
  618. style: TextStyle(
  619. color: const Color(0xFFC4CCDA), fontSize: 14.px),
  620. ),
  621. ),
  622. SizedBox(
  623. height: 28.px,
  624. ),
  625. GestureDetector(
  626. onTap: () {
  627. showDialog(
  628. context: context,
  629. barrierDismissible: false,
  630. barrierColor: const Color(0xF2000D1F),
  631. builder: (BuildContext context) {
  632. return AlertWidget(
  633. title: '确定要清除断食记录吗?',
  634. confirm: '确定',
  635. confirmCallback: () {
  636. clearFast();
  637. });
  638. });
  639. },
  640. child: Text(
  641. '清除断食记录',
  642. style: TextStyle(
  643. color: const Color(0xFFC4CCDA), fontSize: 14.px),
  644. ),
  645. ),
  646. SizedBox(
  647. height: 70.px,
  648. )
  649. ]))),
  650. NaviBar(
  651. title: '设置',
  652. closeCallback: () {
  653. Get.back();
  654. }),
  655. Positioned(
  656. left: 0,
  657. bottom: 0,
  658. child: IgnorePointer(
  659. child: Container(
  660. width: 375.px,
  661. height: 200.px,
  662. decoration: const BoxDecoration(
  663. gradient: LinearGradient(
  664. begin: Alignment.topCenter,
  665. end: Alignment.bottomCenter,
  666. colors: [Color(0x00050F1A), Color(0xFF050F1A)])),
  667. )),
  668. )
  669. ],
  670. ),
  671. );
  672. }
  673. showPicker() {
  674. showCupertinoModalPopup(
  675. context: context,
  676. builder: (BuildContext context) => CupertinoActionSheet(
  677. title: const Text('选择操作'),
  678. actions: [
  679. CupertinoActionSheetAction(
  680. onPressed: () {
  681. getImageFromGallery();
  682. Navigator.pop(context);
  683. },
  684. child: const Text('相册选择')),
  685. CupertinoActionSheetAction(
  686. onPressed: () {
  687. getImageFromCamera();
  688. Navigator.pop(context);
  689. },
  690. child: const Text('拍照选择'))
  691. ],
  692. cancelButton: CupertinoActionSheetAction(
  693. child: const Text('取消'),
  694. onPressed: () {
  695. Navigator.pop(context);
  696. },
  697. ),
  698. ));
  699. }
  700. Future getImageFromCamera() async {
  701. ImagePicker picker = ImagePicker();
  702. var image =
  703. await picker.pickImage(source: ImageSource.camera, imageQuality: 50);
  704. if (image != null) {
  705. uploadImage(image as XFile);
  706. }
  707. return;
  708. }
  709. Future getImageFromGallery() async {
  710. ImagePicker picker = ImagePicker();
  711. var image =
  712. await picker.pickImage(source: ImageSource.gallery, imageQuality: 50);
  713. uploadImage(image as XFile);
  714. return;
  715. }
  716. Future uploadImage(XFile obj) async {
  717. Map<String, dynamic> data = await HttpUtils.get(Api.ossFormUpload,
  718. params: {'type': 'AVATAR', 'file_ext': 'png'});
  719. Dio dio = Dio();
  720. Map<String, dynamic> map = data['fields'];
  721. map['file'] =
  722. await adio.MultipartFile.fromFile(obj.path, filename: 'avatar.png');
  723. adio.FormData formData = adio.FormData.fromMap(map);
  724. adio.Response response = await dio.post(data['upload_url'], data: formData);
  725. var data2 =
  726. await HttpUtils.post(Api.userInfo, data: {'avatar': data['view_url']});
  727. if (data2 != null) {
  728. getUserInfo();
  729. }
  730. }
  731. BoxDecoration borderBottomDecoration() {
  732. return const BoxDecoration(
  733. border: Border(bottom: BorderSide(color: Color(0x1AFFFFFF))));
  734. }
  735. BoxDecoration groupDecoration() {
  736. return BoxDecoration(
  737. color: const Color(0x14C4CCDA),
  738. borderRadius: BorderRadius.all(Radius.circular(16.px)));
  739. }
  740. TextStyle titleStyle() {
  741. return TextStyle(
  742. color: Colors.white, fontSize: 14.px, fontWeight: FontWeight.bold);
  743. }
  744. TextStyle subTitleStyle() {
  745. return TextStyle(color: const Color(0x66FFFFFF), fontSize: 14.px);
  746. }
  747. }