当前位置: 首页 > news >正文

寄生虫网站怎么做投资管理公司注册条件和要求

寄生虫网站怎么做,投资管理公司注册条件和要求,做网站下载那个数据库好,电商培训类网站模板下载03_Flutter自定义下拉菜单 在Flutter的内置api中,可以使用showMenu实现类似下拉菜单的效果,或者使用PopupMenuButton组件,PopupMenuButton内部也是使用了showMenu这个api,但是使用showMenu时,下拉面板的显示已经被约定…

03_Flutter自定义下拉菜单

在Flutter的内置api中,可以使用showMenu实现类似下拉菜单的效果,或者使用PopupMenuButton组件,PopupMenuButton内部也是使用了showMenu这个api,但是使用showMenu时,下拉面板的显示已经被约定死了,只能放一个简单的列表,没有办法定制下来面板的ui,并且下拉面板的宽高需要通过指定constraints进行限制,下面是一个简单的showMenu的用法:

Container(height: 44,margin: EdgeInsetsDirectional.only(top: 30, start: 30, end: 30),color: Colors.red,child: Builder(builder: (context) {return GestureDetector(onTap: () {final RenderBox button = context.findRenderObject()! as RenderBox;final RenderBox overlay = Navigator.of(context).overlay!.context.findRenderObject()! as RenderBox;Offset offset = Offset(0.0, button.size.height);RelativeRect position = RelativeRect.fromRect(Rect.fromPoints(button.localToGlobal(offset, ancestor: overlay),button.localToGlobal(button.size.bottomRight(Offset.zero) + offset, ancestor: overlay),),Offset.zero & overlay.size,);showMenu(context: context,position: position,constraints: BoxConstraints(maxWidth: 315, maxHeight: 200),items: List.generate(5, (index) => PopupMenuItem(child: Container(width: 375,height: 44,alignment: AlignmentDirectional.center,child: Text("item"),))));},);},),
)

在这里插入图片描述

接下来,我们将参照showMenu的源码,依葫芦画个瓢,自定义一个下拉菜单的api,并可自由定制下拉面板的布局内容,篇幅有点长,请耐心观看。

一.确定下拉面板的起始位置

查看PopupMenuButton的源码,可以知道,PopupMenuButton在确定下拉面板的起始位置时,是先获取下拉面板依赖的按钮的边界位置和整个页面的显示区域边界,通过这两个边界计算得到一个RelativeRect,这个RelativeRect就是用来描述下拉面板的起始位置的。

showPopup(BuildContext context) {final RenderBox button = context.findRenderObject()! as RenderBox;final RenderBox overlay = Navigator.of(context).overlay!.context.findRenderObject()! as RenderBox;Offset offset = Offset(0.0, button.size.height);RelativeRect position = RelativeRect.fromRect(Rect.fromPoints(button.localToGlobal(offset, ancestor: overlay),button.localToGlobal(button.size.bottomRight(Offset.zero) + offset, ancestor: overlay),),Offset.zero & overlay.size,);
}

注:上述代码中用的的context对象,必须是下拉面板依赖的按钮对应的context,否则最后计算出来的RelativeRect是不对的。计算过程不做过多解释了,直接上图:

在这里插入图片描述

二.确定下拉面板的布局约束

  • 水平方向确定最大宽度,比较简单,下拉面板的最大宽度和它所依赖的按钮的宽度一致即可
  • 垂直方向上的最大高度,上一步已经确定了position的值,垂直方向上的最大高度可以取position.top - buttonHeight - padding.top - kToolbarHeight和constraints.biggest.height - position.top - padding.bottom的最大值,padding为安全区域的大小
  • 使用CustomSingleChildLayout作为下拉面板的父容器,并实现一个SingleChildLayoutDelegate,重写getConstraintsForChild,确定约束
