import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:get/get_core/src/get_main.dart'; import 'package:location/base/base_page.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:sliding_sheet2/sliding_sheet2.dart'; import '../../../router/app_pages.dart'; import '../../../widget/common_view.dart'; import 'common_point_select_address_controller.dart'; class CommonPointSelectAddressPage extends BasePage { const CommonPointSelectAddressPage({super.key}); static start() { Get.toNamed(RoutePath.commonPointSelectAddress); } @override bool immersive() { return true; } @override Widget buildBody(BuildContext context) { return PopScope( canPop: false, onPopInvokedWithResult: (bool didPop, dynamic result) { if (didPop) { return; } controller.backClick(); }, child: Stack( children: [ buildMapView(), buildHeadBgView(), buildMapLogoView(), buildLocationFunView(), buildSlidingSheetView(), buildToolbarView(), ], ), ); } Widget buildLocationFunView() { return Obx(() { return Positioned( right: 12.w, bottom: 0.48.sh + controller.sheetProgress * 0.48.sh, child: Opacity( opacity: 1 - controller.sheetProgress, child: GestureDetector( onTap: controller.moveToCurrentLocation, child: Container( width: 38.w, height: 38.w, decoration: BoxDecoration( color: ColorName.white, borderRadius: BorderRadius.circular(10.r), boxShadow: [ BoxShadow( color: ColorName.black10, blurRadius: 16, spreadRadius: 2, offset: Offset(0, 4.w), ), ], ), child: Center( child: Assets.images.iconCurrentLocation .image(width: 20.w, height: 20.w), )), ), ), ); }); } Widget buildMapLogoView() { return Visibility( visible: Platform.isAndroid, child: Obx(() { return Positioned( left: 12.w, bottom: 0.465.sh + controller.sheetProgress * 0.48.sh, child: Opacity( opacity: 1 - controller.sheetProgress, 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 buildSlidingSheetView() { return SlidingSheet( listener: (SheetState state) { controller.setSheetProgress(state.progress); }, controller: controller.sheetController, color: ColorName.transparent, elevation: 0, shadowColor: ColorName.transparent, cornerRadius: 0, snapSpec: SnapSpec( initialSnap: 0.45, // Enable snapping. This is true by default. snap: true, // Set custom snapping points. snappings: [0.45, 0.94], // Define to what the snappings relate to. In this case, // the total available space that the sheet can expand to. positioning: SnapPositioning.relativeToAvailableSpace, ), headerBuilder: selectAddressHeaderBuilder, customBuilder: selectAddressCustomBuilder); } Widget buildToolbarView() { return SafeArea( child: Container( padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 14.5.w), child: Row( children: [ GestureDetector( onTap: controller.backClick, child: CommonView.getBackBtnView()), Spacer(), Container( width: 50.w, height: 28.w, decoration: BoxDecoration( color: ColorName.white, borderRadius: BorderRadius.circular(8.r), ), child: Center( child: Text(StringName.selectAddressDone, style: TextStyle( fontSize: 12.sp, color: ColorName.black90, fontWeight: FontWeight.bold)), )), ], ), ), ); } Widget buildHeadBgView() { return IgnorePointer( child: AspectRatio( aspectRatio: 360 / 146, child: Assets.images.bgCommonPointSelectAddressTop.image( width: double.infinity, ), ), ); } Widget selectAddressCustomBuilder(BuildContext context, ScrollController scrollController, SheetState state) { return ListView.builder( padding: EdgeInsets.zero, controller: scrollController, itemBuilder: (ctx, index) => Text('selectAddressCustomBuilder--$index'), itemCount: 100, ); } Widget selectAddressHeaderBuilder(BuildContext context, SheetState state) { return IntrinsicHeight( child: Column( children: [ buildRangeView(), SizedBox(height: 12.w), buildSelectAddressView() ], ), ); } Widget buildSelectAddressView() { return GestureDetector( onTap: controller.onOpenSearchAddressModel, child: Container( padding: EdgeInsets.all(12.w), width: double.infinity, decoration: BoxDecoration( color: ColorName.white, borderRadius: BorderRadius.only( topLeft: Radius.circular(18.r), topRight: Radius.circular(18.r))), child: Obx(() { return Container( width: double.infinity, height: 40.w, decoration: BoxDecoration( border: controller.sheetProgress == 1 ? Border.all(color: '#EAECEE'.color, width: 0.5.w) : null, color: '#F6F6F8'.color, borderRadius: BorderRadius.circular(8.r)), child: Row( children: [ SizedBox(width: 12.w), Assets.images.iconCommonPointSearch .image(width: 14.w, height: 14.w), SizedBox(width: 6.w), Expanded( child: TextField( enabled: controller.sheetProgress == 1, controller: controller.searchEditController, focusNode: controller.searchFocusNode, style: TextStyle( fontSize: 14.sp, color: ColorName.primaryTextColor), maxLines: 1, maxLength: 30, keyboardType: TextInputType.text, textAlignVertical: TextAlignVertical.center, textInputAction: TextInputAction.next, decoration: InputDecoration( hintText: StringName.selectAddressPleaseEnterPlaceName, counterText: '', hintStyle: TextStyle( fontSize: 13.sp, color: ColorName.black40), contentPadding: const EdgeInsets.all(0), border: const OutlineInputBorder( borderSide: BorderSide.none)))), SizedBox(width: 12.w), ], ), ); }), ), ); } Widget buildRangeView() { return Obx(() { return Opacity( opacity: 1 - controller.sheetProgress, child: Container( width: 336.w, height: 41.w, decoration: BoxDecoration( color: ColorName.white, borderRadius: BorderRadius.circular(12.r)), child: Builder(builder: (context) { return Row( children: [ SizedBox(width: 16.w), Text(StringName.selectAddressCommonlyUsedRange, style: TextStyle(fontSize: 12.sp, color: ColorName.black80)), SizedBox(width: 8.w), Expanded( child: SliderTheme( data: SliderTheme.of(context).copyWith( thumbColor: Colors.white, thumbShape: CustomBlueThumb( thumbRadius: 4.6.w, outerRingWidth: 2.5.w, color: ColorName.colorPrimary, ), overlayShape: SliderComponentShape.noOverlay, trackHeight: 6.w, activeTrackColor: ColorName.colorPrimary, inactiveTrackColor: "#E4E4E4".color, trackShape: CustomTrackShape(), ), child: Obx(() { return Slider( value: controller.commonPointRange, min: 50, max: 500, onChanged: (value) { controller.setCommonPointRange(value); }, onChangeStart: (value) { controller.setCommonPointRangeStart(); }, onChangeEnd: (value) { controller.setCommonPointRangeEnd(); }, ); }), ), ), SizedBox(width: 8.w), Container( width: 48.w, height: 21.w, decoration: BoxDecoration( color: '#F6F6F8'.color, borderRadius: BorderRadius.circular(5.w)), child: Center(child: Obx(() { return Text( '${controller.commonPointRange.toInt()}m', style: TextStyle(fontSize: 12.sp, color: ColorName.black80), ); })), ), SizedBox(width: 16.w), ], ); }), ), ); }); } Widget buildMapView() { return SizedBox( width: double.infinity, height: 0.65.sh, child: Stack( children: [ MapWidget(controller: controller.mapController), Center( child: Container( width: 18.w, height: 18.w, decoration: BoxDecoration( shape: BoxShape.circle, color: ColorName.colorPrimary, border: Border.all(color: ColorName.white, width: 2.w), ), ), ) ], )); } } class CustomTrackShape extends RoundedRectSliderTrackShape { @override Rect getPreferredRect({ required RenderBox parentBox, Offset offset = Offset.zero, required SliderThemeData sliderTheme, bool isEnabled = false, bool isDiscrete = false, }) { final trackHeight = sliderTheme.trackHeight; final trackLeft = offset.dx; final trackTop = offset.dy + (parentBox.size.height - trackHeight!) / 2; final trackWidth = parentBox.size.width; return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight); } } class CustomBlueThumb extends SliderComponentShape { final double thumbRadius; final double outerRingWidth; final Color color; const CustomBlueThumb({ this.thumbRadius = 12.0, this.outerRingWidth = 3.0, this.color = const Color(0xFF4476FF), }); @override Size getPreferredSize(bool isEnabled, bool isDiscrete) { return Size.fromRadius(thumbRadius + outerRingWidth); } @override void paint( PaintingContext context, Offset center, { required Animation activationAnimation, required Animation enableAnimation, required bool isDiscrete, required TextPainter labelPainter, required RenderBox parentBox, required SliderThemeData sliderTheme, required TextDirection textDirection, required double value, required double textScaleFactor, required Size sizeWithOverflow, }) { final Canvas canvas = context.canvas; final Paint outerPaint = Paint() ..color = color // 使用您指定的蓝色 ..strokeWidth = outerRingWidth ..style = PaintingStyle.stroke; final Paint innerPaint = Paint() ..color = Colors.white ..style = PaintingStyle.fill; // 绘制蓝色外圈 canvas.drawCircle(center, thumbRadius + outerRingWidth / 2, outerPaint); // 绘制白色内圆 canvas.drawCircle(center, thumbRadius, innerPaint); } }