Old iron remember to forward, Brother Mao will present more Flutter good articles~~~~
WeChat flutter training group ducafecat
original
https://evandrmb.medium.com/flutter-modalbottomsheet-for-beginners-e5f3af271076
Code
https://github.com/evandrmb/bottom_sheet
reference
text
According to the material design guidelines, the bottom table is a small tool that displays additional content anchored at the bottom of the screen. Although it is good to understand the design rules for using this, it is not the goal of this article. To learn more about the design principles of the bottom plate, please refer to " Sheets: bottom — Material Design ".
Now that you know BottomSheet, you might ask yourself: What is ModalBottomSheet? How do we use them in Flutter?
Okay, for the first question, there are two types of underlying tables: modal and persistent. When the user interacts with the screen, the persistence remains visible. The Google Maps application is an example.
On the other hand, the patterned operation will prevent the user from doing other actions in the application. You can use them to confirm certain actions, or to request additional data, such as asking users how much exchange they need when ordering in an e-commerce application, and so on.
In this article, we will show how to use it by creating a simple weight tracking application where we can submit our weight and view our previous weight. We will not enter the details of the application, but directly enter the ModalBottomSheet implementation.
To display it, you need to call showModalBottomSheet from a context with Scaffold, otherwise, you will get an error. That said, let's start building our table.
The first thing to know is that the height of ModalBottomSheets defaults to half of the screen. In order to change it, you must pass true to the isScrollControlled parameter and return a widget that matches the size we expect, so let's do this.
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: [
],
),
);
},
);
}
Now, we need to add something so that our users can enter their weights. Let us add a TextInput and give it a TextEditingController (this way even if our worksheet is closed unexpectedly and the user opens it again, its value still exists ).
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,
),
),
),
],
),
);
},
);
}
It looks good, but now we are in trouble. When the user clicks our TextField keyboard on it, why? When the keyboard is turned on, our worksheet will not adjust the position, we can make the worksheet bigger, but this does not solve our problem, because we still need to add a field in which the user can enter the date they recorded the weight . So what is the solution? It's very simple, if we turn on the keyboard and we let our worksheet on top of it, we can achieve this by giving our container a marginal edge. In viewinset.bottom, we will get the following results:
It looks pretty at first, but don't you think it will be smoother if we add some radius to the paper? Let's do it by adding shapeproperty as shown below.
showModalBottomSheet(
isScrollControlled: true,
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8),
topRight: Radius.circular(8),
)),
Cool, now let's make our gadget to choose a date. Normally, you would create a widget to handle this logic and use the ValueChanged function to expose the selected value, but to illustrate the problems you may face in the future, let's create all the logic inside the worksheet itself.
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),
],
),
),
),
],
),
)
],
),
);
},
);
}
Note that I have added the selectedDatevariable to our homepage, you can see this in the repository link I provided at the end. But now we have a problem. Although we are using setstateoutlinebutton to update the value of selectedDate, the old value will still be displayed before reopening the worksheet, as shown below.
To solve this problem, we need to pass the OutlinedButton to StatefulBuilder (or you can create a new widget and use callbacks to make changes public, as I said earlier, by the way, this is the more correct way).
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')),
],
),
],
),
);
},
);
}
This is the final version of our ModalBottomSheet!
https://github.com/evandrmb/bottom_sheet
© Cat brother
Past
Open source
GetX Quick Start
https://github.com/ducafecat/getx_quick_start
News client
https://github.com/ducafecat/flutter_learn_news
strapi manual translation
WeChat discussion group ducafecat
Series collection
Translation
https://ducafecat.tech/categories/%E8%AF%91%E6%96%87/
Open source project
https://ducafecat.tech/categories/%E5%BC%80%E6%BA%90/
Dart programming language basics
https://space.bilibili.com/404904528/channel/detail?cid=111585
Getting started with Flutter zero foundation
https://space.bilibili.com/404904528/channel/detail?cid=123470
Flutter actual combat from scratch news client
https://space.bilibili.com/404904528/channel/detail?cid=106755
Flutter component development
https://space.bilibili.com/404904528/channel/detail?cid=144262
Flutter Bloc
https://space.bilibili.com/404904528/channel/detail?cid=177519
Flutter Getx4
https://space.bilibili.com/404904528/channel/detail?cid=177514
Docker Yapi
https://space.bilibili.com/404904528/channel/detail?cid=130578
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。