前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter 时间选择组件

Flutter 时间选择组件

作者头像
xiangzhihong
发布2022-11-30 15:42:47
3.5K0
发布2022-11-30 15:42:47
举报
文章被收录于专栏:向治洪向治洪

在Flutter 应用开发过程中,或多或少的都会涉及到时间选择器相关的内容。Flutter默认提供了DatePicker日期选择器,如果对样式没有特殊的要求,那么可以使用它来进行时间的选择,默认的样式如下所示。

在这里插入图片描述
在这里插入图片描述

使用示例代码如下:

代码语言:javascript
复制
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'dart:async';

class DateTimeDemo extends StatefulWidget {
  @override
  _DateTimeDemoState createState() => _DateTimeDemoState();
}

class _DateTimeDemoState extends State<DateTimeDemo> {
  DateTime selectedDate = DateTime.now();
  TimeOfDay selectedTime = TimeOfDay(hour: 9, minute: 30);
  
  Future<void> _selectDate() async {
    final DateTime date = await showDatePicker(
      context: context,
      initialDate: selectedDate,
      firstDate: DateTime(1900),
      lastDate: DateTime(2100),
    );

    if (date == null) return;

    setState(() {
      selectedDate = date;
    });
  }

  Future<void> _selectTime() async {
    final TimeOfDay time = await showTimePicker(
      context: context,
      initialTime: selectedTime,
    );

    if (time == null) return;

    setState(() {
      selectedTime = time;
    });
  } 
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('DateTimeDemo'),
        elevation: 0.0,
      ),
      body: Container(
        padding: EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                InkWell(
                  onTap: _selectDate,
                  child: Row(
                    children: <Widget>[
                      Text(DateFormat.yMMMMd().format(selectedDate)),
                      Icon(Icons.arrow_drop_down),
                    ],
                  ),
                ),
                InkWell(
                  onTap: _selectTime,
                  child: Row(
                    children: <Widget>[
                      Text(selectedTime.format(context)),
                      Icon(Icons.arrow_drop_down),
                    ],
                  ),
                ),
              ],
            ),
          ],
        ),
      )
    );
  }
}

可以发现,默认的样式并不是很友好。通常在移动应用开发中,App的涉及多是参考iOS的设计来的,所以这时候,多半需要进行自定义组件了。不管,为了快速的进行开发我们可以选择一些第三方的组件库,如flutter_custom_calendar,此库具有如下的功能:

  • 支持公历,农历,节气,传统节日,常用节假日
  • 日期范围设置,默认支持的最大日期范围为1971.01-2055.12
  • 禁用日期范围设置,比如想实现某范围的日期内可以点击,范围外的日期置灰
  • 支持单选、多选模式,提供多选超过限制个数的回调和多选超过指定范围的回调。
  • 跳转到指定日期,默认支持动画切换
  • 自定义日历Item,支持组合widget的方式和利用canvas绘制的方式
  • 自定义顶部的WeekBar
  • 根据实际场景,可以给Item添加自定义的额外数据,实现各种额外的功能。比如实- 现进度条风格的日历,实现日历的各种标记
  • 支持周视图的展示,支持月份视图和星期视图的展示与切换联动

如下是部分效果图:

在这里插入图片描述
在这里插入图片描述

实际使用时,我们需要根据样式对该库进行二次开发,首先,新建一个date_picker_widget.dart文件,然后添加如下代码:

代码语言:javascript
复制
import 'package:flutter/material.dart';
import 'package:flutter_custom_calendar/flutter_custom_calendar.dart';

class DatePickerWidget extends StatefulWidget {
  final ValueSetter<String> onSetter;
  DatePickerWidget({@required this.onSetter});
  @override
  _DatePickerWidgetState createState() => _DatePickerWidgetState();
}

class _DatePickerWidgetState extends State<DatePickerWidget> {
  ValueNotifier<String> text;
  ValueNotifier<String> selectText;

