乐趣区

关于flutter:Flutter-ModalBottomSheet-指导

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

退出移动版