setting_page.dart 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'dart:math';
  4. import 'package:amap_flutter_base/amap_flutter_base.dart';
  5. import 'package:amap_flutter_map/amap_flutter_map.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:flutter/services.dart';
  8. import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
  9. import 'package:get_it/get_it.dart';
  10. import 'package:provider/provider.dart';
  11. import 'package:shared_preferences/shared_preferences.dart';
  12. import 'package:sport/pages/run/run_start.dart';
  13. import 'package:sport/pages/run/setting_healthkit_page.dart';
  14. import 'package:sport/pages/run/setting_permission_page.dart';
  15. import 'package:sport/provider/user_model.dart';
  16. import 'package:sport/router/navigator_util.dart';
  17. import 'package:sport/services/api/rest_client.dart';
  18. import 'package:sport/services/app_lifecycle_state.dart';
  19. import 'package:sport/widgets/appbar.dart';
  20. import 'package:sport/widgets/button_cancel.dart';
  21. import 'package:sport/widgets/button_primary.dart';
  22. import 'package:sport/widgets/dialog/alert_dialog.dart';
  23. import 'package:sport/widgets/image.dart';
  24. const MAP_LIST = [
  25. {"type": "normal", "name": "标准", "custom": ""},
  26. {"type": "night", "name": "夜间"},
  27. {"type": "satellite", "name": "卫星"},
  28. // {"type": "normal_blue", "name": "碧空", "custom": "blue_"},
  29. // {"type": "normal_white", "name": "纯白", "custom": "white_"},
  30. // {"type": "normal_earth", "name": "大地", "custom": "earth_"},
  31. {"type": "normal_cyan", "name": "青草", "custom": "cyan_"},
  32. {"type": "normal_dark", "name": "深黑", "custom": "dark_"},
  33. {"type": "navi", "name": "不显示"}
  34. ];
  35. int selectMapIndex(String type) => MAP_LIST.indexWhere((element) => element['type'] == type);
  36. MapType selectMapType(int index) {
  37. if (index == 1) {
  38. return MapType.night;
  39. } else if (index == 2) {
  40. return MapType.satellite;
  41. } else if (index == MAP_LIST.length - 1) {
  42. return MapType.navi;
  43. }
  44. return MapType.normal;
  45. }
  46. mixin RunSetting<T extends StatefulWidget> on State<T> {
  47. static const String KEY_START = "run_start";
  48. static const String KEY_LATITUDE = "run_latitude";
  49. static const String KEY_LONGITUDE = "run_longitude";
  50. static const String KEY_BROADCAST = "run_broadcast";
  51. static const String KEY_BROADCAST_KM = "run_broadcast_KM";
  52. static const String KEY_MAP_TYPE = "run_map_type";
  53. static const String KEY_MAP_KM = "run_map_km";
  54. static const String KEY_STEP_RATE = "run_step_rate";
  55. static const String KEY_TARGET_DURATION = "run_target_duration";
  56. static const String KEY_TARGET_KM = "run_target_km";
  57. int runBroadcastKm = 1;
  58. int runMapType = 0;
  59. bool runMapKm = true;
  60. bool runBroadcast = true;
  61. double runStepRate = 1.02;
  62. double runTargetDuration = 0;
  63. double runTargetKm = 0;
  64. Future loadSetting() async {
  65. var preferences = await SharedPreferences.getInstance();
  66. runBroadcast = preferences.getBool(KEY_BROADCAST) ?? true;
  67. runBroadcastKm = preferences.getInt(KEY_BROADCAST_KM) ?? 1;
  68. runMapType = max(0, preferences.getInt(KEY_MAP_TYPE) ?? 0);
  69. runMapKm = preferences.getBool(KEY_MAP_KM) ?? true;
  70. // runStepRate = max(0.6, preferences.getDouble(KEY_STEP_RATE) ?? 1.02);
  71. runStepRate = 1.02;
  72. runTargetDuration = preferences.getDouble(KEY_TARGET_DURATION) ?? 0;
  73. runTargetKm = preferences.getDouble(KEY_TARGET_KM) ?? 0;
  74. }
  75. bool autoLoadSetting() => true;
  76. Future<bool> requestBackground() async {
  77. var preferences = await SharedPreferences.getInstance();
  78. bool _background = preferences.getBool("RUN_BACKGROUND") ?? false;
  79. if (!_background) {
  80. var result = await showDialog(
  81. context: context,
  82. builder: (context) => CustomAlertDialog(
  83. title: "提示",
  84. child: Padding(
  85. padding: const EdgeInsets.symmetric(horizontal: 20.0),
  86. child: Text(
  87. "由于您当前的操作系统版本会管控后台设置,可能会在运动记录过程中误杀趣动进程,请您开启后台权限以保证正常运动。",
  88. style: TextStyle(fontSize: 16.0, color: Color(0xff333333)),
  89. ),
  90. ),
  91. textCancel: "稍后",
  92. textOk: "立即设置",
  93. ok: () {
  94. Navigator.pop(context, true);
  95. },
  96. ));
  97. if (result == true) {
  98. preferences.setBool("RUN_BACKGROUND", true);
  99. await NavigatorUtil.goPage(context, (context) => SettingPermissionPage());
  100. _background = true;
  101. }
  102. }
  103. return _background;
  104. // bool checkPermissions = await SystemAlertWindow.checkPermissions;
  105. // if (!checkPermissions) {
  106. // var result = await showDialog(
  107. // context: context,
  108. // builder: (context) => CustomAlertDialog(
  109. // title: "提示",
  110. // child: Padding(
  111. // padding: const EdgeInsets.symmetric(horizontal: 20.0),
  112. // child: Text(
  113. // "由于您当前的操作系统版本会管控后台设置,可能会在运动记录过程中误杀趣动进程,请您开启悬浮窗权限以保证正常运动。",
  114. // style: TextStyle(fontSize: 16.0, color: Color(0xff333333)),
  115. // ),
  116. // ),
  117. // textCancel: "稍后",
  118. // textOk: "立即设置",
  119. // ok: () {
  120. // Navigator.pop(context, true);
  121. // },
  122. // ));
  123. // if (result == true) {
  124. // await SystemAlertWindow.requestPermissions;
  125. // }
  126. // return false;
  127. // }
  128. // return true;
  129. }
  130. @override
  131. void initState() {
  132. super.initState();
  133. if (autoLoadSetting()) refreshSetting();
  134. }
  135. void refreshSetting() {
  136. loadSetting().then((value) => loadCustomData());
  137. }
  138. updateSetting() async {
  139. var preferences = await SharedPreferences.getInstance();
  140. preferences.setBool(KEY_BROADCAST, runBroadcast);
  141. preferences.setBool(KEY_MAP_KM, runMapKm);
  142. preferences.setInt(KEY_BROADCAST_KM, runBroadcastKm);
  143. preferences.setInt(KEY_MAP_TYPE, runMapType);
  144. preferences.setDouble(KEY_TARGET_DURATION, runTargetDuration);
  145. preferences.setDouble(KEY_TARGET_KM, runTargetKm);
  146. var result = await GetIt.I<RestClient>().jogSetting(
  147. setting: json.encode({
  148. KEY_BROADCAST: runBroadcast,
  149. KEY_BROADCAST_KM: runBroadcastKm,
  150. KEY_MAP_TYPE: runMapType,
  151. KEY_MAP_KM: runMapKm,
  152. KEY_STEP_RATE: runStepRate,
  153. KEY_TARGET_DURATION: runTargetDuration,
  154. KEY_TARGET_KM: runTargetKm,
  155. }));
  156. }
  157. CustomStyleOptions _customStyleOptions = CustomStyleOptions(false);
  158. CustomStyleOptions get customStyleOptions => _customStyleOptions;
  159. //加载自定义地图样式
  160. void loadCustomData() async {
  161. Map<String, String> map = MAP_LIST[this.runMapType];
  162. if (map.containsKey("custom")) {
  163. ByteData styleByteData = await rootBundle.load('assets/mapstyle/${map["custom"]}style.data');
  164. _customStyleOptions.styleData = styleByteData.buffer.asUint8List();
  165. ByteData styleExtraByteData = await rootBundle.load('assets/mapstyle/${map["custom"]}style_extra.data');
  166. _customStyleOptions.styleExtraData = styleExtraByteData.buffer.asUint8List();
  167. }
  168. //如果需要加载完成后直接展示自定义地图,可以通过setState修改CustomStyleOptions的enable为true
  169. setState(() {
  170. _customStyleOptions.enabled = map.containsKey("custom");
  171. });
  172. }
  173. }
  174. class SettingPage extends StatefulWidget {
  175. final bool run;
  176. final bool map;
  177. const SettingPage({Key? key, this.run = false, this.map = true}) : super(key: key);
  178. @override
  179. State<StatefulWidget> createState() => _PageState();
  180. }
  181. class _PageState extends LifecycleState<SettingPage> with RunSetting {
  182. @override
  183. void initState() {
  184. super.initState();
  185. }
  186. @override
  187. void dispose() {
  188. super.dispose();
  189. }
  190. @override
  191. Widget build(BuildContext context) {
  192. var list = [
  193. {"type": "normal", "name": "标准", "custom": ""},
  194. {"type": "night", "name": "夜间"},
  195. {"type": "satellite", "name": "卫星"},
  196. // {"type": "normal_blue", "name": "碧空", "custom": "blue_"},
  197. // {"type": "normal_white", "name": "纯白", "custom": "white_"},
  198. // {"type": "normal_earth", "name": "大地", "custom": "earth_"},
  199. {"type": "normal_cyan", "name": "青草", "custom": "cyan_"},
  200. {"type": "normal_dark", "name": "深黑", "custom": "dark_"},
  201. {"type": "navi", "name": "不显示"}
  202. ];
  203. var _labelStyle = TextStyle(fontSize: 16.0, color: Color(0xff333333));
  204. return WillPopScope(
  205. onWillPop: () async {
  206. updateSetting();
  207. return true;
  208. },
  209. child: Scaffold(
  210. backgroundColor: Colors.white,
  211. appBar: buildAppBar(context, title: "跑步设置"),
  212. body: Padding(
  213. padding: const EdgeInsets.all(16.0),
  214. child: CustomScrollView(
  215. slivers: [
  216. if (widget.map == true)
  217. SliverGrid.count(
  218. crossAxisCount: 3,
  219. mainAxisSpacing: 10.0,
  220. crossAxisSpacing: 10.0,
  221. childAspectRatio: .7,
  222. children: list
  223. .map((e) => GestureDetector(
  224. onTap: () async {
  225. int type = selectMapIndex(e["type"].toString());
  226. var result = await showDialog(
  227. context: context,
  228. builder: (context) {
  229. GlobalKey<_MapViewState> childKey = GlobalKey();
  230. return CustomAlertDialog(
  231. title: '是否切换地图样式',
  232. child: _MapView(
  233. type: type,
  234. key: childKey,
  235. ),
  236. textOk: '确定',
  237. ok: () {
  238. _MapViewState? state = childKey.currentState;
  239. if (state != null) {
  240. Navigator.of(context).pop(state.runMapType);
  241. } else {
  242. Navigator.of(context).pop(0);
  243. }
  244. });
  245. },
  246. );
  247. if (this.runMapType != result) {
  248. this.runMapType = result;
  249. SharedPreferences.getInstance().then((prefs) {
  250. prefs.setInt(RunSetting.KEY_MAP_TYPE, this.runMapType);
  251. setState(() {});
  252. });
  253. }
  254. },
  255. child: Column(
  256. children: [
  257. AspectRatio(
  258. aspectRatio: 1.0,
  259. child: Container(
  260. foregroundDecoration: this.runMapType == selectMapIndex(e["type"].toString())
  261. ? BoxDecoration(
  262. border: Border.all(
  263. color: Theme.of(context).accentColor,
  264. width: 2,
  265. ),
  266. borderRadius: BorderRadius.circular(10),
  267. )
  268. : null,
  269. decoration: BoxDecoration(
  270. color: Colors.white, // 底色
  271. borderRadius: new BorderRadius.all(Radius.circular(10.0)), // 也可控件一边圆角大小
  272. boxShadow: [BoxShadow(offset: Offset(0.0, 3), blurRadius: 6, spreadRadius: 0, color: Color.fromRGBO(0, 0, 0, 0.07))]),
  273. child: ClipRRect(
  274. borderRadius: BorderRadius.circular(10),
  275. child: Image.asset(
  276. "lib/assets/img/map_img_${e["type"]}.png",
  277. fit: BoxFit.cover,
  278. ),
  279. )),
  280. ),
  281. SizedBox(
  282. height: 8.0,
  283. ),
  284. Text(
  285. e["name"] as String,
  286. style: this.runMapType == selectMapIndex(e["type"].toString()) ? Theme.of(context).textTheme.subtitle1!.copyWith(color: Theme.of(context).accentColor) : Theme.of(context).textTheme.subtitle1!,
  287. )
  288. ],
  289. )))
  290. .toList(),
  291. ),
  292. SliverToBoxAdapter(
  293. child: Column(
  294. children: [
  295. SizedBox(
  296. height: 20.0,
  297. ),
  298. Divider(),
  299. ],
  300. ),
  301. ),
  302. SliverToBoxAdapter(
  303. child: Column(
  304. children: [
  305. ListTile(
  306. title: Row(
  307. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  308. children: <Widget>[
  309. Text("公里标签开关", style: _labelStyle),
  310. Switch(
  311. value: runMapKm,
  312. onChanged: (bool value) {
  313. runMapKm = !runMapKm;
  314. SharedPreferences.getInstance().then((prefs) {
  315. prefs.setBool(RunSetting.KEY_MAP_KM, runMapKm);
  316. setState(() {});
  317. });
  318. },
  319. )
  320. ],
  321. ),
  322. contentPadding: const EdgeInsets.symmetric(horizontal: 0.0),
  323. ),
  324. Divider(),
  325. ],
  326. ),
  327. ),
  328. if (widget.run == true)
  329. SliverToBoxAdapter(
  330. child: Column(
  331. children: [
  332. ListTile(
  333. title: Row(
  334. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  335. children: <Widget>[
  336. Text("语音播报开关", style: _labelStyle),
  337. Switch(
  338. value: runBroadcast,
  339. onChanged: (bool value) {
  340. runBroadcast = !runBroadcast;
  341. SharedPreferences.getInstance().then((prefs) {
  342. prefs.setBool(RunSetting.KEY_BROADCAST, runBroadcast);
  343. setState(() {});
  344. });
  345. },
  346. )
  347. ],
  348. ),
  349. contentPadding: const EdgeInsets.symmetric(horizontal: 0.0),
  350. ),
  351. if (runBroadcast == true)
  352. ListTile(
  353. title: Row(
  354. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  355. children: <Widget>[Text('距离播报', style: _labelStyle)],
  356. ),
  357. onTap: () async {
  358. var result = await showModalBottomSheet(context: context, builder: (context) => _KmWidget(), backgroundColor: Colors.transparent);
  359. if (result != null) {
  360. runBroadcastKm = result;
  361. SharedPreferences.getInstance().then((prefs) {
  362. prefs.setInt(RunSetting.KEY_BROADCAST_KM, runBroadcastKm);
  363. setState(() {});
  364. });
  365. }
  366. },
  367. trailing: Row(
  368. mainAxisSize: MainAxisSize.min,
  369. children: <Widget>[
  370. Text(
  371. "每$runBroadcastKm公里",
  372. style: Theme.of(context).textTheme.bodyText2!,
  373. ),
  374. SizedBox(
  375. width: 5,
  376. ),
  377. arrowRight5(),
  378. ],
  379. ),
  380. contentPadding: const EdgeInsets.symmetric(horizontal: 0.0),
  381. ),
  382. Divider(),
  383. ],
  384. ),
  385. ),
  386. if (Platform.isAndroid)
  387. SliverToBoxAdapter(
  388. child: Column(
  389. children: [
  390. ListTile(
  391. title: Text("跑步权限", style: _labelStyle),
  392. contentPadding: const EdgeInsets.symmetric(horizontal: 0.0),
  393. trailing: arrowRight5(),
  394. onTap: () {
  395. NavigatorUtil.goPage(context, (context) => SettingPermissionPage());
  396. },
  397. ),
  398. Divider(),
  399. ],
  400. ),
  401. ),
  402. if (Platform.isIOS)
  403. SliverToBoxAdapter(
  404. child: Column(
  405. children: [
  406. ListTile(
  407. title: Text("连接苹果健康", style: _labelStyle),
  408. contentPadding: const EdgeInsets.symmetric(horizontal: 0.0),
  409. trailing: Row(
  410. mainAxisSize: MainAxisSize.min,
  411. children: [
  412. FutureBuilder<SharedPreferences>(
  413. builder: (context, snapshot) {
  414. return Text("${snapshot.data?.containsKey("HEALTH_KIT_CONNECTED") == true ? "已连接" : "未连接"}");
  415. },
  416. future: SharedPreferences.getInstance(),
  417. ),
  418. SizedBox(
  419. width: 5,
  420. ),
  421. arrowRight5()
  422. ],
  423. ),
  424. onTap: () {
  425. NavigatorUtil.goPage(context, (context) => SettingHealthKitPage());
  426. },
  427. ),
  428. Divider(),
  429. ],
  430. ),
  431. ),
  432. ],
  433. ),
  434. ),
  435. ),
  436. );
  437. }
  438. }
  439. class _KmWidget extends StatefulWidget {
  440. @override
  441. State<StatefulWidget> createState() => _KmWidgetState();
  442. }
  443. class _KmWidgetState extends State<_KmWidget> {
  444. int _index = 0;
  445. final List<int> items = [1, 2, 5];
  446. final List<String> labels = ["1公里", "2公里", "5公里"];
  447. @override
  448. Widget build(BuildContext context) {
  449. return Container(
  450. width: double.infinity,
  451. padding: EdgeInsets.all(20.0),
  452. decoration: BoxDecoration(
  453. borderRadius: BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10)),
  454. color: Colors.white,
  455. ),
  456. child: Column(
  457. mainAxisSize: MainAxisSize.min,
  458. children: [
  459. AlignedGridView.count(
  460. padding: EdgeInsets.zero,
  461. shrinkWrap: true,
  462. physics: NeverScrollableScrollPhysics(),
  463. crossAxisCount: 3,
  464. itemCount: items.length,
  465. itemBuilder: (BuildContext context, int index) => GestureDetector(
  466. onTap: () {
  467. setState(() {
  468. _index = index;
  469. });
  470. },
  471. child: Center(
  472. child: Container(
  473. height: 44.0,
  474. decoration: BoxDecoration(
  475. image: _index == index ? DecorationImage(image: AssetImage("lib/assets/img/control_img_selected.png"), alignment: Alignment.bottomRight) : null,
  476. borderRadius: BorderRadius.circular(10),
  477. border: Border.all(
  478. color: _index == index ? Theme.of(context).accentColor : const Color(0xffCECECE),
  479. width: .5,
  480. ),
  481. ),
  482. child: Center(child: Text("${labels[index]}", style: _index == index ? Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16.0, color: Theme.of(context).accentColor) : Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16.0))),
  483. )),
  484. ),
  485. crossAxisSpacing: 12.0,
  486. mainAxisSpacing: 12.0,
  487. ),
  488. SizedBox(
  489. height: 16,
  490. ),
  491. Row(
  492. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  493. children: <Widget>[
  494. Expanded(
  495. child: CancelButton(
  496. height: 35,
  497. callback: () {
  498. Navigator.of(context).pop(false);
  499. },
  500. content: "取消"),
  501. ),
  502. SizedBox(
  503. width: 16,
  504. ),
  505. Expanded(
  506. child: PrimaryButton(
  507. height: 35,
  508. callback: () {
  509. Navigator.pop(context, items[_index]);
  510. },
  511. content: "确定"))
  512. ],
  513. )
  514. ],
  515. ),
  516. );
  517. }
  518. }
  519. class _MapView extends StatefulWidget {
  520. final int type;
  521. const _MapView({Key? key, this.type = 0}) : super(key: key);
  522. @override
  523. State<StatefulWidget> createState() => _MapViewState();
  524. }
  525. class _MapViewState extends State<_MapView> {
  526. CustomStyleOptions _customStyleOptions = CustomStyleOptions(false);
  527. CustomStyleOptions get customStyleOptions => _customStyleOptions;
  528. int runMapType = 0;
  529. //加载自定义地图样式
  530. void loadCustomData() async {
  531. Map<String, String> map = MAP_LIST[this.runMapType];
  532. if (map.containsKey("custom")) {
  533. ByteData styleByteData = await rootBundle.load('assets/mapstyle/${map["custom"]}style.data');
  534. _customStyleOptions.styleData = styleByteData.buffer.asUint8List();
  535. ByteData styleExtraByteData = await rootBundle.load('assets/mapstyle/${map["custom"]}style_extra.data');
  536. _customStyleOptions.styleExtraData = styleExtraByteData.buffer.asUint8List();
  537. }
  538. //如果需要加载完成后直接展示自定义地图,可以通过setState修改CustomStyleOptions的enable为true
  539. setState(() {
  540. _customStyleOptions.enabled = map.containsKey("custom");
  541. });
  542. }
  543. LatLng? _initLatLng;
  544. @override
  545. initState() {
  546. super.initState();
  547. runMapType = widget.type;
  548. UserModel userModel = Provider.of(context, listen: false);
  549. if (userModel.latitude != 0 && userModel.longitude != 0) {
  550. _initLatLng = LatLng(userModel.latitude, userModel.longitude);
  551. }
  552. loadCustomData();
  553. }
  554. @override
  555. Widget build(BuildContext context) {
  556. return Container(
  557. height: 150.0,
  558. child: Row(
  559. children: [
  560. GestureDetector(
  561. onTap: () {
  562. runMapType = --runMapType % MAP_LIST.length;
  563. loadCustomData();
  564. },
  565. behavior: HitTestBehavior.opaque,
  566. child: Container(
  567. width: 50,
  568. height: 20,
  569. child: Image.asset(
  570. "lib/assets/img/btn_arrow_left.png",
  571. fit: BoxFit.fitHeight,
  572. color: Color(0xff999999),
  573. ),
  574. ),
  575. ),
  576. Expanded(
  577. child: ClipRRect(
  578. borderRadius: BorderRadius.circular(10.0),
  579. child: Stack(
  580. children: [
  581. Positioned(
  582. top: 0,
  583. right: 0,
  584. left: 0,
  585. bottom: -50,
  586. child: AMapWidget(
  587. privacyStatement: amapPrivacyStatement,
  588. apiKey: AMapApiKey(androidKey: KEY_ANDROID, iosKey: KEY_IOS),
  589. mapType: selectMapType(runMapType),
  590. customStyleOptions: customStyleOptions,
  591. initialCameraPosition: _initLatLng != null ? CameraPosition(target: _initLatLng!, zoom: 15) : const CameraPosition(target: LatLng(39.909187, 116.397451), zoom: 15),
  592. buildingsEnabled: true,
  593. tiltGesturesEnabled: false,
  594. rotateGesturesEnabled: false,
  595. touchPoiEnabled: false,
  596. labelsEnabled: false,
  597. scrollGesturesEnabled: false,
  598. scaleEnabled: false,
  599. ),
  600. ),
  601. Align(
  602. alignment: Alignment.bottomCenter,
  603. child: Container(
  604. height: 26,
  605. color: Colors.black45,
  606. child: Center(
  607. child: Text(
  608. "${MAP_LIST[runMapType]['name']}",
  609. style: TextStyle(color: Colors.white, fontSize: 14.0),
  610. )),
  611. ))
  612. ],
  613. ),
  614. )),
  615. GestureDetector(
  616. onTap: () {
  617. runMapType = ++runMapType % MAP_LIST.length;
  618. loadCustomData();
  619. },
  620. behavior: HitTestBehavior.opaque,
  621. child: Container(
  622. width: 50,
  623. height: 20,
  624. child: Image.asset(
  625. "lib/assets/img/btn_arrow_right.png",
  626. fit: BoxFit.fitHeight,
  627. color: Color(0xff999999),
  628. ),
  629. ),
  630. ),
  631. ],
  632. ),
  633. );
  634. }
  635. }