  CalendarController controller ;
  @override
  Widget build(BuildContext context) {
    return  Column(
      children: <Widget>[
        Container(
          color: Colors.white,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              IconButton(icon: Icon(Icons.navigate_before), onPressed: (){
                controller.moveToPreviousMonth();
              }),
              ValueListenableBuilder(
                  valueListenable: text,
                  builder: (context, value, child) {
                    return Text(text.value);
                  }),
              IconButton(
                  icon: Icon(Icons.navigate_next),
                  onPressed: () {
                    controller.moveToNextMonth();
                  }),
            ],
          ),
        ),
        CalendarViewWidget(
          boxDecoration: BoxDecoration(
              color: Colors.white
          ),
          calendarController: controller,
          weekBarItemWidgetBuilder: () {
            return CustomStyleWeekBarItem();
          },
          dayWidgetBuilder: (dateModel) {
            return CustomStyleDayWidget(dateModel);
          },
        ),
      ],
    );
  }

  List<DateTime> getHighlightedDates() {
    return List<DateTime>.generate(10,
          (int index) => DateTime.now().add(Duration(days: 10 * (index + 1))),
    );
  }

  int maxSelectYear() {
    return DateTime.now().year;
  }

  int maxSelectMounth() {
    return DateTime.now().month;
  }

  int maxSelectDay() {
    return DateTime.now().day;
  }


  @override
  void initState() {
    super.initState();
    controller =  CalendarController(
      maxSelectYear: maxSelectYear(),
      maxSelectMonth: maxSelectMounth(),
      maxSelectDay:maxSelectDay(),
    );

    controller.addMonthChangeListener(
          (year, month) {
        text.value = "$year年$month月";
      },
    );
    controller.addOnCalendarSelectListener((dateModel){
      String month = dateModel.month < 10 ? '0${dateModel.month}': '${dateModel.month}';
      String day = dateModel.day < 10 ? '0${dateModel.day}': '${dateModel.day}';
      this.widget.onSetter('${dateModel.year}-${month}-${day}');
    });

//    controller.addOnCalendarSelectListener((dateModel) {
//      debugPrint(dateModel.toString()+'+++++++++++');
//      //刷新选择的时间
//      selectText.value =
//      "单选模式\n选中的时间:\n${controller.getSingleSelectCalendar()}";
//    });
//
    text = new ValueNotifier("${DateTime.now().year}年${DateTime.now().month}月");
//
//    selectText = new ValueNotifier(
//        "单选模式\n选中的时间:\n${controller.getSingleSelectCalendar()}");
  }
}

class CustomStyleWeekBarItem extends BaseWeekBar {
  final List<String> weekList = ["日","一", "二", "三", "四", "五", "六",];

  @override
  Widget getWeekBarItem(int index) {
    return Container(
      child: Center(
        child: Text(weekList[index]),
      ),
    );
  }
}

class CustomStyleDayWidget extends BaseCustomDayWidget {
  CustomStyleDayWidget(DateModel dateModel) : super(dateModel);

  @override
  void drawNormal(DateModel dateModel, Canvas canvas, Size size) {
    if (!dateModel.isCurrentMonth) {
      return;
    }
    bool isWeekend = dateModel.isWeekend;
    bool isInRange = dateModel.isInRange;

    //顶部的文字
    TextPainter dayTextPainter = new TextPainter()
      ..text = TextSpan(
          text: dateModel.day.toString(),
          style: new TextStyle(
              color: !isInRange
                  ? Colors.grey
                  : isWeekend ? Colors.blue : Colors.black,
              fontSize: 16))
      ..textDirection = TextDirection.ltr
      ..textAlign = TextAlign.center;

    dayTextPainter.layout(minWidth: size.width, maxWidth: size.width);
    dayTextPainter.paint(canvas, Offset(0, 10));

    //下面的文字
    TextPainter lunarTextPainter = new TextPainter()
      ..text = new TextSpan(
          text: dateModel.lunarString,
          style: new TextStyle(
              color: !isInRange
                  ? Colors.grey
                  : isWeekend ? Colors.blue : Colors.grey,
              fontSize: 12))
      ..textDirection = TextDirection.ltr
      ..textAlign = TextAlign.center;

    lunarTextPainter.layout(minWidth: size.width, maxWidth: size.width);
    lunarTextPainter.paint(canvas, Offset(0, size.height / 2));
  }

