Flutter ShowModalBottomSheet 自定义高度和滚动

Posted 一叶飘舟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter ShowModalBottomSheet 自定义高度和滚动相关的知识,希望对你有一定的参考价值。

这是一个官方的组件 但是两个滑动在一起会有冲突简单的实现了一下滑动到头自动收起这个组件

封装成一个通用组件

class ComModalBottomSheet 
  /// 展示ModalBottomSheet
  /// [context] 上下文
  /// [child] 里面的子组件
  /// [isDismissible] 外部是否可以点击 默认是true
  /// [enableDrag] 是否可以拖动
  /// [title] 标题
  /// [isLeftTitle]是否居中
  /// [useRootNavigator ] 是否使用跟路由
  /// [maxHeight] 最小0最大1 取之间数字 小于0.5按0.5算 大于1 按1算
  static void show(
      required context,
      required child,
      isDismissible = true,
      enableDrag = true,
      useRootNavigator = false,
      title,
      isScrollOver = false,
      isLeftTitle = false,
      showRightTopClose = false,
      showBottomClose = true,
      maxHeight = 0.5) 
    showModalBottomSheet(
      context: context,
      //背景颜色(下半部弹出的颜色)
      backgroundColor: Theme.of(context).scaffoldBackgroundColor,
      //上半部分的mask颜色
      barrierColor: Colors.black54,
// 如果设定高度小于0.5 是flase默认最大半屏 ,大于0.5按设定高度来
      isScrollControlled: maxHeight > 0.5,
      //外部是否可以点击
      isDismissible: isDismissible ?? true,
      //是否可以拖动
      enableDrag: enableDrag ?? true,
      //是否用根路由
      useRootNavigator: useRootNavigator ?? false,
      //顶部的圆角矩形
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.only(
            topLeft: Radius.circular(ScreenHelper.width(15)),
            topRight: Radius.circular(ScreenHelper.width(15))),
      ),

      builder: (context) 
//返回一个SingleChildScrollView 设定他永不滚动 实现自适应高度
        return SingleChildScrollView(
          physics: const NeverScrollableScrollPhysics(),
          child: Padding(
///根据设计图设置他的padding
            padding: EdgeInsets.all(ScreenHelper.width(18)),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                //如果有标题或者有右上角的 X就展示这个组件 
                if(title!=null||showRightTopClose)
                  _buildHead(title, isLeftTitle, showRightTopClose, context),
///如果最大宽度小于0.5自适应 否则强制设置最大高度
                if (maxHeight <= 0.5)
                  child
                else
                  SizedBox(
                    height: ScreenHelper.screenHeight *
                        (maxHeight >= 0.87 ? 0.87 : maxHeight),
                    child: child,
                  ),
///是否展示下方的取消按钮
                if (showBottomClose)
                  SizedBox(
                    width: double.infinity,
                    child: comMaterialButton(context,
                        onPressed: () => Navigator.pop(context),
                        buttonName: "取消"),
                  )
              ],
            ),
          ),
        );
      ,
    );
  

  static Stack _buildHead(
      title, isLeftTitle, showRightTopClose, BuildContext context) 
    return Stack(
      children: [
        if(title!=null)
        Container(
          width: double.infinity,
          padding: EdgeInsets.symmetric(vertical: ScreenHelper.width(8)),
          margin: EdgeInsets.only(bottom: ScreenHelper.width(18)),
          child: Text(
            title ?? "",
            textAlign: isLeftTitle ? TextAlign.start : TextAlign.center,
          ),
        ),
        if (showRightTopClose)
          Positioned(
              right: 0,
              child: InkWell(
                onTap: () => Navigator.pop(context),
                child: Padding(
                  padding: EdgeInsets.all(ScreenHelper.width(8)),
                  child: comIcon(context, iconPoint: 0xe7e4),
                ),
              ))
      ],
    );
  

下方按钮的封装

