Flutter-组合组件TurnBox

175 阅读2分钟

实现组合组件TurnBox组件,它可以任意角度来旋转其子节点,而且可以在角度发生变化时执行一个过渡动画到新状态,同时,还可以手动指定动画的速度。
示例:

class SSLTurnBox extends StatefulWidget{
  final double turns;
  final int speed;
  final Widget child;
  const SSLTurnBox({
    Key? key,
    this.turns = 0.0,
    this.speed = 200,
    required this.child,
  }):super(key: key);

  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return SSLTurnBoxState();
  }
}

class SSLTurnBoxState extends State<SSLTurnBox> with SingleTickerProviderStateMixin{
  late AnimationController controller;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    controller = AnimationController(
        vsync: this,
        lowerBound: - double.infinity,
        upperBound: double.infinity,
    );
    controller.value = widget.turns;
  }
  @override
  void dispose() {
    // TODO: implement dispose
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return RotationTransition(
      turns: controller,
      child: widget.child,
    );
  }

  @override
  void didUpdateWidget(covariant SSLTurnBox oldWidget) {
    // TODO: implement didUpdateWidget
    super.didUpdateWidget(oldWidget);
    if (oldWidget.turns != widget.turns){
      controller.animateTo(
        widget.turns,
        duration: Duration(milliseconds: widget.speed??200),
        curve: Curves.easeOut,
      );
    }
  }
}

代码分析:

  • 通过组合RotationTransition和child来实现旋转效果。
  • 在didUpdateWidget中,判断要旋转的角度是否发生变化,如果变化则执行一个动画。

使用示例:

class SSLTurnBoxTest extends StatefulWidget{
  const SSLTurnBoxTest({Key? key}):super(key: key);
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return SSLTurnBoxTestState();
  }
}

class SSLTurnBoxTestState extends State<SSLTurnBoxTest>{
  double turns = 0.0;
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: const Text("TurnBox"),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            SizedBox(
              height: 100,
              width: 100,
              child: SSLTurnBox(
                turns: turns,
                speed: 500,
                child: Image.asset("images/apple.jpg"),
              ),
            ),
            Container(
              height: 150,
              color: Colors.lightBlue,
              child: SSLTurnBox(
                turns: turns,
                speed: 200,
                child: Image.asset("images/device.jpg"),
              ),
            ),
            ElevatedButton(
                onPressed: (){
                  setState(() {
                    turns += 0.2;
                  });
                },
                child: const Text("Start Animation")
            ),
            TextButton(
                onPressed: (){
                  setState(() {
                    turns -= 0.2;
                  });
                },
                child: const Text("Reverse Animation"),
            ),
          ],
        ),
      ),
    );
  }
}

下面封装一个富文本展示组件RichText,可以自动处理url连接,定义如下:

class SSLRichText extends StatefulWidget{
  final String text;
  final TextStyle? linkStyle;
  const SSLRichText({Key? key, required this.text, this.linkStyle}):super(key: key);
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return SSLRichTextState();
  }
}

class SSLRichTextState extends State<SSLRichText>{
  late TextSpan textSpan;

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return RichText(
        text: textSpan
    );
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    textSpan = parseText(widget.text);
  }

  @override
  void didUpdateWidget(covariant SSLRichText oldWidget) {
    // TODO: implement didUpdateWidget
    //文本变化时重新识别,一定要记得同步变化
    if (widget.text != oldWidget.text){
      textSpan = parseText(widget.text);
    }
    super.didUpdateWidget(oldWidget);
  }

  TextSpan parseText(String text){
    //正则解析字符串
    RegExp reg = RegExp(r"((https|http|ftp|rtsp|mms)?://)[^\s]+");
    List<TextSpan> textSpans = [];
    int index = 0;
    reg.allMatches(text).forEach((element) {
        String c = text.substring(element.start, element.end);
        debugPrint("get string $c\n");
        if (element.start == index){
          index = element.end;
        }
        if (element.start > index){
          String d = text.substring(index + 1, element.start);
          index = element.end;
          //未识别字符串添加
          textSpans.add(TextSpan(
            text: d,
            style: const TextStyle(color: Colors.red),
            //TapGestureRecognizer() 相当于是创建了一个TapGestureRecognizer对象,然后给这个对象的onTap赋值
            recognizer: TapGestureRecognizer()..onTap=(){
                debugPrint("ssl chick text 2 $d");
            },
            )
          );
        }
        //识别字符串添加
        if (c.isNotEmpty){
          textSpans.add(TextSpan(
            text: c,
            style: const TextStyle(color: Colors.blue),
            recognizer: TapGestureRecognizer()..onTap=(){
              debugPrint("ssl chick text 1 $c");
            },
          )
          );
        }
    });
  //组装TextSpan
    return TextSpan(children: textSpans);
  }
}

使用示例:

class SSLRichTextTest extends StatefulWidget{
  const SSLRichTextTest({Key? key}):super(key: key);
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return SSLRichTextTestState();
  }
}

class SSLRichTextTestState extends State<SSLRichTextTest>{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: const Text("SSL Rich Text"),
      ),
      //由于正则没有很到位,只能识别两个,但是效果还是出来了
      body: const SSLRichText(text: "农家阿婆古代诗歌http://www.baidu.com,你还是个i说,https://www.wechat.com",),
    );
  }
}