  @override
  void drawSelected(DateModel dateModel, Canvas canvas, Size size) {
    if (!dateModel.isCurrentMonth) {
      return;
    }
    //绘制背景
    Paint backGroundPaint = new Paint()
      ..color = Colors.blue
      ..strokeWidth = 2;
    double padding = 8;
    canvas.drawCircle(Offset(size.width / 2, size.height / 2),
        (size.width - padding) / 2, backGroundPaint);

    //顶部的文字
    TextPainter dayTextPainter = new TextPainter()
      ..text = TextSpan(
          text: dateModel.day.toString(),
          style: new TextStyle(color: Colors.white, fontSize: 16))
      ..textDirection = TextDirection.ltr
      ..textAlign = TextAlign.center;

    dayTextPainter.layout(minWidth: size.width, maxWidth: size.width);
    dayTextPainter.paint(canvas, Offset(0, 10));

    //下面的文字
    TextPainter lunarTextPainter = new TextPainter()
      ..text = new TextSpan(
          text: dateModel.lunarString,
          style: new TextStyle(color: Colors.white, fontSize: 12))
      ..textDirection = TextDirection.ltr
      ..textAlign = TextAlign.center;

    lunarTextPainter.layout(minWidth: size.width, maxWidth: size.width);
    lunarTextPainter.paint(canvas, Offset(0, size.height / 2));
  }
}

然后,我们新建一个date_picker_dialog.dart的Dialog自定义组件,代码如下:

代码语言:javascript
复制
import 'package:flutter/material.dart';
import 'package:gc_data_app/pages/views/gaps.dart';
import 'package:gc_data_app/res/colors.dart';
import 'package:gc_data_app/routes/app_route.dart';
import 'package:gc_data_app/utils/date_utils.dart';
import 'package:gc_data_app/widgets/date_picker_new_widget.dart';
import 'package:gc_data_app/widgets/date_picker_widget.dart';
import 'package:gc_data_app/widgets/drop_down_widget.dart';
import 'package:toast/toast.dart';

class DatePickerDialog extends StatefulWidget {

  const DatePickerDialog({
    Key key,
    @required this.onSelectedDate,
  }) : super(key: key);

  final Function(String) onSelectedDate;

  @override
  State<StatefulWidget> createState() {
    return CustomTimeState();
  }
}

class CustomTimeState extends State<DatePickerDialog> {

  bool showDate = false;
  var chooseDateStr = '';

  @override
  Widget build(BuildContext context) {
    return Material(
      child: SizedBox(
        height: 475,
        child: Container(
          color: Colors.white,
          child: Column(
            children: <Widget>[
              _buildTitleBar(),
              _buildLine(),
              _buildDatePicker(),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildTitleBar() {
    return Container(
      padding: EdgeInsets.only(top: 10, left: 15, bottom: 10, right: 15),
      child: Row(
        children: <Widget>[
          InkWell(
            child: Text(
              '取消',
              style: TextStyle(
                  fontSize: 14,
                  fontWeight: FontWeight.w300,
                  color: ColorRes.text_little_blue),
            ),
            onTap: () {
              AppRoute.pop(context);
            },
          ),
          Expanded(
            child: Center(
              child: Text(
                '选择日期',
                style: TextStyle(fontSize: 15, fontWeight: FontWeight.w300),
              ),
            ),
          ),
          InkWell(
            child: Text('确认',
                style: TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.w300,
                    color: ColorRes.text_little_blue)),
            onTap: () {
              if(chooseDateStr==''){
                Toast.show('选择日期不能为空!', context);
                return ;
              }
              DateTime choose= DateUtils.strToDate(chooseDateStr);
              DateTime limitStart= DateUtils.strToDate('2014-01-01');
              if(choose.isBefore(limitStart)){
                Toast.show('起始日期不能早于2014-01-01', context);
                return ;
              }
              widget.onSelectedDate(chooseDateStr);
              FocusScope.of(context).unfocus();
              Navigator.pop(context);
            },
          )
        ],
      ),
    );
  }

  Widget _buildLine() {
    return Gaps.line;
  }

  Widget _buildDatePicker() {
    return DatePickerWidget(onSetter: (value) {
      showDate = false;
      chooseDateStr = value;
    });
  }
}

实际使用时候,使用showCupertinoModalPopup组件展示出来即可,如下所示。

代码语言:javascript
复制
showCalendarSheet(){
   showCupertinoModalPopup(
      context: context,
      builder: (BuildContext context) {
        return DatePickerDialog(onSelectedDate: (value){
           
        },
        );
      },
    );
  }
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-06-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档