Flutter BottomSheet 拖动分两段展示

第一段

第二段

实现思路

通过 GestureDetector 的 Drag 方法,动态改变Dialog的高度,通过设置一个最大高度和最小高度分成两层进行展示

实现

常用的展示BottomSheet的方法为 showModalBottomSheet

复制代码
/// 设置最高最好以高度的比例进行设置,方便不同屏幕适配
final maxHeight = MediaQuery.of(context).size.height * maxHeightRatio;
showModalBottomSheet(
    context: context,
    builder: (ctx) => BottomSheetDialog(minHeight: minHeight, maxHeight: maxHeight),
    enableDrag: false,
    isScrollControlled: true,
    scrollControlDisabledMaxHeightRatio: maxHeightRatio,
);

因为上面我们隐藏了自带的 DragHeader ,这里自定义一个可拖动的Header

复制代码
GestureDetector(
  behavior: HitTestBehavior.opaque,
  /// 正在拖动
  onVerticalDragUpdate: (detail) {
    /// 得到当前的高度
    double dragOffset = _contentHeight - detail.delta.dy;
    if(dragOffset > maxHeight) {
      dragOffset = maxHeight;
    }
    if(dragOffset < 0) {
      dragOffset = 0;
    }
    setContentHeight(dragOffset);
  },
  /// 拖动结束
  onVerticalDragEnd: (detail) {
    print("onVerticalDragEnd");
    onDragEnd();
  },
  /// 取消拖动,当作拖动结束处理
  onVerticalDragCancel: () {
    onDragEnd();
  },
  child: Container(
    height: 55,
    alignment: Alignment.center,
    child: const Text("Drag"),
  ),
),

拖动结束处理

复制代码
void onDragEnd() {
   /// 以两段中间值为界限,回弹到指定的位置
  final mid = (maxHeight - minHeight) / 2 + minHeight;
  if(_contentHeight > mid) {
    setContentHeight(maxHeight);
  } else if(_contentHeight >= minHeight / 3 * 2) {
    setContentHeight(minHeight);
  } else {
    /// 当滑动到第一段下面位置时,就直接退出BottomSheet
    Navigator.pop(context);
  }
}

完整代码

复制代码
import 'package:ebon_smart_pay/app/core/widgets/bottom_sheet/bottom_sheet_dialog.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class BottomSheetPage extends StatelessWidget {
  const BottomSheetPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return AnnotatedRegion(
      value: const SystemUiOverlayStyle(
        statusBarColor: Colors.transparent
      ),
      child: Center(
        child: FilledButton(
          onPressed: () => BottomSheetDialog.show(context, MediaQuery.of(context).size.height * 0.5, 0.75),
          child: const Text("ShowBottomSheet"),
        ),
      ),
    );
  }
}


import 'package:flutter/material.dart';

class BottomSheetDialog extends StatefulWidget {

  /// 设置高度
  final double minHeight;
  final double maxHeight;

  const BottomSheetDialog({Key? key, required this.minHeight, required this.maxHeight}) : super(key: key);

  static void show(BuildContext context, double minHeight, double maxHeightRatio) {
    final maxHeight = MediaQuery.of(context).size.height * maxHeightRatio;
    showModalBottomSheet(
        context: context,
        builder: (ctx) => BottomSheetDialog(minHeight: minHeight, maxHeight: maxHeight),
        enableDrag: false,
        isScrollControlled: true,
        scrollControlDisabledMaxHeightRatio: maxHeightRatio,
    );
  }

  @override
  State<BottomSheetDialog> createState() => _BottomSheetDialogState();
}

class _BottomSheetDialogState extends State<BottomSheetDialog> {

  double _contentHeight = 0;

  void setContentHeight(double height) => setState(() {
    _contentHeight = height;
  });

