Merge remote-tracking branch 'origin/master'

This commit is contained in:
葛佳祥 2024-01-26 16:22:47 +08:00
commit 5170ce9304
26 changed files with 1311 additions and 82 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -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

View File

@ -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();
// }
}
}

View File

@ -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,
),
),
],
),
),
],
),

View File

@ -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() {

View File

@ -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());

View File

@ -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,
});
}),

View File

@ -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

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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: "",

View File

@ -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),

View File

@ -0,0 +1,4 @@
library flutter_advanced_calendar;
export 'src/controller.dart';
export 'src/widget.dart';

View 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();
}

View 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,
),
),
],
),
),
),
);
}
}

View 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;
}
}

View 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),
),
),
),
),
);
}
}

View 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),
),
),
),
],
),
);
}
}

View 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,
),
),
);
}
}

View File

@ -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;
}

View 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]),
);
}),
),
);
}
}

View 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,
),
),
);
}
}

View 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();
}
}
}

View File

@ -0,0 +1,14 @@
///
class DropDownItem {
//
String itemTitle = '';
//
dynamic itemValue;
//
bool isCheked = false;
DropDownItem(
{required this.itemTitle,
required this.itemValue,
required this.isCheked});
}

View 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();
// },
)
],
);
}
}

View File

@ -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