likes
comments
collection
share

Flutter:CupertinoDatePicker不好使,自己撸一个小时与分钟的时间选择器

作者站长头像
站长
· 阅读数 6

最近在自己的Flutter项目中需要使用iOS风格的小时与分钟的时间选择器。

本来开箱即用的CupertinoDatePicker就基本上可以满足需求,奈何项目里大部分都是黑色背景,而CupertinoDatePicker无法设置文本颜色,结果就是一团黑,根本看不清楚选择的什么时间。

借着大神引领的路,自己撸一个就很简单了。

下面代码只是最最简单的例子,大家可以借鉴其中,自己进行自定义: Flutter:CupertinoDatePicker不好使,自己撸一个小时与分钟的时间选择器

class CustomDatePicker extends StatefulWidget {
  CustomDatePicker({this.timeCallback, Key key}) : super(key: key);

  final ValueChanged<String> timeCallback;

  @override
  _CustomDatePickerState createState() => _CustomDatePickerState();
}

class _CustomDatePickerState extends State<CustomDatePicker> {
  final _hours = List.generate(24, (i) => i.timeFormat);

  final _minutes = List.generate(60, (i) => i.timeFormat);

  var _hourViewIndex = 0;

  var _minuteViewIndex = 0;

  FixedExtentScrollController _hourController;

  FixedExtentScrollController _minuteController;

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

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Stack(
        children: [
          Row(
            children: [
              _hourView(),
              _minuteView(),
            ],
          ),
          Positioned(
            left: 16,
            right: 16,
            top: 68,
            child: Divider(),
          ),
          Positioned(
            left: 16,
            right: 16,
            top: 118,
            child: Divider(),
          ),
        ],
      ),
    );
  }

  Widget _hourView() {
    return _timeWheel(times: _hours, isHour: true, controller: _hourController);
  }

  Widget _minuteView() {
    return _timeWheel(
        times: _minutes, isHour: false, controller: _minuteController);
  }

  Widget _timeWheel(
      {List<String> times,
      bool isHour,
      FixedExtentScrollController controller}) {
    return Flexible(
      child: Container(
        height: 190,
        child: ListWheelScrollView.useDelegate(
          controller: controller,
          childDelegate: ListWheelChildLoopingListDelegate(
            children: times
                .map(
                  (number) => _buildItem(number),
                )
                .toList(),
          ),
          perspective: 0.006,
          itemExtent: 49,

          /// 是否放大
          useMagnifier: true,

          /// 放大倍数
          magnification: 1.2,
          onSelectedItemChanged: (index) {
            if (isHour) {
              _hourViewIndex = index;
            } else {
              _minuteViewIndex = index;
            }
            final time =
                _hours[_hourViewIndex] + ":" + _minutes[_minuteViewIndex];
            widget.timeCallback(time);
          },
        ),
      ),
    );
  }

  Widget _buildItem(String time) {
    return Container(
      key: ValueKey(time),
      alignment: Alignment.center,
      height: 50,
      color: Color(0xFF),
      child: Text(
        time,
        textDirection: TextDirection.rtl,
        style: TextStyle(
          fontWeight: FontWeight.bold,
          fontSize: 23,
          color: Colors.white,
        ),
      ),
    );
  }

  void _nowTimeSetting() {
    final nowTime = DateTime.now();
    final nowHour = nowTime.hour.timeFormat;
    final nowMinute = nowTime.minute.timeFormat;
    _hourViewIndex = _hours.indexOf(nowHour);
    _minuteViewIndex = _minutes.indexOf(nowMinute);
    _hourController = FixedExtentScrollController(initialItem: _hourViewIndex);
    _minuteController =
        FixedExtentScrollController(initialItem: _minuteViewIndex);
  }
  
  @override
  void dispose() {
    _hourController.dispose();
    _minuteController.dispose();
    super.dispose();
  }
}

extension on int {
  String get timeFormat {
    var string = this.toString();
    return string.length == 2 ? string : "0" + string;
  }
}

1.代码的思路是先把两个ListWheelViewport放到一个Row中,左边的是0-24小时,右边的0-59分钟,于是用了两套数据源,在Row中如何让两个ListWheelViewport平分,我稍微做了一个尝试,可以固定宽度,屏幕的一半,或者使用Flexible进行包裹,如果大家有更好的解决方案,可以教教我。

2.然后就是希望点进页面的使用能够滚动到当前时间,这里我想到了在ListWheelScrollView中使用ScrollController,不过如何精确计算滚动到当前的时间,一时半会还没不知道,就在我边查看源码,边看Api的时候,在里面看到了ScrollController的子类FixedExtentScrollController,通过配置该控制器可以在初始化的时候,就准确的滚动到当前时间。

3.最后,就是当前的的时间,在其上下加上横线进行修饰,这个用Stack在最外层包裹即可。