flutter_pickers icon indicating copy to clipboard operation
flutter_pickers copied to clipboard

发现时间选择的一个bug

Open nx6313 opened this issue 3 years ago • 29 comments

当 mode = DateMode.HMS,或者 mode = DateMode.HM 时,只滚轮改变一下小时,只改变一下哈,比如从1小时滚动到2小时,后面对应的分不会自动变化,此时,点击确定选择,返回的分钟值会出现问题;如果小时滚轮改变多次,比如从1小时滚动到3小时或者更多,分钟值滚轮会自动发生变化,预期应该是当前分钟值不应该发生改变,除非分钟值在小时对应的分钟值列表里不存在的时候

nx6313 avatar Aug 23 '21 10:08 nx6313

你好,感谢反馈 能提供一下代码吗? 我分别都按照描述的测试了一下,没有复现喃 QAQ

longer96 avatar Aug 24 '21 02:08 longer96

import 'package:flustars/flustars.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_pickers/pickers.dart'; import 'package:flutter_pickers/time_picker/model/date_mode.dart'; import 'package:flutter_pickers/time_picker/model/pduration.dart';

enum DateTimeInlineType { YYYY_MM_DD, YYYY_MM_DD_HH_MM_SS, YYYY_MM_DD_HH_MM, YYYY_MM, HH_MM_SS, HH_MM, }

GlobalKey<_DateTimeInlineChooseState> dateTimeInlineChooseGlobalKey = GlobalKey(); class DateTimeInlineChoose extends StatefulWidget { const DateTimeInlineChoose({ Key? key, this.dateTimeInlineType = DateTimeInlineType.YYYY_MM_DD, this.limitToToday = true, this.limitIsTodayAfter = false, }): super(key: key);

final DateTimeInlineType dateTimeInlineType; final bool limitToToday; final bool limitIsTodayAfter;

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

class _DateTimeInlineChooseState extends State<DateTimeInlineChoose> { late String _currentDate;

DateTime current() { DateTime _current; if (widget.dateTimeInlineType == DateTimeInlineType.YYYY_MM) { _current = DateUtil.getDateTime(_currentDate + '-01')!; } else if (widget.dateTimeInlineType == DateTimeInlineType.HH_MM_SS) { _current = DateUtil.getDateTime('2021-01-01 ' + _currentDate)!; } else if (widget.dateTimeInlineType == DateTimeInlineType.HH_MM) { _current = DateUtil.getDateTime('2021-01-01 ' + _currentDate)!; } else { _current = DateUtil.getDateTime(_currentDate)!; } return _current; }

@override void initState() { super.initState();

switch (widget.dateTimeInlineType) {
  case DateTimeInlineType.YYYY_MM_DD:
    setState(() {
      _currentDate = DateUtil.formatDate(DateTime.now(), format: 'yyyy-MM-dd');
    });
    break;
  case DateTimeInlineType.YYYY_MM_DD_HH_MM_SS:
    setState(() {
      _currentDate = DateUtil.formatDate(DateTime.now(), format: 'yyyy-MM-dd HH:mm:ss');
    });
    break;
  case DateTimeInlineType.YYYY_MM_DD_HH_MM:
    setState(() {
      _currentDate = DateUtil.formatDate(DateTime.now(), format: 'yyyy-MM-dd HH:mm');
    });
    break;
  case DateTimeInlineType.YYYY_MM:
    setState(() {
      _currentDate = DateUtil.formatDate(DateTime.now(), format: 'yyyy-MM');
    });
    break;
  case DateTimeInlineType.HH_MM_SS:
    setState(() {
      _currentDate = DateUtil.formatDate(DateTime.now(), format: 'HH:mm:ss');
    });
    break;
  case DateTimeInlineType.HH_MM:
    setState(() {
      _currentDate = DateUtil.formatDate(DateTime.now(), format: 'HH:mm');
    });
    break;
}

}

bool _canToPre() { if (widget.dateTimeInlineType == DateTimeInlineType.HH_MM_SS || widget.dateTimeInlineType == DateTimeInlineType.HH_MM) { return false; } if (widget.limitToToday) { DateTime _current; if (widget.dateTimeInlineType == DateTimeInlineType.YYYY_MM) { _current = DateUtil.getDateTime(_currentDate + '-01')!; } else { _current = DateUtil.getDateTime(_currentDate)!; } if (widget.limitIsTodayAfter) { switch (widget.dateTimeInlineType) { case DateTimeInlineType.YYYY_MM_DD: return _current.isAfter(DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day)); case DateTimeInlineType.YYYY_MM_DD_HH_MM_SS: return _current.isAfter(DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, DateTime.now().hour, DateTime.now().minute, DateTime.now().second)); case DateTimeInlineType.YYYY_MM_DD_HH_MM: return _current.isAfter(DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, DateTime.now().hour, DateTime.now().minute)); case DateTimeInlineType.YYYY_MM: return _current.isAfter(DateTime(DateTime.now().year, DateTime.now().month)); default: return true; } } } return true; }

