相对简单灵活的flutter日期选择插件

9,734 阅读4分钟

最近本来想用 flutter 搞一个记账类的APP,没想到刚开始搞的时候,发现有个需要选择月份的需求,找了几个包都不支持, 于是就自己实现了一个,下面分享一下我的思路。

flutter_my_picker

  • CupertinoPicker iOS风格的picker
  • showModalBottomSheet 从底部弹起的控件
  • DateTime dart的日期类型
  • intl dart 扩展库,在这里用作格式化日期

先放个最终效果图:

1. CupertinoPicker.builder

一个iOS风格的选择器。

在 flutter 中,可滚动的控件通常都会有一个 builder 方法,在滚动时根据索引和回调进行实时渲染,而不是直接渲染所有的元素,这样可以避免不必要的渲染。

IndexedWidgetBuilder itemBuilder

每一项的构建回调。

回调参数为 BuildContext context, int index ,需要返回 Widget,如果返回为 null ,就表示不渲染该项。

double itemExtent

每一行的高度,这个参数一定要传。

FixedExtentScrollController scrollController

滚动控制器,在滚动到非期望区域时,可以使用这个属性使其恢复到正常位置,比如超出结束时间等。

ValueChanged onSelectedItemChanged

选中项发生变化时的回调,比如从2020年滚动到2025年,每滑过一年都会触发一次。

Color backgroundColor

背景颜色,想必大家都知道,就不介绍了。

bool useMagnifier 和 double magnification

useMagnifier 为 true 的时候,magnification 才会生效。

被选中项的放大系数,建议设置为 1.2.

double offAxisFraction

被选中项的偏移,建议设置为 0.2.

double squeeze

挤压系数,建议设置为 1.45.

showModalBottomSheet

显示一个 material 设计风格的 bottom sheet。

圆角效果,需两个属性配合使用

shape: RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: const Radius.circular(12), topRight: const Radius.circular(12),)),

clipBehavior: Clip.antiAlias,

取消最大高度的限制

sheet 默认最大高度 9.0 / 16.0,如果想要全屏的效果,就需要设置 isScrollControlled 为 true

Clip 的 几个选项

[Clip] options:
  ///  * [hardEdge], 裁剪最快,但是保真度较低
  ///  * [antiAlias], 比 [hardEdge] 略慢,但是边缘更为平滑
  ///  * [antiAliasWithSaveLayer], 比 [antiAlias] 更慢, 应该很少使用。

DateTime

dart 的 DateTime 本身提供了很多强大的API,稍加组合就能得到更加方便的使用。

当前日期

new DateTime.now()

对传入的日期字符串进行解析

static DateTime parse([date]) {
    return date == null
        ? MyDate.getNow()
        : date.runtimeType == DateTime ? date : DateTime.tryParse(date);
  }

获取某个月的天数

static int daysInMonth(DateTime date) {
    return DateTime(date.year, date.month + 1, 0).day;
}

是否是同一天

static bool isAtSameDay(DateTime day1, DateTime day2) {
    return day1 != null &&
        day2 != null &&
        day1.difference(day2).inDays == 0 &&
        day1.day == day2.day;
  }

判断日期是否在范围内

static bool isInRange(DateTime date, DateTime start, DateTime end) {
    if (start == null && end == null) return true;
    if (start == null && end != null)
      return date.isBefore(end) || date.isAtSameMomentAs(end);
    if (end == null && start != null)
      return date.isAfter(start) || date.isAtSameMomentAs(start);

    return (date.isAfter(start) && date.isBefore(end)) ||
        date.isAtSameMomentAs(start) ||
        date.isAtSameMomentAs(end);
  }

intl

用来将 DateTime 转为 字符串类型。

/// newPattern 日期格式化字符串,比如'yyyy-MM-dd'
/// date DateTime 或者 字符串 2020-03-17 等
static String format(String newPattern, [dynamic date]) {
    return new DateFormat(newPattern).format(MyDate.parse(date));
}

flutter_my_picker

适用于flutter的有关日期和时间的选择器,支持年份(showYearPicker)、月份(showMonthPicker)、日期(showDatePicker)、时间(showTimePicker)、日期时间(showDateTimePicker)等。

支持的平台

  • Android
  • IOS

使用方法

依赖安装

  • 已发布到pub,可直接使用版本号进行下载
dependencies:
  ...
  flutter_my_picker: ^1.0.3

如果你想自己发布包,但是发布的时候报错,可以看这里发布教程

使用案例

导入 flutter_my_picker.dart;

import 'package:flutter_my_picker/flutter_my_picker.dart';

// 日期操作,需要时引入
import 'package:flutter_my_picker/common/date.dart';

年份选择器

_change(formatString) {
  return (_date) {
    setState(() {
      date = _date;
      dateStr = MyDate.format(formatString, _date);
    });
  };
}

MyPicker.showPicker(
  context: context,
  current: date,
  mode: MyPickerMode.year,
  onChange: _change('yyyy'),
);

// MyPicker.showYearPicker 效果一样,参数同上,不需要mode参数

月份选择器

MyPicker.showPicker(
  context: context,
  current: date,
  mode: MyPickerMode.month,
  onChange: _change('yyyy-MM'),
);

// MyPicker.showMonthPicker 效果一样,参数同上,不需要mode参数

日期选择器

MyPicker.showPicker(
  context: context,
  current: date,
  mode: MyPickerMode.date,
  onChange: _change('yyyy-MM-dd'),
);

// MyPicker.showDatePicker 效果一样,参数同上,不需要mode参数

时间选择器

MyPicker.showPicker(
  context: context,
  current: date,
  mode: MyPickerMode.time,
  onChange: _change('HH:mm'),
);

// MyPicker.showTimePicker 效果一样,参数同上,不需要mode参数

日期时间选择器

MyPicker.showPicker(
  context: context,
  current: date,
  mode: MyPickerMode.dateTime,
  onChange: _change('yyyy-MM-dd HH:mm'),
);

// MyPicker.showDateTimePicker 效果一样,参数同上,不需要mode参数

调用 MyPicker.showPicker 方法调起相关选择器,目前的参数有

API

MyPicker.showPicker所需要的的参数:

// 调用 showModalBottomSheet 所需
BuildContext context;

// 当前选中的时间,字符串和DateTime类型皆可,内部做了解析,mode 为 time 时, 可直接传入 '20:12' 的字符串
final dynamic current;
/// 开始时间,不传时表示不限制,mode 为 time 时, 可直接传入 '20:12' 的字符串
final dynamic start;
/// 结束时间,不传时表示不限制,mode 为 time 时, 可直接传入 '20:12' 的字符串
final dynamic end;

// 选中时间结束之后的回调,当滚动未结束时关闭弹窗就不会触发
//typedef DateChangedCallback(DateTime time)
final DateChangedCallback onChange;

// 点击确认按钮之后的回调
//typedef DateChangedCallback(DateTime time)
final DateChangedCallback onConfirm;

// 点击取消按钮之后的回调
//typedef CancelCallback()
final CancelCallback onCancel;

// 选择器模式
/**
enum MyPickerMode {
  year,
  month,
  date,
  time,
  dateTime,
}
*/
final MyPickerMode mode;

// 选择器单行的高度,默认36
final double itemHeight;

/// 挤压系数,默认 1, 建议设置 1.45
final double squeeze;

/// 被选中的内容放大系数,默认 1, 建议设置 1.2
final double magnification;

/// 被选中的内容偏移,默认 0, 建议设置 0.2
final double offAxisFraction; 

如果喜欢的话,希望可以给个 star ^_^