Flutter正在开发一个超级简单的模仿微信QQ的侧滑菜单组件
这个需求肯定很熟悉:
推出菜单。这个需求在Flutter中如何实现呢?
看一下实现的效果:
需求分析
老套路,先分析需求:
- 首先可以滑出菜单
- 菜单滑出一定距离,滑动完全向外滚动,到达距离前向后滚动
- 菜单数量和样式可随意定制
- 菜单点击回调
- 菜单展开时,点击点恢复菜单(见QQ)
代码实现
需求明确后,就可以编写代码了。
1。首先可以将菜单滑出
最基本的是菜单必须能够滑出。我们想一下,如何才能将小部件放置在屏幕之外并仍然推送它呢?
基本上不到一分钟的时间,我想大家都能想出答案:ScrollView,好吧,只有ScrollView才能满足我们的需求。
就这么干吧:
SingleChildScrollView(
physics: ClampingScrollPhysics(),
scrollDirection: Axis.horizontal,
controller: _controller,
child: Row(children: children),
)
---------------
// 第一个 Widget,宽度为屏幕的宽度
SizedBox(
width: screenWidth,
child: child,
),
复制代码
- 先将ScrollView的滑动位置改为水平
- 将滑动效果改为ClampingScrollPhysics,否则iOS会有反弹效果
- 设置滑动距离控制器来监听设置child到 Row ,第一个 Widget 填满横屏,因此后续菜单位于屏幕之外
2。菜单滑动到一定距离后一直滑出,到达距离之前回滚
这个效果需要监控滑动距离和动作。
如果滑动距离大于所有菜单宽度的1/4,则将其全部推出。如果没有,则回滚。
然后判断你的手是否已经离开屏幕并判断此时的距离。
本来想用手势但发现不行。我请教了专家,用了Listener
。
的代码如下:
Listener(
onPointerUp: (d) {
if (_controller.offset < (screenWidth / 5) * menu.length / 4) {
_controller.animateTo(0, duration: Duration(milliseconds: 100), curve: Curves.linear);
} else {
_controller.animateTo(menu.length * (screenWidth / 5), duration: Duration(milliseconds: 100), curve: Curves.linear);
}
},
child: SingleChildScrollView(
physics: ClampingScrollPhysics(),
scrollDirection: Axis.horizontal,
controller: _controller,
child: Row(children: children),
),
)
复制代码
非常简单。它只是在举起手时判断距离,然后调用animteTo
方法。
3。菜单的数量和样式可以随意定制
这个其实很简单,让“用户”提交即可。
我只需要控制菜单的宽度。
于是我把容器的参数全部去掉,包裹在一个组件中SlideMenuItem
:
class SlideMenuItem extends StatelessWidget {
SlideMenuItem({
Key key,
@required this.child,
this.alignment,
this.padding,
Color color,
Decoration decoration,
this.foregroundDecoration,
this.height,
BoxConstraints constraints,
this.margin,
this.transform,
@required this.onTap,
}) : assert(child != null),
assert(margin == null || margin.isNonNegative),
assert(padding == null || padding.isNonNegative),
assert(decoration == null || decoration.debugAssertIsValid()),
assert(constraints == null || constraints.debugAssertIsValid()),
assert(
color == null || decoration == null,
'Cannot provide both a color and a decoration\n'
'The color argument is just a shorthand for "decoration: new BoxDecoration(color: color)".'),
decoration =
decoration ?? (color != null ? BoxDecoration(color: color) : null),
constraints = (height != null)
? constraints?.tighten(height: height) ??
BoxConstraints.tightFor(height: height)
: constraints,
super(key: key);
final BoxConstraints constraints;
final Decoration decoration;
final AlignmentGeometry alignment;
final EdgeInsets padding;
final Decoration foregroundDecoration;
final EdgeInsets margin;
final Matrix4 transform;
final Widget child;
final double height;
final GestureTapCallback onTap;
@override
Widget build(BuildContext context) {
return Container(
child: child,
alignment: alignment,
constraints: constraints,
decoration: decoration,
padding: padding,
width: screenWidth / 5,
height: height,
foregroundDecoration: foregroundDecoration,
margin: margin,
transform: transform,
);
}
}
复制代码
这么长的代码,其实是我自己写的“在宽度上”。?
基于这个问题,当你创建整个SlideItem
时,每个菜单都会添加到GestureDetector
,然后调用T菜单的(back)()方法,然后调用dismiss()
方法恢复菜单。
代码如下:
addAll(menu
.map((w) => GestureDetector(
child: w,
onTap: (){
w.onTap();
dismiss();
},
))
.toList());
复制代码
5。菜单展开时,点击该点即可收回菜单
即菜单展开时,点击该点,先收回菜单。 QQ就这样了。
这里有一个知识点。我们设置的点击事件默认不会命中透明组件,所以我们需要给第一个默认全屏宽度的widget添加一个属性: 完整代码如下: Behavior 有三个值: 引用群里一位大佬的话: 别把问题复杂化了。 实际上,如果我们认真思考一下这个效果,我们几乎可以想出一个解决方案。而且很容易实现。 本来想用ListView包裹起来,后来觉得没有必要。只需将其封装在一个物品中就足够了。 作者:Flutter Notes,行为:HitTestBehavior.opaque♼♼。
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: (){
if(_controller.offset != 0){
dismiss();
}else{
onTap();
}
},
child: SizedBox(
width: screenWidth,
child: child,
),
)
复制代码
概述
链接:https://juejin.im/post/5d82130ce51d453b373b4dc6
来源:掘金❀属于作者所有。商业转载请联系作者获取授权。非商业转载请注明出处。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。