美文网首页
Flutter 之 组合实例:TurnBox& UrlRichT

Flutter 之 组合实例:TurnBox& UrlRichT

作者: maskerII | 来源:发表于2022-05-25 19:21 被阅读0次

1. 前言

我们之前已经介绍过RotatedBox,它可以旋转子组件,但是它有两个缺点:一是只能将其子节点以90度的倍数旋转;二是当旋转的角度发生变化时,旋转角度更新过程没有动画。

本节我们将实现一个TurnBox组件和UrlRichText组件,
TurnBox组件它不仅可以以任意角度来旋转其子节点,而且可以在角度发生变化时执行一个动画以过渡到新状态,同时,我们可以手动指定动画速度。
UrlRichText组件 ,它可以自动处理url链接,点击链接时,会有回调。

2. TurnBox

TurnBox的完整代码如下:


class TurnBox extends StatefulWidget {
  const TurnBox({
    Key? key,
    this.turns = 0.0,
    this.speed = 200,
    this.child,
  }) : super(key: key);

  final double turns; // 选装的圈数 一圈360度 如0.25圈即90度
  final int speed; // 过渡动画执行的总时长 毫秒
  final Widget? child; // 子组件

  @override
  State<TurnBox> createState() => _TurnBoxState();
}

class _TurnBoxState extends State<TurnBox> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  @override
  void initState() {
    _controller = AnimationController(
      vsync: this,
      lowerBound: -double.infinity,
      upperBound: double.infinity,
    );

    _controller.value = widget.turns;
    super.initState();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return RotationTransition(
      turns: _controller,
      child: widget.child,
    );
  }

  @override
  void didUpdateWidget(covariant TurnBox oldWidget) {
    super.didUpdateWidget(oldWidget);

    if (oldWidget.turns != widget.turns) {
      // 执行动画 从当前值到目标值
      _controller.animateTo(widget.turns,
          duration: Duration(milliseconds: widget.speed),
          curve: Curves.easeOut);
    }
  }
}

上面代码中:

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

下面我们测试一下TurnBox的功能,测试代码如下:


class MSTurnBoxDemo extends StatefulWidget {
  const MSTurnBoxDemo({Key? key}) : super(key: key);

  @override
  State<MSTurnBoxDemo> createState() => _MSTurnBoxDemoState();
}

class _MSTurnBoxDemoState extends State<MSTurnBoxDemo> {
  double _turns = .0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("TurnBoxDemo")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TurnBox(
              turns: _turns,
              speed: 500,
              child: Icon(Icons.refresh, size: 50),
            ),
            TurnBox(
              turns: _turns,
              speed: 1000,
              child: Icon(Icons.refresh, size: 100),
            ),
            ElevatedButton(
              onPressed: () {
                _turns += 0.2;
                setState(() {});
              },
              child: Text("顺时针旋转1/5圈"),
            ),
            ElevatedButton(
              onPressed: () {
                _turns -= 0.2;
                setState(() {});
              },
              child: Text("逆时针旋转1/5圈"),
            ),
          ],
        ),
      ),
    );
  }
}


当我们点击旋转按钮时,两个图标的旋转都会旋转1/5圈,但旋转的速度是不同的。

实际上本示例只组合了RotationTransition一个组件,它是一个最简的组合类组件示例。另外,如果我们封装的是StatefulWidget,那么一定要注意在组件更新时是否需要同步状态。

110.gif

3. UrlRichText

我们要封装一个富文本展示组件UrlRichText ,它可以自动处理url链接,点击链接时,会有回调。

UrlRichText 定义如下:


typedef UrlClickedCallBack = void Function(String text);

class UrlRichText extends StatefulWidget {
  const UrlRichText({
    Key? key,
    required this.text,
    this.linkStyle = const TextStyle(color: Colors.blue),
    this.textStyle = const TextStyle(color: Colors.black),
    required this.tapCallback,
  }) : super(key: key);

  final String text; // 文本字符串
  final TextStyle linkStyle; // url链接样式
  final TextStyle textStyle; // 文本样式
  final UrlClickedCallBack tapCallback; // 点击URL 回调
  @override
  State<UrlRichText> createState() => _UrlRichTextState();
}

接下来我们在_UrlRichTextState中要实现的功能有两个:

  1. 解析文本字符串“text”,生成TextSpan缓存起来;
  2. 在build中返回最终的富文本样式;

class _UrlRichTextState extends State<UrlRichText> {
  late TextSpan _textSpan;

  @override
  void initState() {
    _textSpan = parseText(widget.text);
    super.initState();
  }

  @override
  void didUpdateWidget(covariant UrlRichText oldWidget) {
    if (oldWidget.text != widget.text) {
       // 文本不一致时,重新解析文本
      _textSpan = parseText(widget.text);
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {
    return RichText(
      text: _textSpan,
    );
  }

  TextSpan parseText(String text) {
    // 解析文本中的Url
    List<InlineSpan> _contentList = [];

    RegExp exp = new RegExp(
        r'(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?');
    // text = "如果www.baidu.com这是一段文本但是里面包含了连接";
    Iterable<RegExpMatch> matches = exp.allMatches(text);

    int index = 0;
    matches.forEach((match) {
      /// start 0  end 8
      /// start 10 end 12
      String c = text.substring(match.start, match.end);

      // print('---地址-url:--$c');
      if (match.start == index) {
        index = match.end;
      }
      if (index < match.start) {
        String a = text.substring(index, match.start);
        // print('---地址-内容AAAA--$a');
        index = match.end;
        _contentList.add(
          TextSpan(text: a, style: widget.textStyle),
        );
      }

      if (RegexUtil.isURL(c)) {
        _contentList.add(
          TextSpan(
            text: c,
            style: widget.linkStyle,
            recognizer: TapGestureRecognizer()
              ..onTap = () {
                widget.tapCallback(c);
              },
          ),
        );
      } else {
        _contentList.add(
          TextSpan(text: c, style: widget.textStyle),
        );
      }
    });
    if (index < text.length) {
      String a = text.substring(index, text.length);
      // print('---地址-内容BBBB--$a');
      _contentList.add(
        TextSpan(text: a, style: widget.textStyle),
      );
    }
    return TextSpan(children: _contentList);
  }
}

由于解析文本字符串,构建出TextSpan是一个耗时操作,为了不在每次build的时候都解析一次,所以我们在initState中对解析的结果进行了缓存,然后再build中直接使用解析的结果_textSpan。当父Widget触发重建(rebuild)时,需要在didUpdateWidget中重新解析一次。

下面测试下 UrlRichText


class MSUrlRichTextDemo extends StatelessWidget {
  const MSUrlRichTextDemo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("UrlRichTextDemo")),
      body: Center(
        child: UrlRichText(
          text: "百度:http://www.baidu.com 百度一下",
          tapCallback: (c) {
            print("点击URL:$c");
          },
        ),
      ),
    );
  }
}

image.png

点击网址,控制台会有打印

https://book.flutterchina.club/chapter10/turn_box.html

相关文章

网友评论

      本文标题:Flutter 之 组合实例:TurnBox& UrlRichT

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