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 '../../resource/fonts.gen.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_common_view.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: Stack( children: [ buildMapView(), buildMainCaringReportView(), buildMainFunView(), buildMapFunView(), buildMapLogoView(), buildMainSlidingSheetView(), ], ), ); } Widget buildMainFunView() { return Positioned( right: 12.w, top: 35.w, child: SafeArea( child: Column( children: [ Container( child: buildMainFunctionView( Assets.images.iconMainMine.provider(), StringName.mainTabMine, onTap: controller.onMineClick)), SizedBox(height: 20.w), Container( child: buildMainFunctionView( Assets.images.iconMainFriend.provider(), StringName.mainTabFriend, onTap: controller.onFriendClick)), SizedBox(height: 20.w), Container( child: buildMainFunctionView( Assets.images.iconMainNews.provider(), StringName.mainTabNews, onTap: controller.onNewsClick)), SizedBox(height: 20.w), ], ), ), ); } Widget buildMainCaringReportView() { return Positioned( left: -8.w, top: 6.w, child: SafeArea( child: SizedBox( width: 210.w, child: AspectRatio( aspectRatio: 187 / 69, child: Assets.images.imgMainCaringReport.image(), ), )), ); } Widget buildMapView() { return Padding( padding: EdgeInsets.only(bottom: 50.h), child: SizedBox( width: double.infinity, child: MapWidget( controller: controller.mapController, onMarkerTap: controller.onMarkerTap, )), ); } Widget buildMainSlidingSheetView() { return Obx(() { if (controller.lastSelectedGoods == null) { return buildSlidingSheet(false); } else { return buildSlidingSheet(true); } }); } SlidingSheet buildSlidingSheet(bool isShowActivity) { return SlidingSheet( listener: (SheetState state) { controller.setSheetProgress(state.progress); }, 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: '#EFF4FC'.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 buildMapLogoView() { return Visibility( visible: Platform.isAndroid, child: Obx(() { return Positioned( left: 12.w, bottom: 136.w + (controller.lastSelectedGoods != null ? 64.w : 0) + controller.sheetProgress * 143.w, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Assets.images.iconAmapLogo.image(height: 20.w), Text(StringName.locationAmapCo, style: TextStyle( fontSize: 9.sp, color: '#666666'.color, height: 1)) ], )); }), ); } Widget buildMapFunView() { return Obx(() { return Positioned( right: 12.w, bottom: 110.w + (controller.lastSelectedGoods != null ? 64.w : 0) + controller.sheetProgress * 143.w, child: Column( children: [ GestureDetector( onTap: controller.onRefreshFriendLocationClick, child: buildMainFunctionView( Assets.images.iconMainRefresh.provider(), '', onTap: controller.onRefreshFriendLocationClick), ), SizedBox(height: 20.w), GestureDetector( onTap: controller.onCurrentLocationClick, child: buildMainFunctionView( Assets.images.iconMainLocation.provider(), '', onTap: controller.onCurrentLocationClick), ), SizedBox(height: 20.w) ], ), ); }); } 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 buildHeaderBuilder(BuildContext context, SheetState state) { return buildHeaderView(false); } Widget buildHeaderView(bool isShowActivity) { return IntrinsicHeight( child: Column( children: [ Visibility(visible: isShowActivity, child: buildActivityMemberView()), buildSelectFriendPlateView() ], )); } Widget buildSelectFriendPlateView() { return Container( width: double.infinity, padding: EdgeInsets.only(left: 1.5.w, top: 1.5.w, right: 1.5.w), decoration: BoxDecoration( borderRadius: BorderRadius.only( topLeft: Radius.circular(12.r), topRight: Radius.circular(12.r)), gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.white, '#EFF4FC'.color, ])), child: Container( width: double.infinity, decoration: BoxDecoration( color: '#EFF4FC'.color, borderRadius: BorderRadius.only( topLeft: Radius.circular(12.w), topRight: Radius.circular(12.w))), child: Column( children: [ SizedBox(height: 7.h), Container( width: 24.w, height: 3.h, decoration: BoxDecoration( color: '#CDE0FF'.color, borderRadius: BorderRadius.all(Radius.circular(49.w))), ), SizedBox(height: 5.h), buildFriendPlateListView(), SizedBox(height: 12.h) ], )), ); } Widget buildFriendPlateListView() { return SizedBox( width: double.infinity, height: 68.h, child: Row( children: [ Expanded(child: Obx(() { return PageView.builder( controller: controller.pageController, onPageChanged: controller.onFriendPageChanged, physics: const BouncingScrollPhysics(), itemBuilder: (ctx, index) => _buildFriendListItem(controller.integrateList[index]), itemCount: controller.integrateList.length); })), Obx(() { return Visibility( visible: controller.friendsList.isNotEmpty, child: Container( padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 6.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only( topLeft: Radius.circular(10.r), bottomLeft: Radius.circular(10.r)), ), child: IntrinsicHeight( child: Column( children: [ Text(StringName.mainLeftSlipSwitch, style: TextStyle( fontSize: 8.sp, color: ColorName.colorPrimary, fontWeight: FontWeight.bold)), SizedBox(height: 2.w), Assets.images.iconMainLeftSlipArrow .image(width: 5.w, height: 5.w) ], ), ), ), ); }) ], ), ); } Widget _buildFriendListItem(UserInfo userInfo) { return Obx(() { return Padding( padding: EdgeInsets.only( left: 12.w, right: controller.friendsList.isNotEmpty ? 8.w : 12.w), child: buildSelectFriendInfoView(userInfo, controller.memberStatusInfo, onTraceClick: controller.onViewTraceClick), ); }); } Widget buildActivityMemberView() { return GestureDetector( behavior: HitTestBehavior.opaque, onTap: controller.onBuyMemberActivityClick, child: Container( height: 56.w, margin: EdgeInsets.only(left: 12.w, right: 12.w, bottom: 8.w), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(12.r)), gradient: LinearGradient( colors: [ '#FFEEE9'.color, Colors.white, ], stops: [0.0, 0.8], begin: Alignment.topCenter, end: Alignment.bottomCenter, )), 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: '#202020'.color, fontWeight: FontWeight.bold)), Obx(() { return Text( ' -¥${(((controller.lastSelectedGoods?.originalAmount ?? 0) - (controller.lastSelectedGoods?.amount ?? 0)) / 100).toInt()}', style: TextStyle( fontSize: 20.sp, color: '#E74E4E'.color, fontWeight: FontWeight.bold)); }) ], ), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Obx(() { return ActivityCountdownTextView( timeItemHeight: 15.w, contentPadding: EdgeInsets.symmetric(horizontal: 2.w), textStyle: TextStyle( fontSize: 11.sp, color: '#E74F4F'.color, fontWeight: FontWeight.w500, ), duration: controller.activityDuration ?? Duration(seconds: 0), separator: buildCountdownSeparator(), timeBgBoxDecoration: BoxDecoration( gradient: LinearGradient(colors: [ '#FFEFEF'.color, '#FFEFEF'.color, ]), borderRadius: BorderRadius.circular(3.w), )); }), SizedBox(width: 4.w), Text( StringName.memberActivitySpeciallyPreferential, style: TextStyle( fontSize: 10.sp, color: '#202020'.color, ), ) ], ), Spacer(flex: 4), ], ), ], ), Positioned( top: 15.w, bottom: 14.w, right: 10.w, child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(8.w), gradient: LinearGradient( colors: [ '#FA6565'.color, '#D565FA'.color, ], ), ), padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 7.w), child: Center( child: Text(StringName.memberActivityToBuy, style: TextStyle( fontSize: 12.sp, color: Colors.white, 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: '#E74F4F'.color, shape: BoxShape.circle, ), ), SizedBox(height: 3.w), Container( width: 2.w, height: 2.w, decoration: BoxDecoration( color: '#E74F4F'.color, shape: BoxShape.circle, ), ) ], ), ), ); } }