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.

270 lines
8.4 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/calendar_provider.dart';
import 'package:flutter_custom_calendar/configuration.dart';
6 years ago
import 'package:flutter_custom_calendar/constants/constants.dart';
import 'package:flutter_custom_calendar/model/date_model.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 {
final int year;
final int month;
final int day;
6 years ago
final CalendarConfiguration configuration;
6 years ago
const MonthView({
5 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 {
5 years ago
List<DateModel> items = List();
6 years ago
int lineCount;
Map<DateModel, Object> extraDataMap; //自定义额外的数据
6 years ago
@override
void initState() {
super.initState();
extraDataMap = widget.configuration.extraDataMap;
DateModel firstDayOfMonth =
DateModel.fromDateTime(DateTime(widget.year, widget.month, 1));
if (CacheData.getInstance().monthListCache[firstDayOfMonth]?.isNotEmpty ==
true) {
LogUtil.log(TAG: this.runtimeType, message: "缓存中有数据");
items = CacheData.getInstance().monthListCache[firstDayOfMonth];
} else {
LogUtil.log(TAG: this.runtimeType, message: "缓存中无数据");
getItems().then((_){
CacheData.getInstance().monthListCache[firstDayOfMonth] = items;
});
}
6 years ago
lineCount = DateUtil.getMonthViewLineCount(widget.year, widget.month);
//第一帧后,添加监听generation发生变化后需要刷新整个日历
WidgetsBinding.instance.addPostFrameCallback((callback) {
Provider.of<CalendarProvider>(context, listen: false)
.generation
.addListener(() async {
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,
'minSelectDate': widget.configuration.minSelectDate,
'maxSelectDate': widget.configuration.maxSelectDate,
5 years ago
'extraDataMap': extraDataMap
});
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']);
}
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 =
calendarProvider.calendarConfiguration;
return new GridView.builder(
5 years ago
addAutomaticKeepAlives: true,
padding: EdgeInsets.zero,
5 years ago
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 7, mainAxisSpacing: configuration.verticalSpacing),
5 years ago
itemCount: items.isEmpty ? 0 : 7 * lineCount,
itemBuilder: (context, index) {
DateModel dateModel = items[index];
//判断是否被选择
if (configuration.selectMode == CalendarConstants.MODE_MULTI_SELECT) {
if (calendarProvider.selectedDateList.contains(dateModel)) {
dateModel.isSelected = true;
} else {
dateModel.isSelected = false;
}
} else {
if (calendarProvider.selectDateModel == dateModel) {
dateModel.isSelected = true;
} else {
dateModel.isSelected = false;
}
}
return ItemContainer(
dateModel: dateModel,
key: ObjectKey(
dateModel), //这里使用objectKey保证可以刷新。原因1跟flutter的刷新机制有关。原因2statefulElement持有state。
);
});
}
@override
bool get wantKeepAlive => true;
}
/**
* itemitem
*/
class ItemContainer extends StatefulWidget {
final DateModel dateModel;
const ItemContainer({
Key key,
this.dateModel,
}) : super(key: key);
@override
ItemContainerState createState() => ItemContainerState();
}
class ItemContainerState extends State<ItemContainer> {
DateModel dateModel;
CalendarConfiguration configuration;
CalendarProvider calendarProvider;
ValueNotifier<bool> isSelected;
@override
void initState() {
super.initState();
dateModel = widget.dateModel;
isSelected = ValueNotifier(dateModel.isSelected);
WidgetsBinding.instance.addPostFrameCallback((callback) {
if (configuration.selectMode == CalendarConstants.MODE_SINGLE_SELECT &&
dateModel.isSelected) {
calendarProvider.lastClickItemState = this;
}
});
}
5 years ago
/**
* item
*/
void refreshItem() {
/**
Exception caught by gesture
The following assertion was thrown while handling a gesture:
setState() called after dispose()
*/
if (mounted) {
setState(() {
dateModel.isSelected = !dateModel.isSelected;
// isSelected.value = !isSelected.value;
});
}
}
@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}");
//范围外不可点击
if (!dateModel.isInRange) {
//多选回调
if (configuration.selectMode == CalendarConstants.MODE_MULTI_SELECT) {
configuration.multiSelectOutOfRange();
}
return;
}
calendarProvider.lastClickDateModel = dateModel;
if (configuration.selectMode == CalendarConstants.MODE_MULTI_SELECT) {
if (calendarProvider.selectedDateList.contains(dateModel)) {
calendarProvider.selectedDateList.remove(dateModel);
} else {
//多选,判断是否超过限制,超过范围
if (calendarProvider.selectedDateList.length ==
configuration.maxMultiSelectCount) {
if (configuration.multiSelectOutOfSize != null) {
configuration.multiSelectOutOfSize();
}
return;
}
calendarProvider.selectedDateList.add(dateModel);
}
configuration.calendarSelect(dateModel);
//多选也可以弄这些单选的代码
calendarProvider.selectDateModel = dateModel;
} else {
calendarProvider.selectDateModel = dateModel;
configuration.calendarSelect(dateModel);
//单选需要刷新上一个item
if (calendarProvider.lastClickItemState != this) {
calendarProvider.lastClickItemState?.refreshItem();
calendarProvider.lastClickItemState = this;
}
}
refreshItem();
},
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
}
}