|
|
|
import 'dart:convert';
|
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
import 'package:device_info_plus/device_info_plus.dart';
|
|
|
|
import 'package:dio/dio.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/data/ip_data.dart';
|
|
|
|
import 'package:huixiang/generated/l10n.dart';
|
|
|
|
import 'package:huixiang/main.dart';
|
|
|
|
import 'package:huixiang/retrofit/retrofit_api.dart';
|
|
|
|
import 'package:huixiang/union/union_list.dart';
|
|
|
|
import 'package:huixiang/utils/event_type.dart';
|
|
|
|
import 'package:huixiang/utils/location.dart';
|
|
|
|
import 'package:huixiang/view_widget/my_tab.dart';
|
|
|
|
import 'package:permission_handler/permission_handler.dart';
|
|
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import 'package:visibility_detector/visibility_detector.dart';
|
|
|
|
|
|
|
|
import '../data/address.dart';
|
|
|
|
import '../view_widget/location_tips.dart';
|
|
|
|
import '../view_widget/no_data_view.dart';
|
|
|
|
|
|
|
|
class UnionPage extends StatefulWidget {
|
|
|
|
final int initialIndex;
|
|
|
|
|
|
|
|
UnionPage(Key key, this.initialIndex) : super(key: key);
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<StatefulWidget> createState() {
|
|
|
|
return UnionPageState();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class UnionPageState extends State<UnionPage>
|
|
|
|
with
|
|
|
|
AutomaticKeepAliveClientMixin,
|
|
|
|
WidgetsBindingObserver,
|
|
|
|
SingleTickerProviderStateMixin {
|
|
|
|
final TextEditingController editingController = TextEditingController();
|
|
|
|
bool isKeyBoardShow = false;
|
|
|
|
TabController? tabController;
|
|
|
|
Position? latLng;
|
|
|
|
String? areaName;
|
|
|
|
List<GlobalKey> _allKey = [];
|
|
|
|
bool _isShowLocalTips = false;
|
|
|
|
double? visiblePercentage;
|
|
|
|
|
|
|
|
jumpIndex(jpIndex) {
|
|
|
|
tabController?.index = jpIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
super.dispose();
|
|
|
|
WidgetsBinding.instance.removeObserver(this);
|
|
|
|
// Location.instance.aMapFlutterLocation.stopLocation();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
|
|
if (state == AppLifecycleState.resumed) {
|
|
|
|
// 处理应用程序切换回前台的逻辑
|
|
|
|
if (visiblePercentage == 1) permissionSettings();
|
|
|
|
} else if (state == AppLifecycleState.paused) {
|
|
|
|
// 处理应用程序切换到后台的逻辑
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void permissionSettings() async {
|
|
|
|
if (_isShowLocalTips && await Permission.location.isGranted) {
|
|
|
|
_isShowLocalTips = false;
|
|
|
|
getLocation();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void didChangeMetrics() {
|
|
|
|
super.didChangeMetrics();
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
|
|
setState(() {
|
|
|
|
print("object: ${MediaQuery.of(context).viewInsets.bottom}");
|
|
|
|
if (MediaQuery.of(context).viewInsets.bottom == 0) {
|
|
|
|
if (isKeyBoardShow) {
|
|
|
|
isKeyBoardShow = false;
|
|
|
|
//关闭键盘 软键盘关闭了, 清除输入控件的焦点, 否则重新进入页面会导致软键盘再弹出问题
|
|
|
|
FocusScope.of(context).requestFocus(FocusNode());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
isKeyBoardShow = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
tabController = TabController(
|
|
|
|
length: 4, vsync: this, initialIndex: widget.initialIndex);
|
|
|
|
WidgetsBinding.instance.addObserver(this);
|
|
|
|
eventBus.on<EventType>().listen((event) {
|
|
|
|
if (event.type < 3) {
|
|
|
|
setState(() {});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
queryIpInfo();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> isEmulator() async {
|
|
|
|
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
|
|
|
if (Platform.isAndroid) {
|
|
|
|
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
|
|
|
return !androidInfo.isPhysicalDevice;
|
|
|
|
} else if (Platform.isIOS) {
|
|
|
|
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
|
|
|
|
return !iosInfo.isPhysicalDevice;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
getLocation({bool showLoading = true}) async {
|
|
|
|
if (showLoading && !(await isEmulator()))
|
|
|
|
SmartDialog.showLoading(
|
|
|
|
msg: S.current.zhengzaijiazai,
|
|
|
|
);
|
|
|
|
bool powerFlag = false;
|
|
|
|
// bool finallyFlag = false;
|
|
|
|
try {
|
|
|
|
powerFlag = await LocationInstance.getInstance().startLocation(context,
|
|
|
|
(Position? result) async {
|
|
|
|
if (result?.latitude != null && result?.longitude != null) {
|
|
|
|
print("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.getInstance()
|
|
|
|
.getAddress(result.latitude, result.longitude);
|
|
|
|
if (address != null) {
|
|
|
|
await saveLatLng(
|
|
|
|
latLng!, address.province, address.city, address.area);
|
|
|
|
areaName = address.city?.replaceAll("市", "");
|
|
|
|
}
|
|
|
|
LocationInstance.getInstance().stopLocation();
|
|
|
|
} else {
|
|
|
|
await getLatLng();
|
|
|
|
}
|
|
|
|
loadFinish(showLoading: false);
|
|
|
|
});
|
|
|
|
} finally {
|
|
|
|
// finallyFlag = true;
|
|
|
|
if (!powerFlag) {
|
|
|
|
if (await Permission.locationWhenInUse.status.isGranted) {
|
|
|
|
getLocation();
|
|
|
|
} else {
|
|
|
|
_isShowLocalTips = true;
|
|
|
|
loadFinish(showLoading: false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
saveLatLng(Position latLng, province, city, district) async {
|
|
|
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
|
|
await prefs.setString("latitude", "${latLng.latitude}");
|
|
|
|
await prefs.setString("longitude", "${latLng.longitude}");
|
|
|
|
if (province != null) await prefs.setString("province", province);
|
|
|
|
if (city != null) await prefs.setString("city", city);
|
|
|
|
if (district != null) await prefs.setString("district", district);
|
|
|
|
}
|
|
|
|
|
|
|
|
getLatLng() async {
|
|
|
|
var tempLatLng = await SharedPreferences.getInstance();
|
|
|
|
if (tempLatLng.containsKey("latitude") &&
|
|
|
|
tempLatLng.containsKey("longitude") &&
|
|
|
|
tempLatLng.containsKey("province") &&
|
|
|
|
tempLatLng.containsKey("city") &&
|
|
|
|
tempLatLng.containsKey("district")) {
|
|
|
|
latLng = Position(
|
|
|
|
latitude: double.tryParse(tempLatLng.getString("latitude") ?? "")!,
|
|
|
|
longitude: double.tryParse(tempLatLng.getString("longitude") ?? "")!,
|
|
|
|
timestamp: DateTime.now(),
|
|
|
|
accuracy: 0,
|
|
|
|
altitude: 0,
|
|
|
|
heading: 0,
|
|
|
|
speed: 0,
|
|
|
|
speedAccuracy: 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
queryIpInfo() async {
|
|
|
|
SmartDialog.showLoading(
|
|
|
|
msg: S.current.zhengzaijiazai,
|
|
|
|
);
|
|
|
|
try {
|
|
|
|
ApiService apiIpService = ApiService(Dio(), context: context, isIp: true);
|
|
|
|
String? baseData = await apiIpService.getIpInfo().catchError((onError) {
|
|
|
|
debugPrint("onError: ${onError}");
|
|
|
|
return Future.value(null);
|
|
|
|
});
|
|
|
|
if (baseData?.isNotEmpty ?? false) {
|
|
|
|
debugPrint("baseData: ${baseData}");
|
|
|
|
String ipDataStr = baseData!
|
|
|
|
.replaceAll("if(window.IPCallBack) {IPCallBack(", "")
|
|
|
|
.replaceAll(");}", "");
|
|
|
|
IpData ipData = IpData.fromJson(jsonDecode(ipDataStr));
|
|
|
|
areaName = ipData.city?.replaceAll("市", "");
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
getLocation(showLoading: false);
|
|
|
|
}
|
|
|
|
SmartDialog.dismiss();
|
|
|
|
}
|
|
|
|
|
|
|
|
loadFinish({bool showLoading = true}) {
|
|
|
|
SmartDialog.dismiss();
|
|
|
|
if (showLoading)
|
|
|
|
SmartDialog.showLoading(
|
|
|
|
msg: S.current.zhengzaijiazai,
|
|
|
|
);
|
|
|
|
_allKey = [GlobalKey(), GlobalKey(), GlobalKey(), GlobalKey()];
|
|
|
|
setState(() {});
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
super.build(context);
|
|
|
|
return VisibilityDetector(
|
|
|
|
key: Key('my-widget-key'),
|
|
|
|
onVisibilityChanged: (visibilityInfo) {
|
|
|
|
visiblePercentage = visibilityInfo.visibleFraction;
|
|
|
|
if (visiblePercentage == 1) permissionSettings();
|
|
|
|
},
|
|
|
|
child: GestureDetector(
|
|
|
|
behavior: HitTestBehavior.translucent,
|
|
|
|
onTap: () {
|
|
|
|
FocusScope.of(context).requestFocus(FocusNode());
|
|
|
|
},
|
|
|
|
child: Stack(
|
|
|
|
alignment: Alignment.bottomCenter,
|
|
|
|
children: [
|
|
|
|
Container(
|
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
Row(
|
|
|
|
children: [
|
|
|
|
Padding(
|
|
|
|
padding: EdgeInsets.only(left: 18.w, right: 10.w),
|
|
|
|
child: GestureDetector(
|
|
|
|
behavior: HitTestBehavior.opaque,
|
|
|
|
onTap: () {
|
|
|
|
Navigator.of(context).pushNamed(
|
|
|
|
'/router/union_select_city',
|
|
|
|
arguments: {
|
|
|
|
"cityName": areaName
|
|
|
|
}).then((value) {
|
|
|
|
if (value != null) {
|
|
|
|
areaName = "$value";
|
|
|
|
loadFinish();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
child: Row(
|
|
|
|
children: [
|
|
|
|
Text(
|
|
|
|
areaName ?? "",
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
maxLines: 1,
|
|
|
|
style: TextStyle(
|
|
|
|
fontSize: 14.sp,
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
color: Colors.white,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Icon(
|
|
|
|
Icons.keyboard_arrow_down,
|
|
|
|
color: Colors.white,
|
|
|
|
size: 24,
|
|
|
|
)
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Expanded(child: buildSearchItem())
|
|
|
|
],
|
|
|
|
),
|
|
|
|
Align(
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
child: TabBar(
|
|
|
|
controller: tabController,
|
|
|
|
//tab排放方向
|
|
|
|
tabAlignment: TabAlignment.start,
|
|
|
|
isScrollable: true,
|
|
|
|
//可滚动
|
|
|
|
dividerHeight: 0,
|
|
|
|
dividerColor: Colors.transparent,
|
|
|
|
indicatorColor: Colors.white,
|
|
|
|
labelColor: Colors.white,
|
|
|
|
labelStyle: TextStyle(
|
|
|
|
fontSize: 18.sp,
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
),
|
|
|
|
unselectedLabelStyle: TextStyle(
|
|
|
|
fontSize: 15.sp,
|
|
|
|
fontWeight: FontWeight.normal,
|
|
|
|
),
|
|
|
|
//未选中文字颜色
|
|
|
|
unselectedLabelColor: Colors.white,
|
|
|
|
indicatorSize: TabBarIndicatorSize.label,
|
|
|
|
//指示器与文字等宽
|
|
|
|
tabs: <Widget>[
|
|
|
|
MyTab(text: S.of(context).quanbu),
|
|
|
|
MyTab(text: S.of(context).chi),
|
|
|
|
MyTab(text: S.of(context).he),
|
|
|
|
MyTab(text: S.of(context).wan),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
_allKey.isEmpty
|
|
|
|
? NoDataView(
|
|
|
|
src: "assets/image/di_zhi.webp",
|
|
|
|
isShowBtn: false,
|
|
|
|
text: "暂无店铺列表~",
|
|
|
|
fontSize: 16.sp,
|
|
|
|
margin: EdgeInsets.only(top: 120.h),
|
|
|
|
)
|
|
|
|
: Expanded(
|
|
|
|
child: TabBarView(
|
|
|
|
controller: tabController,
|
|
|
|
children: [
|
|
|
|
UnionList(
|
|
|
|
_allKey[0],
|
|
|
|
"",
|
|
|
|
latLng,
|
|
|
|
editingController.text,
|
|
|
|
areaName,
|
|
|
|
),
|
|
|
|
UnionList(_allKey[1], "EATSTORE", latLng,
|
|
|
|
editingController.text, areaName),
|
|
|
|
UnionList(_allKey[2], "DRINKSTORE", latLng,
|
|
|
|
editingController.text, areaName),
|
|
|
|
UnionList(_allKey[3], "HAPPYSTORE", latLng,
|
|
|
|
editingController.text, areaName),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
)
|
|
|
|
],
|
|
|
|
),
|
|
|
|
padding: EdgeInsets.only(
|
|
|
|
top: MediaQuery.of(context).padding.top + 17.h,
|
|
|
|
),
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
gradient: LinearGradient(
|
|
|
|
begin: Alignment.topCenter,
|
|
|
|
end: Alignment.bottomCenter,
|
|
|
|
colors: [
|
|
|
|
Color(0xFF32A060),
|
|
|
|
Color(0xFF32A060),
|
|
|
|
Colors.white,
|
|
|
|
Colors.white,
|
|
|
|
],
|
|
|
|
stops: [0, 0.2, 0.4, 1],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
if (_isShowLocalTips)
|
|
|
|
Padding(
|
|
|
|
padding: EdgeInsets.only(bottom: 70.h),
|
|
|
|
child: LocationTips(() {
|
|
|
|
setState(() {
|
|
|
|
_isShowLocalTips = false;
|
|
|
|
});
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget buildSearchItem() {
|
|
|
|
return Container(
|
|
|
|
margin: EdgeInsets.fromLTRB(6.w, 0, 14.w, 0),
|
|
|
|
padding: EdgeInsets.symmetric(vertical: 6.h),
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
color: Color(0xFFF5FAF7),
|
|
|
|
borderRadius: BorderRadius.circular(4),
|
|
|
|
boxShadow: [
|
|
|
|
BoxShadow(
|
|
|
|
color: Colors.black.withAlpha(12),
|
|
|
|
offset: Offset(0, 3),
|
|
|
|
blurRadius: 14,
|
|
|
|
spreadRadius: 0,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
child: TextField(
|
|
|
|
textInputAction: TextInputAction.search,
|
|
|
|
onEditingComplete: () {
|
|
|
|
FocusScope.of(context).requestFocus(FocusNode());
|
|
|
|
loadFinish();
|
|
|
|
},
|
|
|
|
controller: editingController,
|
|
|
|
style: TextStyle(
|
|
|
|
fontSize: 14.sp,
|
|
|
|
),
|
|
|
|
decoration: InputDecoration(
|
|
|
|
hintText: "搜索联盟会员店",
|
|
|
|
hintStyle: TextStyle(
|
|
|
|
fontSize: 12.sp,
|
|
|
|
color: Color(0xFFB3B3B3),
|
|
|
|
),
|
|
|
|
isCollapsed: true,
|
|
|
|
prefixIcon: Padding(
|
|
|
|
padding: EdgeInsets.only(left: 5.w, right: 5.w),
|
|
|
|
child: Image.asset(
|
|
|
|
"assets/image/icon_search.webp",
|
|
|
|
width: 16.h,
|
|
|
|
height: 16.h,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
prefixIconConstraints: BoxConstraints(),
|
|
|
|
border: InputBorder.none,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
bool get wantKeepAlive => true;
|
|
|
|
}
|