Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
5170ce9304
BIN
star_lock/images/main/icon_lockLog_play.png
Normal file
BIN
star_lock/images/main/icon_lockLog_play.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
@ -268,7 +268,7 @@ SPEC CHECKSUMS:
|
||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||
reactive_ble_mobile: 9ce6723d37ccf701dbffd202d487f23f5de03b4c
|
||||
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
|
||||
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
|
||||
sqflite: 50a33e1d72bd59ee092a519a35d107502757ebed
|
||||
SwiftProtobuf: 407a385e97fd206c4fbe880cc84123989167e0d1
|
||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||
url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
|
||||
|
||||
@ -271,9 +271,9 @@ class DoorLockLogLogic extends BaseGetXController {
|
||||
super.onClose();
|
||||
|
||||
// 获取是否是演示模式 演示模式不获取接口
|
||||
var isDemoMode = await Storage.getBool(ifIsDemoModeOrNot);
|
||||
if (isDemoMode == false) {
|
||||
_replySubscription.cancel();
|
||||
}
|
||||
// var isDemoMode = await Storage.getBool(ifIsDemoModeOrNot);
|
||||
// if (isDemoMode == false) {
|
||||
// _replySubscription.cancel();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_advanced_calendar/flutter_advanced_calendar.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:star_lock/appRouters.dart';
|
||||
import 'package:star_lock/main/lockDetail/doorLockLog/doorLockLog_logic.dart';
|
||||
import 'package:star_lock/tools/advancedCalendar/src/widget.dart';
|
||||
import 'package:star_lock/tools/menuItem/xsDropDownWidget.dart';
|
||||
import 'package:timelines/timelines.dart';
|
||||
|
||||
import '../../../app_settings/app_colors.dart';
|
||||
@ -64,10 +66,17 @@ class _DoorLockLogPageState extends State<DoorLockLogPage> {
|
||||
endIndent: 30.w,
|
||||
),
|
||||
eventDropDownWidget(),
|
||||
SizedBox(
|
||||
height: 20.h,
|
||||
),
|
||||
Expanded(child: timeLineView()),
|
||||
Expanded(child: timeLineView())
|
||||
// Expanded(
|
||||
// // 添加 Expanded 来让 ListView 占据剩余的空间
|
||||
// child: ListView.builder(
|
||||
// itemBuilder: (context, index) {
|
||||
// // 返回你想要在 ListView 中显示的小部件
|
||||
// return timeLineView();
|
||||
// },
|
||||
// itemCount: 5, // 替换成你的列表项数量
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -115,41 +124,24 @@ class _DoorLockLogPageState extends State<DoorLockLogPage> {
|
||||
|
||||
//事件下拉框组件
|
||||
Widget eventDropDownWidget() {
|
||||
return Row(children: [
|
||||
SizedBox(
|
||||
width: 50.w,
|
||||
return Container(
|
||||
margin: EdgeInsets.only(top: 20.h, left: 30.w, bottom: 20.h),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Obx(() => XSDropDownWidget(
|
||||
items: state.getDropDownItemList,
|
||||
value: state.dropdownValue.value,
|
||||
valueChanged: (value) {})),
|
||||
],
|
||||
),
|
||||
Obx(() => DropdownButton<String>(
|
||||
value: state.dropdownValue.value,
|
||||
icon: const Icon(Icons.arrow_drop_down),
|
||||
iconSize: 40,
|
||||
// elevation: 12,
|
||||
style: TextStyle(
|
||||
fontSize: 26.sp,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w600),
|
||||
iconEnabledColor: Colors.grey,
|
||||
underline: Container(
|
||||
height: 0,
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
state.dropdownValue.value = newValue!;
|
||||
},
|
||||
items: state.dropDownItemList.obs
|
||||
.map<DropdownMenuItem<String>>((item) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: item,
|
||||
child: Text(item),
|
||||
);
|
||||
}).toList(),
|
||||
))
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
//时间轴组件
|
||||
Widget timeLineView() {
|
||||
return Container(
|
||||
margin: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 20.h),
|
||||
margin: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 20.h, top: 20.h),
|
||||
//给contain设置一个10像素的圆角
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white, borderRadius: BorderRadius.circular(16.w)),
|
||||
@ -168,11 +160,31 @@ class _DoorLockLogPageState extends State<DoorLockLogPage> {
|
||||
color: Colors.black,
|
||||
fontSize: 24.sp,
|
||||
fontWeight: FontWeight.w600)),
|
||||
Image(
|
||||
image: const AssetImage(
|
||||
'images/main/icon_lockDetail_monitoringvoiceFrist.png'),
|
||||
width: 260.w,
|
||||
height: 260.h,
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Get.toNamed(Routers.videoLogDetailPage);
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
Image(
|
||||
image: const AssetImage(
|
||||
'images/main/icon_lockDetail_monitoringvoiceFrist.png'),
|
||||
width: 260.w,
|
||||
height: 260.h,
|
||||
),
|
||||
//在图片上添加一个三角视频图标
|
||||
Positioned(
|
||||
top: 200.h,
|
||||
left: 10.w,
|
||||
child: Image(
|
||||
image: const AssetImage(
|
||||
'images/main/icon_lockLog_play.png'),
|
||||
width: 24.w,
|
||||
height: 20.w,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import 'package:flutter_advanced_calendar/flutter_advanced_calendar.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:star_lock/tools/advancedCalendar/src/controller.dart';
|
||||
import 'package:star_lock/tools/menuItem/dropDownItem.dart';
|
||||
|
||||
import '../../lockMian/entity/lockListInfo_entity.dart';
|
||||
import '../electronicKey/electronicKeyDetail/keyOperationRecordEntity.dart';
|
||||
@ -14,8 +16,14 @@ class DoorLockLogState {
|
||||
DateTime(2024, 10, 10),
|
||||
];
|
||||
var dropdownValue = '全部事件'.obs;
|
||||
final List dropDownItemList =
|
||||
['全部事件', '门锁异常', '有人出现', '有人按门铃', '一次性密码开门'].obs;
|
||||
List<DropDownItem> getDropDownItemList = [
|
||||
DropDownItem(itemTitle: "全部事件", itemValue: '0', isCheked: false),
|
||||
DropDownItem(itemTitle: "开锁事件", itemValue: '1', isCheked: false),
|
||||
DropDownItem(itemTitle: "异常事件", itemValue: '2', isCheked: false),
|
||||
DropDownItem(itemTitle: "门锁事件", itemValue: '3', isCheked: false),
|
||||
DropDownItem(itemTitle: "视频事件", itemValue: '4', isCheked: false),
|
||||
];
|
||||
|
||||
var currentStep = 0.obs;
|
||||
|
||||
DoorLockLogState() {
|
||||
|
||||
@ -6,6 +6,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../appRouters.dart';
|
||||
import '../../../app_settings/app_colors.dart';
|
||||
import '../../../blue/blue_manage.dart';
|
||||
import '../../../blue/io_protocol/io_addUser.dart';
|
||||
@ -132,6 +133,11 @@ class LockDetailLogic extends BaseGetXController {
|
||||
// 权限校验错误
|
||||
print("${reply.commandType}校验错误");
|
||||
|
||||
break;
|
||||
case 0x16:
|
||||
// 正在开锁中...
|
||||
print("${reply.commandType}正在开锁中...");
|
||||
showToast("正在开锁中...");
|
||||
break;
|
||||
default:
|
||||
//失败
|
||||
@ -331,7 +337,7 @@ class LockDetailLogic extends BaseGetXController {
|
||||
keyType: 0,
|
||||
startDate: DateTime.now().millisecondsSinceEpoch,
|
||||
expireDate: 0x11223344,
|
||||
role: 0,
|
||||
role: state.keyInfos.value.keyRight == 1 ? 1 : 0,
|
||||
password: "123456",
|
||||
needAuthor: 1,
|
||||
publicKey: publicKeyDataList,
|
||||
@ -388,7 +394,7 @@ class LockDetailLogic extends BaseGetXController {
|
||||
keyType: 0,
|
||||
startDate: DateTime.now().millisecondsSinceEpoch,
|
||||
expireDate: 0x11223344,
|
||||
role: 0,
|
||||
role: state.keyInfos.value.keyRight == 1 ? 1 : 0,
|
||||
password: "123456",
|
||||
needAuthor: 1,
|
||||
publicKey: publicKeyDataList,
|
||||
@ -592,6 +598,39 @@ class LockDetailLogic extends BaseGetXController {
|
||||
}
|
||||
}
|
||||
|
||||
// 0开锁 1长按闭锁 2密码 3卡 4指纹 5遥控 6人脸 7监控 8操作记录 9消息提醒 10设置
|
||||
clickItemBtnAction(int type){
|
||||
state.clickNextType = type;
|
||||
if (state.lockUserNo == 0) {
|
||||
// 电子钥匙lockUserNo为0 要先添加用户
|
||||
addUserConnectBlue();
|
||||
} else {
|
||||
clickPushBtnAction();
|
||||
}
|
||||
}
|
||||
|
||||
clickPushBtnAction(){
|
||||
// 0开锁 1长按闭锁 2密码 3卡 4指纹 5遥控 6人脸 7监控 8操作记录 9消息提醒 10设置
|
||||
switch(state.clickNextType){
|
||||
case 0:
|
||||
// 开锁
|
||||
startOpenLock();
|
||||
break;
|
||||
case 1:
|
||||
// 长按闭锁
|
||||
startUnLock();
|
||||
break;
|
||||
case 2:
|
||||
// 密码
|
||||
Get.toNamed(Routers.passwordKeyListPage, arguments: {"keyInfo": state.keyInfos.value});
|
||||
break;
|
||||
case 3:
|
||||
// 卡
|
||||
Get.toNamed(Routers.passwordKeyListPage, arguments: {"keyInfo": state.keyInfos.value});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取手机联网token,根据锁设置里面获取的开锁时是否联网来判断是否调用这个接口
|
||||
void getLockNetToken() async {
|
||||
LockNetTokenEntity entity = await ApiRepository.to.getLockNetToken(lockId: state.keyInfos.value.lockId.toString());
|
||||
|
||||
@ -314,13 +314,13 @@ class _LockDetailPageState extends State<LockDetailPage>
|
||||
var showWidgetArr = [
|
||||
// 操作记录
|
||||
bottomItem('images/main/icon_main_operatingRecord.png', TranslationLoader.lanKeys!.operatingRecord!.tr, () {
|
||||
Get.toNamed(Routers.lockOperatingRecordPage, arguments: {"keyInfo": widget.lockListInfoItemEntity});
|
||||
Get.toNamed(Routers.lockOperatingRecordPage, arguments: {"keyInfo": state.keyInfos.value});
|
||||
}),
|
||||
|
||||
// 设置
|
||||
bottomItem('images/main/icon_main_set.png', TranslationLoader.lanKeys!.set!.tr, () {
|
||||
Get.toNamed(Routers.lockSetPage, arguments: {
|
||||
"lockId": widget.lockListInfoItemEntity.lockId,
|
||||
"lockId": state.keyInfos.value.lockId,
|
||||
"isOnlyOneData": widget.isOnlyOneData,
|
||||
});
|
||||
}),
|
||||
@ -344,14 +344,14 @@ class _LockDetailPageState extends State<LockDetailPage>
|
||||
showWidgetArr.add(bottomItem('images/main/icon_main_electronicKey.png',
|
||||
TranslationLoader.lanKeys!.electronicKey!.tr, () {
|
||||
Get.toNamed(Routers.electronicKeyListPage,
|
||||
arguments: {"keyInfo": widget.lockListInfoItemEntity});
|
||||
arguments: {"keyInfo": state.keyInfos.value});
|
||||
}));
|
||||
|
||||
// 密码
|
||||
showWidgetArr.add(bottomItem('images/main/icon_main_password.png',
|
||||
TranslationLoader.lanKeys!.password!.tr, () {
|
||||
Get.toNamed(Routers.passwordKeyListPage,
|
||||
arguments: {"keyInfo": widget.lockListInfoItemEntity});
|
||||
arguments: {"keyInfo": state.keyInfos.value});
|
||||
}));
|
||||
|
||||
// ic卡
|
||||
@ -361,7 +361,7 @@ class _LockDetailPageState extends State<LockDetailPage>
|
||||
// logic.showEasyLoading();
|
||||
// });
|
||||
Get.toNamed(Routers.cardListPage, arguments: {
|
||||
"lockId": widget.lockListInfoItemEntity.lockId,
|
||||
"lockId": state.keyInfos.value.lockId,
|
||||
});
|
||||
}));
|
||||
}
|
||||
@ -371,7 +371,7 @@ class _LockDetailPageState extends State<LockDetailPage>
|
||||
showWidgetArr.add(bottomItem('images/main/icon_main_fingerprint.png',
|
||||
TranslationLoader.lanKeys!.fingerprint!.tr, () {
|
||||
Get.toNamed(Routers.fingerprintListPage, arguments: {
|
||||
"lockId": widget.lockListInfoItemEntity.lockId,
|
||||
"lockId": state.keyInfos.value.lockId,
|
||||
});
|
||||
}));
|
||||
}
|
||||
@ -390,7 +390,7 @@ class _LockDetailPageState extends State<LockDetailPage>
|
||||
bottomItem('images/main/icon_face.png',
|
||||
TranslationLoader.lanKeys!.humanFace!.tr, () {
|
||||
Get.toNamed(Routers.faceList, arguments: {
|
||||
"lockId": widget.lockListInfoItemEntity.lockId,
|
||||
"lockId": state.keyInfos.value.lockId,
|
||||
}); // Toast.show(msg: "功能暂未开放");
|
||||
}),
|
||||
);
|
||||
@ -402,7 +402,7 @@ class _LockDetailPageState extends State<LockDetailPage>
|
||||
bottomItem('images/main/icon_catEyes.png',
|
||||
TranslationLoader.lanKeys!.monitoring!.tr, () {
|
||||
Get.toNamed(Routers.realTimePicturePage, arguments: {
|
||||
"lockName": widget.lockListInfoItemEntity.lockName,
|
||||
"lockName": state.keyInfos.value.lockName,
|
||||
"isMonitoring": true
|
||||
});
|
||||
}),
|
||||
@ -414,15 +414,15 @@ class _LockDetailPageState extends State<LockDetailPage>
|
||||
bottomItem('images/main/icon_main_authorizedAdmin.png',
|
||||
TranslationLoader.lanKeys!.authorizedAdmin!.tr, () {
|
||||
Get.toNamed(Routers.authorizedAdminListPage,
|
||||
arguments: {"keyInfo": widget.lockListInfoItemEntity});
|
||||
arguments: {"keyInfo": state.keyInfos.value});
|
||||
}),
|
||||
// 操作记录
|
||||
bottomItem('images/main/icon_main_operatingRecord.png',
|
||||
TranslationLoader.lanKeys!.operatingRecord!.tr, () {
|
||||
// Get.toNamed(Routers.lockOperatingRecordPage,
|
||||
// arguments: {"keyInfo": widget.lockListInfoItemEntity});
|
||||
// arguments: {"keyInfo": state.keyInfos.value});
|
||||
Get.toNamed(Routers.doorLockLogPage,
|
||||
arguments: {"keyInfo": widget.lockListInfoItemEntity});
|
||||
arguments: {"keyInfo": state.keyInfos.value});
|
||||
}),
|
||||
// 视频日志
|
||||
bottomItem('images/main/icon_lockDetail_videoLog.png',
|
||||
@ -441,7 +441,7 @@ class _LockDetailPageState extends State<LockDetailPage>
|
||||
() {
|
||||
// BlueManage().stopScan();
|
||||
Get.toNamed(Routers.lockSetPage, arguments: {
|
||||
"lockId": widget.lockListInfoItemEntity.lockId,
|
||||
"lockId": state.keyInfos.value.lockId,
|
||||
"isOnlyOneData": widget.isOnlyOneData,
|
||||
});
|
||||
}),
|
||||
|
||||
@ -35,6 +35,9 @@ class LockDetailState {
|
||||
var iSOpenLock = true.obs; // 是开锁还是关锁
|
||||
Timer? closedUnlockSuccessfulTimer;
|
||||
|
||||
// 0开锁 1长按闭锁 2密码 3卡 4指纹 5遥控 6人脸 7监控 8操作记录 9消息提醒 10设置
|
||||
var clickNextType = 0;
|
||||
|
||||
//过渡动画控制器
|
||||
late AnimationController animationController;
|
||||
// var lockState = 0.obs;// 0未连接普通状态 1连接开锁中(展示动画) 2已连接开锁成功 3检测可用性 4连接失败 5连接失败重连中
|
||||
|
||||
@ -141,6 +141,9 @@ class LockFeature {
|
||||
int? appUnlockOnline;
|
||||
int? bluetoothBroadcast;
|
||||
int? attendance;
|
||||
int? motorTorsion;
|
||||
int? stayWarn;
|
||||
int? abnormalWarn;
|
||||
|
||||
LockFeature(
|
||||
{this.password,
|
||||
@ -191,7 +194,10 @@ class LockFeature {
|
||||
this.hotelLockCardSystem,
|
||||
this.appUnlockOnline,
|
||||
this.bluetoothBroadcast,
|
||||
this.attendance});
|
||||
this.attendance,
|
||||
this.motorTorsion,
|
||||
this.stayWarn,
|
||||
this.abnormalWarn,});
|
||||
|
||||
LockFeature.fromJson(Map<String, dynamic> json) {
|
||||
password = json['password'];
|
||||
@ -243,6 +249,9 @@ class LockFeature {
|
||||
appUnlockOnline = json['appUnlockOnline'];
|
||||
bluetoothBroadcast = json['bluetoothBroadcast'];
|
||||
attendance = json['attendance'];
|
||||
motorTorsion = json['motorTorsion'];
|
||||
stayWarn = json['stayWarn'];
|
||||
abnormalWarn = json['abnormalWarn'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
@ -296,6 +305,9 @@ class LockFeature {
|
||||
data['appUnlockOnline'] = appUnlockOnline;
|
||||
data['bluetoothBroadcast'] = bluetoothBroadcast;
|
||||
data['attendance'] = attendance;
|
||||
data['motorTorsion'] = motorTorsion;
|
||||
data['stayWarn'] = stayWarn;
|
||||
data['abnormalWarn'] = abnormalWarn;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ import '../../../../tools/showTFView.dart';
|
||||
import '../../../../tools/storage.dart';
|
||||
import '../../../../translations/trans_lib.dart';
|
||||
import 'checkingInInfoData_entity.dart';
|
||||
import 'lockSetInfo_entity.dart';
|
||||
import 'lockSet_state.dart';
|
||||
|
||||
typedef BlockSetStateCallback = void Function();
|
||||
@ -421,8 +422,8 @@ class LockSetLogic extends BaseGetXController {
|
||||
}
|
||||
|
||||
// 获取锁设置信息
|
||||
void getLockSettingInfoData() async {
|
||||
var entity = await ApiRepository.to.getLockSettingInfoData(
|
||||
Future<LockSetInfoEntity> getLockSettingInfoData() async {
|
||||
LockSetInfoEntity entity = await ApiRepository.to.getLockSettingInfoData(
|
||||
lockId: state.lockId.toString(),
|
||||
);
|
||||
if (entity.errorCode!.codeIsSuccessful) {
|
||||
@ -441,6 +442,7 @@ class LockSetLogic extends BaseGetXController {
|
||||
// await _readSupportFunctionsNoParameters(56);
|
||||
// _readSupportFunctionsNoParameters(62);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
// 当是锁拥有者的时候,删除锁
|
||||
@ -736,8 +738,6 @@ class LockSetLogic extends BaseGetXController {
|
||||
// TODO: implement onReady
|
||||
super.onReady();
|
||||
|
||||
getLockSettingInfoData();
|
||||
|
||||
_initReplySubscription();
|
||||
_scanListDiscoveredDeviceSubscriptionAction();
|
||||
}
|
||||
|
||||
@ -7,12 +7,14 @@ import 'package:star_lock/blue/blue_manage.dart';
|
||||
|
||||
import '../../../../appRouters.dart';
|
||||
import '../../../../app_settings/app_colors.dart';
|
||||
import '../../../../tools/EasyRefreshTool.dart';
|
||||
import '../../../../tools/appRouteObserver.dart';
|
||||
import '../../../../tools/commonItem.dart';
|
||||
|
||||
import '../../../../tools/submitBtn.dart';
|
||||
import '../../../../tools/titleAppBar.dart';
|
||||
import '../../../../translations/trans_lib.dart';
|
||||
import 'lockSetInfo_entity.dart';
|
||||
import 'lockSet_logic.dart';
|
||||
|
||||
// final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
|
||||
@ -27,6 +29,12 @@ class _LockSetPageState extends State<LockSetPage> with RouteAware {
|
||||
final logic = Get.put(LockSetLogic());
|
||||
final state = Get.find<LockSetLogic>().state;
|
||||
|
||||
Future<void> getHttpData() async {
|
||||
logic.getLockSettingInfoData().then((LockSetInfoEntity value){
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
// TODO: implement initState
|
||||
@ -35,6 +43,8 @@ class _LockSetPageState extends State<LockSetPage> with RouteAware {
|
||||
logic.initLoadDataAction(() {
|
||||
setState(() {});
|
||||
});
|
||||
|
||||
getHttpData();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -45,14 +55,19 @@ class _LockSetPageState extends State<LockSetPage> with RouteAware {
|
||||
barTitle: TranslationLoader.lanKeys!.set!.tr,
|
||||
haveBack: true,
|
||||
backgroundColor: AppColors.mainColor),
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Obx(() => ListView(
|
||||
children: getListWidget(),
|
||||
)),
|
||||
),
|
||||
],
|
||||
body: EasyRefreshTool(
|
||||
onRefresh: (){
|
||||
getHttpData();
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Obx(() => ListView(
|
||||
children: getListWidget(),
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
@ -201,7 +216,7 @@ class _LockSetPageState extends State<LockSetPage> with RouteAware {
|
||||
}))),
|
||||
// 开门器
|
||||
Visibility(
|
||||
visible: true,
|
||||
visible: state.lockFeature.value.doorStatus == 1 ? true : false,
|
||||
child: CommonItem(
|
||||
leftTitel: TranslationLoader.lanKeys!.doorOpener!.tr,
|
||||
rightTitle: "",
|
||||
@ -285,8 +300,7 @@ class _LockSetPageState extends State<LockSetPage> with RouteAware {
|
||||
SizedBox(height: 10.h),
|
||||
// 常开模式
|
||||
Obx(() => Visibility(
|
||||
// visible: state.lockFeature.value.passageMode == 1 ? true : false,
|
||||
visible:true,
|
||||
visible: state.lockFeature.value.passageMode == 1 ? true : false,
|
||||
child: CommonItem(
|
||||
leftTitel: TranslationLoader.lanKeys!.normallyOpenMode!.tr,
|
||||
rightTitle: (state.lockSettingInfo.value.passageMode ?? 0) == 1
|
||||
@ -334,7 +348,7 @@ class _LockSetPageState extends State<LockSetPage> with RouteAware {
|
||||
// Obx(() =>
|
||||
// 面容开锁
|
||||
Visibility(
|
||||
visible: true,
|
||||
visible: state.lockFeature.value.d3Face == 1 ? true : false,
|
||||
child: CommonItem(
|
||||
leftTitel: TranslationLoader.lanKeys!.faceUnlocks!.tr,
|
||||
rightTitle: "",
|
||||
@ -394,7 +408,7 @@ class _LockSetPageState extends State<LockSetPage> with RouteAware {
|
||||
// Obx(() =>
|
||||
// 开门方向设置
|
||||
Visibility(
|
||||
visible: true,
|
||||
visible: state.lockFeature.value.openDirection == 1 ? true : false,
|
||||
child: CommonItem(
|
||||
leftTitel: TranslationLoader.lanKeys!.openingDirectionSet!.tr,
|
||||
rightTitle: "",
|
||||
@ -408,7 +422,7 @@ class _LockSetPageState extends State<LockSetPage> with RouteAware {
|
||||
})),
|
||||
// 电机功率设置
|
||||
Visibility(
|
||||
visible: true,
|
||||
visible: state.lockFeature.value.motorTorsion == 1 ? true : false,
|
||||
child: CommonItem(
|
||||
leftTitel: TranslationLoader.lanKeys!.motorPowerSetting!.tr,
|
||||
rightTitle: "",
|
||||
|
||||
@ -38,7 +38,7 @@ class _AboutPageState extends State<AboutPage> {
|
||||
),
|
||||
SizedBox(height: 20.h),
|
||||
Text(
|
||||
"星锁 1.0.0.08(preRelease-20240123)",
|
||||
"星锁 1.0.0.09(preRelease-20240126-1)",
|
||||
style: TextStyle(fontSize: 24.sp, color: AppColors.blackColor),
|
||||
),
|
||||
SizedBox(height: 20.h),
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
library flutter_advanced_calendar;
|
||||
|
||||
export 'src/controller.dart';
|
||||
export 'src/widget.dart';
|
||||
15
star_lock/lib/tools/advancedCalendar/src/controller.dart
Normal file
15
star_lock/lib/tools/advancedCalendar/src/controller.dart
Normal file
@ -0,0 +1,15 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'datetime_util.dart';
|
||||
|
||||
/// Advanced Calendar controller that manage selection date state.
|
||||
class AdvancedCalendarController extends ValueNotifier<DateTime> {
|
||||
/// Generates controller with custom date selected.
|
||||
AdvancedCalendarController(DateTime value) : super(value);
|
||||
|
||||
/// Generates controller with today date selected.
|
||||
AdvancedCalendarController.today() : this(DateTime.now().toZeroTime());
|
||||
|
||||
@override
|
||||
set value(DateTime newValue) => super.value = newValue.toZeroTime();
|
||||
}
|
||||
94
star_lock/lib/tools/advancedCalendar/src/date_box.dart
Normal file
94
star_lock/lib/tools/advancedCalendar/src/date_box.dart
Normal file
@ -0,0 +1,94 @@
|
||||
part of 'widget.dart';
|
||||
|
||||
/// Unit of calendar.
|
||||
class DateBox extends StatelessWidget {
|
||||
const DateBox({
|
||||
Key? key,
|
||||
required this.child,
|
||||
this.color,
|
||||
this.width = 24.0,
|
||||
this.height = 24.0,
|
||||
this.borderRadius = const BorderRadius.all(Radius.circular(8.0)),
|
||||
this.onPressed,
|
||||
this.showDot = false,
|
||||
this.isSelected = false,
|
||||
this.isToday = false,
|
||||
this.hasEvent = false,
|
||||
}) : super(key: key);
|
||||
|
||||
/// Child widget.
|
||||
final Widget child;
|
||||
|
||||
/// Background color.
|
||||
final Color? color;
|
||||
|
||||
/// Widget width.
|
||||
final double width;
|
||||
|
||||
/// Widget height.
|
||||
final double height;
|
||||
|
||||
/// Container border radius.
|
||||
final BorderRadius borderRadius;
|
||||
|
||||
/// Pressed callback function.
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
/// Show DateBox event in container.
|
||||
final bool showDot;
|
||||
|
||||
/// DateBox is today.
|
||||
final bool isToday;
|
||||
|
||||
/// DateBox selection.
|
||||
final bool isSelected;
|
||||
|
||||
/// Show event in DateBox.
|
||||
final bool hasEvent;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return UnconstrainedBox(
|
||||
alignment: Alignment.center,
|
||||
child: InkResponse(
|
||||
onTap: onPressed,
|
||||
radius: 16.0,
|
||||
borderRadius: borderRadius,
|
||||
highlightShape: BoxShape.rectangle,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 150),
|
||||
width: width,
|
||||
height: height,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? theme.primaryColor
|
||||
: isToday
|
||||
? theme.highlightColor
|
||||
: null,
|
||||
borderRadius: borderRadius,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
child,
|
||||
if (showDot && hasEvent)
|
||||
Container(
|
||||
margin: const EdgeInsets.all(2.0),
|
||||
height: 4,
|
||||
width: 4,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: isSelected
|
||||
? theme.colorScheme.onPrimary
|
||||
: theme.colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
56
star_lock/lib/tools/advancedCalendar/src/datetime_util.dart
Normal file
56
star_lock/lib/tools/advancedCalendar/src/datetime_util.dart
Normal file
@ -0,0 +1,56 @@
|
||||
extension DateTimeUtil on DateTime {
|
||||
/// Generate a new DateTime instance with a zero time.
|
||||
DateTime toZeroTime() => DateTime.utc(year, month, day, 12);
|
||||
|
||||
int findWeekIndex(List<DateTime> dates) {
|
||||
return dates.indexWhere(isAtSameMomentAs) ~/ 7;
|
||||
}
|
||||
|
||||
/// Calculates first week date (Sunday) from this date.
|
||||
DateTime firstDayOfWeek({int? startWeekDay}) {
|
||||
final utcDate = DateTime.utc(year, month, day, 12);
|
||||
if (startWeekDay != null && startWeekDay < 7) {
|
||||
return utcDate.subtract(Duration(days: utcDate.weekday - startWeekDay));
|
||||
}
|
||||
return utcDate.subtract(Duration(days: utcDate.weekday % 7));
|
||||
}
|
||||
|
||||
/// Generates 7 dates according to this date.
|
||||
/// (Supposed that this date is result of [firstDayOfWeek])
|
||||
List<DateTime> weekDates() {
|
||||
return List.generate(
|
||||
7,
|
||||
(index) => add(Duration(days: index)),
|
||||
growable: false,
|
||||
);
|
||||
}
|
||||
|
||||
/// Generates list of list with [DateTime]
|
||||
/// according to [date] and [weeksAmount].
|
||||
/// gives the beginning of the day of the week [startWeekDay]
|
||||
List<List<DateTime>> generateWeeks(int weeksAmount, {int? startWeekDay}) {
|
||||
final firstViewDate = firstDayOfWeek(startWeekDay: startWeekDay).subtract(
|
||||
Duration(
|
||||
days: (weeksAmount ~/ 2) * 7,
|
||||
),
|
||||
);
|
||||
|
||||
return List.generate(
|
||||
weeksAmount,
|
||||
(weekIndex) {
|
||||
final firstDateOfNextWeek = firstViewDate.add(
|
||||
Duration(
|
||||
days: weekIndex * 7,
|
||||
),
|
||||
);
|
||||
|
||||
return firstDateOfNextWeek.weekDates();
|
||||
},
|
||||
growable: false,
|
||||
);
|
||||
}
|
||||
|
||||
bool isSameDate(DateTime other) {
|
||||
return year == other.year && month == other.month && day == other.day;
|
||||
}
|
||||
}
|
||||
39
star_lock/lib/tools/advancedCalendar/src/handlebar.dart
Normal file
39
star_lock/lib/tools/advancedCalendar/src/handlebar.dart
Normal file
@ -0,0 +1,39 @@
|
||||
part of 'widget.dart';
|
||||
|
||||
class HandleBar extends StatelessWidget {
|
||||
const HandleBar({
|
||||
Key? key,
|
||||
this.decoration,
|
||||
this.margin = const EdgeInsets.only(
|
||||
top: 8.0,
|
||||
),
|
||||
this.onPressed,
|
||||
}) : super(key: key);
|
||||
|
||||
final BoxDecoration? decoration;
|
||||
final EdgeInsetsGeometry margin;
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onPressed,
|
||||
behavior: HitTestBehavior.translucent,
|
||||
child: Container(
|
||||
margin: margin,
|
||||
alignment: Alignment.center,
|
||||
child: FractionallySizedBox(
|
||||
widthFactor: 0.1,
|
||||
child: Container(
|
||||
height: 4.0,
|
||||
decoration: decoration ??
|
||||
BoxDecoration(
|
||||
color: Theme.of(context).dividerColor,
|
||||
borderRadius: BorderRadius.circular(2.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
63
star_lock/lib/tools/advancedCalendar/src/header.dart
Normal file
63
star_lock/lib/tools/advancedCalendar/src/header.dart
Normal file
@ -0,0 +1,63 @@
|
||||
part of 'widget.dart';
|
||||
|
||||
class Header extends StatelessWidget {
|
||||
const Header({
|
||||
Key? key,
|
||||
required this.monthDate,
|
||||
this.margin = const EdgeInsets.only(
|
||||
left: 16.0,
|
||||
right: 8.0,
|
||||
top: 4.0,
|
||||
bottom: 4.0,
|
||||
),
|
||||
this.onPressed,
|
||||
this.dateStyle,
|
||||
this.todayStyle,
|
||||
}) : super(key: key);
|
||||
|
||||
static final _dateFormatter = DateFormat().add_yMMMM();
|
||||
// static final _dateFormatter = DateFormat('MM月dd日', 'zh_CN');
|
||||
final DateTime monthDate;
|
||||
final EdgeInsetsGeometry margin;
|
||||
final VoidCallback? onPressed;
|
||||
final TextStyle? dateStyle;
|
||||
final TextStyle? todayStyle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
// initializeDateFormatting('zh_CN', null);
|
||||
|
||||
return Container(
|
||||
margin: margin,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// Text(
|
||||
// _dateFormatter.format(monthDate),
|
||||
// style: dateStyle ?? theme.textTheme.titleMedium,
|
||||
// ),
|
||||
InkWell(
|
||||
onTap: onPressed,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(4.0),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
vertical: 4.0,
|
||||
),
|
||||
child: Text(
|
||||
'今天',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 24.sp,
|
||||
fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
62
star_lock/lib/tools/advancedCalendar/src/month_view.dart
Normal file
62
star_lock/lib/tools/advancedCalendar/src/month_view.dart
Normal file
@ -0,0 +1,62 @@
|
||||
part of 'widget.dart';
|
||||
|
||||
class MonthView extends StatelessWidget {
|
||||
const MonthView({
|
||||
Key? key,
|
||||
required this.monthView,
|
||||
required this.todayDate,
|
||||
required this.selectedDate,
|
||||
required this.weekLineHeight,
|
||||
required this.weeksAmount,
|
||||
required this.innerDot,
|
||||
this.onChanged,
|
||||
this.events,
|
||||
required this.keepLineSize,
|
||||
this.textStyle,
|
||||
}) : super(key: key);
|
||||
|
||||
final ViewRange monthView;
|
||||
final DateTime? todayDate;
|
||||
final DateTime selectedDate;
|
||||
final double weekLineHeight;
|
||||
final int weeksAmount;
|
||||
final ValueChanged<DateTime>? onChanged;
|
||||
final List<DateTime>? events;
|
||||
final bool innerDot;
|
||||
final bool keepLineSize;
|
||||
final TextStyle? textStyle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final index = selectedDate.findWeekIndex(monthView.dates);
|
||||
final offset = index / (weeksAmount - 1) * 2 - 1.0;
|
||||
|
||||
return OverflowBox(
|
||||
alignment: Alignment(0, offset),
|
||||
minHeight: weekLineHeight,
|
||||
maxHeight: weekLineHeight * weeksAmount,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: List<Widget>.generate(
|
||||
6,
|
||||
(weekIndex) {
|
||||
final weekStart = weekIndex * 7;
|
||||
|
||||
return WeekView(
|
||||
innerDot: innerDot,
|
||||
dates: monthView.dates.sublist(weekStart, weekStart + 7),
|
||||
selectedDate: selectedDate,
|
||||
highlightMonth: monthView.firstDay.month,
|
||||
lineHeight: weekLineHeight,
|
||||
onChanged: onChanged,
|
||||
events: events,
|
||||
keepLineSize: keepLineSize,
|
||||
textStyle: textStyle,
|
||||
);
|
||||
},
|
||||
growable: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
part of 'widget.dart';
|
||||
|
||||
class ViewRange {
|
||||
const ViewRange._(this.firstDay, this.dates);
|
||||
|
||||
/// Creates custom filled [ViewRange] instance.
|
||||
const ViewRange.custom(
|
||||
DateTime firstDay,
|
||||
List<DateTime> dates,
|
||||
) : this._(firstDay, dates);
|
||||
|
||||
/// Generates [ViewRange] instance based on [date],
|
||||
/// number of [month] and [weeksAmount].
|
||||
/// gives the beginning of the day of the week [startWeekDay]
|
||||
factory ViewRange.generateDates(
|
||||
DateTime date,
|
||||
int month,
|
||||
int weeksAmount, {
|
||||
int? startWeekDay,
|
||||
}) {
|
||||
final firstMonthDate = DateTime.utc(date.year, month, 1);
|
||||
final firstViewDate =
|
||||
firstMonthDate.firstDayOfWeek(startWeekDay: startWeekDay);
|
||||
|
||||
return ViewRange._(
|
||||
firstMonthDate,
|
||||
List.generate(
|
||||
weeksAmount * 7,
|
||||
(index) => firstViewDate.add(Duration(days: index)),
|
||||
growable: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Month view index.
|
||||
final DateTime firstDay;
|
||||
|
||||
/// Month view dates.
|
||||
final List<DateTime> dates;
|
||||
}
|
||||
36
star_lock/lib/tools/advancedCalendar/src/week_days.dart
Normal file
36
star_lock/lib/tools/advancedCalendar/src/week_days.dart
Normal file
@ -0,0 +1,36 @@
|
||||
part of 'widget.dart';
|
||||
|
||||
/// Week day names line.
|
||||
class WeekDays extends StatelessWidget {
|
||||
const WeekDays({
|
||||
Key? key,
|
||||
this.weekNames = const <String>['日', '一', '二', '三', '四', '五', '六'],
|
||||
this.style,
|
||||
required this.keepLineSize,
|
||||
}) : assert(weekNames.length == 7, '`weekNames` must have length 7'),
|
||||
super(key: key);
|
||||
|
||||
/// Week day names.
|
||||
final List<String> weekNames;
|
||||
|
||||
/// Text style.
|
||||
final TextStyle? style;
|
||||
|
||||
final bool keepLineSize;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTextStyle(
|
||||
style: style!,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: List.generate(weekNames.length, (index) {
|
||||
return DateBox(
|
||||
child: Text(weekNames[index]),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
137
star_lock/lib/tools/advancedCalendar/src/week_view.dart
Normal file
137
star_lock/lib/tools/advancedCalendar/src/week_view.dart
Normal file
@ -0,0 +1,137 @@
|
||||
part of 'widget.dart';
|
||||
|
||||
class WeekView extends StatelessWidget {
|
||||
WeekView({
|
||||
Key? key,
|
||||
required this.dates,
|
||||
required this.selectedDate,
|
||||
required this.lineHeight,
|
||||
this.highlightMonth,
|
||||
this.onChanged,
|
||||
this.events,
|
||||
required this.innerDot,
|
||||
required this.keepLineSize,
|
||||
this.textStyle,
|
||||
}) : super(key: key);
|
||||
|
||||
final DateTime todayDate = DateTime.now().toZeroTime();
|
||||
final List<DateTime> dates;
|
||||
final double lineHeight;
|
||||
final int? highlightMonth;
|
||||
final DateTime selectedDate;
|
||||
final ValueChanged<DateTime>? onChanged;
|
||||
final List<DateTime>? events;
|
||||
final bool innerDot;
|
||||
final bool keepLineSize;
|
||||
final TextStyle? textStyle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return SizedBox(
|
||||
height: lineHeight,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: List<Widget>.generate(
|
||||
7,
|
||||
(dayIndex) {
|
||||
final date = dates[dayIndex];
|
||||
final isToday = date.isAtSameMomentAs(todayDate);
|
||||
final isSelected = date.isAtSameMomentAs(selectedDate);
|
||||
final isHighlight = highlightMonth == date.month;
|
||||
|
||||
final hasEvent =
|
||||
events!.indexWhere((element) => element.isSameDate(date));
|
||||
|
||||
if (keepLineSize) {
|
||||
return InkResponse(
|
||||
onTap: onChanged != null ? () => onChanged!(date) : null,
|
||||
child: Container(
|
||||
height: 36,
|
||||
width: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? theme.primaryColor
|
||||
: isToday
|
||||
? theme.highlightColor
|
||||
: null,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
shape: BoxShape.rectangle,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'${date.day}',
|
||||
style: textStyle?.copyWith(
|
||||
color: isSelected || isToday
|
||||
? theme.colorScheme.onPrimary
|
||||
: isHighlight || highlightMonth == null
|
||||
? null
|
||||
: theme.disabledColor,
|
||||
fontWeight:
|
||||
isSelected && textStyle?.fontWeight != null
|
||||
? FontWeight
|
||||
.values[textStyle!.fontWeight!.index + 2]
|
||||
: textStyle?.fontWeight,
|
||||
),
|
||||
),
|
||||
if (!hasEvent.isNegative)
|
||||
Container(
|
||||
height: 4,
|
||||
width: 4,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
color: isSelected
|
||||
? theme.colorScheme.onPrimary
|
||||
: theme.colorScheme.secondary,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
DateBox(
|
||||
width: innerDot ? 32 : 24,
|
||||
height: innerDot ? 32 : 24,
|
||||
showDot: innerDot,
|
||||
onPressed: onChanged != null ? () => onChanged!(date) : null,
|
||||
isSelected: isSelected,
|
||||
isToday: isToday,
|
||||
hasEvent: !hasEvent.isNegative,
|
||||
child: Text(
|
||||
'${date.day}',
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
color: isSelected || isToday
|
||||
? theme.colorScheme.onPrimary
|
||||
: isHighlight || highlightMonth == null
|
||||
? null
|
||||
: theme.disabledColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!innerDot && !hasEvent.isNegative)
|
||||
Container(
|
||||
height: 6,
|
||||
width: 6,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
color: theme.primaryColor,
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
growable: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
420
star_lock/lib/tools/advancedCalendar/src/widget.dart
Normal file
420
star_lock/lib/tools/advancedCalendar/src/widget.dart
Normal file
@ -0,0 +1,420 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:intl/date_symbol_data_local.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import 'controller.dart';
|
||||
import 'datetime_util.dart';
|
||||
|
||||
part 'date_box.dart';
|
||||
part 'handlebar.dart';
|
||||
part 'header.dart';
|
||||
part 'month_view.dart';
|
||||
part 'month_view_bean.dart';
|
||||
part 'week_days.dart';
|
||||
part 'week_view.dart';
|
||||
|
||||
/// Advanced Calendar widget.
|
||||
class AdvancedCalendar extends StatefulWidget {
|
||||
const AdvancedCalendar({
|
||||
Key? key,
|
||||
this.controller,
|
||||
this.startWeekDay,
|
||||
this.events,
|
||||
this.weekLineHeight = 32.0,
|
||||
this.preloadMonthViewAmount = 13,
|
||||
this.preloadWeekViewAmount = 21,
|
||||
this.weeksInMonthViewAmount = 6,
|
||||
this.todayStyle,
|
||||
this.headerStyle,
|
||||
this.onHorizontalDrag,
|
||||
this.innerDot = false,
|
||||
this.keepLineSize = false,
|
||||
this.calendarTextStyle,
|
||||
}) : assert(
|
||||
keepLineSize && innerDot ||
|
||||
innerDot && !keepLineSize ||
|
||||
!innerDot && !keepLineSize,
|
||||
'keepLineSize should be used only when innerDot is true',
|
||||
),
|
||||
super(key: key);
|
||||
|
||||
/// Calendar selection date controller.
|
||||
final AdvancedCalendarController? controller;
|
||||
|
||||
/// Executes on horizontal calendar swipe. Allows to load additional dates.
|
||||
final Function(DateTime)? onHorizontalDrag;
|
||||
|
||||
/// Height of week line.
|
||||
final double weekLineHeight;
|
||||
|
||||
/// Amount of months in month view to preload.
|
||||
final int preloadMonthViewAmount;
|
||||
|
||||
/// Amount of weeks in week view to preload.
|
||||
final int preloadWeekViewAmount;
|
||||
|
||||
/// Weeks lines amount in month view.
|
||||
final int weeksInMonthViewAmount;
|
||||
|
||||
/// List of points for the week and month
|
||||
final List<DateTime>? events;
|
||||
|
||||
/// The first day of the week starts[0-6]
|
||||
final int? startWeekDay;
|
||||
|
||||
/// Style of headers date
|
||||
final TextStyle? headerStyle;
|
||||
|
||||
/// Style of Today button
|
||||
final TextStyle? todayStyle;
|
||||
|
||||
/// Show DateBox event in container.
|
||||
final bool innerDot;
|
||||
|
||||
/// Keeps consistent line size for dates
|
||||
/// Can't be used without innerDot
|
||||
final bool keepLineSize;
|
||||
|
||||
/// Text style for dates in calendar
|
||||
final TextStyle? calendarTextStyle;
|
||||
|
||||
@override
|
||||
_AdvancedCalendarState createState() => _AdvancedCalendarState();
|
||||
}
|
||||
|
||||
class _AdvancedCalendarState extends State<AdvancedCalendar>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late ValueNotifier<int> _monthViewCurrentPage;
|
||||
late AnimationController _animationController;
|
||||
late AdvancedCalendarController _controller;
|
||||
late double _animationValue;
|
||||
late List<ViewRange> _monthRangeList;
|
||||
late List<List<DateTime>> _weekRangeList;
|
||||
|
||||
PageController? _monthPageController;
|
||||
PageController? _weekPageController;
|
||||
Offset? _captureOffset;
|
||||
DateTime? _todayDate;
|
||||
List<String>? _weekNames;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
final monthPageIndex = widget.preloadMonthViewAmount ~/ 2;
|
||||
|
||||
_monthViewCurrentPage = ValueNotifier(monthPageIndex);
|
||||
|
||||
_monthPageController = PageController(
|
||||
initialPage: monthPageIndex,
|
||||
);
|
||||
|
||||
final weekPageIndex = widget.preloadWeekViewAmount ~/ 2;
|
||||
|
||||
_weekPageController = PageController(
|
||||
initialPage: weekPageIndex,
|
||||
);
|
||||
|
||||
_animationController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
value: 0,
|
||||
);
|
||||
|
||||
_animationValue = _animationController.value;
|
||||
|
||||
_controller = widget.controller ?? AdvancedCalendarController.today();
|
||||
_todayDate = _controller.value;
|
||||
|
||||
_monthRangeList = List.generate(
|
||||
widget.preloadMonthViewAmount,
|
||||
(index) => ViewRange.generateDates(
|
||||
_todayDate!,
|
||||
_todayDate!.month + (index - _monthPageController!.initialPage),
|
||||
widget.weeksInMonthViewAmount,
|
||||
startWeekDay: widget.startWeekDay,
|
||||
),
|
||||
);
|
||||
|
||||
_weekRangeList = _controller.value.generateWeeks(
|
||||
widget.preloadWeekViewAmount,
|
||||
startWeekDay: widget.startWeekDay,
|
||||
);
|
||||
_controller.addListener(() {
|
||||
_weekRangeList = _controller.value.generateWeeks(
|
||||
widget.preloadWeekViewAmount,
|
||||
startWeekDay: widget.startWeekDay,
|
||||
);
|
||||
_weekPageController!.jumpToPage(widget.preloadWeekViewAmount ~/ 2);
|
||||
});
|
||||
if (widget.startWeekDay != null && widget.startWeekDay! < 7) {
|
||||
final time = _controller.value.subtract(
|
||||
Duration(days: _controller.value.weekday - widget.startWeekDay!),
|
||||
);
|
||||
final list = List<DateTime>.generate(
|
||||
8,
|
||||
(index) => time.add(Duration(days: index * 1)),
|
||||
).toList();
|
||||
// _weekNames = List<String>.generate(7, (index) {
|
||||
// return DateFormat("EEEE").format(list[index]).split('').first;
|
||||
// }
|
||||
// );
|
||||
//by DaisyWu 修改源文件为中文环境下 周一到周日
|
||||
_weekNames = List<String>.generate(7, (index) {
|
||||
String fullWeekName =
|
||||
DateFormat.E('zh_CN').format(list[index]); // 获取星期的完整形式
|
||||
return fullWeekName
|
||||
.substring(fullWeekName.length - 1); // 获取最后一个字符,即星期的简称
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: DefaultTextStyle.merge(
|
||||
style: theme.textTheme.bodyMedium,
|
||||
child: GestureDetector(
|
||||
onVerticalDragStart: (details) {
|
||||
_captureOffset = details.globalPosition;
|
||||
},
|
||||
onVerticalDragUpdate: (details) {
|
||||
final moveOffset = details.globalPosition;
|
||||
final diffY = moveOffset.dy - _captureOffset!.dy;
|
||||
|
||||
_animationController.value =
|
||||
_animationValue + diffY / (widget.weekLineHeight * 5);
|
||||
},
|
||||
onVerticalDragEnd: (details) => _handleFinishDrag(),
|
||||
onVerticalDragCancel: _handleFinishDrag,
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ValueListenableBuilder<int>(
|
||||
valueListenable: _monthViewCurrentPage,
|
||||
builder: (_, value, __) {
|
||||
return Header(
|
||||
monthDate:
|
||||
_monthRangeList[_monthViewCurrentPage.value].firstDay,
|
||||
onPressed: _handleTodayPressed,
|
||||
dateStyle: widget.headerStyle,
|
||||
todayStyle: widget.todayStyle,
|
||||
);
|
||||
},
|
||||
),
|
||||
WeekDays(
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
color: Colors.black,
|
||||
),
|
||||
keepLineSize: widget.keepLineSize,
|
||||
weekNames: _weekNames != null
|
||||
? _weekNames!
|
||||
: const <String>['日', '一', '二', '三', '四', '五', '六'],
|
||||
),
|
||||
AnimatedBuilder(
|
||||
animation: _animationController,
|
||||
builder: (_, __) {
|
||||
final height = Tween<double>(
|
||||
begin: widget.weekLineHeight,
|
||||
end:
|
||||
widget.weekLineHeight * widget.weeksInMonthViewAmount,
|
||||
).transform(_animationController.value);
|
||||
return SizedBox(
|
||||
height: height,
|
||||
child: ValueListenableBuilder<DateTime>(
|
||||
valueListenable: _controller,
|
||||
builder: (_, selectedDate, __) {
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
IgnorePointer(
|
||||
ignoring: _animationController.value == 0.0,
|
||||
child: Opacity(
|
||||
opacity: Tween<double>(
|
||||
begin: 0.0,
|
||||
end: 1.0,
|
||||
).evaluate(_animationController),
|
||||
child: PageView.builder(
|
||||
onPageChanged: (pageIndex) {
|
||||
if (widget.onHorizontalDrag != null) {
|
||||
widget.onHorizontalDrag!(
|
||||
_monthRangeList[pageIndex].firstDay,
|
||||
);
|
||||
}
|
||||
_monthViewCurrentPage.value = pageIndex;
|
||||
},
|
||||
controller: _monthPageController,
|
||||
physics: _animationController.value == 1.0
|
||||
? const AlwaysScrollableScrollPhysics()
|
||||
: const NeverScrollableScrollPhysics(),
|
||||
itemCount: _monthRangeList.length,
|
||||
itemBuilder: (_, pageIndex) {
|
||||
return MonthView(
|
||||
innerDot: widget.innerDot,
|
||||
monthView: _monthRangeList[pageIndex],
|
||||
todayDate: _todayDate,
|
||||
selectedDate: selectedDate,
|
||||
weekLineHeight: widget.weekLineHeight,
|
||||
weeksAmount:
|
||||
widget.weeksInMonthViewAmount,
|
||||
onChanged: _handleDateChanged,
|
||||
events: widget.events,
|
||||
keepLineSize: widget.keepLineSize,
|
||||
textStyle: widget.calendarTextStyle,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
ValueListenableBuilder<int>(
|
||||
valueListenable: _monthViewCurrentPage,
|
||||
builder: (_, pageIndex, __) {
|
||||
final index = selectedDate.findWeekIndex(
|
||||
_monthRangeList[_monthViewCurrentPage.value]
|
||||
.dates,
|
||||
);
|
||||
final offset = index /
|
||||
(widget.weeksInMonthViewAmount - 1) *
|
||||
2 -
|
||||
1.0;
|
||||
return Align(
|
||||
alignment: Alignment(0.0, offset),
|
||||
child: IgnorePointer(
|
||||
ignoring:
|
||||
_animationController.value == 1.0,
|
||||
child: Opacity(
|
||||
opacity: Tween<double>(
|
||||
begin: 1.0,
|
||||
end: 0.0,
|
||||
).evaluate(_animationController),
|
||||
child: SizedBox(
|
||||
height: widget.weekLineHeight,
|
||||
child: PageView.builder(
|
||||
onPageChanged: (indexPage) {
|
||||
final pageIndex =
|
||||
_monthRangeList.indexWhere(
|
||||
(index) =>
|
||||
index.firstDay.month ==
|
||||
_weekRangeList[indexPage]
|
||||
.first
|
||||
.month,
|
||||
);
|
||||
|
||||
if (widget.onHorizontalDrag !=
|
||||
null) {
|
||||
widget.onHorizontalDrag!(
|
||||
_monthRangeList[pageIndex]
|
||||
.firstDay,
|
||||
);
|
||||
}
|
||||
_monthViewCurrentPage.value =
|
||||
pageIndex;
|
||||
},
|
||||
controller: _weekPageController,
|
||||
itemCount: _weekRangeList.length,
|
||||
physics: _closeMonthScroll(),
|
||||
itemBuilder: (context, index) {
|
||||
return WeekView(
|
||||
innerDot: widget.innerDot,
|
||||
dates: _weekRangeList[index],
|
||||
selectedDate: selectedDate,
|
||||
lineHeight:
|
||||
widget.weekLineHeight,
|
||||
onChanged:
|
||||
_handleWeekDateChanged,
|
||||
events: widget.events,
|
||||
keepLineSize:
|
||||
widget.keepLineSize,
|
||||
textStyle:
|
||||
widget.calendarTextStyle,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
// HandleBar(
|
||||
// onPressed: () async {
|
||||
// await _animationController.forward();
|
||||
// _animationValue = 1.0;
|
||||
// },
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController.dispose();
|
||||
_monthPageController!.dispose();
|
||||
_monthViewCurrentPage.dispose();
|
||||
|
||||
if (widget.controller == null) {
|
||||
_controller.dispose();
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _handleWeekDateChanged(DateTime date) {
|
||||
_handleDateChanged(date);
|
||||
|
||||
_monthViewCurrentPage.value = _monthRangeList
|
||||
.lastIndexWhere((monthRange) => monthRange.dates.contains(date));
|
||||
}
|
||||
|
||||
void _handleDateChanged(DateTime date) {
|
||||
_controller.value = date;
|
||||
}
|
||||
|
||||
void _handleFinishDrag() async {
|
||||
_captureOffset = null;
|
||||
|
||||
if (_animationController.value > 0.5) {
|
||||
await _animationController.forward();
|
||||
_animationValue = 1.0;
|
||||
} else {
|
||||
await _animationController.reverse();
|
||||
_animationValue = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void _handleTodayPressed() {
|
||||
_controller.value = DateTime.now().toZeroTime();
|
||||
|
||||
_monthPageController!.jumpToPage(widget.preloadMonthViewAmount ~/ 2);
|
||||
_weekPageController!.jumpToPage(widget.preloadWeekViewAmount ~/ 2);
|
||||
}
|
||||
|
||||
ScrollPhysics _closeMonthScroll() {
|
||||
if ((_monthViewCurrentPage.value ==
|
||||
(widget.preloadMonthViewAmount ~/ 2) + 3 ||
|
||||
_monthViewCurrentPage.value ==
|
||||
(widget.preloadMonthViewAmount ~/ 2) - 3)) {
|
||||
return const NeverScrollableScrollPhysics();
|
||||
} else {
|
||||
return const AlwaysScrollableScrollPhysics();
|
||||
}
|
||||
}
|
||||
}
|
||||
14
star_lock/lib/tools/menuItem/dropDownItem.dart
Normal file
14
star_lock/lib/tools/menuItem/dropDownItem.dart
Normal file
@ -0,0 +1,14 @@
|
||||
/// 通用菜单项
|
||||
class DropDownItem {
|
||||
// 显示的文本
|
||||
String itemTitle = '';
|
||||
// 选中的值
|
||||
dynamic itemValue;
|
||||
// 是否选中
|
||||
bool isCheked = false;
|
||||
|
||||
DropDownItem(
|
||||
{required this.itemTitle,
|
||||
required this.itemValue,
|
||||
required this.isCheked});
|
||||
}
|
||||
161
star_lock/lib/tools/menuItem/xsDropDownWidget.dart
Normal file
161
star_lock/lib/tools/menuItem/xsDropDownWidget.dart
Normal file
@ -0,0 +1,161 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:star_lock/app_settings/app_colors.dart';
|
||||
import 'package:star_lock/tools/menuItem/dropDownItem.dart';
|
||||
|
||||
/// @author baorant
|
||||
/// @创建时间:2024/4/11
|
||||
/// 下拉菜单按钮组件
|
||||
class XSDropDownWidget extends StatefulWidget {
|
||||
// 显示的菜单项
|
||||
List<DropDownItem> items = [];
|
||||
// 当前选中的值
|
||||
final dynamic value;
|
||||
// 选择框前的标题
|
||||
final String? title;
|
||||
// 提示语
|
||||
final String tooltip;
|
||||
// 选中数据的回调事件
|
||||
final ValueChanged<dynamic>? valueChanged;
|
||||
XSDropDownWidget(
|
||||
{Key? key,
|
||||
required this.items,
|
||||
this.value,
|
||||
this.valueChanged,
|
||||
this.title,
|
||||
this.tooltip = "点击选择"})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<XSDropDownWidget> createState() => _XSDropDownWidgetState();
|
||||
}
|
||||
|
||||
class _XSDropDownWidgetState extends State<XSDropDownWidget> {
|
||||
String label = '请选择';
|
||||
// 是否展开下拉按钮
|
||||
bool isExpand = false;
|
||||
// 当前的值
|
||||
dynamic currentValue;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
currentValue = widget.value;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
/// 根据当前的value处理当前文本显示
|
||||
void initTitle() {
|
||||
if (currentValue != null) {
|
||||
// 有值查值
|
||||
for (DropDownItem item in widget.items) {
|
||||
if (item.itemValue == currentValue) {
|
||||
label = item.itemTitle;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 没值默认取第一个
|
||||
if (widget.items.isNotEmpty) {
|
||||
label = widget.items[0].itemTitle;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
initTitle();
|
||||
return Wrap(
|
||||
children: [
|
||||
if (widget.title != null)
|
||||
Text(widget.title!,
|
||||
style: TextStyle(
|
||||
fontSize: 26.sp,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w600,
|
||||
)),
|
||||
PopupMenuButton<String>(
|
||||
color: Colors.white,
|
||||
// initialValue: currentValue,
|
||||
tooltip: widget.tooltip,
|
||||
offset: const Offset(0, 30),
|
||||
enableFeedback: true,
|
||||
child: Listener(
|
||||
// 使用listener事件能够继续传递
|
||||
onPointerDown: (event) {
|
||||
setState(() {
|
||||
isExpand = !isExpand;
|
||||
});
|
||||
},
|
||||
child: Wrap(
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 24.sp,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
isExpand
|
||||
? const Icon(Icons.arrow_drop_up)
|
||||
: const Icon(Icons.arrow_drop_down)
|
||||
],
|
||||
),
|
||||
),
|
||||
onSelected: (value) {
|
||||
widget.valueChanged?.call(value);
|
||||
setState(() {
|
||||
currentValue = value;
|
||||
isExpand = !isExpand;
|
||||
});
|
||||
},
|
||||
onCanceled: () {
|
||||
// 取消展开
|
||||
setState(() {
|
||||
isExpand = false;
|
||||
});
|
||||
},
|
||||
itemBuilder: (context) {
|
||||
return widget.items.map((item) {
|
||||
return PopupMenuItem<String>(
|
||||
value: item.itemValue,
|
||||
child: Container(
|
||||
margin:
|
||||
EdgeInsets.only(left: 0.w, right: 0, top: 0, bottom: 0),
|
||||
color: item.itemValue == currentValue
|
||||
? AppColors.mainColor
|
||||
: null, // 设置选中项背景色为蓝色
|
||||
child: Text(
|
||||
item.itemTitle,
|
||||
style: TextStyle(
|
||||
color: item.itemValue == currentValue
|
||||
? Colors.white
|
||||
: Colors.black, // 设置选中项文字颜色为白色
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
// itemBuilder: (context) {
|
||||
// return widget.items
|
||||
// .map(
|
||||
// (item) => item.itemValue == currentValue
|
||||
// ? PopupMenuItem<String>(
|
||||
// value: item.itemValue,
|
||||
// child: Text(
|
||||
// item.itemTitle,
|
||||
// style: TextStyle(color: AppColors.mainColor),
|
||||
// ),
|
||||
// )
|
||||
// : PopupMenuItem<String>(
|
||||
// value: item.itemValue,
|
||||
// child: Text(item.itemTitle),
|
||||
// ),
|
||||
// )
|
||||
// .toList();
|
||||
// },
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -134,7 +134,7 @@ dependencies:
|
||||
flutter_voice_processor: ^1.1.0
|
||||
#监听网络连接状态
|
||||
connectivity_plus: ^5.0.2
|
||||
flutter_advanced_calendar: ^1.4.1
|
||||
#flutter_advanced_calendar: ^1.4.1
|
||||
timelines: ^0.1.0
|
||||
#侧滑删除
|
||||
flutter_slidable: ^3.0.1
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user