import 'dart:convert';
|
import 'dart:ui';
|
|
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/material.dart';
|
import 'package:locations/api/http.dart';
|
import 'package:locations/model/map/location_model.dart';
|
import 'package:locations/model/sos/sos_model.dart';
|
import 'package:locations/model/sos/sos_record_model.dart';
|
import 'package:locations/ui/map/map.dart';
|
import 'package:locations/ui/sos/sos_contacts.dart';
|
import 'package:locations/ui/widget/base_ui.dart';
|
import 'package:locations/ui/widget/button.dart';
|
import 'package:locations/ui/widget/dialog.dart';
|
import 'package:locations/ui/widget/nav.dart';
|
import 'package:locations/ui/widget/sos_ui.dart';
|
import 'package:locations/utils/event_bus_util.dart';
|
import 'package:locations/utils/location_util.dart';
|
import 'package:locations/utils/pageutils.dart';
|
import 'package:locations/utils/string_util.dart';
|
import 'package:locations/utils/ui_constant.dart';
|
import 'package:locations/utils/ui_utils.dart';
|
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
|
//紧急联系人输入框确定事件
|
typedef OnEmergencyContactSure = void Function(
|
String? name, String? phone, String? remarks);
|
|
class SOSPage extends StatefulWidget {
|
SOSPage({Key? key, required this.title}) : super(key: key);
|
|
// This widget is the home page of your application. It is stateful, meaning
|
// that it has a State object (defined below) that contains fields that affect
|
// how it looks.
|
|
// This class is the configuration for the state. It holds the values (in this
|
// case the title) provided by the parent (in this case the App widget) and
|
// used by the build method of the State. Fields in a Widget subclass are
|
// always marked "final".
|
|
final String title;
|
|
@override
|
_SOSPageState createState() => _SOSPageState();
|
}
|
|
class _SOSPageState extends State<SOSPage> with SingleTickerProviderStateMixin {
|
int selectIndex = 1;
|
AnimationController? controller;
|
Animation<double>? animation;
|
|
final RefreshController _refreshController =
|
RefreshController(initialRefresh: false);
|
|
bool sos = false;
|
|
int _page = 1;
|
int _totalCount = 0;
|
|
List<SosRecordModel>? _sosRecordList = null;
|
|
@override
|
void initState() {
|
super.initState();
|
controller = AnimationController(
|
duration: const Duration(milliseconds: 300), vsync: this);
|
final CurvedAnimation curve =
|
CurvedAnimation(parent: controller!, curve: Curves.easeIn);
|
animation = Tween(begin: -245.0, end: 0.0).animate(curve)
|
..addListener(() {
|
setState(() {});
|
});
|
|
_onRefresh();
|
}
|
|
@override
|
void dispose() {
|
controller!.dispose();
|
super.dispose();
|
}
|
|
Future _requestRecord() async {
|
Map<String, dynamic>? result =
|
await SOSApiUtil.listSOSRecord(context, _page);
|
if (result!["code"] == 0) {
|
Map<String, dynamic> data = result["data"];
|
List<dynamic> list = data["list"];
|
List<SosRecordModel> recordList = [];
|
for (var element in list) {
|
print(jsonEncode(element));
|
recordList.add(SosRecordModel.fromJson(element));
|
}
|
if (_page == 1) {
|
_sosRecordList = [];
|
}
|
|
setState(() {
|
_sosRecordList!.addAll(recordList);
|
});
|
|
_totalCount = data["count"];
|
} else {
|
ToastUtil.toast(result["msg"]);
|
}
|
}
|
|
void _onRefresh() async {
|
await _requestRecord();
|
_page = 1;
|
_refreshController.refreshCompleted();
|
}
|
|
void _onLoading() async {
|
await _requestRecord();
|
_refreshController.loadComplete();
|
}
|
|
@override
|
Widget build(BuildContext context) {
|
return Scaffold(
|
backgroundColor: Colors.white,
|
body: Stack(
|
children: [
|
//------------内容层----------
|
Positioned(
|
left: 0,
|
right: 0,
|
bottom: 0,
|
top: 0,
|
child: Container(
|
padding: EdgeInsets.fromLTRB(0,
|
MediaQuery.of(context).viewPadding.top + 48 + 10, 0, 0),
|
decoration: BoxDecoration(
|
gradient: LinearGradient(
|
colors: [Color(0xFFEC0017), Color(0xFFDC002F)],
|
)),
|
child: Column(
|
mainAxisSize: MainAxisSize.max,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
children: [
|
Text(
|
"温馨提示:点击“SOS”系统将会自动推送消息给紧急联系人",
|
style: TextStyle(color: Colors.white, fontSize: 12),
|
),
|
//--------SOS动效--------
|
InkWell(
|
onTap: () {
|
if (!sos) {
|
LocationUtil.startLocation(0, (state, map) {
|
if (LocationState.success == state) {
|
SOSApiUtil.addSOSRecord(context, map!)
|
.then((value) {
|
if (value!["code"] == 0) {
|
setState(() {
|
sos = true;
|
});
|
} else {
|
ToastUtil.toast(value["msg"]);
|
}
|
});
|
} else if (LocationState.permission_denied ==
|
state) {
|
ToastUtil.toast("请开启定位权限");
|
} else {
|
ToastUtil.toast("定位失败");
|
}
|
});
|
} else {
|
setState(() {
|
sos = false;
|
});
|
}
|
},
|
child: Container(
|
margin: const EdgeInsets.only(top: 38),
|
height: 249,
|
width: 249,
|
decoration: BoxDecoration(
|
color: const Color(0xFFFF2B31),
|
border: Border.all(
|
color: const Color(0xFFFF4E4A), width: 2),
|
borderRadius: BorderRadius.circular(125),
|
boxShadow: const [
|
BoxShadow(
|
color: Color(0x85D3001B),
|
blurRadius: 10,
|
spreadRadius: 17)
|
]),
|
child: Stack(
|
alignment: Alignment.topCenter,
|
children: [
|
Container(
|
margin: const EdgeInsets.only(top: 0),
|
child: Image.asset(
|
"assets/images/common/icon_sos.png",
|
width: 250,
|
)),
|
Positioned(
|
bottom: 10,
|
child: Text(
|
!sos ? "sos" : "",
|
style: const TextStyle(
|
fontSize: 45,
|
color: Colors.white,
|
fontWeight: FontWeight.bold),
|
)),
|
sos
|
? SizedBox(
|
width: 250,
|
height: 250,
|
child: RadarView())
|
: Container(),
|
sos
|
? Positioned(
|
bottom: 20,
|
child: Column(children: const [
|
Text(
|
"正在向你的紧急联系人发送警报...",
|
style: TextStyle(
|
fontSize: 12,
|
color: Colors.white,
|
),
|
),
|
Text(
|
"关闭求助",
|
style: TextStyle(
|
fontSize: 20,
|
color: Colors.white,
|
),
|
)
|
]))
|
: Container(),
|
],
|
),
|
)),
|
//----------求助记录-------------
|
Expanded(
|
child: Container(
|
margin: const EdgeInsets.only(top: 30),
|
padding: const EdgeInsets.all(15),
|
decoration: const BoxDecoration(
|
color: Colors.white,
|
borderRadius: BorderRadius.only(
|
topRight: Radius.circular(10),
|
topLeft: Radius.circular(10))),
|
child: Flex(
|
direction: Axis.vertical,
|
mainAxisSize: MainAxisSize.min,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
children: [
|
Flex(
|
direction: Axis.horizontal,
|
mainAxisAlignment: MainAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
children: [
|
const Text(
|
"求助记录",
|
style: TextStyle(
|
color: ColorConstant.theme,
|
fontSize: 16),
|
),
|
Container(
|
width: 5,
|
),
|
Image.asset(
|
"assets/images/common/icon_ask.png",
|
height: 15,
|
)
|
]),
|
Container(
|
width: 12.5,
|
),
|
_totalCount > 0
|
? InkWell(
|
onTap: () {
|
DialogUtil.showDialog(
|
context,
|
NotifyDialog(
|
"温馨提示",
|
"确定要清空记录吗?",
|
() {},
|
() {
|
SOSApiUtil.clearSOSRecord(context)
|
.then((value) {
|
if (value!["code"] == 0) {
|
_onRefresh();
|
} else {
|
ToastUtil.toast(value["msg"]);
|
}
|
});
|
},
|
height: 200,
|
));
|
},
|
child: const Text(
|
"清空记录",
|
style: TextStyle(
|
color: Color(0xFF999999),
|
fontSize: 12),
|
),
|
)
|
: Container(),
|
Expanded(
|
child: SmartRefresher(
|
enablePullDown: true,
|
enablePullUp: true,
|
header: refreshHeader,
|
footer: loadFotter,
|
onRefresh: _onRefresh,
|
onLoading: _onLoading,
|
controller: _refreshController,
|
child: ListView.builder(
|
itemCount: _sosRecordList == null
|
? 0
|
: (_sosRecordList!.isEmpty
|
? 1
|
: _sosRecordList!.length),
|
itemBuilder:
|
(BuildContext context, int index) {
|
if (_sosRecordList!.isEmpty) {
|
return Container(
|
padding:
|
const EdgeInsets.only(top: 20),
|
alignment: Alignment.center,
|
child: const Text(
|
"暂无数据",
|
style:
|
TextStyle(color: Colors.grey),
|
));
|
}
|
|
return getListViewItem(index);
|
},
|
padding:
|
const EdgeInsets.fromLTRB(0, 0, 0, 55),
|
)),
|
)
|
],
|
),
|
)),
|
],
|
),
|
)),
|
//------------导航栏----------
|
TopNavBar(
|
title: "紧急求助",
|
rightText: "紧急联系人",
|
rightClick: () {
|
NavigatorUtil.navigateToNextPage(
|
context, SOSContactPage(title: ""), (data) {});
|
},
|
backIcon: Image.asset(
|
"assets/images/common/icon_sos_back.png",
|
height: 17.5,
|
),
|
textColor: Colors.white,
|
backGround: Colors.transparent,
|
),
|
//------------弹出框----------
|
Positioned(
|
bottom: animation!.value,
|
left: 0,
|
right: 0,
|
child: EmergencyContactEdit("添加紧急联系人", () {
|
setState(() {
|
if (controller!.status == AnimationStatus.completed) {
|
controller!.reverse();
|
} else if (controller!.status ==
|
AnimationStatus.dismissed) {
|
controller!.forward();
|
}
|
});
|
}, () {
|
if (controller!.status == AnimationStatus.completed) {
|
controller!.reverse();
|
}
|
}, (String? name, String? phone, String? remarks) {
|
if (StringUtil.isNullOrEmpty(name)) {
|
ToastUtil.toast("请输入联系人称呼");
|
return;
|
}
|
|
if (StringUtil.isNullOrEmpty(phone)) {
|
ToastUtil.toast("请输入电话号码");
|
return;
|
}
|
|
SOSApiUtil.addEmergencyContacts(context, name, phone, remarks)
|
.then((value) {
|
if (value!["code"] == 0) {
|
ToastUtil.toast("添加成功");
|
if (controller!.status == AnimationStatus.completed) {
|
controller!.reverse();
|
}
|
} else {
|
ToastUtil.toast(value["msg"]);
|
}
|
});
|
}))
|
],
|
));
|
}
|
|
Widget getListViewItem(int index) {
|
SosRecordModel record = _sosRecordList![index];
|
|
return Container(
|
padding: const EdgeInsets.fromLTRB(18, 8, 18, 8),
|
margin: const EdgeInsets.only(bottom: 13),
|
decoration: BoxDecoration(
|
border: Border.all(color: const Color(0xFFCACACA), width: 1),
|
borderRadius: BorderRadius.circular(8)),
|
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
Align(
|
alignment: Alignment.center,
|
child: Container(
|
padding: const EdgeInsets.fromLTRB(0, 4, 0, 4),
|
width: 110,
|
alignment: Alignment.center,
|
decoration: BoxDecoration(
|
color: const Color(0xFFC7C7C7),
|
borderRadius: BorderRadius.circular(10)),
|
child: Text(
|
"${record.createTime}",
|
style: const TextStyle(color: Colors.white, fontSize: 10),
|
),
|
)),
|
Container(
|
height: 6,
|
),
|
Row(
|
children: [
|
Image(
|
image: NetworkImage(record.portrait!),
|
height: 14,
|
),
|
Text(
|
record.from!,
|
style: const TextStyle(fontSize: 12, color: Color(0xFF999999)),
|
)
|
],
|
),
|
Container(
|
height: 6,
|
),
|
Text(
|
record.targetDesc==null?"": record.targetDesc!,
|
style: TextStyle(fontSize: 12, color: const Color(0xFF8CAFC8)),
|
),
|
Container(
|
height: 6,
|
),
|
Text(
|
record.desc!,
|
maxLines: 2,
|
overflow: TextOverflow.ellipsis,
|
style: TextStyle(fontSize: 12, color: Color(0xFFBABABA)),
|
),
|
Container(
|
height: 6,
|
),
|
Container(
|
color: const Color(0xFFCED4D9),
|
height: 1,
|
),
|
Container(
|
height: 6,
|
),
|
InkWell(
|
onTap: () {
|
NavigatorUtil.navigateToNextPage(
|
context,
|
MapPage(
|
"位置",
|
share: false,
|
location: record.location,
|
uid: record.targetUid,
|
),
|
(data) {});
|
},
|
child: Row(
|
crossAxisAlignment: CrossAxisAlignment.center,
|
children: [
|
Image.asset(
|
"assets/images/main/icon_location_position_name.png",
|
height: 16),
|
Container(
|
width: 6,
|
),
|
Expanded(
|
child: Text(
|
record.location!.address! +
|
record.location!.addressDetail!,
|
softWrap: false,
|
overflow: TextOverflow.ellipsis,
|
style: const TextStyle(
|
fontSize: 12, color: Color(0xFF999999)),
|
)),
|
Container(
|
width: 6,
|
),
|
Image.asset(
|
"assets/images/main/icon_location_position_more.png",
|
height: 10,
|
),
|
],
|
))
|
],
|
),
|
);
|
}
|
}
|
|
class EmergencyContactEdit extends StatefulWidget {
|
GestureTapCallback? titleClick;
|
GestureTapCallback? cancelClick;
|
OnEmergencyContactSure? sureClick;
|
SOSContact? contact;
|
final String title;
|
|
EmergencyContactEdit(
|
this.title, this.titleClick, this.cancelClick, this.sureClick,
|
{this.contact});
|
|
@override
|
_EmergencyContactEditState createState() => _EmergencyContactEditState(
|
title, this.titleClick, this.cancelClick, this.sureClick,
|
contact: contact);
|
}
|
|
class _EmergencyContactEditState extends State<EmergencyContactEdit> {
|
GestureTapCallback? titleClick;
|
GestureTapCallback? cancelClick;
|
OnEmergencyContactSure? sureClick;
|
SOSContact? contact;
|
final String title;
|
var eventBusContact;
|
|
TextEditingController? _nameController = TextEditingController();
|
TextEditingController? _phoneController = TextEditingController();
|
TextEditingController? _remarksController = TextEditingController();
|
|
_EmergencyContactEditState(
|
this.title,
|
GestureTapCallback? this.titleClick,
|
GestureTapCallback? this.cancelClick,
|
OnEmergencyContactSure? this.sureClick,
|
{this.contact});
|
|
@override
|
void initState() {
|
super.initState();
|
eventBusContact = eventBus.on<SOSContact>().listen((event) {
|
if (!title.contains("编辑")) {
|
return;
|
}
|
//设置用户头像
|
setState(() {
|
contact = event;
|
});
|
|
if (contact != null) {
|
_phoneController!.text = contact!.phone!;
|
_nameController!.text = contact!.user!.nickName!;
|
if (!StringUtil.isNullOrEmpty(contact!.remarks)) {
|
_remarksController!.text = contact!.remarks!;
|
}
|
}
|
});
|
}
|
|
@override
|
void dispose() {
|
super.dispose();
|
eventBusContact.cancel();
|
}
|
|
@override
|
Widget build(BuildContext context) {
|
return Container(
|
margin: EdgeInsets.fromLTRB(10, 0, 10, 0),
|
padding: EdgeInsets.fromLTRB(25, 0, 25, 15),
|
decoration: BoxDecoration(
|
color: Colors.white,
|
borderRadius: BorderRadius.only(
|
topLeft: Radius.circular(10), topRight: Radius.circular(10)),
|
boxShadow: getViewShadow()),
|
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.center,
|
children: [
|
InkWell(
|
onTap: () {
|
if (titleClick != null && contact == null) {
|
_phoneController!.text = "";
|
_nameController!.text = "";
|
_remarksController!.text = "";
|
titleClick!();
|
}
|
},
|
child: Container(
|
padding: EdgeInsets.fromLTRB(0, 15, 0, 15),
|
child: Row(
|
mainAxisAlignment: MainAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
children: [
|
Image.asset(
|
"assets/images/common/icon_person.png",
|
height: 17,
|
),
|
Container(width: 12),
|
Text(
|
title,
|
style: TextStyle(
|
color: ColorConstant.theme, fontSize: 15),
|
)
|
]))),
|
getInputView("联系人称呼", _nameController),
|
Container(
|
height: 14,
|
),
|
getInputView("联系人手机号码", _phoneController),
|
Container(
|
height: 14,
|
),
|
getInputView("备注(选填)", _remarksController),
|
Container(
|
height: 16,
|
),
|
Flex(
|
direction: Axis.horizontal,
|
children: [
|
Expanded(
|
flex: 1,
|
child: MyOutlineButton(
|
"取消",
|
10,
|
fontSize: 18,
|
height: 40,
|
onClick: () {
|
cancelClick!();
|
},
|
)),
|
Container(width: 13),
|
Expanded(
|
flex: 1,
|
child: MyFillButton(
|
"保存",
|
10,
|
fontSize: 18,
|
height: 40,
|
onClick: () {
|
sureClick!(
|
_nameController!.value.text,
|
_phoneController!.value.text,
|
_remarksController!.value.text);
|
},
|
)),
|
],
|
)
|
],
|
),
|
);
|
}
|
|
Widget getInputView(String hintText, TextEditingController? controller) {
|
return TextField(
|
controller: controller,
|
decoration: InputDecoration(
|
hintText: hintText,
|
hintStyle: const TextStyle(color: Color(0xFFBABABA), fontSize: 14),
|
border: OutlineInputBorder(
|
borderRadius: BorderRadius.circular(10),
|
borderSide: BorderSide.none),
|
focusedBorder: OutlineInputBorder(
|
borderRadius: BorderRadius.circular(10),
|
borderSide: BorderSide.none),
|
fillColor: Color(0xFFF0F0F0),
|
contentPadding: EdgeInsets.fromLTRB(20, 14, 20, 14),
|
filled: true),
|
);
|
}
|
|
//控件阴影
|
List<BoxShadow> getViewShadow() {
|
return [
|
BoxShadow(
|
blurRadius: 6.5,
|
spreadRadius: 1,
|
color: Color(0x4D0E96FF),
|
)
|
];
|
}
|
}
|