Flutter - 自定义SwitchButton

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

昨天折腾了下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
    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 {
  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 animation)
      : animation = animation,
        super(repaint: animation);

  Animation 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 {
  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: [
            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)