EdgeInsets padding = MediaQuery.paddingOf(context);class _CustomPopupRouteLayout extends SingleChildLayoutDelegate {final RelativeRect position;_CustomPopupRouteLayout(this.position);BoxConstraints getConstraintsForChild(BoxConstraints constraints) {Size buttonSize = position.toSize(constraints.biggest);double constraintsWidth = buttonSize.width;double constraintsHeight = max(position.top - buttonSize.height - padding.top - kToolbarHeight, constraints.biggest.height - position.top - padding.bottom);return BoxConstraints.loose(Size(constraintsWidth, constraintsHeight));}bool shouldRelayout(covariant _CustomPopupRouteLayout oldDelegate) {return position != oldDelegate.position;}
}

三.显示下拉面板

我们先把下拉面板显示出来看看效果,这里的下拉面板其实是一个弹出层,而在Flutter中,所有的弹出层的显示和页面路由是一样的,都是通过Navigator.push进行显示,参照showMenu的源码,这里的弹出层我们让其继承PopupRoute

class _CustomPopupRoute<T> extends PopupRoute<T> {final RelativeRect position;final String? barrierLabel;_CustomPopupRoute({required this.position,required this.barrierLabel,});Color? get barrierColor => null;bool get barrierDismissible => true;Duration get transitionDuration => Duration(milliseconds: 200);Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {return CustomSingleChildLayout(delegate: _CustomPopupRouteLayout(position),child: Material(child: Container(color: Colors.yellow,width: double.infinity,height: double.infinity,alignment: AlignmentDirectional.center,child: Text("popup content"),),),);}}
showPopup(BuildContext context) {final RenderBox button = context.findRenderObject()! as RenderBox;final RenderBox overlay = Navigator.of(context).overlay!.context.findRenderObject()! as RenderBox;Offset offset = Offset(0.0, button.size.height);RelativeRect position = RelativeRect.fromRect(Rect.fromPoints(button.localToGlobal(offset, ancestor: overlay),button.localToGlobal(button.size.bottomRight(Offset.zero) + offset, ancestor: overlay),),Offset.zero & overlay.size,);Navigator.of(context).push(_CustomPopupRoute(position: position, barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel));
}

在这里插入图片描述

如图,黄色区域就是下拉面板,可以看到,点击按钮下拉面板显示,点击下拉面板以外的区域,下拉面板关闭,但是位置好像不对,因为我们根本就没去确定下拉面板的位置。

四.确定下拉面板的位置


Offset getPositionForChild(Size size, Size childSize) {return super.getPositionForChild(size, childSize);
}

只需要重写SingleChildLayoutDelegate的getPositionForChild方法,返回一个Offset对象,Offset的x、y的值就代表下拉面板左上角的位置,那么问题来了,x、y的值怎么确定?

  • 确定x

    x = position.left

  • 确定y

    • position.top + constraintsHeight > size.height - paddingBottom

    在这里插入图片描述

    • position.top + constraintsHeight <= size.height - paddingBottom

    在这里插入图片描述

EdgeInsets padding = MediaQuery.paddingOf(context);class _CustomPopupRouteLayout extends SingleChildLayoutDelegate {final RelativeRect position;EdgeInsets padding;_CustomPopupRouteLayout(this.position, this.padding);BoxConstraints getConstraintsForChild(BoxConstraints constraints) {Size buttonSize = position.toSize(constraints.biggest);double constraintsWidth = buttonSize.width;double constraintsHeight = max(position.top - buttonSize.height - padding.top - kToolbarHeight, constraints.biggest.height - position.top - padding.bottom);return BoxConstraints.loose(Size(constraintsWidth, constraintsHeight));}Offset getPositionForChild(Size size, Size childSize) {double x = position.left;double y = position.top;final double buttonHeight = size.height - position.top - position.bottom;double constraintsHeight = max(position.top - buttonHeight - padding.top - kToolbarHeight, size.height - position.top - padding.bottom);if(position.top + constraintsHeight > size.height - padding.bottom) {y = position.top - childSize.height - buttonHeight;}return Offset(x, y);}bool shouldRelayout(covariant _CustomPopupRouteLayout oldDelegate) {return position != oldDelegate.position || padding != oldDelegate.padding;}
}