bool _canToNext() { if (widget.dateTimeInlineType == DateTimeInlineType.HH_MM_SS || widget.dateTimeInlineType == DateTimeInlineType.HH_MM) { return false; } if (widget.limitToToday) { DateTime _current; if (widget.dateTimeInlineType == DateTimeInlineType.YYYY_MM) { _current = DateUtil.getDateTime(_currentDate + '-01')!; } else { _current = DateUtil.getDateTime(_currentDate)!; } if (!widget.limitIsTodayAfter) { switch (widget.dateTimeInlineType) { case DateTimeInlineType.YYYY_MM_DD: return _current.isBefore(DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day)); case DateTimeInlineType.YYYY_MM_DD_HH_MM_SS: return _current.isBefore(DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, DateTime.now().hour, DateTime.now().minute, DateTime.now().second)); case DateTimeInlineType.YYYY_MM_DD_HH_MM: return _current.isBefore(DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, DateTime.now().hour, DateTime.now().minute)); case DateTimeInlineType.YYYY_MM: return _current.isBefore(DateTime(DateTime.now().year, DateTime.now().month)); default: return true; } } } return true; }

@override Widget build(BuildContext context) { return Row( children: [ const SizedBox(width: 6), GestureDetector( child: Padding( padding: EdgeInsets.all(4).copyWith(right: 2, bottom: 5), child: Icon(Icons.arrow_left_rounded, size: 22, color: _canToPre() ? Colors.black87 : Colors.black12,), ), behavior: HitTestBehavior.opaque, onTap: () { _preDateTime(); }, ), GestureDetector( child: Padding( padding: EdgeInsets.only(top: 4, bottom: 4), child: Text(_currentDate, style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black87),), ), behavior: HitTestBehavior.opaque, onTap: () { _showDateChooseDialog(); }, ), GestureDetector( child: Padding( padding: EdgeInsets.all(4).copyWith(left: 2, bottom: 5), child: Icon(Icons.arrow_right_rounded, size: 22, color: _canToNext() ? Colors.black87 : Colors.black12,), ), behavior: HitTestBehavior.opaque, onTap: () { _nextDateTime(); }, ), const SizedBox(width: 6), ], ); }