  @override
  void initState() {
    setContentHeight(widget.minHeight);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: _contentHeight,
      decoration: const BoxDecoration(
        borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)),
        color: Colors.white
      ),
      child: SafeArea(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            GestureDetector(
              behavior: HitTestBehavior.opaque,
              onVerticalDragUpdate: (detail) {
                double dragOffset = _contentHeight - detail.delta.dy;
                if(dragOffset > maxHeight) {
                  dragOffset = maxHeight;
                }
                if(dragOffset < 0) {
                  dragOffset = 0;
                }
                setContentHeight(dragOffset);
              },
              onVerticalDragEnd: (detail) {
                print("onVerticalDragEnd");
                onDragEnd();
              },
              onVerticalDragCancel: () {
                onDragEnd();
              },
              child: Container(
                height: 55,
                alignment: Alignment.center,
                child: const Text("Drag"),
              ),
            ),
            const Divider(),
            Expanded(child: ListView.separated(
                itemBuilder: (ctx, index) => Padding(
                  padding: const EdgeInsets.all(10.0),
                  child: Text("Item - $index"),
                ),
                separatorBuilder: (ctx, index) => const Divider(),
                itemCount: 10))
          ],
        ),
      ),
    );
  }

  void onDragEnd() {
    final mid = (maxHeight - minHeight) / 2 + minHeight;
    if(_contentHeight > mid) {
      setContentHeight(maxHeight);
    } else if(_contentHeight >= minHeight / 3 * 2) {
      setContentHeight(minHeight);
    } else {
      Navigator.pop(context);
    }
  }

  double get minHeight => widget.minHeight;
  double get maxHeight => widget.maxHeight;

}
相关推荐
键盘鼓手苏苏26 分钟前
Flutter 三方库 p2plib 的鸿蒙化适配指南 - 实现高性能的端到端(P2P)加密通讯、支持分布式节点发现与去中心化数据流传输实战
flutter·harmonyos·鸿蒙·openharmony
加农炮手Jinx26 分钟前
Flutter for OpenHarmony:postgrest 直接访问 PostgreSQL 数据库的 RESTful 客户端(Supabase 核心驱动) 深度解析与鸿蒙适配指南
数据库·flutter·华为·postgresql·restful·harmonyos·鸿蒙
加农炮手Jinx26 分钟前
Flutter 组件 heart 适配鸿蒙 HarmonyOS 实战:分布式心跳监控,构建全场景保活检测与链路哨兵架构
flutter·harmonyos·鸿蒙·openharmony
钛态29 分钟前
Flutter 三方库 http_mock_adapter — 赋能鸿蒙应用开发的高效率网络接口 Mock 与自动化测试注入引擎(适配鸿蒙 HarmonyOS Next ohos)
android·网络协议·flutter·http·华为·中间件·harmonyos
王码码203529 分钟前
Flutter for OpenHarmony:Flutter 三方库 algoliasearch 毫秒级云端搜索体验(云原生搜索引擎)
android·前端·git·flutter·搜索引擎·云原生·harmonyos
王码码203530 分钟前
Flutter 三方库 dns_client 的鸿蒙化适配指南 - 告别 DNS 劫持、探索 DNS-over-HTTPS (DoH) 技术、构建安全的鸿蒙网络请求环境
flutter·harmonyos·鸿蒙·openharmony·dns_client
键盘鼓手苏苏30 分钟前
Flutter 组件 highlighter 适配鸿蒙 HarmonyOS 实战:高性能语法高亮,构建大规模代码分析与文本染色架构
flutter·harmonyos·鸿蒙·openharmony
国医中兴32 分钟前
Flutter 三方库 langchain_google 的鸿蒙化适配指南 - 链接 Gemini 智慧中枢、LangChain AI 实战、鸿蒙级智能应用专家
flutter·langchain·harmonyos
左手厨刀右手茼蒿34 分钟前
Flutter for OpenHarmony: Flutter 三方库 shamsi_date 助力鸿蒙应用精准适配波斯历法(中东出海必备)
android·flutter·ui·华为·自动化·harmonyos
雷帝木木34 分钟前
Flutter 三方库 http_client_interceptor 的鸿蒙化适配指南 - 实现原生 HttpClient 的全量请求拦截、支持端侧动态 Headers 注入与网络流量审计实战
flutter·harmonyos·鸿蒙·openharmony·http_client_interceptor