如何在Flutter中创建带有圆形角的模态底部表单?

130

showModalBottomSheet并没有提供任何样式或装饰。我想创建类似于Google任务底部弹出表的东西。

Google任务底部弹出表


使用showModalBottomSheet形状属性,该属性接受ShapeBorder。 - Code with Benji
17个回答

312

2019-08-05更新

现在您可以使用默认的showModalBottomSheet方法来完成它,它支持添加ShapeBorderbackgroundColor

showModalBottomSheet(
  shape: RoundedRectangleBorder(
     borderRadius: BorderRadius.circular(10.0),
  ),
  backgroundColor: Colors.white,
  ...
);

--

与其他答案建议的覆盖整个应用程序主题(导致我的应用程序在各个部分遇到问题)不同,我决定查看showModalBottomSheet的实现并自己找出问题。结果发现,只需要在模态框的主要代码中包装一个包含canvasColor: Colors.transparent技巧的Theme小部件即可。我还使它更容易自定义模态框本身的半径和颜色。

您可以使用pub上的软件包或包含相同代码的gist。不要忘记导入正确的软件包/文件。

showRoundedModalBottomSheet(
    context: context,
    radius: 20.0,  // This is the default
    color: Colors.white,  // Also default
    builder: (context) => ???,
);

1
对我来说,仅包装包含显示模态栏方法的按钮是不够的。但是将包含屏幕的Scaffold与这样的Theme一起包装可以解决问题。@override Widget build(BuildContext context) { return Theme( // 我们需要这个主题覆盖,以便底部表单模态的角可以被圆角化 data: ThemeData(canvasColor: Colors.transparent), child: Scaffold( .... - Simon Olsen
这种方法比已批准的方法更好。 - Chidi
完美的解决方案,谢谢! - Aziz
非常感谢您的回答。它帮了我很多。 - 4xMafole
1
对我来说,我还需要添加 clipBehaviour: Clip.hardEdge 以使边角变圆。 - Fah

134
showModalBottomSheet(
context:context
shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(
          top: Radius.circular(20),
        ),
      ),
      clipBehavior: Clip.antiAliasWithSaveLayer,
)

3
如果您解释一下提供的代码是如何回答问题的话,那么这将是一个更好的答案。 - pppery
更好的答案。谢谢。 - abrsh
2
这应该被接受为答案,因为它不需要全局设置画布颜色。它更加简洁。 - laszlo
5
Clip.antiAliasWithSaveLayer 会使得如果模态对话框中的内容可滚动,那么可滚动内容也会被裁剪成与模态对话框的边界半径相同的形状。 - テッド
clipBehavior: Clip.antiAlias 应该被优先选择,因为 Clip.antiAliasWithSaveLayer 很慢。 - linxie
只有这一个对我起作用 - Windbox

119
这是所需的modalBottomSheet函数。
    void _modalBottomSheetMenu(){
        showModalBottomSheet(
            context: context,
            builder: (builder){
              return new Container(
                height: 350.0,
                color: Colors.transparent, //could change this to Color(0xFF737373), 
                           //so you don't have to change MaterialApp canvasColor
                child: new Container(
                    decoration: new BoxDecoration(
                        color: Colors.white,
                        borderRadius: new BorderRadius.only(
                            topLeft: const Radius.circular(10.0),
                            topRight: const Radius.circular(10.0))),
                    child: new Center(
                      child: new Text("This is a modal sheet"),
                    )),
              );
            }
        );
      }

此功能正常工作的最重要部分是,在MaterialApp中将canvasColor设置为透明,如下所示。

return new MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Tasks',
      theme: new ThemeData(
        primarySwatch: Colors.teal,
        canvasColor: Colors.transparent,
      ),
      home: new TasksHomePage(),
    );
  }

我已经测试了代码,它可以正常运行。同时,我也在克隆谷歌任务应用程序,该应用程序将会在我的GitHub上开源。


