You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

380 lines
12 KiB

import 'package:flutter/foundation.dart';
6 years ago
import 'package:flutter/material.dart';
import 'package:flutter_custom_calendar/cache_data.dart';
import 'package:flutter_custom_calendar/configuration.dart';
4 years ago
import 'package:flutter_custom_calendar/flutter_custom_calendar.dart';
import 'package:flutter_custom_calendar/utils/LogUtil.dart';
6 years ago
import 'package:flutter_custom_calendar/utils/date_util.dart';
import 'package:provider/provider.dart';
6 years ago
/**
*
*/
class MonthView extends StatefulWidget {
3 years ago
final int? year;
final int? month;
final int? day;
6 years ago
3 years ago
final CalendarConfiguration? configuration;
6 years ago
const MonthView({
3 years ago
Key? key,
required this.year,
required this.month,
this.day,
this.configuration,
5 years ago
}) : super(key: key);
6 years ago
@override
_MonthViewState createState() => _MonthViewState();
}
class _MonthViewState extends State<MonthView>
with AutomaticKeepAliveClientMixin {
3 years ago
List<DateModel>? items =[];
6 years ago
3 years ago
int? lineCount;
Map<DateModel, Object>? extraDataMap; //自定义额外的数据
6 years ago
@override
void initState() {
super.initState();
3 years ago
extraDataMap = widget.configuration!.extraDataMap;
DateModel firstDayOfMonth =
3 years ago
DateModel.fromDateTime(DateTime(widget.year!, widget.month!, 1));
if (CacheData.getInstance()!.monthListCache[firstDayOfMonth]?.isNotEmpty ==
true) {
LogUtil.log(TAG: this.runtimeType, message: "缓存中有数据");
3 years ago
items = CacheData.getInstance()!.monthListCache[firstDayOfMonth];
} else {
LogUtil.log(TAG: this.runtimeType, message: "缓存中无数据");
getItems().then((_) {
3 years ago
CacheData.getInstance()!.monthListCache[firstDayOfMonth] = items;
});
}
6 years ago
4 years ago
lineCount = DateUtil.getMonthViewLineCount(
3 years ago
widget.year!, widget.month!, widget.configuration!.offset);
//第一帧后,添加监听generation发生变化后需要刷新整个日历
3 years ago
WidgetsBinding.instance!.addPostFrameCallback((callback) {
Provider.of<CalendarProvider>(context, listen: false)
.generation
.addListener(() async {
3 years ago
extraDataMap = widget.configuration!.extraDataMap;
await getItems();
});
});
6 years ago
}
Future getItems() async {
5 years ago
items = await compute(initCalendarForMonthView, {
'year': widget.year,
'month': widget.month,
3 years ago
'minSelectDate': widget.configuration!.minSelectDate,
'maxSelectDate': widget.configuration!.maxSelectDate,
'extraDataMap': extraDataMap,
3 years ago
'offset': widget.configuration!.offset
5 years ago
});
setState(() {});
}
static Future<List<DateModel>> initCalendarForMonthView(Map map) async {
return DateUtil.initCalendarForMonthView(
map['year'], map['month'], DateTime.now(), DateTime.sunday,
minSelectDate: map['minSelectDate'],
maxSelectDate: map['maxSelectDate'],
extraDataMap: map['extraDataMap'],
offset: map['offset']);
5 years ago
}
6 years ago
@override
Widget build(BuildContext context) {
super.build(context);
LogUtil.log(TAG: this.runtimeType, message: "_MonthViewState build");
6 years ago
CalendarProvider calendarProvider =
Provider.of<CalendarProvider>(context, listen: false);
CalendarConfiguration configuration =
3 years ago
calendarProvider.calendarConfiguration!;
return new GridView.builder(
5 years ago
addAutomaticKeepAlives: true,
padding: EdgeInsets.zero,
5 years ago
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
3 years ago
crossAxisCount: 7, mainAxisSpacing: configuration.verticalSpacing!),
itemCount: items!.isEmpty ? 0 : items!.length,
itemBuilder: (context, index) {
3 years ago
DateModel dateModel = items![index];
//判断是否被选择
4 years ago
switch (configuration.selectMode) {
/// 多选
4 years ago
case CalendarSelectedMode.multiSelect:
3 years ago
if (calendarProvider.selectedDateList!.contains(dateModel)) {
4 years ago
dateModel.isSelected = true;
} else {
dateModel.isSelected = false;
}
break;
/// 选择开始和结束 中间的自动选择
4 years ago
case CalendarSelectedMode.mutltiStartToEndSelect:
3 years ago
if (calendarProvider.selectedDateList!.contains(dateModel)) {
4 years ago
dateModel.isSelected = true;
} else {
dateModel.isSelected = false;
}
break;
/// 单选
4 years ago
case CalendarSelectedMode.singleSelect:
4 years ago
if (calendarProvider.selectDateModel == dateModel) {
dateModel.isSelected = true;
} else {
dateModel.isSelected = false;
}
break;
3 years ago
/// 单选
case null:
break;
}
return ItemContainer(
dateModel: dateModel,
4 years ago
key: ObjectKey(dateModel),
clickCall: () {
setState(() {});
// if (configuration.selectMode ==
// CalendarSelectedMode.mutltiStartToEndSelect)
/// 如果是选择开始和结束则进行刷新日历
},
//这里使用objectKey保证可以刷新。原因1跟flutter的刷新机制有关。原因2statefulElement持有state。
);
});
}
@override
bool get wantKeepAlive => true;
}
/**
* itemitem
*/
class ItemContainer extends StatefulWidget {
3 years ago
final DateModel? dateModel;
3 years ago
final GestureTapCallback? clickCall;
const ItemContainer({Key? key, this.dateModel, this.clickCall})
4 years ago
: super(key: key);
@override
ItemContainerState createState() => ItemContainerState();
}
class ItemContainerState extends State<ItemContainer> {
3 years ago
DateModel? dateModel;
CalendarConfiguration? configuration;
late CalendarProvider calendarProvider;
3 years ago
ValueNotifier<bool?>? isSelected;
@override
void initState() {
super.initState();
dateModel = widget.dateModel;
3 years ago
isSelected = ValueNotifier(dateModel!.isSelected);
}
5 years ago
/**
* item
*/
void refreshItem(bool v) {
5 years ago
/**
Exception caught by gesture
The following assertion was thrown while handling a gesture:
setState() called after dispose()
*/
3 years ago
// v ??= false;
5 years ago
if (mounted) {
setState(() {
3 years ago
dateModel!.isSelected = v;
5 years ago
});
4 years ago
if (widget.clickCall != null) {
3 years ago
widget.clickCall!();
}
4 years ago
}
}
3 years ago
void _notifiCationUnCalendarSelect(DateModel? element) {
if (configuration!.unCalendarSelect != null) {
configuration!.unCalendarSelect!(element);
4 years ago
}
}
3 years ago
void _notifiCationCalendarSelect(DateModel? element) {
if (configuration!.calendarSelect != null) {
configuration!.calendarSelect!(element);
4 years ago
}
5 years ago
}
@override
Widget build(BuildContext context) {
// LogUtil.log(TAG: this.runtimeType, message: "ItemContainerState build");
calendarProvider = Provider.of<CalendarProvider>(context, listen: false);
configuration = calendarProvider.calendarConfiguration;
return GestureDetector(
//点击整个item都会触发事件
behavior: HitTestBehavior.opaque,
onTap: () {
LogUtil.log(
TAG: this.runtimeType,
message: "GestureDetector onTap: $dateModel}");
//范围外不可点击
3 years ago
if (!dateModel!.isInRange) {
//多选回调
3 years ago
if (configuration!.selectMode == CalendarSelectedMode.multiSelect) {
configuration!.multiSelectOutOfRange();
}
return;
}
calendarProvider.lastClickDateModel = dateModel;
3 years ago
switch (configuration!.selectMode) {
4 years ago
//简单多选
4 years ago
case CalendarSelectedMode.multiSelect:
3 years ago
if (calendarProvider.selectedDateList!.contains(dateModel)) {
calendarProvider.selectedDateList!.remove(dateModel);
4 years ago
_notifiCationUnCalendarSelect(dateModel);
4 years ago
} else {
//多选,判断是否超过限制,超过范围
3 years ago
if (calendarProvider.selectedDateList!.length ==
configuration!.maxMultiSelectCount) {
if (configuration!.multiSelectOutOfSize != null) {
configuration!.multiSelectOutOfSize!();
4 years ago
}
return;
}
3 years ago
dateModel!.isSelected = !dateModel!.isSelected!;
calendarProvider.selectedDateList!.add(dateModel);
4 years ago
}
4 years ago
//多选也可以弄这些单选的代码
calendarProvider.selectDateModel = dateModel;
4 years ago
print('多选:${calendarProvider.selectedDateList.toString()}');
4 years ago
break;
4 years ago
/// 单选
4 years ago
case CalendarSelectedMode.singleSelect:
/// 加入已经选择了多个 则进行取消操作
3 years ago
calendarProvider.selectedDateList!.forEach((element) {
element!.isSelected = false;
4 years ago
_notifiCationUnCalendarSelect(element);
4 years ago
});
3 years ago
calendarProvider.selectedDateList!.clear();
4 years ago
//单选需要刷新上一个item
if (calendarProvider.lastClickItemState != this) {
calendarProvider.lastClickItemState?.refreshItem(false);
calendarProvider.lastClickItemState = this;
}
4 years ago
_notifiCationUnCalendarSelect(calendarProvider.selectDateModel);
3 years ago
dateModel!.isSelected = true;
4 years ago
calendarProvider.selectDateModel = dateModel;
4 years ago
_notifiCationCalendarSelect(dateModel);
4 years ago
setState(() {});
4 years ago
break;
/// 选择范围
4 years ago
case CalendarSelectedMode.mutltiStartToEndSelect:
3 years ago
if (calendarProvider.selectedDateList!.length == 0) {
calendarProvider.selectedDateList!.add(dateModel);
} else if (calendarProvider.selectedDateList!.length == 1) {
DateModel? d2 = calendarProvider.selectedDateList!.first;
if (calendarProvider.selectedDateList!.contains(dateModel)) {
4 years ago
/// 选择同一个第二次则进行取消
3 years ago
dateModel!.isSelected = false;
4 years ago
_notifiCationUnCalendarSelect(dateModel);
setState(() {});
return;
}
4 years ago
DateTime t1, t2;
3 years ago
if (d2!.getDateTime().isAfter(dateModel!.getDateTime())) {
4 years ago
t2 = d2.getDateTime();
3 years ago
t1 = dateModel!.getDateTime();
4 years ago
} else {
t1 = d2.getDateTime();
3 years ago
t2 = dateModel!.getDateTime();
4 years ago
}
for (; t1.isBefore(t2);) {
3 years ago
calendarProvider.selectedDateList!
4 years ago
.add(DateModel.fromDateTime(t1));
t1 = t1.add(Duration(days: 1));
}
3 years ago
calendarProvider.selectedDateList!.add(DateModel.fromDateTime(t1));
4 years ago
} else {
/// 加入已经选择了多个 则进行取消操作
3 years ago
calendarProvider.selectedDateList!.forEach((element) {
element!.isSelected = false;
4 years ago
_notifiCationUnCalendarSelect(element);
});
/// 清空删除的 数组
3 years ago
calendarProvider.selectedDateList!.clear();
4 years ago
setState(() {});
4 years ago
}
4 years ago
3 years ago
break;
case null:
4 years ago
break;
}
4 years ago
4 years ago
/// 所有数组操作完了 进行通知分发
3 years ago
if (configuration!.calendarSelect != null &&
calendarProvider.selectedDateList!.length > 0) {
calendarProvider.selectedDateList!.forEach((element) {
4 years ago
_notifiCationCalendarSelect(element);
});
}
3 years ago
refreshItem(!this.dateModel!.isSelected!);
},
3 years ago
child: configuration!.dayWidgetBuilder!(dateModel!),
);
}
5 years ago
@override
void deactivate() {
// LogUtil.log(
// TAG: this.runtimeType, message: "ItemContainerState deactivate");
5 years ago
super.deactivate();
}
@override
void dispose() {
// LogUtil.log(TAG: this.runtimeType, message: "ItemContainerState dispose");
5 years ago
super.dispose();
}
@override
void didUpdateWidget(ItemContainer oldWidget) {
// LogUtil.log(
// TAG: this.runtimeType, message: "ItemContainerState didUpdateWidget");
5 years ago
super.didUpdateWidget(oldWidget);
6 years ago
}
}