Padding comMaterialButton(BuildContext context,
    required buttonName, onPressed, padding) 
  return Padding(
    padding: EdgeInsets.symmetric(
        horizontal: ScreenHelper.width(18)),
    child: MaterialButton(
      color: Theme.of(context).textTheme.button?.color,
      height: ScreenHelper.width(44),
      shape: ContinuousRectangleBorder(
          borderRadius: BorderRadius.circular(ScreenHelper.width(15))),
      onPressed: () 
        if (onPressed != null) 
          onPressed();
        
      ,
      child: Text(
        buttonName ?? "",
        style: TextStyle(color: Theme.of(context).textTheme.subtitle2?.color),
      ),
    ),
  );

showModalBottomSheet里面的ScrollView的封装

class BottomSheetScrollView extends StatefulWidget 
//传进来的列表
  final List<dynamic> list;
//传进来的Widget
  final Widget Function(dynamic) child;

  const BottomSheetScrollView(
      Key? key, required this.list, required this.child)
      : super(key: key);
  @override
  State<BottomSheetScrollView> createState() => _BottomSheetScrollViewState();


class _BottomSheetScrollViewState extends State<BottomSheetScrollView> 
  ///最后一次点击的位置
  double lastPoint = 0.0;

  ///当前条目的距离
  double offset = 0.0;

  ///是否滚动到头了
  bool isOverScroll = true;
///滚动控制器
  ScrollController controller = ScrollController();


  @override
  void initState() 
    // TODO: implement initState
    super.initState();
///添加监听 滚动的距离保存
    controller.addListener(() 
      offset = controller.offset;
    );
  

  @override
  Widget build(BuildContext context) 
    return Listener(
///添加对界面手指移动的监听
      onPointerMove: (event) 
       if (event.position.dx - lastPoint > 0) 
         如果当前的位置比按下的位置大 那么是向上滑的 那么永远滚不到最顶上
          setState(() => isOverScroll = false);
         else 
//如果当前滚动到最顶上的时候就设置isOverScroll 是true
        setState(() => isOverScroll = offset == 0.0);
        
      ,
      onPointerDown: (event) 
///按下时记录此时的位置
        setState(() 
           lastPoint = event.position.dx;
         );
      ,
      child: Padding(
        padding: EdgeInsets.symmetric(vertical: ScreenHelper.width(18)),
        child: ListView.builder(
          //控制器
            controller: controller,
            ///如果到头了就禁止它滑动,如果没到头就继续滑动
            physics: isOverScroll
                ? const NeverScrollableScrollPhysics()
                : const ClampingScrollPhysics(),
//长度是传进来的列表的长度
           itemCount: widget.list.length,
构造也是传进来的
                  itemBuilder: (context, index) =>
                      widget.child(model.list[index])),
      ),
    );
  

使用

TextButton(
                                onPressed: () 
                                  model.addModelBottomSheetListener();
                                  ComModalBottomSheet.show(
                                      useRootNavigator: false,
                                      maxHeight: 0.7,
                                      isDismissible: false,
                                      context: context,
                                      child: BottomSheetScrollView(
                                        list: ["1", "2", "3", "4","1", "2", "3", "4","1", "2", "3", "4","1", "2", "3", "4","1", "2", "3", "4""1", "2", "3", "4""1", "2", "3", "4""1", "2", "3", "4"],
                                        child: (item) => ListTile(title: Text(item),),
                                      ));
                                ,
                                child: Text("弹出测试"),
                              ),

以上是关于Flutter ShowModalBottomSheet 自定义高度和滚动的主要内容,如果未能解决你的问题,请参考以下文章

[Flutter] flutter项目一直卡在 Running Gradle task 'assembleDebug'...

flutter 日志输出,Flutter打印日志,flutter log,flutter 真机日志

Flutter开发 Flutter 包和插件 ( Flutter 包和插件简介 | 创建 Flutter 插件 | 创建 Dart 包 )

flutter与原生混编(iOS)

Flutter-布局

如何解决flutter gradle build error?C:\flutter\packages\flutter_tools\gradle\flutter.gradle' line: 991