六.下拉动画实现

创建动画插值器,其值从0 ~ 1之间变化,动画时长为PopupRoute中重写的transitionDuration,及200ms时间内,从0变到1,或者从1变到0

final CurveTween heightFactorTween = CurveTween(curve: const Interval(0.0, 1.0));

使用AnimatedBuilder改造PopupRoute的布局结构,根据heightFactorTween的动画执行值 * 下拉菜单内容容器的高度,改变拉菜单内容的高度即可,这里暂时将高度设置为固定值300。

class _CustomPopupRoute<T> extends PopupRoute<T> {...Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {EdgeInsets padding = MediaQuery.paddingOf(context);final CurveTween heightFactorTween = CurveTween(curve: const Interval(0.0, 1.0));return MediaQuery.removePadding(context: context,removeTop: true,removeBottom: true,removeLeft: true,removeRight: true,child: CustomSingleChildLayout(delegate: _CustomPopupRouteLayout(position, padding),child: AnimatedBuilder(animation: animation,builder: (context, child) {return Material(child: Container(height: 300*heightFactorTween.evaluate(animation),child: child,));},child: Container(color: Colors.yellow,width: double.infinity,height: 300,alignment: AlignmentDirectional.center,child: Text("popup content"),),),),);}
}

下拉动画效果已经出来了,但是实际情况下,下拉面板的高度是不能直接在组件层固定写死的,所以这里需要动态计算出下拉面板的高度。

七.下拉面板动态高度,支持下拉动画

想要获取组件的高度,需要等到组件的layout完成后,才能获取到组件的大小,因此,我们需要自定义一个RenderObject,重写其performLayout,在子控件第一次layout完后,获取到子控件的初始高度,子控件的初始化高度结合动画的高度比例系数来最终确定自身的大小。

class _RenderHeightFactorBox extends RenderShiftedBox {double _heightFactor;_RenderHeightFactorBox({RenderBox? child,double? heightFactor,}):_heightFactor = heightFactor ?? 1.0, super(child);double get heightFactor => _heightFactor;set heightFactor(double value) {if (_heightFactor == value) {return;}_heightFactor = value;markNeedsLayout();}void performLayout() {final BoxConstraints constraints = this.constraints;if (child == null) {size = constraints.constrain(Size.zero);return;}child!.layout(constraints, parentUsesSize: true);size = constraints.constrain(Size(child!.size.width,child!.size.height,));child!.layout(constraints.copyWith(maxWidth: size.width, maxHeight: size.height * heightFactor), parentUsesSize: true);size = constraints.constrain(Size(child!.size.width,child!.size.height,));}
}

接着定义一个SingleChildRenderObjectWidget,并引用_RenderHeightFactorBox

class _HeightFactorBox extends SingleChildRenderObjectWidget {final double? heightFactor;const _HeightFactorBox({super.key,this.heightFactor,super.child,});RenderObject createRenderObject(BuildContext context) => _RenderHeightFactorBox(heightFactor: heightFactor);void updateRenderObject(BuildContext context, _RenderHeightFactorBox renderObject) {renderObject.heightFactor = heightFactor ?? 1.0;}
}

最后把下拉面板中,执行动画的child使用_HeightFactorBox包裹,并传入heightFactorTween的执行结果即可。


Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {EdgeInsets padding = MediaQuery.paddingOf(context);final CurveTween heightFactorTween = CurveTween(curve: const Interval(0.0, 1.0));return MediaQuery.removePadding(context: context,removeTop: true,removeBottom: true,removeLeft: true,removeRight: true,child: CustomSingleChildLayout(delegate: _CustomPopupRouteLayout(position, padding),child: AnimatedBuilder(animation: animation,builder: (context, child) {return Material(child: _HeightFactorBox(heightFactor: heightFactorTween.evaluate(animation),child: child,));},child: Container(color: Colors.yellow,width: double.infinity,height: double.infinity,alignment: AlignmentDirectional.center,child: Text("popup content"),),),),);
}

