|
@@ -0,0 +1,331 @@
|
|
|
|
|
+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<CommonPointSelectAddressController> {
|
|
|
|
|
+ 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: [
|
|
|
|
|
+ MapWidget(),
|
|
|
|
|
+ buildHeadBgView(),
|
|
|
|
|
+ buildSlidingSheetView(),
|
|
|
|
|
+ buildToolbarView(),
|
|
|
|
|
+ ],
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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);
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ }),
|
|
|
|
|
+ ),
|
|
|
|
|
+ ),
|
|
|
|
|
+ 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),
|
|
|
|
|
+ ],
|
|
|
|
|
+ );
|
|
|
|
|
+ }),
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+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<double> activationAnimation,
|
|
|
|
|
+ required Animation<double> 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);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|