/// 前一个日期 _preDateTime() { if (_canToPre()) { switch (widget.dateTimeInlineType) { case DateTimeInlineType.YYYY_MM_DD: DateTime date = DateUtils.addDaysToDate(DateUtil.getDateTime(_currentDate)!, -1); setState(() { _currentDate = DateUtil.formatDate(date, format: 'yyyy-MM-dd'); }); break; case DateTimeInlineType.YYYY_MM_DD_HH_MM_SS: DateTime _current = DateUtil.getDateTime(_currentDate)!; int hour = _current.hour; int minute = _current.minute; int second = _current.second; DateTime dateTo = DateUtils.addDaysToDate(_current, -1); if (widget.limitToToday && widget.limitIsTodayAfter) { if (DateUtils.isSameDay(_current, DateTime.now())) { dateTo = DateTime(_current.year, _current.month, _current.day, DateTime.now().hour, DateTime.now().minute, DateTime.now().second); } } DateTime date = DateTime(dateTo.year, dateTo.month, dateTo.day, hour, minute, second); setState(() { _currentDate = DateUtil.formatDate(date, format: 'yyyy-MM-dd HH:mm:ss'); }); break; case DateTimeInlineType.YYYY_MM_DD_HH_MM: DateTime _current = DateUtil.getDateTime(_currentDate)!; int hour = _current.hour; int minute = _current.minute; DateTime dateTo = DateUtils.addDaysToDate(_current, -1); if (widget.limitToToday && widget.limitIsTodayAfter) { if (DateUtils.isSameDay(_current, DateTime.now())) { dateTo = DateTime(_current.year, _current.month, _current.day, DateTime.now().hour, DateTime.now().minute); } } DateTime date = DateTime(dateTo.year, dateTo.month, dateTo.day, hour, minute); setState(() { _currentDate = DateUtil.formatDate(date, format: 'yyyy-MM-dd HH:mm'); }); break; case DateTimeInlineType.YYYY_MM: DateTime date = DateUtils.addMonthsToMonthDate(DateUtil.getDateTime(_currentDate + '-01')!, -1); setState(() { _currentDate = DateUtil.formatDate(date, format: 'yyyy-MM'); }); break; case DateTimeInlineType.HH_MM_SS: break; case DateTimeInlineType.HH_MM: break; } } } /// 后一个日期 _nextDateTime() { if (_canToNext()) { switch (widget.dateTimeInlineType) { case DateTimeInlineType.YYYY_MM_DD: DateTime date = DateUtils.addDaysToDate(DateUtil.getDateTime(_currentDate)!, 1); setState(() { _currentDate = DateUtil.formatDate(date, format: 'yyyy-MM-dd'); }); break; case DateTimeInlineType.YYYY_MM_DD_HH_MM_SS: DateTime _current = DateUtil.getDateTime(_currentDate)!; int hour = _current.hour; int minute = _current.minute; int second = _current.second; DateTime dateTo = DateUtils.addDaysToDate(_current, 1); if (widget.limitToToday && !widget.limitIsTodayAfter) { if (DateUtils.isSameDay(_current, DateTime.now())) { dateTo = DateTime(_current.year, _current.month, _current.day, DateTime.now().hour, DateTime.now().minute, DateTime.now().second); } } DateTime date = DateTime(dateTo.year, dateTo.month, dateTo.day, hour, minute, second); setState(() { _currentDate = DateUtil.formatDate(date, format: 'yyyy-MM-dd HH:mm:ss'); }); break; case DateTimeInlineType.YYYY_MM_DD_HH_MM: DateTime _current = DateUtil.getDateTime(_currentDate)!; int hour = _current.hour; int minute = _current.minute; DateTime dateTo = DateUtils.addDaysToDate(_current, 1); if (widget.limitToToday && !widget.limitIsTodayAfter) { if (DateUtils.isSameDay(_current, DateTime.now())) { dateTo = DateTime(_current.year, _current.month, _current.day, DateTime.now().hour, DateTime.now().minute); } } DateTime date = DateTime(dateTo.year, dateTo.month, dateTo.day, hour, minute); setState(() { _currentDate = DateUtil.formatDate(date, format: 'yyyy-MM-dd HH:mm'); }); break; case DateTimeInlineType.YYYY_MM: DateTime date = DateUtils.addMonthsToMonthDate(DateUtil.getDateTime(_currentDate + '-01')!, 1); setState(() { _currentDate = DateUtil.formatDate(date, format: 'yyyy-MM'); }); break; case DateTimeInlineType.HH_MM_SS: break; case DateTimeInlineType.HH_MM: break; } } }

