Flutter - 自定义SwitchButton

作者: Cosecant | 来源:发表于2018-04-27 15:55 被阅读84次

    人生,就是要不断的折腾。没有折腾的人生是不完美的人生。--- 鲁迅
    哈哈,当然鲁迅一定会沉下脸来,大声道:“劳资没说过!”

    昨天折腾了下CircleProgressBar组件,今天觉得不过瘾,继续折腾了一个新的组件:SwitchButton。真是不容易,现在最恼火的是没有找到办法精确的去处理SwitchButton的点击事件。如果亲爱的Reader有办法,麻烦你们不吝赐教,小弟在此先谢了。

    还是先上Demo效果图吧!
    项目地址:https://gitee.com/yugecse/Switch-Button

    switch-button效果图.gif
    1. 构建SwitchButton的Lerp函数及类
    class SwitchButtonLerp {
      SwitchButtonLerp({this.isOpen = false, this.fraction});
    
      bool isOpen;
    
      double fraction;
    
      static SwitchButtonLerp lerp(
          SwitchButtonLerp begin, SwitchButtonLerp end, double fraction) =>
          new SwitchButtonLerp(
              fraction: lerpDouble(begin.fraction, end.fraction, fraction));
    }
    
    1. SwitchButton类
    class SwitchButton extends StatefulWidget {
      SwitchButton(
          {Key key, this.size = const Size(50.0, 30.0), this.isOpen = true})
          : super(key: key);
    
      Size size;
    
      bool isOpen;
    
      @override
      State createState() => new _SwitchButtonState();
    }
    
    class _SwitchButtonState extends State<SwitchButton>
        with TickerProviderStateMixin {
      AnimationController animationController;
      SwitchButtonTween switchButtonTween;
    
      void _tap(PointerDownEvent event) {
        final radius = widget.size.height / 2.0;
        final circleCenter = new Offset(widget.isOpen ? widget.size.width - radius : radius, radius);
        print("坐标信息:$circleCenter,  ${event.position}");
        setState(() {
          widget.isOpen = !widget.isOpen;
          print("=======>changed!");
        });
        //以下代码可以暂时注释,因为没用上
        if(_isInCircle(radius, circleCenter, event.position)){ 
             //判断用户是否点击在小圆上,暂未找到解决办法。event.position是针对屏幕的坐标位置
        }else {
          print("-------->区域外点击");
        }
      }
    
      ///
      /// 判断某个点是否在圆内及圆上
      /// radius  圆半径
      /// circleCenter 圆心坐标
      /// point  目标点
      ///
      bool _isInCircle(double radius, Offset circleCenter, Offset point) =>
          pow(point.dx - circleCenter.dx, 2) + pow(point.dy - circleCenter.dy, 2) <=
          pow(radius, 2);
    
      @override
      Widget build(BuildContext context) {
        setState(() {
          animationController = new AnimationController(
              vsync: this, duration: new Duration(milliseconds: 300));
          switchButtonTween = new SwitchButtonTween(
              new SwitchButtonLerp(
                  isOpen: widget.isOpen, fraction: widget.isOpen ? 0.0 : 1.0),
              new SwitchButtonLerp(
                  isOpen: widget.isOpen, fraction: widget.isOpen ? 1.0 : 0.0));
          animationController.forward();
        });
        return new Listener(
          onPointerDown: _tap,
          child: new CustomPaint(
            size: widget.size,
            painter: new SwitchButtonPainter(
                switchButtonTween.animate(animationController)),
          ),
        );
      }
    }
    
    1. SwitchButtonPainter类,绘制Switch-Button图形。
    class SwitchButtonTween extends Tween<SwitchButtonLerp> {
      SwitchButtonTween(SwitchButtonLerp begin, SwitchButtonLerp end)
          : super(begin: begin, end: end);
    
      @override
      SwitchButtonLerp lerp(double fraction) => SwitchButtonLerp.lerp(begin, end, fraction);
    }
    
    class SwitchButtonPainter extends CustomPainter {
    
      SwitchButtonPainter(Animation<SwitchButtonLerp> animation)
          : animation = animation,
            super(repaint: animation);
    
      Animation<SwitchButtonLerp> animation;
    
      @override
      void paint(Canvas canvas, Size size) {
        final double animationValue = animation.value.fraction;
        final bool openState = animation.value.isOpen;
        final paint = Paint()
          ..color = openState ? Colors.green : Colors.grey[400]
          ..style = PaintingStyle.fill;
        final path = Path();
        final pRadius = size.height / 2.0;
        for (double a = 0.0; a < 360.0; a += 0.001) {  //利用数学公式计算在圆边上的点的坐标,半圆图形
          double x = pRadius + pRadius * cos(a * pi / 180.0);
          double y = pRadius + pRadius * sin(a * pi / 180.0);
          if (a != 0.0)
            path.lineTo(x, y);
          else
            path.moveTo(x, y);
        }
        path.addRect(  //绘制矩形
            new Rect.fromLTWH(pRadius, 0.0, size.width - 2 * pRadius, size.height));
        for (double a = 0.0; a < 360.0; a += 0.001) {  //利用数学公式计算在圆边上的点的坐标,半圆图形
          double x =
              size.width - 2 * pRadius + pRadius + pRadius * cos(a * pi / 180.0);
          double y = pRadius + pRadius * sin(a * pi / 180.0);
          if (a != 0.0)
            path.lineTo(x, y);
          else
            path.moveTo(x, y);
        }
        path.close();
        canvas.drawPath(path, paint);
        paint.color = Colors.white70;
        canvas.drawCircle( //绘制动圆,根据动画值处理动圆的x坐标
            new Offset(
                pRadius + animationValue * (size.width - 2 * pRadius), pRadius),
            pRadius,
            paint);  
      }
    
      @override
      bool shouldRepaint(CustomPainter oldDelegate) => false;
    }
    

    测试类

    void main() => runApp(new MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Flutter Demo',
          theme: new ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: new MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => new _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      int _counter = 0;
      bool isSwitchButtonOpen = true;
    
      void _incrementCounter() {
        setState(() {
          _counter++;
        });
      }
    
      void _switchState() {
        setState(() {
          this.isSwitchButtonOpen = !this.isSwitchButtonOpen;
          print("======> 状态变化了: ${this.isSwitchButtonOpen}");
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
            title: new Text(widget.title),
          ),
          body: new Center(
            child: new Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                new SwitchButton(
                  isOpen: isSwitchButtonOpen,
                  size: new Size(50.0, 28.0),
                ),
                new RaisedButton(
                  onPressed: _switchState,
                  child: new Text("点我啊!"),
                ),
                new Text(
                  'You have pushed the button this many times:',
                ),
                new Text(
                  '$_counter',
                  style: Theme.of(context).textTheme.display1,
                ),
              ],
            ),
          ),
          floatingActionButton: new FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
            child: new Icon(Icons.add),
          ),
        );
      }
    }
    

    现存问题

    目前最蛋疼的问题是怎么去处理精确点击。。。

    相关文章

      网友评论

        本文标题:Flutter - 自定义SwitchButton

        本文链接:https://www.haomeiwen.com/subject/heablftx.html