老铁记得 转发 ,猫哥会出现更多 Flutter 好文~~~~
微信 flutter 研修群 ducafecat
原文
https://evandrmb.medium.com/f...
代码
https://github.com/evandrmb/b...
参考
- https://material.io/component...
注释
依据材质设计指南,底部表是一个小工具,用于显示锚定在屏幕底部的附加内容。尽管理解应用这个的设计规定很好,但这不是本文的指标。要理解更多对于底板设计准则的详细信息,请参阅“Sheets: bottom — Material Design”。
当初你晓得了 BottomSheet,你可能会问本人: 什么是 ModalBottomSheet?咱们如何应用他们在 Flutter?
好的,第一个问题,有两种底层表: 模态的和长久的。当用户与屏幕交互时,长久化放弃可见。谷歌地图利用就是一个例子。
另一方面,模式化的操作会阻止用户在应用程序中做其余动作。您能够应用它们来确认某些操作,或者申请额定的数据,比方询问用户在电子商务应用程序中订购时须要多少替换,等等。
在本文中,咱们将通过创立一个简略的体重跟踪应用程序来展现如何应用它,在这个应用程序中咱们能够提交咱们的体重并查看咱们之前的体重。咱们不会输出应用程序的详细信息,而是间接进入 ModalBottomSheet 实现。
要显示它,您须要从具备 Scaffold 的上下文调用 showModalBottomSheet,否则,您将失去一个谬误。也就是说,让咱们开始构建咱们的表格。
首先要晓得的是 ModalBottomSheets 的高度默认为屏幕的一半,为了扭转它,必须传递 true 给 isScrollControlled 参数,并返回一个与咱们冀望的大小相匹配的小部件,所以让咱们这样做。
void addWeight() { showModalBottomSheet( isScrollControlled: true, context: context, builder: (context) { var date = DateTime.now(); return Container( height: 302, padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ], ), ); }, ); }
当初,咱们须要增加一些货色,以便咱们的用户能够输出他们的权重让咱们增加一个 TextInput 并给它一个 TextEditingController (这种形式即便咱们的工作表意外敞开时,用户再次关上它,它的值依然存在)。
void addWeight() { showModalBottomSheet( isScrollControlled: true, context: context, builder: (context) { var date = DateTime.now(); return Container( height: 302, padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ padding: EdgeInsets.only(bottom: 24.0), child: Text( 'Register Weight', style: Styles.titleStyle, ), ), TextField( controller: weightInputController, keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Weight (KG)', border: OutlineInputBorder( borderRadius: Styles.borderRadius, ), ), ), ], ), ); }, ); }
看起来不错,但当初咱们有麻烦了。当用户点击咱们的 TextField 键盘在它下面,为什么?当键盘关上时,咱们的工作表不会调整地位,咱们能够把工作表做得更大,但这不能解决咱们的问题,因为咱们依然须要增加一个字段,用户能够在其中输出他们记录分量的日期。那么解决方案是什么呢?这很简略,如果关上键盘,咱们让咱们的工作表在它下面,咱们能够实现这一点,给咱们的容器一个边距的边缘。在 viewinset.bottom 中,咱们将失去以下后果:
它开始看起来很漂亮,然而你不认为如果咱们在纸上加一些半径会更平滑吗?让咱们通过增加如下所示的 shapeproperty 来实现。
showModalBottomSheet( isScrollControlled: true, context: context, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.only( topLeft: Radius.circular(8), topRight: Radius.circular(8), )),
酷,当初让咱们做咱们的小工具来抉择一个日期。通常,您会创立一个小部件来解决这个逻辑,并应用 ValueChanged 函数公开选定的值,然而为了阐明您未来可能面临的问题,让咱们在工作表自身外部创立所有逻辑。
void addWeight() { showModalBottomSheet( isScrollControlled: true, context: context, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.only( topLeft: Radius.circular(8), topRight: Radius.circular(8), )), builder: (context) { return Container( height: 360, width: MediaQuery.of(context).size.width, margin: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( padding: EdgeInsets.only(bottom: 24.0), child: Text( 'Register Weight', style: Styles.titleStyle, ), ), TextField( controller: weightInputController, keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Weight (KG)', border: OutlineInputBorder( borderRadius: Styles.borderRadius, ), ), ), Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Row( children: [ Expanded( child: Text( 'Select a date', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 4), margin: const EdgeInsets.symmetric(vertical: 8.0), height: 36, decoration: BoxDecoration( borderRadius: Styles.borderRadius, ), child: OutlinedButton( onPressed: () async { final now = DateTime.now(); final result = await showDatePicker( context: context, initialDate: now, firstDate: now.subtract( const Duration( days: 90, ), ), lastDate: now); if (result != null) { setState(() { selectedDate = result; }); } }, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( padding: const EdgeInsets.only(right: 16.0), child: Text('${formatDateToString(selectedDate)}'), ), Icon(Icons.calendar_today_outlined), ], ), ), ), ], ), ) ], ), ); }, ); }
须要留神的是,我曾经在咱们的主页中增加了 selectedDatevariable,你能够在我最初提供的存储库链接中看到这一点。然而当初咱们遇到了一个问题,只管咱们正在应用 setstateoutlinebutton 更新 selectedDate 的值,然而在从新关上工作表之前,依然会显示旧的值,如下所示。
为了解决这个问题,咱们须要将 OutlinedButton 传递给 StatefulBuilder (或者您能够创立一个新的小部件并应用回调公开更改,正如我后面所说的,顺便说一下,这是更正确的办法)。
void addWeight() { showModalBottomSheet( isScrollControlled: true, context: context, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.only( topLeft: Radius.circular(8), topRight: Radius.circular(8), )), builder: (context) { return Container( height: 360, width: MediaQuery.of(context).size.width, margin: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( padding: EdgeInsets.only(bottom: 24.0), child: Text( 'Register Weight', style: Styles.titleStyle, ), ), TextField( controller: weightInputController, keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Weight (KG)', border: OutlineInputBorder( borderRadius: Styles.borderRadius, ), ), ), Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Row( children: [ Expanded( child: Text( 'Select a date', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 4), margin: const EdgeInsets.symmetric(vertical: 8.0), height: 36, decoration: BoxDecoration( borderRadius: Styles.borderRadius, ), child: StatefulBuilder( builder: (context, setState) { return OutlinedButton( onPressed: () async { final now = DateTime.now(); final result = await showDatePicker( context: context, initialDate: now, firstDate: now.subtract( const Duration( days: 90, ), ), lastDate: now); if (result != null) { setState(() { selectedDate = result; }); } }, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( padding: const EdgeInsets.only(right: 16.0), child: Text( '${formatDateToString(selectedDate)}'), ), Icon(Icons.calendar_today_outlined), ], ), ); }, )), ], ), ), Expanded(child: Container()), ButtonBar( children: [ ElevatedButton( onPressed: () => Navigator.pop(context), child: Text('Cancel', style: TextStyle( color: Theme.of(context).primaryColor, )), style: ElevatedButton.styleFrom( primary: Colors.white, // minimumSize: Size(96, 48), ), ), ElevatedButton( onPressed: () { setState(() { weights.insert( 0, WeightModel( value: double.parse(weightInputController.text), date: selectedDate, )); }); Navigator.pop(context); }, child: const Text('Register')), ], ), ], ), ); }, ); }
这是咱们的 ModalBottomSheet 的最终版本!
https://github.com/evandrmb/b...
© 猫哥
https://ducafecat.tech/
https://github.com/ducafecat
往期
开源
GetX Quick Start
https://github.com/ducafecat/...
新闻客户端
https://github.com/ducafecat/...
strapi 手册译文
https://getstrapi.cn
微信探讨群 ducafecat
系列汇合
译文
https://ducafecat.tech/catego...
开源我的项目
https://ducafecat.tech/catego...
Dart 编程语言根底
https://space.bilibili.com/40...
Flutter 零根底入门
https://space.bilibili.com/40...
Flutter 实战从零开始 新闻客户端
https://space.bilibili.com/40...
Flutter 组件开发
https://space.bilibili.com/40...
Flutter Bloc
https://space.bilibili.com/40...
Flutter Getx4
https://space.bilibili.com/40...
Docker Yapi
https://space.bilibili.com/40...