颤振中的侧面径向菜单
Posted
技术标签:
【中文标题】颤振中的侧面径向菜单【英文标题】:Side Radial Menu in flutter 【发布时间】:2019-03-21 16:02:29 【问题描述】:请教如何创建像图片一样颤动的侧面径向菜单,并在用户点击时滚动
任何帮助将不胜感激。
【问题讨论】:
你能找到解决办法吗? 明天我会尝试解决这个问题,我有一个想法,但是圆是否必须像视频中那样可扩展,这将更加困难,或者圆也可以固定大小 【参考方案1】:这可以通过使用GestureDetector、Transform、三角函数和使用ClipRect 进行一些裁剪来实现。
使用GestureDetector
,可以看到用户输入的拖动距离。这可用于确定小部件旋转多少。
使用Transform
,可以将小部件移动到特定位置。
三角函数用于确定小部件到圆心的位置。
使用ClipRect
,可以剪掉小部件的左侧。
可以通过将拖动距离变为负数来反转滚动方向。
这是制作旋转菜单的代码,该菜单使用我最近为回答这个问题而创建的自定义小部件(如果需要,可以在小部件列表中添加更多小部件):
import 'dart:math' as math;
import 'package:flutter/material.dart';
void main()
runApp(MyApp());
class MyApp extends StatelessWidget
// This widget is the root of your application.
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body:CircularScrollView(//wrap this with align if you want it to be aligned to the right of the screen
[//add more widgets or remove as you'd like
GestureDetector(
onTap: (),//insert function when icon is tapped
child: Container(
child: Center(child: Text('a')),
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
GestureDetector(
onTap: (),//insert function when icon is tapped
child: Container(
child: Center(child: Text('b')),
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
GestureDetector(
onTap: (),//insert function when icon is tapped
child: Container(
child: Center(child: Text('c')),
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
GestureDetector(
onTap: (),//insert function when icon is tapped
child: Container(
child: Center(child: Text('d')),
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
GestureDetector(
onTap: (),//insert function when icon is tapped
child: Container(
child: Center(child: Text('e')),
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
],
radius: 100,
padding: 0,//add double the radius entered to clip out the right side
itemMaxHeight: 20,//effects clipping border height
itemMaxWidth: 20,//effects clipping border width
),
),
);
class CircularScrollView extends StatefulWidget
final List<Widget> items;
final double radius;
final double itemMaxHeight;
final double itemMaxWidth;
final double padding;
final bool reverse;
CircularScrollView(this.items, Key key, this.radius=10, this.itemMaxHeight=0, this.itemMaxWidth=0, this.padding=0, this.reverse=false) : super(key: key);
@override
_CircularScrollViewState createState() => _CircularScrollViewState();
class _CircularScrollViewState extends State<CircularScrollView>
double lastPosition;
List<Widget> transformItems= [];
double degreesRotated = 0;
@override
void initState()
setState(()
_calculateTransformItems();
);
super.initState();
void _calculateTransformItems()
transformItems= [];
for(int i = 0; i<widget.items.length; i++)
double startAngle = (i/widget.items.length)*2*math.pi;
double currentAngle = degreesRotated+startAngle;
transformItems.add(
Transform(
transform: Matrix4.identity()..translate(
(widget.radius)*math.cos(currentAngle),
(widget.radius)*math.sin(currentAngle),
),
child: widget.items[i],
),
);
void _calculateScroll(DragUpdateDetails details)
if (lastPosition == null)
lastPosition = details.localPosition.dy;
return;
double distance = details.localPosition.dy - lastPosition;
double distanceWithReversal = widget.reverse?-distance:distance;
lastPosition =details.localPosition.dy;
degreesRotated += distanceWithReversal/(widget.radius);
_calculateTransformItems();
@override
Widget build(BuildContext context)
return Align(
alignment: Alignment.centerLeft,
child: Container(
height: widget.radius*2+widget.itemMaxHeight,
width: widget.radius*2 + widget.itemMaxWidth,
child: GestureDetector(
onVerticalDragUpdate: (details)=>setState(()_calculateScroll(details);),
onVerticalDragEnd: (details)lastPosition=null;,
child: Container(
height: double.infinity,
width: double.infinity,
color: Colors.transparent,
child: ClipRect(
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: EdgeInsets.only(left: widget.padding),
child: Stack(
children: transformItems,
),
),
),
),
),
),
),
);
使用此代码时,请勿修改自定义小部件的内部,除非您确切知道该部分代码的作用。对齐小部件时,请改为从外部包裹自定义小部件。
【讨论】:
非常感谢 :) 当我将 align 更改为centerRight
时,scrollPhysic 不正确
我怎样才能找到像这个屏幕截图这样的中心项目:i.stack.imgur.com/fDv0Y.jpg
第一个评论我没看懂,但我会尽量回答。确保不要更改自定义 CircularScrollView 小部件中的代码。用 align 或你想要的来包装自定义小部件。对于您的第二条评论,我编辑了代码以使项目居中成为可能。您应该输入填充参数:(the parent widget width that you want to make it the centre of)/2 - (the values of the radius)
。 '-' 符号是减法符号,只是为了澄清。
非常感谢,当我将 alignment: Alignment.centerLeft,
更改为 alignment: Alignment.centerRight,
滚动方向是明智的,反之亦然
请看这个屏幕视频imgur.com/a/T2YDVwm【参考方案2】:
你可以尝试使用这个包,circle_wheel_scroll,在 Stack 内移动这个小部件,如果需要的话,用负左位置放置。
CircleListScrollView(
physics: CircleFixedExtentScrollPhysics(),
axis: Axis.horizontal,
itemExtent: 80,
children: List.generate(20, _buildItem),
radius: MediaQuery.of(context).size.width * 0.6,
),
或者这个listwheelscrollview
ListWheelScrollView(
itemExtent: 100,
// diameterRatio: 1.6,
// offAxisFraction: -0.4,
// squeeze: 0.8,
clipToSize: true,
children: <Widget>[
RaisedButton(onPressed:null ,
child: Text("Item 1",textAlign:TextAlign.start,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 2",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 3",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 4",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 5",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 6",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 7",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 8",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
],
),
【讨论】:
以上是关于颤振中的侧面径向菜单的主要内容,如果未能解决你的问题,请参考以下文章