老鐵記得 轉發 ,貓哥會呈現更多 Flutter 好文~~~~
微信 flutter 研修群 ducafecat
原文
程式碼
參考
正文
根據材質設計指南,底部表是一個小工具,用於顯示錨定在螢幕底部的附加內容。雖然瞭解使用這個的設計規則很好,但這不是本文的目標。要了解更多關於底板設計原則的詳細資訊,請參閱“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 的最終版本!
© 貓哥
往期
開源
GetX Quick Start
新聞客戶端
strapi 手冊譯文
微信討論群 ducafecat
系列集合
譯文
開源專案
Dart 程式語言基礎
space.bilibili.com/404904528/c…
Flutter 零基礎入門
space.bilibili.com/404904528/c…
Flutter 實戰從零開始 新聞客戶端
space.bilibili.com/404904528/c…
Flutter 元件開發
space.bilibili.com/404904528/c…
Flutter Bloc
space.bilibili.com/404904528/c…
Flutter Getx4
space.bilibili.com/404904528/c…