123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673 |
- import 'dart:convert';
- import 'dart:io';
- import 'dart:math';
- import 'package:amap_flutter_base/amap_flutter_base.dart';
- import 'package:amap_flutter_map/amap_flutter_map.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
- import 'package:get_it/get_it.dart';
- import 'package:provider/provider.dart';
- import 'package:shared_preferences/shared_preferences.dart';
- import 'package:sport/pages/run/run_start.dart';
- import 'package:sport/pages/run/setting_healthkit_page.dart';
- import 'package:sport/pages/run/setting_permission_page.dart';
- import 'package:sport/provider/user_model.dart';
- import 'package:sport/router/navigator_util.dart';
- import 'package:sport/services/api/rest_client.dart';
- import 'package:sport/services/app_lifecycle_state.dart';
- import 'package:sport/widgets/appbar.dart';
- import 'package:sport/widgets/button_cancel.dart';
- import 'package:sport/widgets/button_primary.dart';
- import 'package:sport/widgets/dialog/alert_dialog.dart';
- import 'package:sport/widgets/image.dart';
- const MAP_LIST = [
- {"type": "normal", "name": "标准", "custom": ""},
- {"type": "night", "name": "夜间"},
- {"type": "satellite", "name": "卫星"},
- // {"type": "normal_blue", "name": "碧空", "custom": "blue_"},
- // {"type": "normal_white", "name": "纯白", "custom": "white_"},
- // {"type": "normal_earth", "name": "大地", "custom": "earth_"},
- {"type": "normal_cyan", "name": "青草", "custom": "cyan_"},
- {"type": "normal_dark", "name": "深黑", "custom": "dark_"},
- {"type": "navi", "name": "不显示"}
- ];
- int selectMapIndex(String type) => MAP_LIST.indexWhere((element) => element['type'] == type);
- MapType selectMapType(int index) {
- if (index == 1) {
- return MapType.night;
- } else if (index == 2) {
- return MapType.satellite;
- } else if (index == MAP_LIST.length - 1) {
- return MapType.navi;
- }
- return MapType.normal;
- }
- mixin RunSetting<T extends StatefulWidget> on State<T> {
- static const String KEY_START = "run_start";
- static const String KEY_LATITUDE = "run_latitude";
- static const String KEY_LONGITUDE = "run_longitude";
- static const String KEY_BROADCAST = "run_broadcast";
- static const String KEY_BROADCAST_KM = "run_broadcast_KM";
- static const String KEY_MAP_TYPE = "run_map_type";
- static const String KEY_MAP_KM = "run_map_km";
- static const String KEY_STEP_RATE = "run_step_rate";
- static const String KEY_TARGET_DURATION = "run_target_duration";
- static const String KEY_TARGET_KM = "run_target_km";
- int runBroadcastKm = 1;
- int runMapType = 0;
- bool runMapKm = true;
- bool runBroadcast = true;
- double runStepRate = 1.02;
- double runTargetDuration = 0;
- double runTargetKm = 0;
- Future loadSetting() async {
- var preferences = await SharedPreferences.getInstance();
- runBroadcast = preferences.getBool(KEY_BROADCAST) ?? true;
- runBroadcastKm = preferences.getInt(KEY_BROADCAST_KM) ?? 1;
- runMapType = max(0, preferences.getInt(KEY_MAP_TYPE) ?? 0);
- runMapKm = preferences.getBool(KEY_MAP_KM) ?? true;
- // runStepRate = max(0.6, preferences.getDouble(KEY_STEP_RATE) ?? 1.02);
- runStepRate = 1.02;
- runTargetDuration = preferences.getDouble(KEY_TARGET_DURATION) ?? 0;
- runTargetKm = preferences.getDouble(KEY_TARGET_KM) ?? 0;
- }
- bool autoLoadSetting() => true;
- Future<bool> requestBackground() async {
- var preferences = await SharedPreferences.getInstance();
- bool _background = preferences.getBool("RUN_BACKGROUND") ?? false;
- if (!_background) {
- var result = await showDialog(
- context: context,
- builder: (context) => CustomAlertDialog(
- title: "提示",
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 20.0),
- child: Text(
- "由于您当前的操作系统版本会管控后台设置,可能会在运动记录过程中误杀趣动进程,请您开启后台权限以保证正常运动。",
- style: TextStyle(fontSize: 16.0, color: Color(0xff333333)),
- ),
- ),
- textCancel: "稍后",
- textOk: "立即设置",
- ok: () {
- Navigator.pop(context, true);
- },
- ));
- if (result == true) {
- preferences.setBool("RUN_BACKGROUND", true);
- await NavigatorUtil.goPage(context, (context) => SettingPermissionPage());
- _background = true;
- }
- }
- return _background;
- // bool checkPermissions = await SystemAlertWindow.checkPermissions;
- // if (!checkPermissions) {
- // var result = await showDialog(
- // context: context,
- // builder: (context) => CustomAlertDialog(
- // title: "提示",
- // child: Padding(
- // padding: const EdgeInsets.symmetric(horizontal: 20.0),
- // child: Text(
- // "由于您当前的操作系统版本会管控后台设置,可能会在运动记录过程中误杀趣动进程,请您开启悬浮窗权限以保证正常运动。",
- // style: TextStyle(fontSize: 16.0, color: Color(0xff333333)),
- // ),
- // ),
- // textCancel: "稍后",
- // textOk: "立即设置",
- // ok: () {
- // Navigator.pop(context, true);
- // },
- // ));
- // if (result == true) {
- // await SystemAlertWindow.requestPermissions;
- // }
- // return false;
- // }
- // return true;
- }
- @override
- void initState() {
- super.initState();
- if (autoLoadSetting()) refreshSetting();
- }
- void refreshSetting() {
- loadSetting().then((value) => loadCustomData());
- }
- updateSetting() async {
- var preferences = await SharedPreferences.getInstance();
- preferences.setBool(KEY_BROADCAST, runBroadcast);
- preferences.setBool(KEY_MAP_KM, runMapKm);
- preferences.setInt(KEY_BROADCAST_KM, runBroadcastKm);
- preferences.setInt(KEY_MAP_TYPE, runMapType);
- preferences.setDouble(KEY_TARGET_DURATION, runTargetDuration);
- preferences.setDouble(KEY_TARGET_KM, runTargetKm);
- var result = await GetIt.I<RestClient>().jogSetting(
- setting: json.encode({
- KEY_BROADCAST: runBroadcast,
- KEY_BROADCAST_KM: runBroadcastKm,
- KEY_MAP_TYPE: runMapType,
- KEY_MAP_KM: runMapKm,
- KEY_STEP_RATE: runStepRate,
- KEY_TARGET_DURATION: runTargetDuration,
- KEY_TARGET_KM: runTargetKm,
- }));
- }
- CustomStyleOptions _customStyleOptions = CustomStyleOptions(false);
- CustomStyleOptions get customStyleOptions => _customStyleOptions;
- //加载自定义地图样式
- void loadCustomData() async {
- Map<String, String> map = MAP_LIST[this.runMapType];
- if (map.containsKey("custom")) {
- ByteData styleByteData = await rootBundle.load('assets/mapstyle/${map["custom"]}style.data');
- _customStyleOptions.styleData = styleByteData.buffer.asUint8List();
- ByteData styleExtraByteData = await rootBundle.load('assets/mapstyle/${map["custom"]}style_extra.data');
- _customStyleOptions.styleExtraData = styleExtraByteData.buffer.asUint8List();
- }
- //如果需要加载完成后直接展示自定义地图,可以通过setState修改CustomStyleOptions的enable为true
- setState(() {
- _customStyleOptions.enabled = map.containsKey("custom");
- });
- }
- }
- class SettingPage extends StatefulWidget {
- final bool run;
- final bool map;
- const SettingPage({Key? key, this.run = false, this.map = true}) : super(key: key);
- @override
- State<StatefulWidget> createState() => _PageState();
- }
- class _PageState extends LifecycleState<SettingPage> with RunSetting {
- @override
- void initState() {
- super.initState();
- }
- @override
- void dispose() {
- super.dispose();
- }
- @override
- Widget build(BuildContext context) {
- var list = [
- {"type": "normal", "name": "标准", "custom": ""},
- {"type": "night", "name": "夜间"},
- {"type": "satellite", "name": "卫星"},
- // {"type": "normal_blue", "name": "碧空", "custom": "blue_"},
- // {"type": "normal_white", "name": "纯白", "custom": "white_"},
- // {"type": "normal_earth", "name": "大地", "custom": "earth_"},
- {"type": "normal_cyan", "name": "青草", "custom": "cyan_"},
- {"type": "normal_dark", "name": "深黑", "custom": "dark_"},
- {"type": "navi", "name": "不显示"}
- ];
- var _labelStyle = TextStyle(fontSize: 16.0, color: Color(0xff333333));
- return WillPopScope(
- onWillPop: () async {
- updateSetting();
- return true;
- },
- child: Scaffold(
- backgroundColor: Colors.white,
- appBar: buildAppBar(context, title: "跑步设置"),
- body: Padding(
- padding: const EdgeInsets.all(16.0),
- child: CustomScrollView(
- slivers: [
- if (widget.map == true)
- SliverGrid.count(
- crossAxisCount: 3,
- mainAxisSpacing: 10.0,
- crossAxisSpacing: 10.0,
- childAspectRatio: .7,
- children: list
- .map((e) => GestureDetector(
- onTap: () async {
- int type = selectMapIndex(e["type"].toString());
- var result = await showDialog(
- context: context,
- builder: (context) {
- GlobalKey<_MapViewState> childKey = GlobalKey();
- return CustomAlertDialog(
- title: '是否切换地图样式',
- child: _MapView(
- type: type,
- key: childKey,
- ),
- textOk: '确定',
- ok: () {
- _MapViewState? state = childKey.currentState;
- if (state != null) {
- Navigator.of(context).pop(state.runMapType);
- } else {
- Navigator.of(context).pop(0);
- }
- });
- },
- );
- if (this.runMapType != result) {
- this.runMapType = result;
- SharedPreferences.getInstance().then((prefs) {
- prefs.setInt(RunSetting.KEY_MAP_TYPE, this.runMapType);
- setState(() {});
- });
- }
- },
- child: Column(
- children: [
- AspectRatio(
- aspectRatio: 1.0,
- child: Container(
- foregroundDecoration: this.runMapType == selectMapIndex(e["type"].toString())
- ? BoxDecoration(
- border: Border.all(
- color: Theme.of(context).accentColor,
- width: 2,
- ),
- borderRadius: BorderRadius.circular(10),
- )
- : null,
- decoration: BoxDecoration(
- color: Colors.white, // 底色
- borderRadius: new BorderRadius.all(Radius.circular(10.0)), // 也可控件一边圆角大小
- boxShadow: [BoxShadow(offset: Offset(0.0, 3), blurRadius: 6, spreadRadius: 0, color: Color.fromRGBO(0, 0, 0, 0.07))]),
- child: ClipRRect(
- borderRadius: BorderRadius.circular(10),
- child: Image.asset(
- "lib/assets/img/map_img_${e["type"]}.png",
- fit: BoxFit.cover,
- ),
- )),
- ),
- SizedBox(
- height: 8.0,
- ),
- Text(
- e["name"] as String,
- style: this.runMapType == selectMapIndex(e["type"].toString()) ? Theme.of(context).textTheme.subtitle1!.copyWith(color: Theme.of(context).accentColor) : Theme.of(context).textTheme.subtitle1!,
- )
- ],
- )))
- .toList(),
- ),
- SliverToBoxAdapter(
- child: Column(
- children: [
- SizedBox(
- height: 20.0,
- ),
- Divider(),
- ],
- ),
- ),
- SliverToBoxAdapter(
- child: Column(
- children: [
- ListTile(
- title: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: <Widget>[
- Text("公里标签开关", style: _labelStyle),
- Switch(
- value: runMapKm,
- onChanged: (bool value) {
- runMapKm = !runMapKm;
- SharedPreferences.getInstance().then((prefs) {
- prefs.setBool(RunSetting.KEY_MAP_KM, runMapKm);
- setState(() {});
- });
- },
- )
- ],
- ),
- contentPadding: const EdgeInsets.symmetric(horizontal: 0.0),
- ),
- Divider(),
- ],
- ),
- ),
- if (widget.run == true)
- SliverToBoxAdapter(
- child: Column(
- children: [
- ListTile(
- title: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: <Widget>[
- Text("语音播报开关", style: _labelStyle),
- Switch(
- value: runBroadcast,
- onChanged: (bool value) {
- runBroadcast = !runBroadcast;
- SharedPreferences.getInstance().then((prefs) {
- prefs.setBool(RunSetting.KEY_BROADCAST, runBroadcast);
- setState(() {});
- });
- },
- )
- ],
- ),
- contentPadding: const EdgeInsets.symmetric(horizontal: 0.0),
- ),
- if (runBroadcast == true)
- ListTile(
- title: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: <Widget>[Text('距离播报', style: _labelStyle)],
- ),
- onTap: () async {
- var result = await showModalBottomSheet(context: context, builder: (context) => _KmWidget(), backgroundColor: Colors.transparent);
- if (result != null) {
- runBroadcastKm = result;
- SharedPreferences.getInstance().then((prefs) {
- prefs.setInt(RunSetting.KEY_BROADCAST_KM, runBroadcastKm);
- setState(() {});
- });
- }
- },
- trailing: Row(
- mainAxisSize: MainAxisSize.min,
- children: <Widget>[
- Text(
- "每$runBroadcastKm公里",
- style: Theme.of(context).textTheme.bodyText2!,
- ),
- SizedBox(
- width: 5,
- ),
- arrowRight5(),
- ],
- ),
- contentPadding: const EdgeInsets.symmetric(horizontal: 0.0),
- ),
- Divider(),
- ],
- ),
- ),
- if (Platform.isAndroid)
- SliverToBoxAdapter(
- child: Column(
- children: [
- ListTile(
- title: Text("跑步权限", style: _labelStyle),
- contentPadding: const EdgeInsets.symmetric(horizontal: 0.0),
- trailing: arrowRight5(),
- onTap: () {
- NavigatorUtil.goPage(context, (context) => SettingPermissionPage());
- },
- ),
- Divider(),
- ],
- ),
- ),
- if (Platform.isIOS)
- SliverToBoxAdapter(
- child: Column(
- children: [
- ListTile(
- title: Text("连接苹果健康", style: _labelStyle),
- contentPadding: const EdgeInsets.symmetric(horizontal: 0.0),
- trailing: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- FutureBuilder<SharedPreferences>(
- builder: (context, snapshot) {
- return Text("${snapshot.data?.containsKey("HEALTH_KIT_CONNECTED") == true ? "已连接" : "未连接"}");
- },
- future: SharedPreferences.getInstance(),
- ),
- SizedBox(
- width: 5,
- ),
- arrowRight5()
- ],
- ),
- onTap: () {
- NavigatorUtil.goPage(context, (context) => SettingHealthKitPage());
- },
- ),
- Divider(),
- ],
- ),
- ),
- ],
- ),
- ),
- ),
- );
- }
- }
- class _KmWidget extends StatefulWidget {
- @override
- State<StatefulWidget> createState() => _KmWidgetState();
- }
- class _KmWidgetState extends State<_KmWidget> {
- int _index = 0;
- final List<int> items = [1, 2, 5];
- final List<String> labels = ["1公里", "2公里", "5公里"];
- @override
- Widget build(BuildContext context) {
- return Container(
- width: double.infinity,
- padding: EdgeInsets.all(20.0),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10)),
- color: Colors.white,
- ),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- AlignedGridView.count(
- padding: EdgeInsets.zero,
- shrinkWrap: true,
- physics: NeverScrollableScrollPhysics(),
- crossAxisCount: 3,
- itemCount: items.length,
- itemBuilder: (BuildContext context, int index) => GestureDetector(
- onTap: () {
- setState(() {
- _index = index;
- });
- },
- child: Center(
- child: Container(
- height: 44.0,
- decoration: BoxDecoration(
- image: _index == index ? DecorationImage(image: AssetImage("lib/assets/img/control_img_selected.png"), alignment: Alignment.bottomRight) : null,
- borderRadius: BorderRadius.circular(10),
- border: Border.all(
- color: _index == index ? Theme.of(context).accentColor : const Color(0xffCECECE),
- width: .5,
- ),
- ),
- 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))),
- )),
- ),
- crossAxisSpacing: 12.0,
- mainAxisSpacing: 12.0,
- ),
- SizedBox(
- height: 16,
- ),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: <Widget>[
- Expanded(
- child: CancelButton(
- height: 35,
- callback: () {
- Navigator.of(context).pop(false);
- },
- content: "取消"),
- ),
- SizedBox(
- width: 16,
- ),
- Expanded(
- child: PrimaryButton(
- height: 35,
- callback: () {
- Navigator.pop(context, items[_index]);
- },
- content: "确定"))
- ],
- )
- ],
- ),
- );
- }
- }
- class _MapView extends StatefulWidget {
- final int type;
- const _MapView({Key? key, this.type = 0}) : super(key: key);
- @override
- State<StatefulWidget> createState() => _MapViewState();
- }
- class _MapViewState extends State<_MapView> {
- CustomStyleOptions _customStyleOptions = CustomStyleOptions(false);
- CustomStyleOptions get customStyleOptions => _customStyleOptions;
- int runMapType = 0;
- //加载自定义地图样式
- void loadCustomData() async {
- Map<String, String> map = MAP_LIST[this.runMapType];
- if (map.containsKey("custom")) {
- ByteData styleByteData = await rootBundle.load('assets/mapstyle/${map["custom"]}style.data');
- _customStyleOptions.styleData = styleByteData.buffer.asUint8List();
- ByteData styleExtraByteData = await rootBundle.load('assets/mapstyle/${map["custom"]}style_extra.data');
- _customStyleOptions.styleExtraData = styleExtraByteData.buffer.asUint8List();
- }
- //如果需要加载完成后直接展示自定义地图,可以通过setState修改CustomStyleOptions的enable为true
- setState(() {
- _customStyleOptions.enabled = map.containsKey("custom");
- });
- }
- LatLng? _initLatLng;
- @override
- initState() {
- super.initState();
- runMapType = widget.type;
- UserModel userModel = Provider.of(context, listen: false);
- if (userModel.latitude != 0 && userModel.longitude != 0) {
- _initLatLng = LatLng(userModel.latitude, userModel.longitude);
- }
- loadCustomData();
- }
- @override
- Widget build(BuildContext context) {
- return Container(
- height: 150.0,
- child: Row(
- children: [
- GestureDetector(
- onTap: () {
- runMapType = --runMapType % MAP_LIST.length;
- loadCustomData();
- },
- behavior: HitTestBehavior.opaque,
- child: Container(
- width: 50,
- height: 20,
- child: Image.asset(
- "lib/assets/img/btn_arrow_left.png",
- fit: BoxFit.fitHeight,
- color: Color(0xff999999),
- ),
- ),
- ),
- Expanded(
- child: ClipRRect(
- borderRadius: BorderRadius.circular(10.0),
- child: Stack(
- children: [
- Positioned(
- top: 0,
- right: 0,
- left: 0,
- bottom: -50,
- child: AMapWidget(
- privacyStatement: amapPrivacyStatement,
- apiKey: AMapApiKey(androidKey: KEY_ANDROID, iosKey: KEY_IOS),
- mapType: selectMapType(runMapType),
- customStyleOptions: customStyleOptions,
- initialCameraPosition: _initLatLng != null ? CameraPosition(target: _initLatLng!, zoom: 15) : const CameraPosition(target: LatLng(39.909187, 116.397451), zoom: 15),
- buildingsEnabled: true,
- tiltGesturesEnabled: false,
- rotateGesturesEnabled: false,
- touchPoiEnabled: false,
- labelsEnabled: false,
- scrollGesturesEnabled: false,
- scaleEnabled: false,
- ),
- ),
- Align(
- alignment: Alignment.bottomCenter,
- child: Container(
- height: 26,
- color: Colors.black45,
- child: Center(
- child: Text(
- "${MAP_LIST[runMapType]['name']}",
- style: TextStyle(color: Colors.white, fontSize: 14.0),
- )),
- ))
- ],
- ),
- )),
- GestureDetector(
- onTap: () {
- runMapType = ++runMapType % MAP_LIST.length;
- loadCustomData();
- },
- behavior: HitTestBehavior.opaque,
- child: Container(
- width: 50,
- height: 20,
- child: Image.asset(
- "lib/assets/img/btn_arrow_right.png",
- fit: BoxFit.fitHeight,
- color: Color(0xff999999),
- ),
- ),
- ),
- ],
- ),
- );
- }
- }
|