八.完整代码

class TestPage extends StatelessWidget {Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("下拉菜单"),backgroundColor: Colors.blue,),body: Container(width: 375,child: Column(mainAxisSize: MainAxisSize.max,mainAxisAlignment: MainAxisAlignment.spaceBetween,crossAxisAlignment: CrossAxisAlignment.center,children: [Container(height: 44,margin: const EdgeInsetsDirectional.only(top: 30, start: 30, end: 30),color: Colors.red,child: Builder(builder: (context) {return GestureDetector(onTap: () {showPopup(context: context, builder: (context) {return Container(height: 400,decoration: const BoxDecoration(color: Colors.yellow),child: SingleChildScrollView(physics: const ClampingScrollPhysics(),child: Column(mainAxisSize: MainAxisSize.max,mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.stretch,children: List<Widget>.generate(29, (index) {int itemIndex = index ~/ 2;if(index.isEven) {return Container(height: 44,alignment: AlignmentDirectional.center,child: Text("item$itemIndex"),);} else {return Container(height: 1,color: Colors.grey,);}}),),),);});},);},),),],),),);}}showPopup({required BuildContext context,required WidgetBuilder builder,double? elevation,Color? shadowColor,Duration animationDuration = const Duration(milliseconds: 200)
}) {final RenderBox button = context.findRenderObject()! as RenderBox;final RenderBox overlay = Navigator.of(context).overlay!.context.findRenderObject()! as RenderBox;Offset offset = Offset(0.0, button.size.height);RelativeRect position = RelativeRect.fromRect(Rect.fromPoints(button.localToGlobal(offset, ancestor: overlay),button.localToGlobal(button.size.bottomRight(Offset.zero) + offset, ancestor: overlay),),Offset.zero & overlay.size,);Navigator.of(context).push(_CustomPopupRoute(position: position,builder: builder,elevation: elevation,shadowColor: shadowColor,animationDuration: animationDuration,barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel));
}class _CustomPopupRoute<T> extends PopupRoute<T> {final WidgetBuilder builder;final RelativeRect position;final double? elevation;final Color? shadowColor;final String? barrierLabel;final Duration animationDuration;_CustomPopupRoute({required this.builder,required this.position,required this.barrierLabel,this.elevation,this.shadowColor,Duration? animationDuration}): animationDuration = animationDuration ?? const Duration(milliseconds: 200),super(traversalEdgeBehavior: TraversalEdgeBehavior.closedLoop);Color? get barrierColor => null;bool get barrierDismissible => true;Duration get transitionDuration => animationDuration;Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {EdgeInsets padding = MediaQuery.paddingOf(context);final CurveTween heightFactorTween = CurveTween(curve: const Interval(0.0, 1.0));return MediaQuery.removePadding(context: context,removeTop: true,removeBottom: true,removeLeft: true,removeRight: true,child: CustomSingleChildLayout(delegate: _CustomPopupRouteLayout(position, padding),child: AnimatedBuilder(animation: animation,builder: (context, child) {return Material(child: _HeightFactorBox(heightFactor: heightFactorTween.evaluate(animation),child: child,));},child: builder(context),),),);}}class _CustomPopupRouteLayout extends SingleChildLayoutDelegate {final RelativeRect position;EdgeInsets padding;double childHeightMax = 0;_CustomPopupRouteLayout(this.position, this.padding);BoxConstraints getConstraintsForChild(BoxConstraints constraints) {Size buttonSize = position.toSize(constraints.biggest);double constraintsWidth = buttonSize.width;double constraintsHeight = max(position.top - buttonSize.height - padding.top - kToolbarHeight, constraints.biggest.height - position.top - padding.bottom);return BoxConstraints.loose(Size(constraintsWidth, constraintsHeight));}Offset getPositionForChild(Size size, Size childSize) {double x = position.left;double y = position.top;final double buttonHeight = size.height - position.top - position.bottom;double constraintsHeight = max(position.top - buttonHeight - padding.top - kToolbarHeight, size.height - position.top - padding.bottom);if(position.top + constraintsHeight > size.height - padding.bottom) {y = position.top - childSize.height - buttonHeight;}return Offset(x, y);}bool shouldRelayout(covariant _CustomPopupRouteLayout oldDelegate) {return position != oldDelegate.position || padding != oldDelegate.padding;}
}class _RenderHeightFactorBox extends RenderShiftedBox {double _heightFactor;_RenderHeightFactorBox({RenderBox? child,double? heightFactor,}):_heightFactor = heightFactor ?? 1.0, super(child);double get heightFactor => _heightFactor;set heightFactor(double value) {if (_heightFactor == value) {return;}_heightFactor = value;markNeedsLayout();}void performLayout() {final BoxConstraints constraints = this.constraints;if (child == null) {size = constraints.constrain(Size.zero);return;}child!.layout(constraints, parentUsesSize: true);size = constraints.constrain(Size(child!.size.width,child!.size.height,));child!.layout(constraints.copyWith(maxWidth: size.width, maxHeight: size.height * heightFactor), parentUsesSize: true);size = constraints.constrain(Size(child!.size.width,child!.size.height,));}
}class _HeightFactorBox extends SingleChildRenderObjectWidget {final double? heightFactor;const _HeightFactorBox({super.key,this.heightFactor,super.child,});RenderObject createRenderObject(BuildContext context) => _RenderHeightFactorBox(heightFactor: heightFactor);void updateRenderObject(BuildContext context, _RenderHeightFactorBox renderObject) {renderObject.heightFactor = heightFactor ?? 1.0;}
}
http://www.yayakq.cn/news/897004/

