Flutter bottomSheet 高度自适应及溢出处理

8,020 阅读2分钟

最近在创建 bottomSheet的时候遇到一个问题:弹窗的高度无法根据其内容自适应

先放上显示弹窗的代码,如下:

Future<T?> showSheet<T>(
    BuildContext context,
    Widget body, {
      bool scrollControlled = false,
      Color bodyColor = Colors.white,
      EdgeInsets? bodyPadding,
      BorderRadius? borderRadius,
    }) {
  const radius = Radius.circular(16);
  borderRadius ??= const BorderRadius.only(topLeft: radius, topRight: radius);
  bodyPadding ??= const EdgeInsets.all(20);
  return showModalBottomSheet(
      context: context,
      elevation: 0,
      backgroundColor: bodyColor,
      shape: RoundedRectangleBorder(borderRadius: borderRadius),
      barrierColor: Colors.black.withOpacity(0.25),
      // A处
      constraints: BoxConstraints(
          maxHeight: MediaQuery.of(context).size.height - MediaQuery.of(context).viewPadding.top),
      isScrollControlled: scrollControlled,
      builder: (ctx) => Padding(
        padding: EdgeInsets.only(
          left: bodyPadding!.left,
          top: bodyPadding.top,
          right: bodyPadding.right,
          // B处
          bottom: bodyPadding.bottom + MediaQuery.of(ctx).viewPadding.bottom,
        ),
        child: body,
      ));
}

其中,A处、B处的作用就是,让弹窗的内容始终显示在安全区域内

高度自适应问题

首先,我们在弹窗中显示点内容:

showSheet(context, Column(
    crossAxisAlignment: CrossAxisAlignment.stretch,
    children: const [
        Text('这是第一行'),
    ],
));

效果如下图所示:

此时,我们只需要将显示内容的代码改为如下:

showSheet(context, Column(
  mainAxisSize: MainAxisSize.min,  // 这一行是关键所在
  crossAxisAlignment: CrossAxisAlignment.stretch,
  children: const [
    Text('这是第一行'),
  ],
));

现在的效果图如下:

现在我们可以看到,弹窗的高度已经根据内容自适应了。

内容溢出问题

前面的解决方式,仅在内容高度小于默认高度时有效。当内容过多,高度大于默认高度时,就会出现溢出警告,如下图所示:

此时,我们该怎么办呢?

答案是:运用 showModalBottomSheet 的 isScrollControlled 参数,将其设置为true即可,代码如下:

showSheet(context, Column(
  mainAxisSize: MainAxisSize.min,
  crossAxisAlignment: CrossAxisAlignment.stretch,
  children: const [
    Text('这是第一行'),
    Text('这是很长长..(此处省略若干字)..的一段话,')
  ],
), scrollControlled: true); // 这一行用于告诉系统,弹窗的内容完全由我们自己管理

此时,效果图如下:

showSheet 补充说明

对前面showSheet代码中,A处、B处的进一步说明:

A处:如果不对内容的高度进行限制,则内容会显示在状态栏之后,而引起用户交互问题。如下图所示:

B处:如果不加 MediaQuery.of(ctx).viewPadding.bottom 这一句,则内容有可能会显示在底部横条的下方,此时也不利于交互

最终版本图样

内容较少(高度跟随内容自适应):

内容很多(顶部、底部均显示在安全区域内):