track_page.dart 14 KB

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