import 'package:dio/dio.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:geolocator/geolocator.dart'; import 'package:huixiang/constant.dart'; import 'package:huixiang/data/address.dart'; import 'package:huixiang/data/base_list_data.dart'; import 'package:huixiang/data/store.dart'; import 'package:huixiang/generated/l10n.dart'; import 'package:huixiang/utils/app_util.dart'; import 'package:huixiang/utils/constant.dart'; import 'package:huixiang/utils/font_weight.dart'; import 'package:huixiang/utils/location.dart'; import 'package:huixiang/utils/shared_preference.dart'; import 'package:huixiang/view_widget/custom_image.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:shimmer/shimmer.dart'; import 'package:visibility_detector/visibility_detector.dart'; import '../data/product_show.dart'; import '../retrofit/retrofit_api.dart'; import '../utils/flutter_utils.dart'; import '../view_widget/classic_header.dart'; import '../view_widget/no_data_view.dart'; import '../view_widget/round_button.dart'; class UnionList extends StatefulWidget { final String serviceType; final Position? latLng; final String? searchKey; final String? city; UnionList(Key key, this.serviceType, this.latLng, this.searchKey, this.city) : super(key: key); @override State createState() { return UnionListState(); } } class UnionListState extends State with AutomaticKeepAliveClientMixin { ApiService? apiService; List? storeList; Position? latLng; final RefreshController _refreshController = RefreshController(); int networkStatus = 0; bool powerFlag = false; @override bool get wantKeepAlive => true; late String cityName; @override void initState() { super.initState(); powerFlag = false; cityName = widget.city ?? "所有门店"; getLocation(); } getLocation() async { try { powerFlag = await LocationInstance.instance.startLocation(context, (Position? result) async { if (result?.latitude != null && result?.longitude != null) { debugPrint("location: $result"); latLng = Position( latitude: result!.latitude, longitude: result.longitude, timestamp: DateTime.now(), accuracy: 0, altitude: 0, heading: 0, speed: 0, speedAccuracy: 0, ); Address? address = await LocationInstance.instance.getAddress(result.latitude, result.longitude); if (address != null) { await saveLatLng( latLng!, address.province, address.city, address.area); } LocationInstance.instance.stopLocation(); } else { await getLatLng(); } queryStore(); }); } finally { if (!powerFlag) { if (await Permission.locationWhenInUse.status.isGranted) { getLocation(); } else { queryStore(); } } } } saveLatLng(Position latLng, String? province, String? city, String? district) async { SharedInstance.instance.saveLatLng(latLng); if (province?.isNotEmpty ?? false) SharedInstance.instance.setProvince(province); if (city?.isNotEmpty ?? false) SharedInstance.instance.setProvince(city); if (district?.isNotEmpty ?? false) SharedInstance.instance.setProvince(district); } getLatLng() async { if (SharedInstance.instance.language.isNotEmpty && SharedInstance.instance.longitude.isNotEmpty && SharedInstance.instance.province.isNotEmpty && SharedInstance.instance.city.isNotEmpty && SharedInstance.instance.district.isNotEmpty) { latLng = Position( latitude: double.tryParse(SharedInstance.instance.language)!, longitude: double.tryParse(SharedInstance.instance.longitude)!, timestamp: DateTime.now(), accuracy: 0, altitude: 0, heading: 0, speed: 0, speedAccuracy: 0, ); } } void refreshData(String areaName) { this.cityName = areaName; getLocation(); } queryStore() async { apiService ??= ApiService( Dio(), context: context, token: SharedInstance.instance.token, ); BaseListData? baseData = await apiService?.queryStore({ "city": cityName, "latitude": (widget.latLng?.latitude ?? "").toString(), "longitude": (widget.latLng?.longitude ?? "").toString(), if (widget.searchKey != "") "searchKey": widget.searchKey, "serviceType": widget.serviceType, "exchange": false, }).catchError((error) { networkStatus = -1; _refreshController.refreshFailed(); return BaseListData()..isSuccess = false; }); if (baseData?.isSuccess ?? false) { storeList = baseData!.data; _refreshController.refreshCompleted(); networkStatus = 1; } else { _refreshController.refreshFailed(); } if (!mounted) return; setState(() {}); } @override Widget build(BuildContext context) { super.build(context); return SmartRefresher( controller: _refreshController, enablePullDown: true, enablePullUp: false, header: MyHeader( color: Colors.white, ), physics: BouncingScrollPhysics(), onRefresh: () { powerFlag = false; getLocation(); }, child: networkStatus == 0 ? ListView.builder( itemCount: 10, physics: BouncingScrollPhysics(), shrinkWrap: true, itemBuilder: (context, position) { return GestureDetector( onTap: () {}, child: buildStoreItemSm(), ); }, ) : ((storeList?.isEmpty ?? true) ? NoDataView( src: "assets/image/di_zhi.webp", isShowBtn: false, text: ((widget.city ?? "") != "武汉" && (widget.city ?? "") != "郑州" && (widget.city ?? "") != "北京") ? " 当前海峡姐妹开放门店 仅包含 武汉/北京/郑州,您当前的位置无法获取,请手动更换城市" : "暂无店铺列表~", fontSize: 16.sp, margin: EdgeInsets.only( top: 120.h, left: 45.w, right: 45.w, ), ) : ListView.builder( itemCount: storeList!.length, padding: EdgeInsets.only( top: 8.h, bottom: 100.h, ), itemBuilder: (context, position) { return InkWell( onTap: () { String storeId = storeList![position].id ?? ""; String tenant = storeList![position].tenantCode ?? ""; miniLogin(apiService!, tenant, storeId, (token) { Navigator.of(context).pushNamed( '/router/store_order', arguments: { "id": storeList![position].id, "tenant": storeList![position].tenantCode, "storeName": storeList![position].storeName, "distance": storeList![position].distance, "miniToken": token, }, ); }); }, child: buildStoreItem(storeList![position], position), ); }, )), ); } Widget buildStoreItemSm() { return Container( margin: EdgeInsets.fromLTRB(16.w, 8.h, 16.w, 12.h), padding: EdgeInsets.symmetric( horizontal: 12.w, vertical: 12.h, ), width: double.infinity, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(6)), boxShadow: [ BoxShadow( color: Colors.black.withAlpha(25), offset: Offset(0, 1), blurRadius: 12, spreadRadius: 0, ) ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Shimmer.fromColors( baseColor: Color(0XFFD8D8D8), highlightColor: Color(0XFFD8D8D8), child: Container( color: Color(0XFFD8D8D8), width: double.infinity, height: 140.h, ), ), Padding( padding: EdgeInsets.only( left: 6.w, bottom: 5.h, top: 12.h, ), child: Shimmer.fromColors( baseColor: Color(0XFFD8D8D8), highlightColor: Color(0XFFD8D8D8), child: Container( decoration: BoxDecoration( color: Color(0XFFD8D8D8), borderRadius: BorderRadius.circular(2), ), width: 108.w, height: 20.h, ), ), ), Padding( padding: EdgeInsets.only( left: 6.w, ), child: Shimmer.fromColors( baseColor: Color(0XFFD8D8D8), highlightColor: Color(0XFFD8D8D8), child: Container( decoration: BoxDecoration( color: Color(0XFFD8D8D8), borderRadius: BorderRadius.circular(2), ), width: 260.w, height: 20.h, ), ), ), ], ), ); } Widget buildStoreItem(Store store, position) { return Container( margin: EdgeInsets.fromLTRB( 14.w, 8, 14.w, 12, ), padding: EdgeInsets.symmetric( horizontal: 12.w, vertical: 12.h, ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all( Radius.circular(6), ), boxShadow: [ BoxShadow( color: Colors.black.withAlpha(25), offset: Offset(0, 1), blurRadius: 12, spreadRadius: 0, ), ], ), width: double.infinity, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 74.h, child: Row( children: [ SizedBox( height: 74.h, width: 74.h, child: Stack( children: [ MImage( store.facade ?? '', width: 74.h, height: 74.h, fit: BoxFit.cover, radius: BorderRadius.circular(6), errorSrc: "assets/image/default_1.webp", fadeSrc: "assets/image/default_1.webp", ), Container( decoration: BoxDecoration( color: Color(0xFF32A060), borderRadius: BorderRadius.only( topLeft: Radius.circular(6), bottomRight: Radius.circular(6), topRight: Radius.circular(2), bottomLeft: Radius.circular(2), ), ), padding: EdgeInsets.symmetric( horizontal: 3.w, vertical: 2.h, ), child: Text( "品牌", style: TextStyle( color: Colors.white, fontSize: 10.sp, fontWeight: MyFontWeight.regular, ), ), ), ], ), ), 11.vd, Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( store.storeName ?? "", overflow: TextOverflow.ellipsis, style: TextStyle( color: Color(0xFF0D0D0D), fontSize: 14.sp, fontWeight: MyFontWeight.bold, ), ), Row( children: [ if (store.perCapitaConsumption != null && (store.perCapitaConsumption ?? "") != "0") Padding( padding: EdgeInsets.only(right: 22.w), child: Text( S .of(context) .ren(store.perCapitaConsumption ?? ""), style: TextStyle( color: Color(0xFF4D4D4D), fontSize: 12.sp, fontWeight: MyFontWeight.regular, ), ), ), Image.asset( "assets/image/icon_union_location_black.webp", fit: BoxFit.fill, height: 12, width: 12, ), SizedBox(width: 4.w), Text( (store.distance ?? 0) > 1000 ? S.of(context).gongli( ((store.distance ?? 0) / 1000 * 100) .toInt() / 100.0) : S.of(context).mi( ((store.distance ?? 0) * 100).toInt() / 100.0), style: TextStyle( color: Color(0xFF4D4D4D), fontSize: 12.sp, ), ), ], ), Row( children: itemServer(store.businessService ?? ""), ), ], ), ), ], ), ), if (storeList?[position].productShow?.isNotEmpty ?? false) 18.d, if (storeList?[position].productShow?.isNotEmpty ?? false) Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded(child: unionItem(position, 0)), 14.vd, if ((storeList?[position].productShow?.length ?? 0) > 1) Expanded(child: unionItem(position, 1)) else Spacer(), 14.vd, if ((storeList?[position].productShow?.length ?? 0) > 2) Expanded(child: unionItem(position, 2)) else Spacer(), ], ), ], ), ); } ///标签 List itemServer(String businessService) { if (businessService.isEmpty) return []; var list = businessService.split(","); return list .map((e) => Container( margin: EdgeInsets.only(right: 8.w), child: RoundButton( height: 17.h * AppUtils.textScale(context), text: "$e", backgroup: Color(0xFFF65720), padding: EdgeInsets.only( left: 4.w, right: 4.w, ), fontSize: 10.sp, textColor: Colors.white, ), )) .toList(); } Widget unionItem(int position, int unionIndex) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ AspectRatio( aspectRatio: 1, child: MImage( storeList?[position].productShow?[unionIndex].imgs?[0] ?? "", width: double.infinity, height: double.infinity, fit: BoxFit.cover, radius: BorderRadius.circular(4), errorSrc: "assets/image/default_1.webp", fadeSrc: "assets/image/default_1.webp", ), ), 6.d, Text( storeList?[position].productShow?[unionIndex].productName ?? "", overflow: TextOverflow.ellipsis, style: TextStyle( color: Color(0xFF0D0D0D), fontSize: 12.sp, fontWeight: MyFontWeight.regular, ), ), 4.d, Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ 2.vd, Text.rich( TextSpan( children: [ TextSpan( text: "¥", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 12.sp, color: Color(0xFFF65720), ), ), TextSpan( text: "${(storeList?[position].productShow?[unionIndex].price ?? "").split(".")[0]}", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16.sp, color: Color(0xFFF65720), ), ), if (storeList?[position] .productShow?[unionIndex] .price ?.contains(".") ?? false) TextSpan( text: ".", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16.sp, color: Color(0xFFF65720), ), ), if (storeList?[position] .productShow?[unionIndex] .price ?.contains(".") ?? false) TextSpan( text: (storeList?[position] .productShow?[unionIndex] .price ?? "") .split(".")[1], style: TextStyle( fontWeight: FontWeight.bold, fontSize: 12.sp, color: Color(0xFFF65720), ), ), ], ), textDirection: TextDirection.ltr, ), Text( "¥${storeList?[position].productShow?[unionIndex].applyPrice ?? ""}", overflow: TextOverflow.ellipsis, style: TextStyle( color: Color(0xFFA29E9E), fontSize: 10.sp, fontWeight: MyFontWeight.regular, decoration: TextDecoration.lineThrough, decorationColor: Color(0xFFA29E9E), ), ), ], ), ], ); } Widget unionGoodsItem(ProductShow productShow) { return Column( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.start, children: [ MImage( productShow.imgs?[0] ?? "", width: 97.h, height: 97.h, fit: BoxFit.cover, radius: BorderRadius.circular(4), errorSrc: "assets/image/default_1.webp", fadeSrc: "assets/image/default_1.webp", ), Padding( padding: EdgeInsets.only( top: 8.h, bottom: 6.h, ), child: Text( productShow.productName ?? "", overflow: TextOverflow.ellipsis, style: TextStyle( color: Color(0xFF0D0D0D), fontSize: 12.sp, fontWeight: MyFontWeight.regular, ), ), ), Expanded( child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Padding( padding: EdgeInsets.only(right: 2.w), child: Text.rich( TextSpan( children: [ TextSpan( text: "¥", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 12.sp, color: Color(0xFFF65720), ), ), TextSpan( text: "${(productShow?.price ?? "").split(".")[0]}", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16.sp, color: Color(0xFFF65720), ), ), if (productShow.price?.contains(".") ?? false) TextSpan( text: ".", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16.sp, color: Color(0xFFF65720), ), ), if (productShow.price?.contains(".") ?? false) TextSpan( text: (productShow.price ?? "").split(".")[1], style: TextStyle( fontWeight: FontWeight.bold, fontSize: 12.sp, color: Color(0xFFF65720), ), ), ], ), textDirection: TextDirection.ltr, ), ), Text( "¥${productShow.applyPrice ?? ""}", overflow: TextOverflow.ellipsis, style: TextStyle( color: Color(0xFFA29E9E), fontSize: 10.sp, fontWeight: MyFontWeight.regular, decoration: TextDecoration.lineThrough, decorationColor: Color(0xFFA29E9E), ), ), ], ), ), ], ); } //旧ui Widget buildStoreItems(Store store, position) { return Container( margin: EdgeInsets.fromLTRB(16.w, 8.h, 16.w, 12.h), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(8)), boxShadow: [ BoxShadow( color: Colors.black.withAlpha(25), offset: Offset(0, 1), blurRadius: 12, spreadRadius: 0, ) ], ), width: double.infinity, height: 235.h, child: Stack( children: [ Positioned( top: 0, left: 0, right: 0, child: ClipRRect( child: MImage( store.facade ?? "", width: double.infinity, height: 140.h, fit: BoxFit.cover, errorSrc: "assets/image/default_1.webp", fadeSrc: "assets/image/default_1.webp", ), borderRadius: BorderRadius.vertical( top: Radius.circular(4), ), ), ), Positioned( bottom: 0, left: 0, right: 0, child: Container(), ), Positioned( bottom: 16.h, left: 12.w, right: 12.w, child: Container( height: 107.h, child: Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ MImage( store.logo ?? "", width: 57, height: 57, fit: BoxFit.cover, isCircle: true, errorSrc: "assets/image/default_1.webp", fadeSrc: "assets/image/default_1.webp", ), SizedBox( width: 6.w, ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 40.h, ), Text( store.storeName ?? "", overflow: TextOverflow.ellipsis, style: TextStyle( color: Color(0xFF0D0D0D), fontSize: 14.sp, fontWeight: MyFontWeight.bold, ), ), SizedBox( height: 5.h, ), Expanded( child: Text( "${S.of(context).dizhi}:${store.address}", maxLines: 2, overflow: TextOverflow.ellipsis, style: TextStyle( color: Color(0xFF4D4D4D), fontSize: 12.sp, fontWeight: MyFontWeight.regular, ), ), ), ], ), ), if (store.distance != null) Container( width: 59.w, height: 18.h, alignment: Alignment.center, margin: EdgeInsets.only(top: 20.h), decoration: BoxDecoration( borderRadius: BorderRadius.circular(2), color: Color(0xFF32A060), ), child: Visibility( child: Text( (store.distance ?? 0) > 1000 ? S.of(context).gongli( ((store.distance ?? 0) / 1000 * 100).toInt() / 100.0) : S.of(context).mi( ((store.distance ?? 0) * 100).toInt() / 100.0), style: TextStyle( color: Color(0xFFFFFFFF), fontSize: 10.sp, ), ), visible: store.distance != null, ), ), ], ), ), ), ], ), ); } }