user_info_page.dart 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. import 'dart:io';
  2. import 'dart:math';
  3. // import 'package:amap_location/amap_location.dart';
  4. import 'package:amap_flutter_location/amap_flutter_location.dart';
  5. import 'package:amap_flutter_location/amap_location_option.dart';
  6. import 'package:flutter/cupertino.dart';
  7. import 'package:flutter/material.dart';
  8. import 'package:get_it/get_it.dart';
  9. import 'package:permission_handler/permission_handler.dart';
  10. import 'package:provider/provider.dart';
  11. import 'package:shared_preferences/shared_preferences.dart';
  12. import 'package:sport/pages/login/login_widget.dart';
  13. import 'package:sport/pages/my/weight_page.dart';
  14. import 'package:sport/pages/run/location.dart';
  15. import 'package:sport/pages/run/run_page.dart';
  16. import 'package:sport/pages/social/user_friend_add_page.dart';
  17. import 'package:sport/provider/login_info_model.dart';
  18. import 'package:sport/provider/user_model.dart';
  19. import 'package:sport/router/navigator_util.dart';
  20. import 'package:sport/services/api/inject_api.dart';
  21. import 'package:sport/utils/toast.dart';
  22. import 'package:sport/widgets/appbar.dart';
  23. import 'package:sport/widgets/button_primary.dart';
  24. import 'package:sport/widgets/decoration.dart';
  25. import 'package:sport/widgets/dialog/alert_dialog.dart';
  26. import 'package:sport/widgets/dialog/request_dialog.dart';
  27. import 'package:sport/widgets/image.dart';
  28. import 'package:sport/widgets/list_tile.dart';
  29. import 'package:sport/widgets/space.dart';
  30. class UserInfoPage extends StatefulWidget {
  31. @override
  32. State<StatefulWidget> createState() => _PageState();
  33. }
  34. class _PageState extends State<UserInfoPage> with InjectLoginApi,InjectApi {
  35. late LoginInfoModel _loginInfoModel;
  36. int _genderController = 1; // 默认选中男
  37. String? _name; // 用户名
  38. File? _lastCropped;
  39. late ValueNotifier<int> num;
  40. @override
  41. void initState() {
  42. super.initState();
  43. _loginInfoModel = GetIt.I();
  44. initNameCardCount();
  45. }
  46. initNameCardCount() async{
  47. int? _num = (await api.getCountNameCard()).data;
  48. num = new ValueNotifier(_num ?? 0);
  49. }
  50. @override
  51. void dispose() {
  52. super.dispose();
  53. }
  54. @override
  55. Widget build(BuildContext context) {
  56. return Scaffold(
  57. backgroundColor: Colors.white,
  58. body: NestedScrollView(
  59. headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
  60. return <Widget>[buildSliverAppBar(context, "用户信息")];
  61. },
  62. body: ListView(
  63. padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 12.0),
  64. physics: NeverScrollableScrollPhysics(),
  65. shrinkWrap: true,
  66. children: divideTiles(
  67. context: context,
  68. includeLast: true,
  69. tiles: [
  70. ListTile(
  71. title: Row(
  72. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  73. children: <Widget>[
  74. Text(
  75. "头像",
  76. style: TextStyle(fontSize: 16.0),
  77. ),
  78. Consumer<UserModel>(
  79. builder: (_, model, __) {
  80. // 没有头像就为空...
  81. return model.user.avatar != null
  82. ? Container(
  83. width: 40.0,
  84. height: 40.0,
  85. child: CircleAvatar(
  86. backgroundColor: Colors.black26,
  87. radius: 100,
  88. backgroundImage: userAvatarProvider(model.user.avatar),
  89. ),
  90. )
  91. : Container();
  92. },
  93. )
  94. ],
  95. ),
  96. contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
  97. trailing: arrowRight5(),
  98. onTap: () async {
  99. selectImage(context);
  100. },
  101. ),
  102. ListTile(
  103. title: Row(
  104. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  105. children: <Widget>[
  106. Text(
  107. "用户名称",
  108. style: TextStyle(fontSize: 16.0),
  109. ),
  110. Consumer<UserModel>(
  111. builder: (_, model, __) {
  112. return Text(model.user.name, style: Theme.of(context).textTheme.bodyText2!);
  113. },
  114. )
  115. ],
  116. ),
  117. contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
  118. trailing: arrowRight5(),
  119. onTap: () async {
  120. SharedPreferences prefs = await SharedPreferences.getInstance();
  121. String name = prefs.getString("name") ??"";
  122. _showDialog((TextEditingValue name) async {
  123. await request(context, () async {
  124. await _loginInfoModel.updateUserInfo(context, name.text, Provider.of<UserModel>(context, listen: false).user.gender ?? 0);
  125. });
  126. Navigator.maybePop(context);
  127. },name:name);
  128. },
  129. ),
  130. ListTile(
  131. title: Row(
  132. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  133. children: <Widget>[
  134. Text(
  135. "性别",
  136. style: TextStyle(fontSize: 16.0),
  137. ),
  138. Consumer<UserModel>(
  139. builder: (_, model, __) {
  140. return Text(model.user.gender == 1 ? "男" : "女", style: Theme.of(context).textTheme.bodyText2!);
  141. },
  142. )
  143. ],
  144. ),
  145. contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
  146. trailing: arrowRight5(),
  147. onTap: () async{
  148. var user = Provider.of<UserModel>(context, listen: false).user;
  149. await _showGenderDialog(user.gender ?? 0, (int _gender) async {
  150. await request(context, () async {
  151. await _loginInfoModel.updateUserInfo(context, Provider.of<UserModel>(context, listen: false).user.name, _gender);
  152. });
  153. });
  154. await _showMetDialog("修改性别后将影响下次运动的卡路里、运动强度的计算");
  155. },
  156. ),
  157. ListTile(
  158. title: Row(
  159. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  160. children: <Widget>[
  161. Text(
  162. "年龄",
  163. style: TextStyle(fontSize: 16.0),
  164. ),
  165. Consumer<UserModel>(
  166. builder: (_, model, __) {
  167. return Text(model.user.age != null ? "${model.user.age}岁" : "请选择你的年龄", style: Theme.of(context).textTheme.bodyText2!);
  168. },
  169. )
  170. ],
  171. ),
  172. contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
  173. trailing: arrowRight5(),
  174. onTap: () {
  175. var user = Provider.of<UserModel>(context, listen: false).user;
  176. _showAgeDialog(user.age ?? 1, (int age) async {
  177. await request(context, () async {
  178. await _loginInfoModel.updateUserInfo(
  179. context, Provider.of<UserModel>(context, listen: false).user.name, Provider.of<UserModel>(context, listen: false).user.gender ?? 0,
  180. age: age);
  181. });
  182. Navigator.maybePop(context);
  183. });
  184. },
  185. ),
  186. ListTile(
  187. title: Row(
  188. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  189. children: <Widget>[
  190. Text(
  191. "体重",
  192. style: TextStyle(fontSize: 16.0),
  193. ),
  194. Consumer<UserModel>(
  195. builder: (_, model, __) {
  196. return Text(model.user.weight != 0 ? "${model.user.weight}kg" : "请选择你的体重", style: Theme.of(context).textTheme.bodyText2!);
  197. },
  198. )
  199. ],
  200. ),
  201. contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
  202. trailing: arrowRight5(),
  203. onTap: () async {
  204. await NavigatorUtil.goPage(context, (context) => WeightPage(update: true,));
  205. await _showMetDialog("修改体重后将影响下次运动的卡路里、运动强度的计算");
  206. },
  207. ),
  208. ListTile(
  209. title: Row(
  210. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  211. children: <Widget>[
  212. Text(
  213. "城市",
  214. style: TextStyle(fontSize: 16.0),
  215. ),
  216. Consumer<UserModel>(
  217. builder: (_, model, __) {
  218. String? province = model.user.province;
  219. String? city = model.user.city;
  220. String? district = model.user.district;
  221. String? _address = "";
  222. // print(_address);
  223. if (province != null && city != null && district != null) {
  224. _address = "$province-$city";
  225. }
  226. return Text(_address != "" ? "$_address" : "请选择你的城市", style: Theme.of(context).textTheme.bodyText2!);
  227. },
  228. )
  229. ],
  230. ),
  231. contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
  232. trailing: arrowRight5(),
  233. onTap: () async {
  234. Map<Permission, PermissionStatus> result = await [Permission.locationWhenInUse].request();
  235. if (!result.values.any((element) => element.isGranted)) {
  236. ToastUtil.show("请授权后使用该功能!");
  237. return;
  238. }
  239. if (await showDialog(
  240. context: context,
  241. builder: (context) => CustomAlertDialog(title: '是否重新定位?', ok: () => Navigator.of(context).pop(true)),
  242. ) ==
  243. true) {
  244. await request(context, () async {
  245. try {
  246. Location? location = await _location();
  247. if (location != null) {
  248. await _loginInfoModel.updateUserInfo(
  249. context, Provider.of<UserModel>(context, listen: false).user.name, Provider.of<UserModel>(context, listen: false).user.gender??0,
  250. districtId: int.parse(location.adcode!));
  251. }
  252. } catch (e, s) {
  253. print(e);
  254. debugPrintStack(stackTrace: s);
  255. }
  256. });
  257. }
  258. //
  259. // var user = Provider.of<UserModel>(context, listen: false).user;
  260. // CityResult initCity = CityResult();
  261. // initCity.province = user.province;
  262. // initCity.city = user.city;
  263. // initCity.county = user.district;
  264. // CityResult result = await showCityPicker(context,initCity: initCity);
  265. //
  266. // if(result == null)
  267. // return;
  268. //
  269. // print(result.county);
  270. // print(result.city);
  271. // print(result.province);
  272. //
  273. // print(result.cityCode);
  274. // print(result.countyCode);
  275. // print(result.provinceCode);
  276. //
  277. // await _loginInfoModel.updateUserInfo(
  278. // context, Provider.of<UserModel>(context, listen: false).user.name, Provider.of<UserModel>(context, listen: false).user.gender,
  279. // cityId: int.parse(result.cityCode), districtId: int.parse(result.countyCode), provinceId: int.parse(result.provinceCode));
  280. },
  281. ),
  282. ListTile(
  283. title: Row(
  284. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  285. children: <Widget>[
  286. Text(
  287. "二维码名片",
  288. style: TextStyle(fontSize: 16.0),
  289. ),
  290. Container(
  291. width: 22.0,
  292. height: 22.0,
  293. child: Image.asset("lib/assets/img/mine_icon_code.png"),
  294. ),
  295. ],
  296. ),
  297. contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
  298. trailing: arrowRight5(),
  299. onTap: () async {
  300. NavigatorUtil.goPage(context, (context) => UserFriendAddPage());
  301. },
  302. ),
  303. ],
  304. ).toList(),
  305. ),
  306. ));
  307. ;
  308. }
  309. _showDialog(Function callBack,{String? name}) {
  310. TextEditingController _controller = new TextEditingController();
  311. _controller.text = name ??"";
  312. return showDialog(
  313. context: context,
  314. barrierDismissible: true,
  315. builder: (context) {
  316. return AlertDialog(
  317. contentPadding: EdgeInsets.all(6.0),
  318. shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
  319. content: Container(
  320. width: 300,
  321. height: 200,
  322. child: Column(
  323. children: <Widget>[
  324. // Divider(),
  325. Space(
  326. height: 30.0,
  327. ),
  328. Padding(
  329. padding: const EdgeInsets.all(16.0),
  330. child: TextField(
  331. textAlign: TextAlign.center,
  332. controller: _controller,
  333. autofocus: true,
  334. maxLength: 7,
  335. decoration: InputDecoration(
  336. counterText: '',
  337. // helperText: '用户名长度为2-10个字母,数字,中文',
  338. // hintText: name,
  339. helperMaxLines: 1,
  340. focusedBorder: UnderlineInputBorder(
  341. borderSide: BorderSide(color: Color(0xffF1F1F1)),
  342. ))),
  343. ),
  344. Padding(
  345. padding: EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 0),
  346. child: Row(
  347. children: <Widget>[
  348. Expanded(
  349. child: MainButton(
  350. type: "cancel",
  351. content: "取消",
  352. callback: () => {Navigator.of(context).pop()},
  353. height: 35.0,
  354. textColor: Color(0xff333333),
  355. ),
  356. ),
  357. Space(
  358. width: 12.0,
  359. ),
  360. Expanded(
  361. child: ValueListenableBuilder<int>(
  362. valueListenable: num,
  363. builder: (context, index, child) {
  364. return MainButton(
  365. type: "confirm",
  366. content: "改名卡($index)",
  367. callback: () {
  368. if (index > 0) {
  369. callBack(_controller.value);
  370. }else{
  371. ToastUtil.show("您没有改名卡喔~!");
  372. }
  373. },
  374. height: 35.0,
  375. buttonColor: index <= 0
  376. ? Color(0xffFFC400).withOpacity(0.3)
  377. : Color(0xffFFC400),
  378. );
  379. },
  380. ),
  381. ),
  382. ],
  383. ),
  384. ),
  385. ],
  386. ),
  387. ),
  388. );
  389. });
  390. }
  391. _showGenderDialog(int gender, Function callback) {
  392. var array = ["男", "女"];
  393. int _index = gender == 2 ? 1 : 0;
  394. return showDialog(
  395. context: context,
  396. barrierDismissible: true,
  397. builder: (context) {
  398. return AlertDialog(
  399. contentPadding: EdgeInsets.all(6.0),
  400. shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
  401. content: Container(
  402. width: 300,
  403. height: 200,
  404. child: Column(
  405. children: <Widget>[
  406. Row(
  407. children: <Widget>[
  408. IconButton(
  409. onPressed: () {
  410. Navigator.of(context).pop();
  411. },
  412. icon: Text("取消")),
  413. Expanded(
  414. child: Center(
  415. child: Text(
  416. "修改性别",
  417. style: Theme.of(context).textTheme.headline3,
  418. ))),
  419. IconButton(
  420. onPressed: () async {
  421. await callback(_index);
  422. Navigator.of(context).pop();
  423. },
  424. icon: Text(
  425. "确认",
  426. style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Theme.of(context).accentColor),
  427. )),
  428. ],
  429. ),
  430. Divider(),
  431. Expanded(
  432. child: Padding(
  433. padding: const EdgeInsets.all(16.0),
  434. child: CupertinoPicker.builder(
  435. scrollController: FixedExtentScrollController(initialItem: _index),
  436. itemExtent: 40,
  437. backgroundColor: Colors.white,
  438. onSelectedItemChanged: (index) {
  439. // print(index);
  440. _index = index + 1;
  441. },
  442. itemBuilder: (context, index) {
  443. return Text("${array[index]}");
  444. },
  445. childCount: array.length,
  446. )),
  447. ),
  448. ],
  449. ),
  450. ),
  451. );
  452. });
  453. }
  454. _showAgeDialog(int age, Function callback) {
  455. List<int> _ages = List.generate(100, (index) => index);
  456. int _index = min(100, age);
  457. return showDialog(
  458. context: context,
  459. barrierDismissible: true,
  460. builder: (context) {
  461. return AlertDialog(
  462. contentPadding: EdgeInsets.all(6.0),
  463. shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
  464. content: Container(
  465. width: 300,
  466. height: 200,
  467. child: Column(
  468. children: <Widget>[
  469. Row(
  470. children: <Widget>[
  471. IconButton(
  472. onPressed: () {
  473. Navigator.of(context).pop();
  474. },
  475. icon: Text("取消")),
  476. Expanded(
  477. child: Center(
  478. child: Text(
  479. "修改年龄",
  480. style: Theme.of(context).textTheme.headline3,
  481. ))),
  482. IconButton(
  483. onPressed: () {
  484. callback(_index);
  485. Navigator.of(context).pop();
  486. },
  487. icon: Text(
  488. "确认",
  489. style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Theme.of(context).accentColor),
  490. )),
  491. ],
  492. ),
  493. Divider(),
  494. Expanded(
  495. child: Padding(
  496. padding: const EdgeInsets.all(16.0),
  497. child: CupertinoPicker.builder(
  498. scrollController: FixedExtentScrollController(initialItem: _index),
  499. itemExtent: 40,
  500. backgroundColor: Colors.white,
  501. onSelectedItemChanged: (index) {
  502. // print(index);
  503. _index = index;
  504. },
  505. itemBuilder: (context, index) {
  506. return Center(child: Text("${_ages[index]}", style: Theme.of(context).textTheme.subtitle1?.copyWith(fontSize: 18.0),));
  507. },
  508. childCount: _ages.length,
  509. )),
  510. ),
  511. ],
  512. ),
  513. ),
  514. );
  515. });
  516. }
  517. _showMetDialog(String title) async {
  518. await showDialog(
  519. context: context,
  520. barrierDismissible: true,
  521. builder: (BuildContext context) => Dialog(
  522. child: Container(
  523. padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 25.0),
  524. decoration: circular(),
  525. child: Column(
  526. mainAxisSize: MainAxisSize.min,
  527. children: [
  528. Center(
  529. child: Text(
  530. title,
  531. style: Theme.of(context).textTheme.headline1,
  532. ),
  533. ),
  534. Padding(
  535. padding: const EdgeInsets.only(top: 20.0),
  536. child: PrimaryButton(
  537. width: 120,
  538. callback: () {
  539. Navigator.maybePop(context);
  540. },
  541. content: "知道了"),
  542. ),
  543. ],
  544. ),
  545. ),
  546. ));
  547. }
  548. Future<Location?> _location() async{
  549. AMapLocationOption locationOption = new AMapLocationOption();
  550. AMapFlutterLocation.updatePrivacyShow(true, true);
  551. AMapFlutterLocation.updatePrivacyAgree(true);
  552. AMapFlutterLocation.setApiKey(KEY_ANDROID, KEY_IOS);
  553. ///是否单次定位
  554. locationOption.onceLocation = true;
  555. ///是否需要返回逆地理信息
  556. locationOption.needAddress = true;
  557. locationOption.fullAccuracyPurposeKey = "AMapLocationScene";
  558. ///设置Android端连续定位的定位间隔
  559. locationOption.locationMode = AMapLocationMode.Hight_Accuracy;
  560. locationOption.distanceFilter = 100;
  561. locationOption.desiredAccuracy = DesiredAccuracy.Best;
  562. ///设置iOS端是否允许系统暂停定位
  563. locationOption.pausesLocationUpdatesAutomatically = false;
  564. AMapFlutterLocation _locationPlugin = new AMapFlutterLocation();
  565. ///将定位参数设置给定位插件
  566. _locationPlugin.setLocationOption(locationOption);
  567. _locationPlugin.startLocation();
  568. Map<String, Object> result = await _locationPlugin.onLocationChanged().first;
  569. print("$result");
  570. _locationPlugin.destroy();
  571. return Location.fromJson(result);
  572. }
  573. }