_showDateChooseDialog() { late PDuration current; DateMode mode = DateMode.YMD; switch (widget.dateTimeInlineType) { case DateTimeInlineType.YYYY_MM_DD: current = PDuration(year: DateUtil.getDateTime(_currentDate)!.year, month: DateUtil.getDateTime(_currentDate)!.month, day: DateUtil.getDateTime(_currentDate)!.day,); mode = DateMode.YMD; break; case DateTimeInlineType.YYYY_MM_DD_HH_MM_SS: current = PDuration(year: DateUtil.getDateTime(_currentDate)!.year, month: DateUtil.getDateTime(_currentDate)!.month, day: DateUtil.getDateTime(_currentDate)!.day, hour: DateUtil.getDateTime(_currentDate)!.hour, minute: DateUtil.getDateTime(_currentDate)!.minute, second: DateUtil.getDateTime(_currentDate)!.second,); mode = DateMode.YMDHMS; break; case DateTimeInlineType.YYYY_MM_DD_HH_MM: current = PDuration(year: DateUtil.getDateTime(_currentDate)!.year, month: DateUtil.getDateTime(_currentDate)!.month, day: DateUtil.getDateTime(_currentDate)!.day, hour: DateUtil.getDateTime(_currentDate)!.hour, minute: DateUtil.getDateTime(_currentDate)!.minute,); mode = DateMode.YMDHM; break; case DateTimeInlineType.YYYY_MM: current = PDuration(year: DateUtil.getDateTime(_currentDate + '-01')!.year, month: DateUtil.getDateTime(_currentDate + '-01')!.month,); mode = DateMode.YM; break; case DateTimeInlineType.HH_MM_SS: current = PDuration(hour: DateUtil.getDateTime('2021-01-01 ' + _currentDate)!.hour, minute: DateUtil.getDateTime('2021-01-01 ' + _currentDate)!.minute, second: DateUtil.getDateTime('2021-01-01 ' + _currentDate)!.second,); mode = DateMode.HMS; break; case DateTimeInlineType.HH_MM: current = PDuration(hour: DateUtil.getDateTime('2021-01-01 ' + _currentDate)!.hour, minute: DateUtil.getDateTime('2021-01-01 ' + _currentDate)!.minute,); mode = DateMode.HM; break; } Pickers.showDatePicker(context, mode: mode, selectDate: current, minDate: widget.limitToToday && widget.limitIsTodayAfter ? PDuration.now() : null, maxDate: widget.limitToToday && !widget.limitIsTodayAfter ? PDuration.now() : null, onConfirm: (PDuration res) { switch (widget.dateTimeInlineType) { case DateTimeInlineType.YYYY_MM_DD: DateTime date = DateTime(res.year ?? 0, res.month ?? 0, res.day ?? 0); setState(() { _currentDate = DateUtil.formatDate(date, format: 'yyyy-MM-dd'); }); break; case DateTimeInlineType.YYYY_MM_DD_HH_MM_SS: DateTime date = DateTime(res.year ?? 0, res.month ?? 0, res.day ?? 0, res.hour ?? 0, res.minute ?? 0, res.second ?? 0); setState(() { _currentDate = DateUtil.formatDate(date, format: 'yyyy-MM-dd HH:mm:ss'); }); break; case DateTimeInlineType.YYYY_MM_DD_HH_MM: DateTime date = DateTime(res.year ?? 0, res.month ?? 0, res.day ?? 0, res.hour ?? 0, res.minute ?? 0); setState(() { _currentDate = DateUtil.formatDate(date, format: 'yyyy-MM-dd HH:mm'); }); break; case DateTimeInlineType.YYYY_MM: DateTime date = DateTime(res.year ?? 0, res.month ?? 0); setState(() { _currentDate = DateUtil.formatDate(date, format: 'yyyy-MM'); }); break; case DateTimeInlineType.HH_MM_SS: DateTime date = DateTime(2021, 1, 1, res.hour ?? 0, res.minute ?? 0, res.second ?? 0); setState(() { _currentDate = DateUtil.formatDate(date, format: 'HH:mm:ss'); }); break; case DateTimeInlineType.HH_MM: DateTime date = DateTime(2021, 1, 1, res.hour ?? 0, res.minute ?? 0); setState(() { _currentDate = DateUtil.formatDate(date, format: 'HH:mm'); }); break; } } ); } }

nx6313 avatar Aug 24 '21 06:08 nx6313

写的一个小组件,你用这个试一下

nx6313 avatar Aug 24 '21 06:08 nx6313

还有,可否把滚轮容器内滚动到顶部时继续下拉的蓝色下拉效果去除呢,(系统自带的效果),有一丝丝的不好看

nx6313 avatar Aug 24 '21 06:08 nx6313

ScrollConfiguration( behavior: CusBehavior(), child: 滚动容器组件, );

/// 自定义behavior /// ScrollConfiguration包裹ListView去除滑动蓝色波纹 class CusBehavior extends ScrollBehavior { @override Widget buildOverscrollIndicator(BuildContext context, Widget child, ScrollableDetails details) { if (Platform.isAndroid || Platform.isFuchsia) return child; return super.buildOverscrollIndicator(context, child, details); } }

nx6313 avatar Aug 24 '21 06:08 nx6313

兄弟 有联系方式吗? 我拷贝了上面的代码 还是没有复现到喃

longer96 avatar Aug 24 '21 06:08 longer96

还有,可否把滚轮容器内滚动到顶部时继续往下拉的蓝色下拉效果呢,(系统自带的效果),有丝的不好看

关于自定义的样式,建议最好自己fork 一份修改。因为每个人对UI的看法不一,你也知道这是flutter分别为ios、android适配的,所以建议自己根据喜欢做修改

longer96 avatar Aug 24 '21 06:08 longer96

好的,那个bug一会儿我来录个屏,这地方能传视频上去吗

nx6313 avatar Aug 24 '21 06:08 nx6313

视频好像不得行,gif 可以 Aug-24-2021 15-03-27 (1)

longer96 avatar Aug 24 '21 07:08 longer96

是模拟器吗?我用的真机,您试试真机呢?

nx6313 avatar Aug 24 '21 07:08 nx6313

是模拟器吗?我用的真机,您试试真机呢?

真机也试过了,你看看是不是代码限制了时间范围 minDate maxDate喃

