Flutter 如何有效避免日期事件监听中的混乱问题

105 阅读1分钟

背景描述

最近接到了一个日历需求,类似于钉钉的日历,需要能在周日历和月日历间来回切换。其中要实现下面2点功能:
1.日历左滑/右滑切换视图的时候,需要重新定位选中日期
2.在月日历上选中日期的时候,要自动切回周日历

类似以下钉钉截图:
周日历: 1.PNG.JPG

月日历: 2.PNG.JPG

问题描述

日期控件用了这个库:syncfusion_flutter_datepicker 23.2.7,刚开始代码是这样写的(这里只展示关键代码):

int numberOfWeeksInView = 1; // 视图周数:周日历是1周,月日历是6周。这里默认1,即默认显示周日历。

// 创建日历
Stack(
    children: [
        calendarPicker,
        switchBtn
    ]
);

Widget calendarPicker(){
	return SfDateRangePicker(
		numberOfWeeksInView: numberOfWeeksInView,
		// 日期选择事件
		onSelectionChanged: (DateRangePickerSelectionChangedArgs args) {
		  print('onSelectionChanged-${widget.controller.selectedDate}');
		  changeToWeekView(); // 在月日历上选中日期的时候,要自动切回周日历
		  WidgetsBinding.instance.addPostFrameCallback((_) {
		    widget.controller.displayDate = widget.controller.selectedDate;
		  });
		  request();
		},
		// 月份切换事件
		onViewChanged: (DateRangePickerViewChangedArgs args) {
		  WidgetsBinding.instance.addPostFrameCallback((_) {
		    print(
		        'onViewChanged-${widget.controller.selectedDate}-${widget.controller.displayDate}');
		    // 日历左滑/右滑切换视图的时候,需要重新定位选中日期
		    widget.controller.selectedDate = widget.controller.displayDate;
		  });
		},
	);
}

// 切换周/月的按钮
Widget switchBtn(){
    return GestureDetector(
        onTap: () {
              setState(() {
                if (myNumberOfWeeksInView.value == 1) {
                  changeToMonthView();
                } else {
                  changeToWeekView();
                }
              });
          },
        child: Text('切换')
    );
}

// 请求后台数据
request() {
  print('request');
}

// 变为周日历
changeToWeekView() {
	setState(() {
		numberOfWeeksInView = 1;
	}
}

// 变为月日历
changeToMonthView() {
	setState(() {
		numberOfWeeksInView = 6;
	} 
}

这样写就带来了个很严重的问题,从周日历切到月日历,在月日历上左右滑动的时候,因为触发了onViewChanged事件,导致selectedDate重新赋值,接着就触发了onSelectionChanged事件,导致又切回了周日历。

尝试用一个变量进行加锁,加了一堆代码后,毫无疑问,把自己搞晕了 😵‍💫

解决方案

看了下官方库,发现有提供addPropertyChangedListener和removePropertyChangedListener方法,那就即刻用起来,哈哈😁
代码示例如下:

// 日期变量声明
DateRangePickerController dateRangePickerController =
      DateRangePickerController();
      
// 日期事件监听方法
pickerValueChangedListener(String value) {
	if (value == 'selectedDate') {
	  print('onSelectionChanged-${widget.controller.selectedDate}');
     // 在月日历上选中日期的时候,要自动切回周日历
	  changeToWeekView();
	  WidgetsBinding.instance.addPostFrameCallback((_) {
	    widget.controller.displayDate = widget.controller.selectedDate;
	  });
	  request();
	} else if (value == 'displayDate') { 
	  // 移除监听事件,防止日历左右滑动会触发onSelectionChanged事件
	  removePickerListener();
     WidgetsBinding.instance.addPostFrameCallback((_) {
	    print(
	        'onViewChanged-${widget.controller.selectedDate}-${widget.controller.displayDate}');
	    // 日历左滑/右滑切换视图的时候,需要重新定位选中日期
	    widget.controller.selectedDate = widget.controller.displayDate;
	    
	    // 左右滑动结束后,重新绑定监听事件
	    addPickerListener();
	  });
	}
}

// 添加日期事件监听方法
addPickerListener() {
  dateRangePickerController
      .addPropertyChangedListener(pickerValueChangedListener);
}

// 移除日期事件监听方法
removePickerListener() {
  dateRangePickerController
      .removePropertyChangedListener(pickerValueChangedListener);
}

引用

  1. syncfusion_flutter_datepicker的库地址:pub.dev/packages/sy…