13
改变全局 canvasColor 来实现底部弹窗圆角并不是一个好的取舍。应该使用 Theme 包装器来代替。如果底部弹窗中的控件依赖于主题,则可以使用 data: yourTheme().copyWith(canvasColor: Colors.transparent),只将画布颜色应用于其子元素。最好的选择是使用更新后的方法。 - sh0umik
谢谢@sh0umik,那是一个很好的提示! - Delicia Fernandes
1
这样做将改变整个应用程序的画布颜色,这意味着例如“选择/复制/粘贴”本地工具提示将具有透明的背景颜色,而不是默认的白色。我认为这应该是正确的答案:https://dev59.com/plUL5IYBdhLWcg3wbne4#62457068 - Jose Jet
1
主题包装器与canvasColor不起作用。因此,在MaterialApp主题中,我使用了bottomSheetTheme: BottomSheetThemeData(backgroundColor: Colors.transparent) - temirbek
1
我认为在这种情况下,使用bottomSheetTheme作为@temirbek的答案是最好的方式。 - apieceofcode1801

43

我认为实现圆角模态框的最佳方法是使用一个具有垂直BorderRadiusRoundedRectangleBorder,仅设置其top属性:

showModalBottomSheet(
        context: context,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.vertical(top: Radius.circular(25.0)),
        ),
        builder: (BuildContext context) {
          // return your layout
        });

使用不同的半径来处理左上角和右上角,会变得更加冗长和容易出错。


23

我有这段代码,它对我来说很好用。请检查它并告诉我你的看法。

showBottomSheet(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.vertical(
              top: Radius.circular(20),
            ),
          ),
          context: context,
          builder: (context) => Container(
                height: 250,

                child: new Container(
                    decoration: new BoxDecoration(
                        color: Theme.of(context).primaryColor,
                        borderRadius: new BorderRadius.only(
                            topLeft: const Radius.circular(20.0),
                            topRight: const Radius.circular(20.0))),
                    child: new Center(
                      child: new Text("This is a modal sheet"),
                    )),
              ))

19

现在你可以简单地设置shape参数。例如:

showModalBottomSheet(
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.all(Radius.circular(10.0)),
  ),
  context: context,
  builder: (context) => MyBottomSheet(),
);

12

将形状设置为RoundedRectangleBorder应该可以解决问题,但有时内容会溢出,使得圆角缺失,此时将clipBehavior设置为Clip.antiAlias即可解决。

showModalBottomSheet(
    isScrollControlled: true,
    backgroundColor: colorOnPrimary,
    // set this when inner content overflows, making RoundedRectangleBorder not working as expected
    clipBehavior: Clip.antiAlias,
    // set shape to make top corners rounded
    shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.only(
            topLeft: Radius.circular(16), 
            topRight: Radius.circular(16),
        ),
    ),
    context: context,
    builder: (context) {
        return SingleChildScrollView(
            child: Container(),
        );
    },
)

12

一种方法是使用showModalBottomSheetshape属性。

showModalBottomSheet(
  context:context
  shape: const RoundedRectangleBorder(
    borderRadius: BorderRadius.only(
      topLeft: Radius.circular(10.0),
      topRight: Radius.circular(10.0),
    ),
  ),
)

7

综合之前的所有答案,我认为已经达到了最佳结果(在我的看法中)。

showModalBottomSheet(
        context: context,
        backgroundColor: Colors.white,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.only(topLeft: Radius.circular(15.0), topRight: Radius.circular(15.0)),
        ),
        builder: (context) {
          return Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[     
              ListTile(
                leading: Icon(Icons.email),
                title: Text('Send email'),
                onTap: () {
                  print('Send email');
                },
              ),
              ListTile(
                leading: Icon(Icons.phone),
                title: Text('Call phone'),
                 onTap: () {
                    print('Call phone');
                  },
               ),                  
            ],
          );
        });

这个不起作用。请确认后回复。 - Abdul Qadir

6

您可以在 showModalBottomSheet 中添加一个 RoundedRectangleBorder 组件:

showModalBottomSheet<void>(
    shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.only(
            topLeft: Radius.circular(10.0),
            topRight: Radius.circular(10.0)
        ),
    ),
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接