颤振中的侧面径向菜单

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),),) , 
       ], 
      ), 

【讨论】:

以上是关于颤振中的侧面径向菜单的主要内容,如果未能解决你的问题,请参考以下文章

如何删除侧面菜单离子2中的下划线

颤振动画不起作用

CSS 径向菜单

如何在 CSS 中创建径向菜单?

径向动画

侧面导航中的角材料滑动过渡?