前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Flutter 仿ios自定义一个DatePicker

Flutter 仿ios自定义一个DatePicker

作者头像
赵哥窟
发布于 2021-03-02 06:32:36
发布于 2021-03-02 06:32:36
1.1K00
代码可运行
举报
文章被收录于专栏:日常技术分享日常技术分享
运行总次数:0
代码可运行

Screenshot_1612747215.png

编辑个人资料,修改生日的时候需要用到,需求就是如果传了日期就要滚动到传的日期位置,如果没有穿就是系统当前时间。所以动手撸一个,有需要的同学可以拿去做轮子。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/screenutil.dart';
import 'package:toofoo/common/color/colorsUtil.dart';

typedef OnSelectedDate = void Function(String date);

class DatePicker extends StatefulWidget {
  DatePicker(
      {this.onSelectedDate,
      this.selectedDate,
      this.startYear = 1970,
      this.endYear = 2500});

  // 结果返回
  final OnSelectedDate onSelectedDate;
  final String selectedDate; //选中的时间
  final int startYear;
  final int endYear;

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

class _DatePickerState extends State<DatePicker> {
  //年数组
  List<String> yearList = [];
  //月数组
  List<String> monthList = [];
  //天数组
  List<String> dayList = [];
  //年的索引
  int yearIndex;
  //月的索引
  int monthIndex;
  //天的索引
  int dayIndex;
  //每列的宽度
  double _columnWidth;

  FixedExtentScrollController yearScrollController;
  FixedExtentScrollController monthScrollController;
  FixedExtentScrollController dayScrollController;

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

    _columnWidth = ScreenUtil.screenWidth / 3;

    _setupData();

    _initSelectedIndex();
  }

