import 'dart:io'; import 'dart:ui'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:location/base/base_page.dart'; import 'package:location/data/bean/user_info.dart'; import 'package:location/module/main/main_controller.dart'; import 'package:location/module/main/today_track_helper.dart'; import 'package:location/module/main/view.dart'; import 'package:location/resource/assets.gen.dart'; import 'package:location/resource/colors.gen.dart'; import 'package:location/resource/string.gen.dart'; import 'package:location/utils/common_expand.dart'; import 'package:location/widget/shimmer_effect.dart'; import 'package:sliding_sheet2/sliding_sheet2.dart'; import 'package:visibility_detector/visibility_detector.dart'; import '../../data/consts/constants.dart'; import '../../router/app_pages.dart'; import '../../utils/common_style.dart'; import '../../utils/common_util.dart'; import '../../widget/activity_countdown_txt_view.dart'; import '../../widget/marquee_text.dart'; import '../../widget/relative_time_text.dart'; import 'main_friend_item.dart'; class MainPage extends BasePage { const MainPage({super.key}); static start({bool? isNotClear, Map? arguments}) { if (isNotClear == null || !isNotClear) { Get.offAllNamed(RoutePath.mainTab, arguments: arguments); } else { Get.toNamed(RoutePath.mainTab, arguments: arguments); } } @override bool immersive() { return true; } @override Widget buildBody(BuildContext context) { return PopScope( canPop: false, onPopInvokedWithResult: (bool didPop, dynamic result) { controller.onAppBack(); }, child: Column( children: [ Expanded( child: Stack( children: [ buildMapView(), buildMapFunView(), buildMainBottomView(), buildFriendListView(), ], )), buildTabContainer() ], ), ); } Widget buildMapView() { return Padding( padding: EdgeInsets.only(bottom: 50.h), child: SizedBox( width: double.infinity, child: MapWidget( controller: controller.mapController, onMarkerTap: controller.onMarkerTap, )), ); } Widget buildMainBottomView() { return Obx(() { if (controller.lastSelectedGoods == null) { return buildSlidingSheet(false); } else { return buildSlidingSheet(true); } }); } SlidingSheet buildSlidingSheet(bool isShowActivity) { return SlidingSheet( key: Key(isShowActivity ? 'activity_view' : 'not_activity_view'), color: ColorName.transparent, snapSpec: SnapSpec( initialSnap: SnapSpec.headerSnap, // Enable snapping. This is true by default. snap: true, // Set custom snapping points. snappings: [SnapSpec.headerSnap, SnapSpec.expanded], // Define to what the snappings relate to. In this case, // the total available space that the sheet can expand to. positioning: SnapPositioning.relativeToAvailableSpace, ), headerBuilder: (BuildContext context, SheetState state) { return buildHeaderView(isShowActivity); }, builder: buildTrackEntranceBuilder, ); } Widget buildTrackEntranceBuilder(BuildContext context, SheetState state) { return VisibilityDetector( key: Key('main_today_track'), onVisibilityChanged: (VisibilityInfo info) { final visibleFraction = info.visibleFraction; controller.onFriendVisibleFraction(visibleFraction); }, child: Container( color: '#F9F9F9'.color, child: AspectRatio( aspectRatio: 336 / 134, child: Container( margin: EdgeInsets.only(left: 12.w, right: 12.w, bottom: 12.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20.r)), child: Obx(() { final todayTrack = controller.selectedFriend?.id == null ? null : controller .todayTrackReportMap[controller.selectedFriend?.id]; return buildTodayTrackView(todayTrack); })), ), ), ); } Widget buildTodayTrackView(TodayTrackReportBean? todayTrack) { return Stack(children: [ buildTodayTrackDetailView(todayTrack), Visibility( visible: todayTrack == null || todayTrack.isRequestSuccess.value == false, child: buildTodayTrackLoadingView()), Visibility( visible: controller.memberStatusInfo.value == null || controller.memberStatusInfo.value?.expired == true, child: buildNoMemberView()) ]); } Widget buildNoMemberView() { return GestureDetector( onTap: controller.onTrackNoMemberClick, child: Assets.images.imgTrackNoMemberTips .image(width: double.infinity, fit: BoxFit.fill)); } Widget buildTodayTrackLoadingView() { return Container( width: double.infinity, height: double.infinity, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20.r)), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CupertinoActivityIndicator( color: '#A3A3A5'.color, radius: 16.w, ), SizedBox(height: 15.w), Text(StringName.mainTodayTrackLoading, style: TextStyle( fontSize: 14.sp, color: '#666666'.color.withOpacity(0.87))) ], ), ); } Widget buildTodayTrackDetailView(TodayTrackReportBean? todayTrackReportBean) { return GestureDetector( behavior: HitTestBehavior.opaque, onTap: controller.onTodayTraceClick, child: Padding( padding: EdgeInsets.only(left: 12.w, right: 12.w, top: 12.w, bottom: 9.w), child: Column( children: [ Row( children: [ Text(StringName.todaySimpleTrack, style: TextStyle( fontSize: 13.sp, color: '#333333'.color, fontWeight: FontWeight.bold)), Spacer(), Assets.images.iconMainTrackArrow .image(width: 10.w, height: 10.w), ], ), SizedBox(height: 7.w), buildSelectedFriendTodayTrackDetailView(todayTrackReportBean), ], ), ), ); } Widget buildSelectedFriendTodayTrackDetailView( TodayTrackReportBean? todayTrackReportBean) { final startAddr = todayTrackReportBean?.startPoint?.addr; final errorAddr = todayTrackReportBean?.exceptionPoint?.addr; MainTrackType startPointType = startAddr?.isNotEmpty == true ? MainTrackType.startPoint : MainTrackType.normalPoint; MainTrackType errorPointType = errorAddr?.isNotEmpty == true ? MainTrackType.errorPoint : MainTrackType.normalPoint; Color startPointColor; if (startPointType == MainTrackType.startPoint) { startPointColor = '#15CBA1'.color; } else if (startPointType == MainTrackType.errorPoint) { startPointColor = '#E94949'.color; } else { startPointColor = '#D6D6D6'.color; } Color errorPointColor; if (errorPointType == MainTrackType.errorPoint) { errorPointColor = '#E94949'.color; } else { errorPointColor = '#D6D6D6'.color; } return Expanded( child: Row( children: [ Container( margin: EdgeInsets.only(bottom: 3.w, top: 3.w), child: AspectRatio( aspectRatio: 1, child: Stack( children: [ buildTodayMapView(todayTrackReportBean), GestureDetector( behavior: HitTestBehavior.opaque, onTap: controller.onTodayTraceClick, child: SizedBox( width: double.infinity, height: double.infinity, ), ) ], ), ), ), SizedBox(width: 10.w), Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox(height: 6.w), getMainTrackDot(startPointColor), Expanded( child: Container( margin: EdgeInsets.symmetric(vertical: 4.w), width: 1.w, height: double.infinity, decoration: BoxDecoration( color: '#F0F0F0'.color, borderRadius: BorderRadius.circular(100.r), ), ), ), getMainTrackDot(errorPointColor), SizedBox(height: 6.w), ], ), SizedBox(width: 8.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Visibility( visible: startPointType == MainTrackType.normalPoint, child: SizedBox(height: 2.w)), buildTrackPoint(startPointType, startAddr ?? ''), Spacer(), buildTrackPoint(errorPointType, errorAddr ?? ''), Visibility( visible: errorPointType == MainTrackType.normalPoint, child: SizedBox(height: 5.w)) ], ), ) ], )); } Widget buildTodayMapView(TodayTrackReportBean? todayTrackReportBean) { return ClipRRect( borderRadius: BorderRadius.circular(8.r), child: MapWidget( controller: controller.todayTrackSmallMapController, isShowLogo: false, interactionIsEnabled: false, mapPadding: todayTrackReportBean?.mapPadding, markers: todayTrackReportBean?.markers, polyline: todayTrackReportBean?.polylines, )); } Widget buildFriendListView() { return SafeArea( child: Container( margin: EdgeInsets.only(top: 26.w), child: Row( children: [ Expanded(child: buildMainFriendList()), GestureDetector( onTap: () { controller.onAddFriendClick(); }, child: Container( margin: EdgeInsets.only(right: 16.w, left: 8.w), child: Assets.images.iconMainAddFriend .image(width: 60.w, height: 60.w)), ) ], ), ), ); } Widget buildMapFunView() { return Obx(() { return Positioned( right: 0.w, bottom: controller.lastSelectedGoods == null ? 140.w : 180.w, child: Column( children: [ GestureDetector( onTap: controller.onRefreshFriendLocationClick, child: Container( margin: EdgeInsets.only(right: 12.w), child: Assets.images.iconMainRefreshFriendLocation .image(width: 42.w, height: 42.w)), ), SizedBox(height: 14.w), GestureDetector( onTap: controller.onCurrentLocationClick, child: Container( margin: EdgeInsets.only(right: 12.w), child: Assets.images.iconMainRefreshMineLocation .image(width: 42.w, height: 42.w)), ), SizedBox(height: 20.w) ], ), ); }); } Container buildTabContainer() { return Container( decoration: BoxDecoration( color: ColorName.white, borderRadius: BorderRadius.only( topLeft: Radius.circular(20.w), topRight: Radius.circular(20.w), ), boxShadow: [ BoxShadow( color: ColorName.black.withOpacity(0.01), blurRadius: 10, offset: const Offset(0, -2), // changes position of shadow ), ], ), padding: EdgeInsets.only(top: 13.w, bottom: 23.w), child: buildMainFunList()); } Widget buildMainFunList() { return Row( children: [ Expanded( child: buildFunItem( Platform.isIOS ? Assets.images.iconMainFriendGuard.provider() : Assets.images.iconMainFriendGuardAndroid.provider(), StringName.mainFriendListTab, () => controller.onFriendClick())), Expanded(child: Obx(() { return buildFunItem( Platform.isIOS ? Assets.images.iconMainNews.provider() : Assets.images.iconMainNewsAndroid.provider(), StringName.mainNewsTab, () => controller.onNewsClick(), isShowDot: controller.hasUnreadMessage); })), Expanded( child: buildFunItem( Platform.isIOS ? Assets.images.iconMainHelp.provider() : Assets.images.iconMainHelpAndroid.provider(), StringName.mainHelpTab, () => controller.onUrgentContactClick())), Expanded( child: buildFunItem( Platform.isIOS ? Assets.images.iconMainMine.provider() : Assets.images.iconMainMineAndroid.provider(), StringName.mainMineTab, () => controller.onMineClick())) ], ); } Widget buildFunItem(ImageProvider imgProvider, String title, Function() onTap, {bool? isShowDot}) { return GestureDetector( onTap: onTap, child: IntrinsicHeight( child: Column( children: [ Stack(children: [ SizedBox( width: 44.w, height: 44.w, child: Image(image: imgProvider)), Visibility( visible: isShowDot ?? false, child: Positioned( top: 6.w, right: 6.w, child: Container( width: 10.w, height: 10.w, decoration: BoxDecoration( shape: BoxShape.circle, color: '#FF333D'.color, // 背景颜色 ), ), ), ) ]), Text(title, style: TextStyle( fontSize: 12.sp, color: ColorName.black70, fontWeight: FontWeight.bold)) ], ), ), ); } Widget buildMainFriendList() { return SizedBox( height: 58.w, child: Obx(() { return ListView( physics: const BouncingScrollPhysics( parent: AlwaysScrollableScrollPhysics()), padding: EdgeInsets.only(left: 12.w), scrollDirection: Axis.horizontal, children: [ for (UserInfo userInfo in controller.integrateList) Obx(() { return mainFriendItem( userInfo, controller.selectedFriend?.id == userInfo.id, onTap: () { controller.onSelectUserClick(userInfo); }); }) ], ); }), ); } Widget buildFooterBuilder(BuildContext context, SheetState state) { return buildTabContainer(); } Widget buildHeaderBuilder(BuildContext context, SheetState state) { return buildHeaderView(false); } Widget buildHeaderView(bool isShowActivity) { return IntrinsicHeight( child: Stack( alignment: Alignment.topCenter, children: [ Visibility( visible: isShowActivity, child: Assets.images.bgMemberActivityMain .image(width: 336.w, height: 67.w), ), Visibility(visible: isShowActivity, child: buildActivityMemberView()), Column( children: [ Visibility( visible: isShowActivity, child: SizedBox(height: 58.w)), Container( width: double.infinity, decoration: BoxDecoration( color: '#F9F9F9'.color, borderRadius: BorderRadius.only( topLeft: Radius.circular(20.w), topRight: Radius.circular(20.w), ), boxShadow: [ BoxShadow( color: ColorName.black.withOpacity(0.01), blurRadius: 10, offset: const Offset(0, -2), // changes position of shadow ), ], ), child: Column( children: [ SizedBox(height: 5.w), Container( width: 32.w, height: 3.w, decoration: BoxDecoration( color: '#D9D9D9'.color, borderRadius: BorderRadius.all(Radius.circular(49.w))), ), SizedBox(height: 12.w), buildSelectFriendInfoView(), SizedBox(height: 13.w) ], ), ) ], ) ], ), ); } Widget buildActivityMemberView() { return GestureDetector( behavior: HitTestBehavior.opaque, onTap: controller.onBuyMemberActivityClick, child: Container( height: 58.w, margin: EdgeInsets.only(left: 12.w, right: 12.w), child: ShimmerEffect( image: Assets.images.imgMemberBtnShadow.provider(), child: Stack( children: [ Row( children: [ SizedBox(width: 12.w), Assets.images.iconMemberActivityCoupon .image(width: 40.w, height: 40.w), SizedBox(width: 8.w), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Spacer(flex: 1), Row( children: [ Text('您有一订单未支付专属优惠券', style: TextStyle( fontSize: 10.sp, color: ColorName.white87, fontWeight: FontWeight.bold)), Obx(() { return Text( ' -¥${(((controller.lastSelectedGoods?.originalAmount ?? 0) - (controller.lastSelectedGoods?.amount ?? 0)) / 100).toInt()}', style: TextStyle( fontSize: 20.sp, color: '#E7DBA7'.color, fontWeight: FontWeight.bold)); }) ], ), Row( children: [ Obx(() { return ActivityCountdownTextView( timeItemHeight: 15.w, contentPadding: EdgeInsets.zero, timeItemWidth: 16.w, textStyle: TextStyle( fontSize: 10.sp, color: '#322C54'.color), duration: controller.activityDuration ?? Duration(seconds: 0), separator: buildCountdownSeparator(), timeBgBoxDecoration: BoxDecoration( gradient: LinearGradient( colors: [ ColorName.white, ColorName.white87 ], begin: Alignment.topCenter, end: Alignment.bottomCenter), borderRadius: BorderRadius.circular(3.w), )); }), SizedBox(width: 4.w), Text( StringName.memberActivitySpeciallyPreferential, style: TextStyle( fontSize: 10.sp, color: ColorName.white87, fontWeight: FontWeight.bold), ) ], ), Spacer(flex: 4), ], ), ], ), Positioned( top: 15.w, bottom: 14.w, right: 10.w, child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(100.w), gradient: LinearGradient( colors: [ '#FFFEF3'.color, '#FFE5A3'.color, ], begin: Alignment.topCenter, end: Alignment.bottomCenter), ), padding: EdgeInsets.symmetric(horizontal: 12.w), child: Center( child: Text(StringName.memberActivityToBuy, style: TextStyle( fontSize: 14.sp, color: '#40338B'.color, height: 1, fontWeight: FontWeight.bold)), )), ) ], )), ), ); } Widget buildCountdownSeparator() { return Container( margin: EdgeInsets.symmetric(horizontal: 2.w), child: IntrinsicHeight( child: Column( children: [ Container( width: 2.w, height: 2.w, decoration: BoxDecoration( color: ColorName.white87, shape: BoxShape.circle, ), ), SizedBox(height: 3.w), Container( width: 2.w, height: 2.w, decoration: BoxDecoration( color: ColorName.white87, shape: BoxShape.circle, ), ) ], ), ), ); } Widget buildSelectFriendInfoView() { return Container( width: 336.w, height: 86.w, decoration: BoxDecoration( color: ColorName.white, borderRadius: BorderRadius.all(Radius.circular(20.w))), child: Row( children: [ SizedBox(width: 7.w), Obx(() { return buildCustomAvatarOrDefaultAvatarView( size: 50.w, avatar: controller.selectedFriend?.avatar, isMine: controller.selectedFriend?.isMine == true); }), SizedBox(width: 5.w), Expanded( child: Container( margin: EdgeInsets.symmetric(vertical: 15.w), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Obx(() { return ConstrainedBox( constraints: BoxConstraints( maxWidth: 0.26.sw, ), child: Text( maxLines: 1, controller.selectedFriend?.getUserNickName() ?? '', overflow: TextOverflow.ellipsis, style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16.sp, color: '#202020'.color), ), ); }), SizedBox(width: 7.w), Obx(() { return RelativeTimeText( timestamp: controller.selectedFriend?.lastLocation .value?.lastUpdateTime, updateInterval: Duration(minutes: 1), style: TextStyle( fontSize: 12.sp, color: '#A7A7A7'.color)); }), Spacer(), Obx(() { return controller.selectedFriend != null ? GestureDetector( onTap: () => controller.onViewTraceClick( controller.selectedFriend!), child: Container( margin: EdgeInsets.only(right: 12.w), decoration: getPrimaryBtnDecoration(32.w), padding: EdgeInsets.symmetric( horizontal: 21.w, vertical: 5.w), child: Text(StringName.locationTrace, style: TextStyle( fontSize: 15.sp, color: Colors.white))), ) : SizedBox.shrink(); }), ], ), Expanded( child: Container( margin: EdgeInsets.only(right: 17.w), child: Obx(() { return ImageFiltered( enabled: controller.selectedFriend?.blockedMe == true || ((controller.memberStatusInfo.value == null || controller.memberStatusInfo.value ?.expired == true) && !(controller.selectedFriend?.isMine == true)), imageFilter: ImageFilter.blur( sigmaX: Constants.blurredX, sigmaY: Constants.blurredY, ), child: controller.selectedFriend?.blockedMe == true || ((controller.memberStatusInfo.value == null || controller.memberStatusInfo.value?.expired == true) && !(controller.selectedFriend?.isMine == true)) ? Text(addressCheck(controller.selectedFriend?.lastLocation.value?.address), style: TextStyle( fontSize: 13.sp, color: ColorName.black50)) : MarqueeText.marquee( text: addressCheck(controller.selectedFriend ?.lastLocation.value?.address), textStyle: TextStyle( fontSize: 13.sp, color: ColorName.black50), containerWidth: 244.w), ); }), ), ) // Text('广东省广州市天河区XX街街XX街区XX村XX') ], ), ), ) ], )); } }