1,新增门锁日志日历组件源文件
2,优化门锁日志UI参考米家
This commit is contained in:
parent
88cc26a672
commit
c3d55d225b
@ -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,8 @@
|
||||
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/main/lockDetail/doorLockLog/doorLockLog_logic.dart';
|
||||
import 'package:star_lock/tools/advancedCalendar/src/widget.dart';
|
||||
import 'package:timelines/timelines.dart';
|
||||
|
||||
import '../../../app_settings/app_colors.dart';
|
||||
@ -64,10 +64,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, // 替换成你的列表项数量
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -123,7 +130,7 @@ class _DoorLockLogPageState extends State<DoorLockLogPage> {
|
||||
value: state.dropdownValue.value,
|
||||
icon: const Icon(Icons.arrow_drop_down),
|
||||
iconSize: 40,
|
||||
// elevation: 12,
|
||||
elevation: 12,
|
||||
style: TextStyle(
|
||||
fontSize: 26.sp,
|
||||
color: Colors.black,
|
||||
@ -149,7 +156,7 @@ class _DoorLockLogPageState extends State<DoorLockLogPage> {
|
||||
//时间轴组件
|
||||
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)),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import 'package:flutter_advanced_calendar/flutter_advanced_calendar.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:star_lock/tools/advancedCalendar/src/controller.dart';
|
||||
|
||||
import '../../lockMian/entity/lockListInfo_entity.dart';
|
||||
import '../electronicKey/electronicKeyDetail/keyOperationRecordEntity.dart';
|
||||
|
||||
@ -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: 22.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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