Skip to content

Commit

Permalink
完成0.2.0版本
Browse files Browse the repository at this point in the history
  • Loading branch information
baydm committed Dec 15, 2019
1 parent 96bf747 commit d92943a
Show file tree
Hide file tree
Showing 19 changed files with 908 additions and 435 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
## 0.2.0
#### 更新内容
- 代码重构
- 滑动切换
- 可单选、可连选
- 月视图添加背景
- 星期头部可自定义

#### 下版本计划发布
- 下一月
- 上一月
- 月份跳转

## 0.1.0

- 实现左右滑动切换月
Expand Down
125 changes: 113 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,116 @@

Date component developed with Flutter, plans to support display, swipe left and right, add date mark, radio, display week, etc.

Flutter开发日历组件。

### 计划实现的功能
- [] 展示日历周视图
- [] 支持左右滑动切换月视图
- [] 支持添加自定义Marker
- [ ] 支持农历和阳历显示
- [ ] 支持添加自定义事件
- [ ] 支持自定义起始周
- [ ] 支持自定义日期控件
- [ ] 优化性能
- ……
使用Flutter开发的日期组件,计划支持显示,左右滑动,添加日期标记,单选,显示星期等功能。


<a name="sVRq8"></a>
## 开发日记
<a name="ria82"></a>
### 2019-12-09 记
> 主要想实现的内容
![](https://cdn.nlark.com/yuque/0/2019/svg/179485/1576425808448-b294ad6f-a230-4a01-bfc3-6e3196ed22e1.svg)
<a name="WzUDX"></a>
### 2019-12-10 记
> 整体上,从简单的显示开始,然后再加入交互和控制功能。第一版显示方面,先考虑实现最常用的月视图。
<a name="ahzoY"></a>
#### 月视图实现
> 日历的月视图,是非常规则的表格形式。所以就考虑用GridView来实现。
1. 写两个类,来管理月和日,并重构了比较运算符。

```dart
/// 月
class DateMonth {
int year;
int month;
int maxDays;
// ……
}
/// 日
class DateDay extends DateMonth{
int day;
// ……
}
```

2. 在写月视图时,需要考虑开始星期所在位置,非当月日期的显示情况等。这里的关键在于计算位置。



<a name="mXiJH"></a>
### 2019-12-12 记
> 由于GridView在控制高度上面有所不便(可能有好多方法,我没有尝试到),我换成了Warp的方式来实现。
<a name="wa1Sw"></a>
#### 切换月份
> 常见的月份切换方式,有类似小米的左右滑动切换,也有上下滑动切换。第一个想到的就是用PageView来实现该功能。
问题:如何实现无限切换?

1. 构建前,全部加载完成,然后就可以随意切换了。否决,不够灵活,性能开销大。
1. 动态分配,初始化3页,然后监听滑动,动态首尾动态添加对应月份。可以考虑,但跨度大了,数据会不断膨胀,可以再优化下。
1. 动态分配,动态移除,设定缓存大小,超出缓存范围的将被移除。可以,那就按照这个方案进行!

问题:如何实现动态分配?
> 原理如下,先动态分配数据,然后跳转到指定页面。若考虑缓存机制,可在remove前加判断即可。
![image.png](https://cdn.nlark.com/yuque/0/2019/png/179485/1576167627925-f9a55fd4-bcd9-4c12-b808-79bb74f6eab8.png#align=left&display=inline&height=281&name=image.png&originHeight=281&originWidth=720&size=28161&status=done&style=none&width=720)

> 下面是核心代码

```dart
List<int> pages = [-1, 0, 1];
PageController _controller = PageController(initialPage: 1);
_controller.addListener(() {
double position = _controller.position.pixels;
if (position == 0) {
pages.insert(0, pages.first - 1);
pages.remove(pages.last);
_controller.jumpToPage(1);
} else if (position == _controller.position.maxScrollExtent) {
pages.add(pages.last + 1);
pages.remove(pages.first);
_controller.jumpToPage(1);
} else {
return;
}
setState((){});
});
```
有了上面的基础,切换月份,也就变成一件简单的事情了。
<a name="apfsx"></a>
### 2019-12-14 记
<a name="7iLxK"></a>
#### 实现简单日历
![日历.gif](https://cdn.nlark.com/yuque/0/2019/gif/179485/1576424449186-d5ef3e7f-099d-4c7c-8db1-c21e5c88fde6.gif#align=left&display=inline&height=888&name=%E6%97%A5%E5%8E%86.gif&originHeight=973&originWidth=548&size=529806&status=done&style=shadow&width=500)
<a name="mfYMK"></a>
#### 连选实现
> 主要是处理日期选中后的赋值问题。
当下的处理逻辑有很多,我这里采用的是以下逻辑:

1. 单选和连选只同时支持一种(理论上可以同时支持,后续可以再优化实现)
1. 第一次选择日期,给到开始日期上。
1. 第二次选择日期,将小日期给到开始日期,大日期给到结束日期上。
1. 第三次选择日期,当落在小、大之间时,更换大日期。当不落在之间时,小、大日期全部清空。相当于取消选择。

<a name="IppLr"></a>
### 2019-12-15 记
<a name="Z2pEK"></a>
#### 实现可单选、连选、左右滑动日历控件

- 月视图添加背景
- 可自定义起始星期
- 可自定义实现周头部控件
- 可自定义实现标记组件
- 利用[切换月份](#%E5%88%87%E6%8D%A2%E6%9C%88%E4%BB%BD)[连选实现](#%E8%BF%9E%E9%80%89%E5%AE%9E%E7%8E%B0)完成左右切换的可单选、连选的日期控件

   ![单选.gif](https://cdn.nlark.com/yuque/0/2019/gif/179485/1576424343540-a574f9fd-7739-4b65-aa48-bab9896e679a.gif#align=left&display=inline&height=614&name=%E5%8D%95%E9%80%89.gif&originHeight=973&originWidth=548&size=705846&status=done&style=shadow&width=346)   ![多选.gif](https://cdn.nlark.com/yuque/0/2019/gif/179485/1576424344370-f2debb5d-776f-416a-9410-b5090a54d81a.gif#align=left&display=inline&height=614&name=%E5%A4%9A%E9%80%89.gif&originHeight=973&originWidth=548&size=597980&status=done&style=shadow&width=346)

79 changes: 64 additions & 15 deletions example/lib/calendar_view.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:math';

import 'package:flutter/material.dart';

import 'package:mini_calendar/mini_calendar.dart';
Expand All @@ -10,33 +12,80 @@ class CalendarViewPage extends StatefulWidget {
class _CalendarViewPageState extends State<CalendarViewPage> {
DateDay currentDay = DateDay.now();
DateMonth currentMonth = DateMonth.now();
List<Widget> months = [];
List<MonthController<String>> controllers = [];

@override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
List<Widget> months = [];
void initState() {
List.generate(12, (index) {
controllers.add(MonthController.init(MonthOption<String>(
currentDay: currentDay,
currentMonth: DateMonth(2019, index + 1),
firstSelectDay: DateDay(2019, index + 1, Random().nextInt(5) + 1),
secondSelectDay: DateDay(2019, index + 1, Random().nextInt(5) + 10),
firstWeek: Random().nextInt(7) + 1,
enableContinuous: Random().nextBool(),
marks: {DateDay(2019, index + 1, Random().nextInt(27) + 1): '随机mark'},
)));
months
..add(Container(
height: 40,
alignment: Alignment.center,
child: Text("2019年${index + 1}月"),
))
..add(Container(
width: width,
child: MonthWidget(
currentMonth: DateMonth(2019, index + 1),
width: width,
..add(
Container(
child: MonthWidget<String>(
controller: controllers[index],
padding: EdgeInsets.only(
left: Random().nextInt(15).ceilToDouble(),
right: Random().nextInt(15).ceilToDouble(),
bottom: Random().nextInt(15).ceilToDouble(),
top: Random().nextInt(15).ceilToDouble()),
buildMark: (ctx, day, data) {
return Positioned(
bottom: 0,
right: 0,
left: 1,
child: Center(
child: Icon(
Icons.access_alarm,
color: Colors.pink,
size: 14,
)));
},
color: Colors.white,
firstWeek: (index+1)%7,
)));
onDaySelected: (day, data) {
currentDay = day.copyWith();
print(currentDay);
if (data != null) {
print(data);
}
controllers[index]
..setCurrentDay(day)
..reLoad();
},
),
),
);
});
super.initState();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('显示日历'),
centerTitle: true,
),
body: ListView(children: months));
appBar: AppBar(
title: Text('显示日历'),
centerTitle: true,
),
body: ListView(children: months),
);
}

@override
void dispose() {
controllers.forEach((f) => f?.dispose());
super.dispose();
}
}
8 changes: 3 additions & 5 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import 'package:flutter/material.dart';
import 'dart:async';

import 'calendar_view.dart';
import 'page_view_page.dart';
import 'month_page_view_demo.dart';

void main() => runApp(MyApp());

Expand All @@ -27,7 +26,7 @@ class HomeWidget extends StatelessWidget {
body: ListView(
children: <Widget>[
_buildItem(context, '显示日历', CalendarViewPage()),
_buildItem(context, '测试切屏', PageViewPage()),
_buildItem(context, '滑动日历', MonthPageViewDemo()),
],
),
);
Expand All @@ -36,8 +35,7 @@ class HomeWidget extends StatelessWidget {
Widget _buildItem(BuildContext context, String title, Widget page) {
return ListTile(
title: Text(title ?? ''),
onTap: () =>
Navigator.push(context, MaterialPageRoute(builder: (ctx) => page)),
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (ctx) => page)),
);
}
}
91 changes: 91 additions & 0 deletions example/lib/month_page_view_demo.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import 'package:flutter/material.dart';
import 'package:mini_calendar/mini_calendar.dart';

class MonthPageViewDemo extends StatefulWidget {
@override
_MonthPageViewDemoState createState() => _MonthPageViewDemoState();
}

class _MonthPageViewDemoState extends State<MonthPageViewDemo> {
MonthPageController controller;
DateMonth _month = DateMonth.now();
DateDay _firstDay, _secondDay, _day;
bool _checked = true;
String _data = '';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('滑动日历'),
centerTitle: true,
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
Text('单选'),
Switch(
value: _checked,
onChanged: (select) {
_checked = select;
if (controller != null)
controller
..setEnableContinuous(_checked)
..reLoad();
setState(() {});
},
),
Text('连选')
],
),
Container(
height: MediaQuery.of(context).size.width,
child: MonthPageView(
option: MonthOption(
currentDay: DateDay.now(),
enableContinuous: true,
marks: {
DateDay(2019, 12, 1): '111',
DateDay(2019, 12, 4): '222',
DateDay(2019, 12, 11): '333',
DateDay(2019, 12, 20): '444',
},
),
showWeekHead: true,
onContinuousSelectListen: (firstDay, endFay) {
_firstDay = firstDay;
_secondDay = endFay;
setState(() {});
},
onMonthChange: (month) {
_month = month;
setState(() {});
},
onDaySelected: (day, data) {
_day = day;
_data = data;
setState(() {});
},
onCreated: (c) => controller = c,
),
),
Container(child: Text('''
当前月份:$_month
当前模式:${_checked ? '连选' : '单选'}
单选日期:${_day ?? ''} :: ${_data ?? ''}
连选日期:${_firstDay ?? ''} 至 ${_secondDay ?? ''}
'''), padding: EdgeInsets.all(10))
],
),
),
);
}

@override
void dispose() {
controller?.dispose();
super.dispose();
}
}
Loading

0 comments on commit d92943a

Please sign in to comment.