longer96 avatar Aug 24 '21 07:08 longer96

image 给你发的代码内,这里加了限制,默认是仅仅只有 maxDate 的限制

nx6313 avatar Aug 24 '21 07:08 nx6313

image 给你发的代码内,这里加了限制,默认是仅仅只有 maxDate 的限制

嗯,这个我看到了,默认maxDate是 取得now,也没有复现你描述的 QAQ

longer96 avatar Aug 24 '21 07:08 longer96

稍等哦,我也来整个gif

nx6313 avatar Aug 24 '21 07:08 nx6313

, dateTimeInlineType: DateTimeInlineType.HH_MM_SS, limitToToday: false,

这两个参数你这样传一下试试

limitToToday 【false】,不限制最大最小值

nx6313 avatar Aug 24 '21 07:08 nx6313

我试了一下,没有发现问题,,有可能是昨晚我使用的时候发现问题的时间是 分钟数为40多,50多的时候?

nx6313 avatar Aug 24 '21 07:08 nx6313

稍等,一会儿出现问题了,我再录gif

nx6313 avatar Aug 24 '21 07:08 nx6313

好吧,我搬砖去了

longer96 avatar Aug 24 '21 07:08 longer96

dateTimeInlineType: DateTimeInlineType.HH_MM_SS, limitToToday: true, limitIsTodayAfter: true

我复现了,你按照这样传参应该也可以看到

image

nx6313 avatar Aug 24 '21 08:08 nx6313

还是没有复现你上面描述的问题,再者,这是你封装的参数。 我打印了数据 会设置:minDate 为now

longer96 avatar Aug 24 '21 08:08 longer96

对,就是在 minDate 为 now 的情况下

nx6313 avatar Aug 24 '21 08:08 nx6313

https://user-images.githubusercontent.com/11155919/130582964-aac4cdea-1bd2-4e6c-b7d8-04e42f72c49a.mp4

你看看这个 或者你可以试着改一下这里: image image

nx6313 avatar Aug 24 '21 08:08 nx6313

刚开始是 18:46:12, 最后选择一个时后, 会变成 18:13:12 , 应该是19:46:12 才对呢,

nx6313 avatar Aug 24 '21 08:08 nx6313

确实存在该问题,应该是这个引起的 https://github.com/flutter/flutter/issues/22999

先修改pubspec.yaml为临时引用吧

  flutter_pickers:
    git:
      url: https://github.com/longer96/flutter_pickers.git
      ref: fix16

longer96 avatar Aug 25 '21 08:08 longer96

没关系,我暂时没用到时分秒的选择,,等你正式更新哈☺️

nx6313 avatar Aug 25 '21 08:08 nx6313

兄弟 抱歉,之前修复的临时版本好像有问题。我太笨 改不动了😂 这版本只有先这样

longer96 avatar Sep 13 '21 08:09 longer96

视频好像不得行,gif 可以 Aug-24-2021 15-03-27 (1)

时间如果小于10前面的0默认不带的是本身如此还是版本限制或者需要额外设置? 比如6点8分5秒 目前demo的格式是6:8:5 而比较标准的写法应该是 06:08:05

Glorylan avatar Sep 27 '21 06:09 Glorylan

视频好像不得行,gif 可以 Aug-24-2021 15-03-27 (1)

时间如果小于10前面的0默认不带的是本身如此还是版本限制或者需要额外设置? 比如6点8分5秒 目前demo的格式是6:8:5 而比较标准的写法应该是 06:08:05

这只是一个改进建议哈,目前可以临时写个工具类去转化下,比如我这样写: String conversionFormat(int date){ if(date < 10){ return '0$date'; } else { return '$date'; } }

Glorylan avatar Sep 27 '21 07:09 Glorylan

视频好像不得行,gif 可以 Aug-24-2021 15-03-27 (1)

时间如果小于10前面的0默认不带的是本身如此还是版本限制或者需要额外设置? 比如6点8分5秒 目前demo的格式是6:8:5 而比较标准的写法应该是 06:08:05

这只是一个改进建议哈,目前可以临时写个工具类去转化下,比如我这样写: String conversionFormat(int date){ if(date < 10){ return '0$date'; } else { return '$date'; } }

非常感谢你的建议,下次提建议最好重新开一个。 选择器用的时间格式是自定义的,你可以通过DateFormat转化成想要的格式。或者你上面所写的方法 可以参考https://github.com/longer96/flutter_pickers/issues/13

longer96 avatar Sep 27 '21 07:09 longer96