Flutter --底部抽屉页面的实现思路

252 阅读1分钟

IMG_ADD78D3D9831-1.jpeg `

class ParentSheetPage extends StatefulWidget {
  const ParentSheetPage({
    super.key,
    required this.buildBottomSheet,
    required this.buildTopSheet,
  });

  /// 位于下方的[Widget]
  final Widget Function(BuildContext context) buildBottomSheet;

  /// 位于上方的[Widget]
  final Widget Function(BuildContext context) buildTopSheet;

  @override
  State<StatefulWidget> createState() => _ParentSheetState();
}

class _ParentSheetState extends State<ParentSheetPage> with TickerProviderStateMixin {

  /// 默认高度
  double bottomBarToTop = 300;
  
  /// 上层widget的两段高度级别
  double bottomBarHigh = 300;
  double bottomBarLow = 600;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Positioned.fill(child: widget.buildBottomSheet(context)),
        Positioned(left: 0, right: 0, bottom: 0, top: bottomBarToTop, child: body(context)),
      ],
    );
  }

  Widget body(BuildContext context) {
    return Container(
      decoration: const BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.only(topRight: Radius.circular(10), topLeft: Radius.circular(10)),
      ),
       child: Column(
          children: [_dragBar(), Expanded(child: widget.buildTopSheet(context))],
        )
    );
  }

  Widget _dragBar() {
    return GestureDetector(
      onVerticalDragUpdate: (DragUpdateDetails details) {
        if (details.globalPosition.dy > bottomBarLow || details.globalPosition.dy < bottomBarHigh) return;
        setState(() {
          bottomBarToTop = details.globalPosition.dy;
        });
      },
      onVerticalDragCancel: () {
        calibrationBottomHeight();
      },
      onVerticalDragEnd: (DragEndDetails details) {
        calibrationBottomHeight();
      },
      child: Container(
        padding: EdgeInsets.only(top: 14.w, bottom: 13.w),
        color: Colors.transparent,
        alignment: AlignmentDirectional.center,
        child: Container(
          width: 36.w,
          height: 5.w,
          decoration: BoxDecoration(
            color: AppColors.grey_color300.withOpacity(0.3),
            borderRadius: AppRadii.k4pxRadius,
          ),
        ),
      ),
    );
  }

  void calibrationBottomHeight() async {
    final mediumLimit = (bottomBarHigh + bottomBarLow) / 2;
    double endValue = bottomBarToTop > mediumLimit ? bottomBarLow : bottomBarHigh;
    //根据本次偏移的行程调整动画时长
    double stroke = (bottomBarToTop - endValue).abs();
    double proportion = stroke / (bottomBarLow - bottomBarHigh);

    AnimationController controller = AnimationController(
      duration: Duration(milliseconds:  (200 * proportion).round()),
      vsync: this,
    );
    Animation<double> valueAnimation = Tween(
      begin: bottomBarToTop,
      end: endValue,
    ).animate(
      CurvedAnimation(
        parent: controller,
        curve: Curves.easeIn,
      ),
    );
    valueAnimation.addListener(() {
      setState(() {
        bottomBarToTop = valueAnimation.value;
      });
    });
    await controller.forward();
    controller.dispose();
  }
}

`