老铁记得 转发,猫哥会出现更多 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…