相关文章:

  • 网站建设策电脑html如何转wordpress
  • 开发网站多少钱家装网站
  • 上传到ftp网站模板做网站的人 优帮云
  • 免费做店招哪个网站好六年级毕业留言册页面设计模板
  • 专业网站建设科技公司长图可以在哪些网站做
  • 利用国外免费空间做网站毕业设计网站开发类题目
  • 做网站外包工作怎么样箱包外贸订单网
  • 南宁商城网站建设wordpress主题首页怎么修改
  • 重新建设网站的请示制作手机app软件要多少钱
  • php个人网站简洁西安电商网站制作
  • 关于网站制作报价北京网站设计网站公司
  • wordpress发表评论项秦皇岛市做网站优化
  • q王商城 网站是怎么做的网站设计的研究方法有哪些
  • 青岛网站建设新元创想免费视频app交友软件哪个好
  • 个人网页制作与网站建设镇江网站建设优化
  • 做ftp网站怎么设置免费网站设计作业
  • 运城公司做网站把html文件生成网址
  • 珠宝营销型网站淘宝客网站 建设要钱不
  • 建个什么网站好呢搭建平台激发活力
  • 招聘网站可以同时做两份简历吗网络公司代理
  • 网站打开显示域名错误wordpress图片左对齐
  • 章丘营销型网站设计公司很多国外ip访问网站
  • 做外贸是在什么网站企业移动网站制作
  • 女性时尚资讯+淘宝客模式系列网站源码北京通州区网站建设
  • 做化妆品注册和注册的网站新乡商城网站建设价格
  • 新网网站后台登陆无锡营销型网站建站
  • 广州公司网站网站上的通话功能怎么做
  • aspnet网站开发工具网站建设好处zu97
  • 如何做网站聚合页公司官网制作
  • 网站建设微信软文营销网站外包