track_page.dart 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  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: [SnapSpec.headerFooterSnap, 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. headerBuilder: (context, state) {
  68. return IntrinsicHeight(
  69. child: Column(
  70. children: [
  71. SizedBox(height: 5.w),
  72. Align(
  73. alignment: Alignment.center,
  74. child: Container(
  75. width: 32.w,
  76. height: 3.w,
  77. decoration: BoxDecoration(
  78. color: '#D9D9D9'.color,
  79. borderRadius: BorderRadius.circular(49.w),
  80. ),
  81. ),
  82. ),
  83. SizedBox(height: 25.w),
  84. buildTrackHeaderView(),
  85. ],
  86. ));
  87. },
  88. builder: (context, state) {
  89. return Column(
  90. children: [
  91. SizedBox(
  92. width: double.infinity,
  93. height: 220.w,
  94. child: TabBarView(
  95. controller: controller.tabController,
  96. children: [
  97. buildTrackHistoryContentView(),
  98. buildTrackNowContentView()
  99. ]),
  100. )
  101. ],
  102. );
  103. },
  104. ),
  105. SafeArea(
  106. child: Container(
  107. width: double.infinity,
  108. padding: EdgeInsets.only(right: 12.w),
  109. child: Column(
  110. crossAxisAlignment: CrossAxisAlignment.end,
  111. children: [
  112. Assets.images.iconAmapLogo.image(height: 22.w),
  113. Container(
  114. padding: EdgeInsets.all(1.w),
  115. color: ColorName.white30,
  116. child: Text(StringName.locationCo,
  117. style:
  118. TextStyle(fontSize: 11.sp, color: ColorName.black40)),
  119. )
  120. ],
  121. ),
  122. ),
  123. ),
  124. ],
  125. );
  126. }
  127. Widget buildOperationBtn() {
  128. return Container(
  129. margin: EdgeInsets.only(bottom: 18.w, top: 9.w),
  130. child: Row(
  131. mainAxisAlignment: MainAxisAlignment.center,
  132. children: [
  133. Obx(() {
  134. return GestureDetector(
  135. onTap: controller.onTraceDetailClick,
  136. child: AnimatedOpacity(
  137. opacity: controller.currentIndex == 0 ? 1 : 0,
  138. duration: Duration(milliseconds: 250),
  139. child: AnimatedContainer(
  140. width: controller.currentIndex == 0 &&
  141. controller.isShowTraceDetailBtn
  142. ? 152.w
  143. : 0.w,
  144. height: 46.w,
  145. decoration: BoxDecoration(
  146. color: '#147B7DFF'.color,
  147. border:
  148. Border.all(color: ColorName.colorPrimary, width: 1.w),
  149. borderRadius: BorderRadius.circular(46.w),
  150. ),
  151. duration: Duration(milliseconds: 250),
  152. child: Center(
  153. child: Text(
  154. maxLines: 1,
  155. StringName.traceDetail,
  156. style: TextStyle(
  157. fontSize: 14.sp, color: ColorName.colorPrimary),
  158. ),
  159. ),
  160. ),
  161. ),
  162. );
  163. }),
  164. Obx(() {
  165. double width = 152.w;
  166. if (controller.currentIndex == 1) {
  167. width = 322.w;
  168. } else if (controller.isShowTraceDetailBtn) {
  169. width = 152.w;
  170. } else {
  171. width = 322.w;
  172. }
  173. return GestureDetector(
  174. onTap: controller.onTrackQueryClick,
  175. child: AnimatedContainer(
  176. margin: EdgeInsets.only(
  177. left: controller.currentIndex == 0 &&
  178. controller.isShowTraceDetailBtn
  179. ? 18.w
  180. : 0),
  181. duration: Duration(milliseconds: 250),
  182. width: width,
  183. height: 46.w,
  184. decoration: getPrimaryBtnDecoration(46.w),
  185. child: Center(
  186. child: Obx(() {
  187. return Text(
  188. controller.currentIndex == 0
  189. ? StringName.trackQueryPath
  190. : StringName.trackNowLocation,
  191. style: TextStyle(fontSize: 14.sp, color: Colors.white),
  192. );
  193. }),
  194. ),
  195. ),
  196. );
  197. })
  198. ],
  199. ),
  200. );
  201. }
  202. Widget buildTrackHeaderView() {
  203. return Row(
  204. children: [
  205. SizedBox(width: 14.w),
  206. Obx(() {
  207. return Image(
  208. image: controller.userInfo?.isMine == true
  209. ? Assets.images.iconDefaultMineAvatar.provider()
  210. : Assets.images.iconDefaultFriendAvatar.provider(),
  211. width: 32.w,
  212. height: 32.w);
  213. }),
  214. SizedBox(width: 10.w),
  215. Expanded(
  216. child: Text(
  217. '${controller.userInfo?.getUserNickName() ?? ''}的轨迹',
  218. style: TextStyle(
  219. overflow: TextOverflow.ellipsis,
  220. fontSize: 16.sp,
  221. color: ColorName.black80,
  222. fontWeight: FontWeight.bold),
  223. ),
  224. ),
  225. buildOperationTabBar(),
  226. SizedBox(width: 12.w),
  227. ],
  228. );
  229. }
  230. Widget buildOperationTabBar() {
  231. return IntrinsicWidth(
  232. child: Container(
  233. padding: EdgeInsets.all(2.w),
  234. decoration: BoxDecoration(
  235. color: '#F3F3F3'.color,
  236. borderRadius: BorderRadius.circular(48.w),
  237. ),
  238. height: 32.w,
  239. child: TabBar(
  240. controller: controller.tabController,
  241. indicator: BoxDecoration(
  242. color: ColorName.colorPrimary,
  243. borderRadius: BorderRadius.circular(48.w),
  244. ),
  245. dividerHeight: 0,
  246. indicatorSize: TabBarIndicatorSize.tab,
  247. unselectedLabelStyle:
  248. TextStyle(fontSize: 14.sp, color: '#666666'.color),
  249. labelStyle: TextStyle(fontSize: 14.sp, color: ColorName.white),
  250. tabs: [
  251. Tab(text: StringName.trackHistory),
  252. Tab(text: StringName.trackNowLocation)
  253. ],
  254. ),
  255. ),
  256. );
  257. }
  258. Widget buildTrackHistoryContentView() {
  259. return Column(
  260. children: [
  261. SizedBox(height: 10.w),
  262. Builder(builder: (context) {
  263. return GestureDetector(
  264. onTap: () {
  265. controller.onTrackStartTimeClick(context);
  266. },
  267. child: Padding(
  268. padding: EdgeInsets.symmetric(vertical: 9.w),
  269. child: Row(
  270. children: [
  271. SizedBox(width: 14.w),
  272. Text(
  273. StringName.trackStartTime,
  274. style: TextStyle(
  275. fontSize: 14.sp,
  276. color: ColorName.black80,
  277. fontWeight: FontWeight.bold),
  278. ),
  279. Spacer(),
  280. Obx(() {
  281. return Text(
  282. controller.trackStartTime?.format('yyyy-MM-dd HH:mm') ??
  283. '',
  284. style:
  285. TextStyle(fontSize: 14.sp, color: ColorName.black50),
  286. );
  287. }),
  288. SizedBox(width: 6.w),
  289. Assets.images.iconTrackSelectTimeArrow
  290. .image(width: 16.w, height: 16.w),
  291. SizedBox(width: 14.w),
  292. ],
  293. ),
  294. ),
  295. );
  296. }),
  297. Builder(builder: (context) {
  298. return GestureDetector(
  299. onTap: () {
  300. controller.onTrackEndTimeClick(context);
  301. },
  302. child: Padding(
  303. padding: EdgeInsets.symmetric(vertical: 9.w),
  304. child: Row(
  305. children: [
  306. SizedBox(width: 14.w),
  307. Text(
  308. StringName.trackEndTime,
  309. style: TextStyle(
  310. fontSize: 14.sp,
  311. color: ColorName.black80,
  312. fontWeight: FontWeight.bold),
  313. ),
  314. Spacer(),
  315. Obx(() {
  316. return Text(
  317. controller.trackEndTime?.format('yyyy-MM-dd HH:mm') ?? '',
  318. style:
  319. TextStyle(fontSize: 14.sp, color: ColorName.black50),
  320. );
  321. }),
  322. SizedBox(width: 6.w),
  323. Assets.images.iconTrackSelectTimeArrow
  324. .image(width: 16.w, height: 16.w),
  325. SizedBox(width: 14.w),
  326. ],
  327. ),
  328. ),
  329. );
  330. }),
  331. SizedBox(height: 12.w),
  332. Obx(() {
  333. return Visibility(
  334. visible: controller.startAddress != null ||
  335. controller.endAddress != null,
  336. child: Container(
  337. padding: EdgeInsets.all(12.w),
  338. margin: EdgeInsets.symmetric(horizontal: 14.w),
  339. width: double.infinity,
  340. decoration: BoxDecoration(
  341. color: '#E5F8F8F8'.color,
  342. borderRadius: BorderRadius.circular(12.w),
  343. ),
  344. child: Column(
  345. children: [
  346. buildAddressInfoView(
  347. '#12C172'.color,
  348. StringName.trackStartLocation,
  349. controller.startAddress ?? ''),
  350. Align(
  351. alignment: Alignment.centerLeft,
  352. child: Container(
  353. margin: EdgeInsets.only(left: 3.5.w),
  354. child: Assets.images.bgTrackLocationTie
  355. .image(width: 1.5.w))),
  356. buildAddressInfoView(
  357. '#F3353A'.color,
  358. StringName.trackEndLocation,
  359. controller.endAddress ?? ''),
  360. ],
  361. )),
  362. );
  363. })
  364. ],
  365. );
  366. }
  367. Widget buildAddressInfoView(Color color, String title, String content) {
  368. return Row(
  369. children: [
  370. Container(
  371. width: 10.w,
  372. height: 10.w,
  373. decoration: BoxDecoration(
  374. shape: BoxShape.circle,
  375. border: Border.all(color: color, width: 2.w),
  376. ),
  377. ),
  378. SizedBox(width: 11.w),
  379. Text(title,
  380. style: TextStyle(
  381. fontSize: 13.sp,
  382. color: ColorName.black80,
  383. fontWeight: FontWeight.bold)),
  384. Expanded(
  385. child: Text(content,
  386. style: TextStyle(fontSize: 13.sp, color: ColorName.black70)),
  387. )
  388. ],
  389. );
  390. }
  391. Widget buildTrackNowContentView() {
  392. return Column(
  393. children: [
  394. SizedBox(height: 20.w),
  395. Row(
  396. children: [
  397. SizedBox(width: 12.w),
  398. Assets.images.iconTrackLocationNow.image(width: 20.w, height: 20.w),
  399. SizedBox(width: 3.w),
  400. Obx(() {
  401. return RelativeTimeText(
  402. startPerchText: '当前位置·',
  403. endPerchText:
  404. controller.currentLocation?.lastUpdateTime == null
  405. ? ''
  406. : '更新',
  407. timestamp: controller.currentLocation?.lastUpdateTime,
  408. updateInterval: Duration(minutes: 1),
  409. style: TextStyle(
  410. fontSize: 15.sp,
  411. color: '#333333'.color,
  412. fontWeight: FontWeight.bold));
  413. })
  414. ],
  415. ),
  416. SizedBox(height: 16.w),
  417. Container(
  418. width: double.infinity,
  419. margin: EdgeInsets.symmetric(horizontal: 12.w),
  420. padding: EdgeInsets.all(14.w),
  421. decoration: BoxDecoration(
  422. color: '#F9F9F9'.color,
  423. borderRadius: BorderRadius.circular(6.w),
  424. ),
  425. child: Obx(() {
  426. return Text(controller.currentLocation?.address ?? '--',
  427. style: TextStyle(fontSize: 14.sp, color: '#666666'.color));
  428. }),
  429. )
  430. ],
  431. );
  432. }
  433. }