  @override
  void dispose() {
    yearScrollController.dispose();
    monthScrollController.dispose();
    dayScrollController.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          _headerWidget(),
          _datePicker(),
        ],
      ),
    );
  }

  ///初始化数据
  void _setupData() {
    for (int i = widget.startYear; i <= widget.endYear; i++) {
      yearList.add(i.toString());
    }

    for (int i = 1; i <= 12; i++) {
      monthList.add(i.toString().padLeft(2, '0'));
    }

    // 初始化天数(当前时间系统时间的天数)
    int year = DateTime.now().year;
    int month;
    if (widget.selectedDate == null || widget.selectedDate.isEmpty) {
      month = DateTime.now().month;
    } else {
      List<String> date = widget.selectedDate.split('-');
      month = int.parse(date[1]);
    }

    dayList = _getDayList(year: year, month: month);
  }

  int _getDayCount({int year, int month}) {
    int dayCount = DateTime(year, month + 1, 0).day;
    return dayCount;
  }

  List<String> _getDayList({int year, int month}) {
    List<String> dayList = [];
    int days = _getDayCount(year: year, month: month);
    for (int i = 1; i <= days; i++) {
      dayList.add(i.toString().padLeft(2, '0'));
    }

    return dayList;
  }

  ///选中年月后更新天
  void _updateDayList() {
    int year = int.parse(yearList[yearIndex]);
    int month = int.parse(monthList[monthIndex]);

    setState(() {
      dayIndex = 0;
      dayList = _getDayList(year: year, month: month);

      if (dayScrollController.positions.length > 0) {
        dayScrollController.jumpTo(0);
      }
    });
  }

  ///初始化时间索引
  void _initSelectedIndex() {
    final List uniqueYearList = Set.from(yearList).toList();
    final List uniqueMonthList = Set.from(monthList).toList();
    final List uniqueDayList = Set.from(dayList).toList();

    ///获取索引
    if (widget.selectedDate != null && widget.selectedDate.isNotEmpty) {
      ///传了选中日期的时候
      List<String> date = widget.selectedDate.split('-');

     setState(() {
       yearIndex = uniqueYearList.indexOf(date[0]);
       monthIndex = uniqueMonthList.indexOf(date[1]);
       dayIndex = uniqueDayList.indexOf(date[2]);
     });
    } else {
      ///没有传选中日期默认当前系统时间
      String year = DateTime.now().year.toString();
      String month = DateTime.now().month.toString().padLeft(2, '0');
      String day = DateTime.now().day.toString().padLeft(2, '0');

     setState(() {
       yearIndex = uniqueYearList.indexOf(year);
       monthIndex = uniqueMonthList.indexOf(month);
       dayIndex = uniqueDayList.indexOf(day);
     });
    }

    yearScrollController = FixedExtentScrollController(initialItem: yearIndex);
    monthScrollController =
        FixedExtentScrollController(initialItem: monthIndex);
    dayScrollController = FixedExtentScrollController(initialItem: dayIndex);
  }

  Widget _headerWidget() {
    return Container(
      height: 60,
      child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: <Widget>[
            FlatButton(
              child: Text(
                '取消',
                style: TextStyle(
                  fontSize: 16.0,
                ),
              ),
              onPressed: () {
                Navigator.pop(context);
              },
            ),
            FlatButton(
              child: Text(
                '确定',
                style: TextStyle(
                  fontSize: 16.0,
                ),
              ),
              onPressed: () {
                if (widget.onSelectedDate != null) {
                  String date = yearList[yearIndex]+'-'+monthList[monthIndex]+'-'+dayList[dayIndex];
                  widget.onSelectedDate(date);
                }
              },
            ),
          ]),
      decoration: BoxDecoration(
        border: Border(
            bottom: BorderSide(color: Colors.grey.withOpacity(0.1), width: 1)),
      ),
    );
  }

  Widget _datePicker() {
    return Container(
      color: Colors.white,
      height: 200,
      child: Stack(
        children: [
          Row(
            children: <Widget>[
              Expanded(child: _yearPickerView()),
              Expanded(child: _monthPickerView()),
              Expanded(child: _dayPickerView()),
            ],
          ),
          Container(
            color: ColorsUtil.hexStringColor('D1D1D6'),
            margin: EdgeInsets.only(top: 78),
            height: 1,
          ),
          Container(
            color: ColorsUtil.hexStringColor('D1D1D6'),
            margin: EdgeInsets.only(top: 124),
            height: 1,
          ),
          Container(
            margin: EdgeInsets.only(top: 78),
            child: Row(
              children: [
                Expanded(
                  child: Padding(
                    padding: EdgeInsets.only(left: _columnWidth - 40, top: 9),
                    child: Text(
                      '年',
                      style: TextStyle(color: Colors.grey, fontSize: 16),
                      maxLines: 1,
                    ),
                  ),
                ),
                Expanded(
                  child: Padding(
                    padding: EdgeInsets.only(left: _columnWidth - 50, top: 9),
                    child: Text(
                      '月',
                      style: TextStyle(color: Colors.grey, fontSize: 16),
                      maxLines: 1,
                    ),
                  ),
                ),
                Expanded(
                  child: Padding(
                    padding: EdgeInsets.only(left: _columnWidth - 50, top: 9),
                    child: Text(
                      '日',
                      style: TextStyle(color: Colors.grey, fontSize: 16),
                      maxLines: 1,
                    ),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  ///年
  Widget _yearPickerView() {
    return Container(
      child: CupertinoPicker(
        scrollController: yearScrollController,
        children: _buildYearWidget(),
        looping: true,
        selectionOverlay: Center(),
        onSelectedItemChanged: (index) {
          setState(() {
            yearIndex = index;
          });
          _updateDayList();
        },
        itemExtent: 44,
      ),
    );
  }

  ///月
  Widget _monthPickerView() {
    return Container(
      child: CupertinoPicker(
        scrollController: monthScrollController,
        children: _buildMonthWidget(),
        looping: true,
        selectionOverlay: Center(),
        onSelectedItemChanged: (index) {
          setState(() {
            monthIndex = index;
          });
          _updateDayList();
        },
        itemExtent: 44,
      ),
    );
  }

  ///日
  Widget _dayPickerView() {
    return Container(
      child: CupertinoPicker(
        scrollController: dayScrollController,
        children: _buildDayWidget(),
        looping: true,
        selectionOverlay: Center(),
        onSelectedItemChanged: (index) {
          setState(() {
            dayIndex = index;
          });
        },
        itemExtent: 44,
      ),
    );
  }

  ///年Widget
  List<Widget> _buildYearWidget() {
    List<Widget> yearListWidget = []; //先建一个数组用于存放循环生成的widget
    Widget content; //单独一个widget组件,用于返回需要生成的内容widget
    for (var item in yearList) {
      yearListWidget.add(
        new Center(
          child: Text(
            item,
            style: TextStyle(color: Colors.black87, fontSize: 16),
            maxLines: 1,
          ),
        ),
      );
    }

    return yearListWidget;
  }

  ///月Widget
  List<Widget> _buildMonthWidget() {
    List<Widget> monthListWidget = []; //先建一个数组用于存放循环生成的widget
    Widget content; //单独一个widget组件,用于返回需要生成的内容widget
    for (var item in monthList) {
      monthListWidget.add(
        new Center(
          child: Text(
            item,
            style: TextStyle(color: Colors.black87, fontSize: 16),
            maxLines: 1,
          ),
        ),
      );
    }

    return monthListWidget;
  }

  ///日Widget
  List<Widget> _buildDayWidget() {
    List<Widget> dayListWidget = []; //先建一个数组用于存放循环生成的widget
    Widget content; //单独一个widget组件,用于返回需要生成的内容widget
    for (var item in dayList) {
      dayListWidget.add(
        new Center(
          child: Text(
            item,
            style: TextStyle(color: Colors.black87, fontSize: 16),
            maxLines: 1,
          ),
        ),
      );
    }

    return dayListWidget;
  }
}

使用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void _showBirthdayDialog(BuildContext context, Dispatch dispatch) {
  showModalBottomSheet<void>(
      context: context,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.only(
          topLeft: Radius.circular(20),
          topRight: Radius.circular(20),
        ),
      ),
      builder: (BuildContext context) {
        return DatePicker(
          endYear: DateTime.now().year,
          selectedDate: null,
          onSelectedDate: (String date) {
            Navigator.pop(context);
          },
        );
      });
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
第186天:js深入理解构造函数和原型对象
1.在典型的oop的语言中,如java,都存在类的概念,类就是对象的模板,对象就是类的实例。但在js中不存在类的概念,js不是基于类,而是通过构造函数(constructor)和原型链(propotype chains)实现的。但在ES6中引入了类(class)这个概念,作为对象的模板,新的class写法知识让原型对象的写法更加清晰,这里不重点谈这个
半指温柔乐
2018/09/11
7280
第186天:js深入理解构造函数和原型对象
JS高级——构造函数和原型
在典型的OOP的语言中(如Java),都存在类的概念,类就是对象的模板,对象就是类的实例,但在ES6之前,JS中并没有引入类的概念。
岳泽以
2022/10/26
1.6K0
JS高级——构造函数和原型
构造函数和原型
实例成员就是构造函数内部通过this添加的成员 如下列代码中uname age sing 就是实例成员,实例成员只能通过实例化的对象来访问
梨涡浅笑
2020/10/27
4080
构造函数和原型
详解js原型,构造函数以及class之间的原型关系
含义: 是一个函数的属性,这个属性是一个指针,指向一个对象 作用: 构造函数调用 访问该构造函数所关联的原型对象
念念不忘
2019/03/29
1.7K0
详解js原型,构造函数以及class之间的原型关系
再谈构造函数、原型、原型链之间的关系
构造函数、原型、原型链作为ES5的内容,已经是老生常谈的问题了。首先说说为什么要再次拿起这个话题去说呢?这几天有空我会看一些源码,这些源码的底层实现考虑到兼容性还是来源于ES5,很多方法的封装以及实现(不管是按照模块封装还是统一实现)都是面向对象的思想,而且webpack以及rollup打包之后解析出来的代码利用@babel/core和@babel/preset-env转化之后也都是ES5的代码,所以有想再次谈起这个话题,回顾回顾旧知识,温故而知新。
小丑同学
2020/09/21
7530
「JS高级」构造函数和原型
请注意,本文编写于 2063 天前,最后修改于 173 天前,其中某些信息可能已经过时。
曼亚灿
2023/05/17
1.6K0
「JS高级」构造函数和原型
深入理解原型对象和原型链
原型对象和原型链在前端的工作中虽然不怎么显式的使用到,但是也会隐式的使用了,比如使用的jquery,vue等啦。在进入正题的时候,我们还是需要明白什么是__proto__,prototype等知识点,主要讲解构造函数,这篇博文大多是问答形式进行...
Jimmy_is_jimmy
2019/07/31
6300
构造函数和原型
在典型的OOP的语言中(如Java) , 都存在类的概念,类就是对象的模板,对象就是类的实例,但在ES6之前, JS中并没用引入类的概念。 ES6,全称ECMAScript6.0 , 2015.06发版。但是目前浏览器的JavaScript是ES5版本,大多数高版本的浏 览器也支持ES6 ,不过只实现了ES6的部分特性和功能。 在ES6之前,对象不是基于类创建的,而是用一种称为构建函数的特殊函数来定义对象和它们的特征。
星辰_大海
2020/10/23
5440
构造函数和原型
JS面向对象编程
通过instanceof来判断当前的的对象是否是实例化出来的,如果是实例化出来的this指向实例化出来的对象,也就是这里的Person,否则作为普通函数来说当前的this指向window
小丞同学
2021/08/16
4.2K0
深入理解原型和继承
这几天在掘金上阅读到了一篇关于原型的文章,角度较之前看到的几篇博客都不一样,顿时感觉我对于原型的知识点还没有完全吃透。鉴于本篇文章很可能会进行不定期的修订和拓展,故在此附上更新日志,以简单记录我在学习上的认知更新。
Chor
2019/11/07
4840
JavaScript OOP(三):prototype原型对象(即构造函数的prototype属性)
通过构造函数生成的实例化对象,无法共享属性或方法(即每个实例化对象上都有构造函数中的属性和方法);造成了一定的资源浪费 1 function Obj(name,age){ 2 this.name=name; 3 this.age=age; 4 this.func=function(){ 5 return 'this is a test function'; 6 }; 7 } 8 var o1=new Obj('小明',10); 9 var o
用户1149564
2018/01/11
1.2K0
JavaScript OOP(三):prototype原型对象(即构造函数的prototype属性)
秒懂JS对象、构造器函数和原型对象之间的关系
学习JS的过程中,想要掌握面向对象的程序设计风格,对象模型(原型和继承)是其中的重点和难点,拜读了各类经典书籍和各位前辈的技术文章,感觉都太过高深,花费了不少时间才搞明白(个人智商是硬伤/(ㄒoㄒ)/~~),这里略作总结尽量通俗易懂。 一、基本概念   1、对象:属性和方法的集合,即变量和函数的封装。每个对象都有一个__proto__属性,指向这个对象的构造函数的原型对象。   2、构造器函数:用于创建对象的函数,通过new关键字生成对象。函数名一般首字母大写的。   3、原型对象:每个函数都有一个prot
小古哥
2018/03/08
2.1K0
秒懂JS对象、构造器函数和原型对象之间的关系
JS中对象+构造函数+原型对象谈恋爱了嘛。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script> function Person(myName,myAge) { this.name=myName; this.age=myAge; this.currentType="构造函数中的属性"; this.say=function() { console.log("构造函数里面的方法");
贵哥的编程之路
2020/10/28
1.3K0
JS中对象+构造函数+原型对象谈恋爱了嘛。
第202天:js---原型与原型链终极详解
JavaScript 中,万物皆对象!但对象也是有区别的。分为普通对象和函数对象,Object 、Function 是 JS 自带的函数对象。下面举例说明
半指温柔乐
2018/09/11
9850
第202天:js---原型与原型链终极详解
前端学习(38)~js学习(十五):原型对象
上方代码中,我们的sayName方法是写在构造函数 Person 内部的,然后在两个实例中进行了调用。造成的结果是,构造函数每执行一次,就会给每个实例创建一个新的 sayName 方法。也就是说,每个实例的sayName都是唯一的。因此,最后一行代码的打印结果为false。
Vincent-yuan
2020/03/18
5270
JavaScript学习总结(四)——this、原型链、javascript面向对象
根据题目要求,对给定的文章进行摘要总结。
张果
2018/01/04
1.5K0
JavaScript学习总结(四)——this、原型链、javascript面向对象
深入理解JavaScript面向对象的程序设计(一)——对象的创建
类似Java等面向对象语言中创建对象的语法,在 JavaScript中可以通过执行 new操作符后跟要创建的对象类型的名称来创建。JavaScript中通过如下方式可以创建一个Object对象:
CherishTheYouth
2020/11/12
4590
深入理解JavaScript面向对象的程序设计(一)——对象的创建
【JS 构造|原型|原型链|继承(圣杯模式)|ES6类语法】上篇
❗️ ❗️ ❗️本篇系将带来JavaScript中的构造——原型——原型链——继承——ES6类语法系列知识完整讲解。 ❗️ ❗️ ❗️ ❕上篇涉及:构造——原型——原型链 ❕下篇涉及:继承——ES6类语法
好吃懒洋洋
2022/11/15
7930
【JS 构造|原型|原型链|继承(圣杯模式)|ES6类语法】上篇
JS面向对象笔记二
注意点:当构造函数里面有return关键字时,如果返回的是非对象,new命令会忽略返回的信息,最后返回时构造之后的this对象;   如果return返回的是与this无关的新对象,则最后new命令会返回新对象,而不是this对象。示例代码:
tandaxia
2018/09/27
5.8K0
JS面向对象笔记二
【JS 构造|原型|原型链|继承(圣杯模式)|ES6类语法】下篇
❗️ ❗️ ❗️本篇系将带来JavaScript中的构造——原型——原型链——继承——ES6类语法系列知识完整讲解。 ❗️ ❗️ ❗️ ❕上篇涉及:构造——原型——原型链 ❕下篇涉及:继承——ES6类语法
好吃懒洋洋
2022/11/21
8080
【JS 构造|原型|原型链|继承(圣杯模式)|ES6类语法】下篇
相关推荐
第186天:js深入理解构造函数和原型对象
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验