当前位置: 首页 > news >正文

Flutter---自定义日期选择对话框

效果图

功能概述

  • 页面背景是蓝白渐变色;

  • 页面上有一个按钮,点击后弹出一个自定义日期选择对话框

  • 弹窗里使用了 三个 CupertinoPicker 分别选择“年 / 月 / 日”;

  • 选择完后点击“确定”,日期会显示在主页上;

  • 点击“取消”会关闭弹窗,不做修改。

实现步骤

1.定义一个生日日期变量

var _birthday = "1900-01-01"; //1.设置变量并赋默认值

2.构建主题UI

return Scaffold(//2.构建UIbody: Container(height: double.infinity,//覆盖高width: double.infinity,//覆盖宽padding: const EdgeInsets.symmetric(vertical: 10,horizontal:10),//边内边距//主页背景颜色上下渐变decoration: const BoxDecoration(gradient: LinearGradient(begin: Alignment.topCenter,end: Alignment.centerRight,colors: [Colors.blue,Colors.white,],),),child: GestureDetector(onTap:changeBirthdayDialog ,//设置按钮的点击事件child: Column(children: [SizedBox(height: 100,),//按钮Container(//按钮大小height: 50,width: 200,//按钮的样式decoration: BoxDecoration(color: Colors.white,borderRadius: BorderRadius.circular(20),),child: Center(child: Text("选择生日的按钮"),)),SizedBox(height: 20,),//日期的显示Row(mainAxisAlignment: MainAxisAlignment.center,children: [Text("您的生日是 :"),Text(_birthday)],)],))),);

3.实现日期选择对话框:定义一些初始值

//1.设定初始值int selectedYear = 1900; //默认选中年份int selectedMonth = 1; //默认选中月份int selectedDay = 1; //默认选中日期//2.设置年的初始选中项为2000final controllerYear = FixedExtentScrollController(initialItem: 100);

4.构建年份选择(请重点理解CupertinoPicker的相关属性)

// 年份选择Expanded(child: Column(children: [Text('年', style: TextStyle(fontSize: 14, color: Colors.grey)), //顶部的标识SizedBox(height: 120,child: Stack(children: [CupertinoPicker(itemExtent: 40, //选中项的高度scrollController: controllerYear,//设置初始选中项的值onSelectedItemChanged: (int index) { //当选中项变化时触发,参数为索引selectedYear = 1900 + index; //存储用户选择的数据(将数组索引转换为实际的年份值)},// 移除选择覆盖层selectionOverlay: null,//DateTime.now().year获取当年年份//List.generate(126,(index)):生成指定长度的列表children: List.generate(DateTime.now().year - 1899, (index) {return Center(child: Text('${1900 + index}', //年滑动框显示的文本style: TextStyle(fontSize: 18),//文本的样式),);}),),// 上横线Positioned(top: 40, // 调整到合适位置left: 0,right: 0,child: Container(height: 1,color: Color(0xFFD8D8D8).withOpacity(0.6),),),// 下横线Positioned(bottom: 40, // 调整到合适位置left: 0,right: 0,child: Container(height: 1,color: Color(0xFFD8D8D8).withOpacity(0.6),),),],),),],),),

5.相同手法构建月和日

// 月份选择Expanded(child: Column(children: [Text('月', style: TextStyle(fontSize: 14, color: Colors.grey)),SizedBox(height: 120,child: Stack(children: [CupertinoPicker(itemExtent: 40,onSelectedItemChanged: (int index) {selectedMonth = index + 1;},// 移除选择覆盖层selectionOverlay: null,children: List.generate(12, (index) {return Center(child: Text('${index + 1}',//月滑动框显示的文本style: TextStyle(fontSize: 18),),);}),),Positioned(top: 40,left: 0,right: 0,child: Container(height: 1, color: Color(0xFFD8D8D8).withOpacity(0.6),),),Positioned(bottom: 40,left: 0,right: 0,child: Container(height: 1, color: Color(0xFFD8D8D8).withOpacity(0.6),),),],),),],),),// 日期选择Expanded(child: Column(children: [Text('日', style: TextStyle(fontSize: 14, color: Colors.grey)),SizedBox(height: 120,child: Stack(children: [CupertinoPicker(itemExtent: 40,onSelectedItemChanged: (int index) {selectedDay = index + 1;},// 移除选择覆盖层selectionOverlay: null,children: List.generate(31, (index) {return Center(child: Text('${index + 1}',//日滑动框显示的文本style: TextStyle(fontSize: 18),),);}),),Positioned(top: 40,left: 0,right: 0,child: Container(height: 1, color: Color(0xFFD8D8D8).withOpacity(0.6),),),Positioned(bottom: 40,left: 0,right: 0,child: Container(height: 1, color: Color(0xFFD8D8D8).withOpacity(0.6),),),],),),],),),],),

6.构建两个按钮,以及确定按钮背后的实现的逻辑

 // 确认按钮Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: [TextButton(style: TextButton.styleFrom(backgroundColor: Color(0xFFD8D8D8),foregroundColor: Color(0xFF3D3D3D),minimumSize: Size(100, 40),shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20),),),onPressed: () => Navigator.pop(context),child: const Text('取消',style: TextStyle(fontSize: 16),),),TextButton(style: TextButton.styleFrom(backgroundColor: Color(0xFF1F8FFF),foregroundColor: Colors.white,minimumSize: Size(100, 40),shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20),),),onPressed: () {///将年、月、日组合成标准格式的日期字符串String formattedDate = "$selectedYear-${selectedMonth.toString().padLeft(2, '0')}-${selectedDay.toString().padLeft(2, '0')}";setState(() {_birthday = formattedDate;});Navigator.pop(context);},child: const Text('确定',style: TextStyle(fontSize: 16),),),],),

7.确认按钮的实现逻辑

// 假设当前选中的是:selectedYear = 2024, selectedMonth = 5, selectedDay = 8// 1. 年份部分
"$selectedYear" → "2024"// 2. 月份部分(UI确保要两位数)
selectedMonth.toString() → "5"(字符串)
.padLeft(2, '0') → "05"(左侧补零到2位)// 3. 日期部分(UI确保要两位数)  
selectedDay.toString() → "8"(字符串)
.padLeft(2, '0') → "08"(左侧补零到2位)// 4. 最终组合
"2024-05-08"//5.把这个值赋值给变量_brithday,变量发生改变,UI重建

完整数据流程图

用户点击按钮↓
打开生日选择对话框↓  
用户滑动选择器 → 更新局部变量 (selectedYear/Month/Day)↓
用户点击"确定"按钮↓
格式化日期字符串 "YYYY-MM-DD"↓  
setState() 更新 _birthday 状态↓
页面自动重新构建 (rebuild)↓  
UI 显示新的生日

如果你不喜欢年月日在最上面,想要年月日在选中区,效果图类似这样

可以把最上面实现的代码行注释掉。在两条横线中间加上年月日,以下是年的实现代码,月和日也是相同的实现方法。

// 上横线Positioned(top: 40, // 调整到合适位置left: 0,right: 0,child: Container(height: 1,color: Color(0xFFD8D8D8).withOpacity(0.6),),),Positioned(top: 48, // 调整到合适位置left: 70,// 调整到合适位置right: 0,child: Container(child: Text("年",style: TextStyle(color: Color(0xFF3D3D3D),fontSize: 20),),//color: Color(0xFFD8D8D8).withOpacity(0.6),),),// 下横线Positioned(bottom: 40, // 调整到合适位置left: 0,right: 0,child: Container(height: 1,color: Color(0xFFD8D8D8).withOpacity(0.6),),),

代码实例

home_page.dart


import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:my_flutter/person_information_page.dart';class HomePage extends StatefulWidget{const HomePage({super.key});@overrideState<StatefulWidget> createState() => _HomePageState();}class _HomePageState extends State<HomePage> {var _birthday = "1900-01-01"; //1.设置变量并赋默认值@overrideWidget build(BuildContext context) {return Scaffold(//2.构建UIbody: Container(height: double.infinity,//覆盖高width: double.infinity,//覆盖宽padding: const EdgeInsets.symmetric(vertical: 10,horizontal:10),//边内边距//主页背景颜色上下渐变decoration: const BoxDecoration(gradient: LinearGradient(begin: Alignment.topCenter,end: Alignment.centerRight,colors: [Colors.blue,Colors.white,],),),child: GestureDetector(onTap:changeBirthdayDialog ,//设置按钮的点击事件child: Column(children: [SizedBox(height: 100,),//按钮Container(//按钮大小height: 50,width: 200,//按钮的样式decoration: BoxDecoration(color: Colors.white,borderRadius: BorderRadius.circular(20),),child: Center(child: Text("选择生日的按钮"),)),SizedBox(height: 20,),//日期的显示Row(mainAxisAlignment: MainAxisAlignment.center,children: [Text("您的生日是 :"),Text(_birthday)],)],))),);}//3.构建选择生日的弹窗void changeBirthdayDialog() {//1.设定初始值int selectedYear = 1900; //默认选中年份int selectedMonth = 1; //默认选中月份int selectedDay = 1; //默认选中日期//2.设置年的初始选中项为2000final controllerYear = FixedExtentScrollController(initialItem: 100);showDialog(context: context,builder: (context) {return Dialog(backgroundColor: Colors.white,shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(26),),child: Container(width: 320,padding: const EdgeInsets.all(16),child: Column(mainAxisSize: MainAxisSize.min,children: [const SizedBox(height: 16),// 自定义年月日选择器Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,children: [// 年份选择Expanded(child: Column(children: [Text('年', style: TextStyle(fontSize: 14, color: Colors.grey)), //顶部的标识SizedBox(height: 120,child: Stack(children: [CupertinoPicker(itemExtent: 40, //选中项的高度scrollController: controllerYear,//设置初始选中项的值onSelectedItemChanged: (int index) { //当选中项变化时触发,参数为索引selectedYear = 1900 + index; //存储用户选择的数据(将数组索引转换为实际的年份值)},// 移除选择覆盖层selectionOverlay: null,//DateTime.now().year获取当年年份//List.generate(126,(index)):生成指定长度的列表children: List.generate(DateTime.now().year - 1899, (index) {return Center(child: Text('${1900 + index}', //年滑动框显示的文本style: TextStyle(fontSize: 18),//文本的样式),);}),),// 上横线Positioned(top: 40, // 调整到合适位置left: 0,right: 0,child: Container(height: 1,color: Color(0xFFD8D8D8).withOpacity(0.6),),),Positioned(top: 48, // 调整到合适位置left: 70,// 调整到合适位置right: 0,child: Container(child: Text("年",style: TextStyle(color: Color(0xFF3D3D3D),fontSize: 20),),//color: Color(0xFFD8D8D8).withOpacity(0.6),),),// 下横线Positioned(bottom: 40, // 调整到合适位置left: 0,right: 0,child: Container(height: 1,color: Color(0xFFD8D8D8).withOpacity(0.6),),),],),),],),),// 月份选择Expanded(child: Column(children: [Text('月', style: TextStyle(fontSize: 14, color: Colors.grey)),SizedBox(height: 120,child: Stack(children: [CupertinoPicker(itemExtent: 40,onSelectedItemChanged: (int index) {selectedMonth = index + 1;},// 移除选择覆盖层selectionOverlay: null,children: List.generate(12, (index) {return Center(child: Text('${index + 1}',//月滑动框显示的文本style: TextStyle(fontSize: 18),),);}),),Positioned(top: 40,left: 0,right: 0,child: Container(height: 1, color: Color(0xFFD8D8D8).withOpacity(0.6),),),Positioned(top: 48, // 调整到合适位置left: 60,right: 0,child: Container(child: Text("月",style: TextStyle(color: Color(0xFF3D3D3D),fontSize: 20),),//color: Color(0xFFD8D8D8).withOpacity(0.6),),),Positioned(bottom: 40,left: 0,right: 0,child: Container(height: 1, color: Color(0xFFD8D8D8).withOpacity(0.6),),),],),),],),),// 日期选择Expanded(child: Column(children: [Text('日', style: TextStyle(fontSize: 14, color: Colors.grey)),SizedBox(height: 120,child: Stack(children: [CupertinoPicker(itemExtent: 40,onSelectedItemChanged: (int index) {selectedDay = index + 1;},// 移除选择覆盖层selectionOverlay: null,children: List.generate(31, (index) {return Center(child: Text('${index + 1}',//日滑动框显示的文本style: TextStyle(fontSize: 18),),);}),),Positioned(top: 40,left: 0,right: 0,child: Container(height: 1, color: Color(0xFFD8D8D8).withOpacity(0.6),),),Positioned(top: 48, // 调整到合适位置left: 60,right: 0,child: Container(child: Text("日",style: TextStyle(color: Color(0xFF3D3D3D),fontSize: 20),),//color: Color(0xFFD8D8D8).withOpacity(0.6),),),Positioned(bottom: 40,left: 0,right: 0,child: Container(height: 1, color: Color(0xFFD8D8D8).withOpacity(0.6),),),],),),],),),],),const SizedBox(height: 16),// 确认按钮Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: [TextButton(style: TextButton.styleFrom(backgroundColor: Color(0xFFD8D8D8),foregroundColor: Color(0xFF3D3D3D),minimumSize: Size(100, 40),shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20),),),onPressed: () => Navigator.pop(context),child: const Text('取消',style: TextStyle(fontSize: 16),),),TextButton(style: TextButton.styleFrom(backgroundColor: Color(0xFF1F8FFF),foregroundColor: Colors.white,minimumSize: Size(100, 40),shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20),),),onPressed: () {//将年、月、日组合成标准格式的日期字符串String formattedDate = "$selectedYear-${selectedMonth.toString().padLeft(2, '0')}-${selectedDay.toString().padLeft(2, '0')}";setState(() {_birthday = formattedDate;});Navigator.pop(context);},child: const Text('确定',style: TextStyle(fontSize: 16),),),],),],),),);},);}}

http://www.dtcms.com/a/524809.html

相关文章:

  • 怎么代码放到网站上网站建设需要的公司
  • k8s部署容器化应用-tomcat
  • AI开发工具实战解析:如何实现企业数据处理流程自动化
  • asp装饰公司网站源码黑龙江 俄语网站制作
  • 网站设计公司无锡网站初期建设的成本来源
  • 通过API接口创建1688订单,一键采购指南
  • UniGetUI下载安装图文教程(附安装包,适合新手)
  • 网站开发怎么自动获取位置wordpress免费博客主题
  • thinkphp做的网站源码怎么做网页啊
  • 备份一体机:数据同步困局突围指南:毫秒级实时同步如何根治80%业务痛点
  • nexus上传jar包图文步骤
  • 【2025 最新】ArcGIS for JS TileLayer/FeatureLayer/ImageLayer 用法对比
  • AntV X6实战:实现节点四边自动连接与自定义箭头的完整配置
  • 黄骅市网站建设广州市官方网站
  • 华为OD机考双机位A卷 - 最长的密码 / 寻找密码 (C++ Python JAVA JS GO)
  • ELK日志分析系统完整部署与应用指南
  • 浦江做网站茂名网站建设公司
  • 【CVE-2025-40778】通过未经请求的答复记录进行 BIND 9 缓存中毒(内含复现步骤)
  • 架构权衡与实践:基于“约束大于规范”的缓存组件封装
  • 【实战经验】飞牛云 如何使用 SSD 缓存加速?
  • 数据结构--顺序表与链表
  • 网站排名优化课程深圳网站建设开发哪家好
  • 使用 WebSocket 实现手机控制端与电脑展示端的实时通信,支持断线重连、状态同步和双向数据交互。(最优方案)
  • 快递鸟 MCP Server:AI 工具解锁 物流 API 能力,开启智能物流新生态
  • UV Python 包和项目管理工具
  • 使用 Quill 实现编辑器功能
  • 企业网站建设的可行性图片编辑软件加文字
  • 零基础网站建设视频教程做淘宝美工的网站
  • 微米级光斑分析仪市场报告:政策、趋势与前景深度解析
  • 达梦 DM Database 集群:从概念到开发场景