美文网首页
flutter 写一个BottomSheet

flutter 写一个BottomSheet

作者: 卢融霜 | 来源:发表于2021-11-26 10:18 被阅读0次

    效果图

    底部弹出.gif

    网页版(第一次打开网址加载会很慢)
    网页版

    布局结构

      StatefulWidget
            //控制动画
            AnimatedBuilder
                    //控制位置
                    Positioned         
                            //手势监听
                            GestureDetector
                                    //内部布局
                                    Container
    

    各个widget用到的方法

    AnimatedBuilder

      //控制器
    _controller = AnimationController(
          vsync: this,
          duration: const Duration(milliseconds: 600),
    );
    
    //动画状态
    _controller.status     返回: AnimationStatus(dismissed:关闭,forward:向前,reverse:向后,completed:完成)
    
    //velocity  传递正数 执行到 upperBound 值  负数 执行到 lowerBound 值
    _controller.fling(velocity: isOpen ? -1 : 1);
    
    //动画执行的数值 默认 0.0 - 1.0
    _controller.value
    

    GestureDetector

     //监听垂直方向的滑动
    onVerticalDragUpdate
     //监听垂直方向,停止滑动时的移动速度
    onVerticalDragEnd
    

    其他

    // 根据 _controller.value 的0 - 1的值,返回 minHeight - maxHeight 相对于的值,完成高度设置
    lerpDouble(num? a, num? b, double t)
    

    核心逻辑代码

      /// 滑动核心代码, 根据 _controller.value 的0 - 1的值,返回 minHeight - maxHeight 相对于的值,完成高度设置
      double lerp(double minHeight, double maxHeight) {
        var height = lerpDouble(minHeight, maxHeight, _controller.value);
        return height;
      }
    
      ///开关切换
      void _toggle() {
        //判断打开状态 进行切换
        bool isOpen = _controller.status == AnimationStatus.completed;
        //velocity  传递正数 执行到 upperBound 值  负数 执行到 lowerBound 值
        _controller.fling(velocity: isOpen ? -1 : 1);
      }
    
      ///滑动关键代码,监听纵向滑动阀值,设置响应高度
      void _dragUpdate(DragUpdateDetails details) {
        _controller.value -= details.primaryDelta / widget.maxHeight;
      }
    
    
      /// 开关核心代码: 根据滑动速度,滑动高度,来判断 开关
      void _handleDragEnd(DragEndDetails details) {
    
        //排除 动画过程中,和完成状态。
        if (_controller.isAnimating ||
            _controller.status == AnimationStatus.completed) return;
    
        //获取滑动速度
        final double flingVelocity =
            details.velocity.pixelsPerSecond.dy / widget.maxHeight;
    
        //设置滑动执行方向
        if (flingVelocity < 0.0) {
          _controller.fling(velocity: max(1.0, -flingVelocity));
        } else if (flingVelocity > 0.0) {
          _controller.fling(velocity: min(-1.0, -flingVelocity));
        } else {
          _controller.fling(velocity: _controller.value < 0.5 ? -1.0 : 1.0);
        }
      }
    

    整体代码

    使用

    import 'package:flutter/material.dart';
    import 'package:flutter_screenutil/flutter_screenutil.dart';
    
    import 'base/base_routes_widget.dart';
    import 'bottom_sheet.dart';
    
    // @description 作用:
    // @date: 2021/11/25
    // @author: 卢融霜
    
    class BottomSheetRoutes extends StatefulWidget {
      const BottomSheetRoutes({Key key}) : super(key: key);
    
      @override
      _BottomSheetRoutesState createState() => _BottomSheetRoutesState();
    }
    
    class _BottomSheetRoutesState extends State<BottomSheetRoutes> {
      @override
      Widget build(BuildContext context) {
        return BaseRoutesWidget(
          title: "底部弹出",
          child: SizedBox(
            width: double.infinity,
            child: Stack(
              children: [
                Column(
                  children: const [Text("底部弹出")],
                ),
                MyBottomSheet(100.r, MediaQuery.of(context).size.height - 90.r)
              ],
            ),
          ),
        );
      }
    }
    

    组件

    import 'dart:math';
    import 'dart:ui';
    import 'package:flutter/material.dart';
    import 'package:flutter_screenutil/flutter_screenutil.dart';
    
    // @description 作用:底部弹出
    // @date: 2021/11/25
    // @author: 卢融霜
    
    class MyBottomSheet extends StatefulWidget {
      double minHeight;
      double maxHeight;
    
      MyBottomSheet(this.minHeight, this.maxHeight, {Key key}) : super(key: key);
    
      @override
      _BottomSheetState createState() => _BottomSheetState();
    }
    
    class _BottomSheetState extends State<MyBottomSheet>
        with SingleTickerProviderStateMixin {
      AnimationController _controller;
    
      @override
      void initState() {
        super.initState();
        _controller = AnimationController(
          vsync: this,
          duration: const Duration(milliseconds: 600),
        );
      }
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return AnimatedBuilder(
            animation: _controller,
            builder: (context, child) {
              return Stack(
                children: [
                  Positioned(
                      height: lerp(widget.minHeight, widget.maxHeight),
                      left: 0,
                      right: 0,
                      bottom: 0,
                      child: GestureDetector(
                        onVerticalDragUpdate: _dragUpdate,
                        onVerticalDragEnd: _handleDragEnd,
                        child: Container(
                            decoration: const BoxDecoration(
                              color: Colors.blueAccent,
                              borderRadius:
                                  BorderRadius.vertical(top: Radius.circular(32)),
                            ),
                            padding: const EdgeInsets.symmetric(horizontal: 32),
                            child: Container(
                              alignment: Alignment.topRight,
                              padding: EdgeInsets.only(top: 20.r),
                              child: InkWell(
                                onTap: _toggle,
                                child: Text(
                                  "切换",
                                  style: TextStyle(
                                      fontSize: 14.sp, color: Colors.white),
                                ),
                              ),
                            )),
                      ))
                ],
              );
            });
      }
    
      /// 滑动核心代码, 根据 _controller.value 的0 - 1的值,返回 minHeight - maxHeight 相对于的值,完成高度设置
      double lerp(double minHeight, double maxHeight) {
        var height = lerpDouble(minHeight, maxHeight, _controller.value);
        return height;
      }
    
      ///开关切换
      void _toggle() {
        //判断打开状态 进行切换
        bool isOpen = _controller.status == AnimationStatus.completed;
        //velocity  传递正数 执行到 upperBound 值  负数 执行到 lowerBound 值
        _controller.fling(velocity: isOpen ? -1 : 1);
      }
    
      ///滑动关键代码,监听纵向滑动阀值,设置响应高度
      void _dragUpdate(DragUpdateDetails details) {
        _controller.value -= details.primaryDelta / widget.maxHeight;
      }
    
      /// 开关核心代码: 根据滑动速度,滑动高度,来判断 开关
      void _handleDragEnd(DragEndDetails details) {
    
        //排除 动画过程中,和完成状态。
        if (_controller.isAnimating ||
            _controller.status == AnimationStatus.completed) return;
    
        //获取滑动速度
        final double flingVelocity =
            details.velocity.pixelsPerSecond.dy / widget.maxHeight;
    
    
        //设置滑动执行方向
        if (flingVelocity < 0.0) {
          _controller.fling(velocity: max(1.0, -flingVelocity));
        } else if (flingVelocity > 0.0) {
          _controller.fling(velocity: min(-1.0, -flingVelocity));
        } else {
          _controller.fling(velocity: _controller.value < 0.5 ? -1.0 : 1.0);
        }
      }
    }
    
    

    相关文章

      网友评论

          本文标题:flutter 写一个BottomSheet

          本文链接:https://www.haomeiwen.com/subject/sdptxrtx.html