track_page.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/src/widgets/framework.dart';
  4. import 'package:flutter_map/flutter_map.dart';
  5. import 'package:flutter_screenutil/flutter_screenutil.dart';
  6. import 'package:get/get.dart';
  7. import 'package:get/get_core/src/get_main.dart';
  8. import 'package:location/base/base_page.dart';
  9. import 'package:location/data/bean/user_info.dart';
  10. import 'package:location/module/track/track_controller.dart';
  11. import 'package:location/resource/assets.gen.dart';
  12. import 'package:location/resource/colors.gen.dart';
  13. import 'package:location/resource/string.gen.dart';
  14. import 'package:location/utils/common_expand.dart';
  15. import 'package:location/utils/common_style.dart';
  16. import 'package:location/utils/date_util.dart';
  17. import 'package:sliding_sheet2/sliding_sheet2.dart';
  18. import '../../router/app_pages.dart';
  19. import '../../widget/common_view.dart';
  20. import '../../widget/relative_time_text.dart';
  21. class TrackPage extends BasePage<TrackController> {
  22. const TrackPage({super.key});
  23. static void start(UserInfo userInfo) {
  24. Get.toNamed(RoutePath.track, arguments: userInfo);
  25. }
  26. @override
  27. bool immersive() {
  28. return true;
  29. }
  30. @override
  31. Widget buildBody(BuildContext context) {
  32. return Stack(
  33. children: [
  34. SizedBox(
  35. width: double.infinity,
  36. height: double.infinity,
  37. child: MapWidget(
  38. controller: controller.mapController,
  39. ),
  40. ),
  41. SafeArea(
  42. child: Container(
  43. margin: EdgeInsets.only(top: 14.w, left: 12.w),
  44. child: GestureDetector(
  45. onTap: controller.back, child: CommonView.getBackBtnView()),
  46. ),
  47. ),
  48. SlidingSheet(
  49. color: ColorName.white,
  50. controller: controller.sheetController,
  51. elevation: 10,
  52. shadowColor: Colors.black.withOpacity(0.1),
  53. cornerRadius: 18.w,
  54. snapSpec: SnapSpec(
  55. initialSnap: 1,
  56. // Enable snapping. This is true by default.
  57. snap: true,
  58. // Set custom snapping points.
  59. snappings: [168 / Get.height, 1.0],
  60. // Define to what the snappings relate to. In this case,
  61. // the total available space that the sheet can expand to.
  62. positioning: SnapPositioning.relativeToAvailableSpace,
  63. ),
  64. footerBuilder: (context, state) {
  65. return buildOperationBtn();
  66. },
  67. builder: (context, state) {
  68. return Column(
  69. children: [
  70. SizedBox(height: 5.w),
  71. Align(
  72. alignment: Alignment.center,
  73. child: Container(
  74. width: 32.w,
  75. height: 3.w,
  76. decoration: BoxDecoration(
  77. color: '#D9D9D9'.color,
  78. borderRadius: BorderRadius.circular(49.w),
  79. ),
  80. ),
  81. ),
  82. SizedBox(height: 25.w),
  83. buildTrackHeaderView(),
  84. SizedBox(
  85. width: double.infinity,
  86. height: 220.w,
  87. child: TabBarView(
  88. controller: controller.tabController,
  89. children: [
  90. buildTrackHistoryContentView(),
  91. buildTrackNowContentView()
  92. ]),
  93. )
  94. ],
  95. );
  96. },
  97. )
  98. ],
  99. );
  100. }
  101. Widget buildOperationBtn() {
  102. return Container(
  103. margin: EdgeInsets.only(bottom: 18.w, top: 9.w),
  104. child: Row(
  105. mainAxisAlignment: MainAxisAlignment.center,
  106. children: [
  107. Obx(() {
  108. return GestureDetector(
  109. onTap: controller.onTraceDetailClick,
  110. child: AnimatedOpacity(
  111. opacity: controller.currentIndex == 0 ? 1 : 0,
  112. duration: Duration(milliseconds: 250),
  113. child: AnimatedContainer(
  114. width: controller.currentIndex == 0 &&
  115. controller.isShowTraceDetailBtn
  116. ? 152.w
  117. : 0.w,
  118. height: 46.w,
  119. decoration: BoxDecoration(
  120. color: '#147B7DFF'.color,
  121. border:
  122. Border.all(color: ColorName.colorPrimary, width: 1.w),
  123. borderRadius: BorderRadius.circular(46.w),
  124. ),
  125. duration: Duration(milliseconds: 250),
  126. child: Center(
  127. child: Text(
  128. maxLines: 1,
  129. StringName.traceDetail,
  130. style: TextStyle(
  131. fontSize: 14.sp, color: ColorName.colorPrimary),
  132. ),
  133. ),
  134. ),
  135. ),
  136. );
  137. }),
  138. Obx(() {
  139. double width = 152.w;
  140. if (controller.currentIndex == 1) {
  141. width = 322.w;
  142. } else if (controller.isShowTraceDetailBtn) {
  143. width = 152.w;
  144. } else {
  145. width = 322.w;
  146. }
  147. return GestureDetector(
  148. onTap: controller.onTrackQueryClick,
  149. child: AnimatedContainer(
  150. margin: EdgeInsets.only(
  151. left: controller.currentIndex == 0 &&
  152. controller.isShowTraceDetailBtn
  153. ? 18.w
  154. : 0),
  155. duration: Duration(milliseconds: 250),
  156. width: width,
  157. height: 46.w,
  158. decoration: getPrimaryBtnDecoration(46.w),
  159. child: Center(
  160. child: Obx(() {
  161. return Text(
  162. controller.currentIndex == 0
  163. ? StringName.trackQueryPath
  164. : StringName.trackNowLocation,
  165. style: TextStyle(fontSize: 14.sp, color: Colors.white),
  166. );
  167. }),
  168. ),
  169. ),
  170. );
  171. })
  172. ],
  173. ),
  174. );
  175. }
  176. Widget buildTrackHeaderView() {
  177. return Row(
  178. children: [
  179. SizedBox(width: 14.w),
  180. Obx(() {
  181. return Image(
  182. image: controller.userInfo?.isMine == true
  183. ? Assets.images.iconDefaultMineAvatar.provider()
  184. : Assets.images.iconDefaultFriendAvatar.provider(),
  185. width: 32.w,
  186. height: 32.w);
  187. }),
  188. SizedBox(width: 10.w),
  189. Expanded(
  190. child: Text(
  191. '${controller.userInfo?.getUserNickName() ?? ''}的轨迹',
  192. style: TextStyle(
  193. overflow: TextOverflow.ellipsis,
  194. fontSize: 16.sp,
  195. color: ColorName.black80,
  196. fontWeight: FontWeight.bold),
  197. ),
  198. ),
  199. buildOperationTabBar(),
  200. SizedBox(width: 12.w),
  201. ],
  202. );
  203. }
  204. Widget buildOperationTabBar() {
  205. return IntrinsicWidth(
  206. child: Container(
  207. padding: EdgeInsets.all(2.w),
  208. decoration: BoxDecoration(
  209. color: '#F3F3F3'.color,
  210. borderRadius: BorderRadius.circular(48.w),
  211. ),
  212. height: 32.w,
  213. child: TabBar(
  214. controller: controller.tabController,
  215. indicator: BoxDecoration(
  216. color: ColorName.colorPrimary,
  217. borderRadius: BorderRadius.circular(48.w),
  218. ),
  219. dividerHeight: 0,
  220. indicatorSize: TabBarIndicatorSize.tab,
  221. unselectedLabelStyle:
  222. TextStyle(fontSize: 14.sp, color: '#666666'.color),
  223. labelStyle: TextStyle(fontSize: 14.sp, color: ColorName.white),
  224. tabs: [
  225. Tab(text: StringName.trackHistory),
  226. Tab(text: StringName.trackNowLocation)
  227. ],
  228. ),
  229. ),
  230. );
  231. }
  232. Widget buildTrackHistoryContentView() {
  233. return Column(
  234. children: [
  235. SizedBox(height: 10.w),
  236. Builder(builder: (context) {
  237. return GestureDetector(
  238. onTap: () {
  239. controller.onTrackStartTimeClick(context);
  240. },
  241. child: Padding(
  242. padding: EdgeInsets.symmetric(vertical: 9.w),
  243. child: Row(
  244. children: [
  245. SizedBox(width: 14.w),
  246. Text(
  247. StringName.trackStartTime,
  248. style: TextStyle(
  249. fontSize: 14.sp,
  250. color: ColorName.black80,
  251. fontWeight: FontWeight.bold),
  252. ),
  253. Spacer(),
  254. Obx(() {
  255. return Text(
  256. controller.trackStartTime?.format('yyyy-MM-dd HH:mm') ??
  257. '',
  258. style:
  259. TextStyle(fontSize: 14.sp, color: ColorName.black50),
  260. );
  261. }),
  262. SizedBox(width: 6.w),
  263. Assets.images.iconTrackSelectTimeArrow
  264. .image(width: 16.w, height: 16.w),
  265. SizedBox(width: 14.w),
  266. ],
  267. ),
  268. ),
  269. );
  270. }),
  271. Builder(builder: (context) {
  272. return GestureDetector(
  273. onTap: () {
  274. controller.onTrackEndTimeClick(context);
  275. },
  276. child: Padding(
  277. padding: EdgeInsets.symmetric(vertical: 9.w),
  278. child: Row(
  279. children: [
  280. SizedBox(width: 14.w),
  281. Text(
  282. StringName.trackEndTime,
  283. style: TextStyle(
  284. fontSize: 14.sp,
  285. color: ColorName.black80,
  286. fontWeight: FontWeight.bold),
  287. ),
  288. Spacer(),
  289. Obx(() {
  290. return Text(
  291. controller.trackEndTime?.format('yyyy-MM-dd HH:mm') ?? '',
  292. style:
  293. TextStyle(fontSize: 14.sp, color: ColorName.black50),
  294. );
  295. }),
  296. SizedBox(width: 6.w),
  297. Assets.images.iconTrackSelectTimeArrow
  298. .image(width: 16.w, height: 16.w),
  299. SizedBox(width: 14.w),
  300. ],
  301. ),
  302. ),
  303. );
  304. }),
  305. SizedBox(height: 12.w),
  306. Obx(() {
  307. return Visibility(
  308. visible: controller.startAddress != null ||
  309. controller.endAddress != null,
  310. child: Container(
  311. padding: EdgeInsets.all(12.w),
  312. margin: EdgeInsets.symmetric(horizontal: 14.w),
  313. width: double.infinity,
  314. decoration: BoxDecoration(
  315. color: '#E5F8F8F8'.color,
  316. borderRadius: BorderRadius.circular(12.w),
  317. ),
  318. child: Column(
  319. children: [
  320. buildAddressInfoView(
  321. '#12C172'.color,
  322. StringName.trackStartLocation,
  323. controller.startAddress ?? ''),
  324. Align(
  325. alignment: Alignment.centerLeft,
  326. child: Container(
  327. margin: EdgeInsets.only(left: 3.5.w),
  328. child: Assets.images.bgTrackLocationTie
  329. .image(width: 1.5.w))),
  330. buildAddressInfoView(
  331. '#F3353A'.color,
  332. StringName.trackEndLocation,
  333. controller.endAddress ?? ''),
  334. ],
  335. )),
  336. );
  337. })
  338. ],
  339. );
  340. }
  341. Widget buildAddressInfoView(Color color, String title, String content) {
  342. return Row(
  343. children: [
  344. Container(
  345. width: 10.w,
  346. height: 10.w,
  347. decoration: BoxDecoration(
  348. shape: BoxShape.circle,
  349. border: Border.all(color: color, width: 2.w),
  350. ),
  351. ),
  352. SizedBox(width: 11.w),
  353. Text(title,
  354. style: TextStyle(
  355. fontSize: 13.sp,
  356. color: ColorName.black80,
  357. fontWeight: FontWeight.bold)),
  358. Expanded(
  359. child: Text(content,
  360. style: TextStyle(fontSize: 13.sp, color: ColorName.black70)),
  361. )
  362. ],
  363. );
  364. }
  365. Widget buildTrackNowContentView() {
  366. return Column(
  367. children: [
  368. SizedBox(height: 20.w),
  369. Row(
  370. children: [
  371. SizedBox(width: 12.w),
  372. Assets.images.iconTrackLocationNow.image(width: 20.w, height: 20.w),
  373. SizedBox(width: 3.w),
  374. Obx(() {
  375. return RelativeTimeText(
  376. startPerchText: '当前位置·',
  377. endPerchText:
  378. controller.currentLocation?.lastUpdateTime == null
  379. ? ''
  380. : '更新',
  381. timestamp: controller.currentLocation?.lastUpdateTime,
  382. updateInterval: Duration(minutes: 1),
  383. style: TextStyle(
  384. fontSize: 15.sp,
  385. color: '#333333'.color,
  386. fontWeight: FontWeight.bold));
  387. })
  388. ],
  389. ),
  390. SizedBox(height: 16.w),
  391. Container(
  392. width: double.infinity,
  393. margin: EdgeInsets.symmetric(horizontal: 12.w),
  394. padding: EdgeInsets.all(14.w),
  395. decoration: BoxDecoration(
  396. color: '#F9F9F9'.color,
  397. borderRadius: BorderRadius.circular(6.w),
  398. ),
  399. child: Obx(() {
  400. return Text(controller.currentLocation?.address ?? '--',
  401. style: TextStyle(fontSize: 14.sp, color: '#666666'.color));
  402. }),
  403. )
  404. ],
  405. );
  406. }
  407. }