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

找人做淘宝网站关于我们做网站

找人做淘宝网站,关于我们做网站,网站开发税收分类,microsoft做网站03_Flutter自定义下拉菜单 在Flutter的内置api中#xff0c;可以使用showMenu实现类似下拉菜单的效果#xff0c;或者使用PopupMenuButton组件#xff0c;PopupMenuButton内部也是使用了showMenu这个api#xff0c;但是使用showMenu时#xff0c;下拉面板的显示已经被约定… 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);overrideBoxConstraints 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));}overridebool shouldRelayout(covariant _CustomPopupRouteLayout oldDelegate) {return position ! oldDelegate.position;} }三.显示下拉面板 我们先把下拉面板显示出来看看效果这里的下拉面板其实是一个弹出层而在Flutter中所有的弹出层的显示和页面路由是一样的都是通过Navigator.push进行显示参照showMenu的源码这里的弹出层我们让其继承PopupRoute class _CustomPopupRouteT extends PopupRouteT {final RelativeRect position;overridefinal String? barrierLabel;_CustomPopupRoute({required this.position,required this.barrierLabel,});overrideColor? get barrierColor null;overridebool get barrierDismissible true;overrideDuration get transitionDuration Duration(milliseconds: 200);overrideWidget buildPage(BuildContext context, Animationdouble animation, Animationdouble 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)); }如图黄色区域就是下拉面板可以看到点击按钮下拉面板显示点击下拉面板以外的区域下拉面板关闭但是位置好像不对因为我们根本就没去确定下拉面板的位置。 四.确定下拉面板的位置 override 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);overrideBoxConstraints 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));}overrideOffset 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);}overridebool 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 _CustomPopupRouteT extends PopupRouteT {...overrideWidget buildPage(BuildContext context, Animationdouble animation, Animationdouble 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();}overridevoid 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,});overrideRenderObject createRenderObject(BuildContext context) _RenderHeightFactorBox(heightFactor: heightFactor);overridevoid updateRenderObject(BuildContext context, _RenderHeightFactorBox renderObject) {renderObject.heightFactor heightFactor ?? 1.0;} }最后把下拉面板中执行动画的child使用_HeightFactorBox包裹并传入heightFactorTween的执行结果即可。 override Widget buildPage(BuildContext context, Animationdouble animation, Animationdouble 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 {overrideWidget 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: ListWidget.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 _CustomPopupRouteT extends PopupRouteT {final WidgetBuilder builder;final RelativeRect position;final double? elevation;final Color? shadowColor;overridefinal 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);overrideColor? get barrierColor null;overridebool get barrierDismissible true;overrideDuration get transitionDuration animationDuration;overrideWidget buildPage(BuildContext context, Animationdouble animation, Animationdouble 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);overrideBoxConstraints 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));}overrideOffset 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);}overridebool 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();}overridevoid 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,});overrideRenderObject createRenderObject(BuildContext context) _RenderHeightFactorBox(heightFactor: heightFactor);overridevoid updateRenderObject(BuildContext context, _RenderHeightFactorBox renderObject) {renderObject.heightFactor heightFactor ?? 1.0;} }
http://www.zqtcl.cn/news/142051/

相关文章:

  • 电商网站建设方案100例用什么做php网站
  • 网站开发设计课程教案南宁网站建设招聘
  • 常州微信网站建设wordpress 中英主题
  • 新零售型网站开发网络营销常用的工具和方法
  • 陕西省建设监理协会网站证书网站建设去哪里找客户
  • 上海网站注销吗如何在wordpress上调用百度地图
  • 网站设计与开发实例网站semseo先做哪个
  • 一个网站做3个关键词够找人建设一个网站多少钱
  • 网站群软件阿里云虚拟主机wordpress
  • 自己做个网站要多少钱温州网站开发平台
  • 北京鑫创网站建设找个网站你知道的
  • 做网站找客户电子商务网站开发与管理
  • 宝安高端网站设计怎么样qq钓鱼网站怎么制作
  • 学习教建网站公众号小程序怎么注销
  • 网站建设或网站优化排名做建筑设计网站
  • 外贸seo外贸推广外贸网站建设外贸网站建设用图片设置网站首页
  • 网站模板安装出入成都通知今天
  • wordpress网站 添加微信网站设计O2O平台佛山总代理
  • 广州网站开发定制方案网站建设应该考虑哪些方面
  • 在线网站建设培训门户类网站图片
  • 佛山乐从网站建设自媒体平台收益排行榜
  • 网站建设项目需求陕西陕煤建设集团有限公司网站
  • 鼓楼网站开发永州做网站tuantaogou
  • ui网站建设站评价文山建设5G网站
  • 深圳 网站设计公司企业网络搭建教程
  • 做网站策划遇到的问题全网营销型网站模版
  • 网站建设费属于无形资产吗广州高铁新建站在哪里
  • 网站建设平台报价深圳市房产交易中心官网
  • 注册网站网前端素材网
  • 快3网站制作 优帮云贾汪区建设局网站