【Flutter学习笔记】10.3 组合实例:TurnBox

参考资料:《Flutter实战·第二版》 10.3 组合实例:TurnBox


这里尝试实现一个更为复杂的例子,其能够旋转子组件。Flutter中的RotatedBox可以旋转子组件,但是它有两个缺点:

  • 一是只能将其子节点以90度的倍数旋转
  • 二是当旋转的角度发生变化时,旋转角度更新过程没有动画

因此,这里将自定义一个TurnBox,不仅可以设置任意角度旋转的子Widget,还能再角度发生改变时执行一个过渡动画,同时,还可以手动设置动画的执行时长。
首先,组件一定是一个动画组件,需要实现SingleTickerProviderStateMixin并设置Controler对象,这里没有定义AnimationController对象,直接在Controler内部设置起始值,默认值是[0, 1],类型为浮点数。输入参数有旋转的多少、旋转动画的时长和子Widget,其具有默认的初始值。
在组件初始化阶段,首先定义_controller,其取值范围为 [ − ∞ , + ∞ ] [-\infin,+\infin] [,+],并将其初始值设为传入参数,否则为默认值0。
组件通过RotationTransition构建,需传入一个Animation<double>对象并设置子Widget。
当外部传入的参数turnsspeed变化时(turns为主要控制变量),则执行动画到目标状态。
注意在组件销毁的dispose()函数当中销毁_controller防止内存泄漏的问题。

class TurnBox extends StatefulWidget {
  const TurnBox({
    Key? key,
    this.turns = .0, //旋转的“圈”数,一圈为360度,如0.25圈即90度
    this.speed = 200, //过渡动画执行的总时长
    this.child
  }) :super(key: key);

  final double turns;
  final int speed;
  final Widget? child;

  
  TurnBoxState createState() => TurnBoxState();
}

class TurnBoxState extends State<TurnBox>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  
  void initState() {
    super.initState();
    _controller = AnimationController(
        vsync: this,
        lowerBound: -double.infinity,
        upperBound: double.infinity
    );
    _controller.value = widget.turns;
  }

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

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

  
  void didUpdateWidget(TurnBox oldWidget) {
    super.didUpdateWidget(oldWidget);
    //旋转角度发生变化时执行过渡动画
    if (oldWidget.turns != widget.turns) {
      _controller.animateTo(
        widget.turns,
        duration: Duration(milliseconds: widget.speed??200),
        curve: Curves.easeOut,
      );
    }
  }
}

下面可以测试一下定义好组件的功能,大小两个组件全部采用一个state控制,但是旋转速度不同,大的会慢一些:

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

  
  TurnBoxRouteState createState() => TurnBoxRouteState();
}

class TurnBoxRouteState extends State<TurnBoxRoute> {
  double _turns = .0;

  
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          TurnBox(
            turns: _turns,
            speed: 500,
            child: const Icon(
              Icons.refresh,
              size: 50,
            ),
          ),
          TurnBox(
            turns: _turns,
            speed: 1000,
            child: const Icon(
              Icons.refresh,
              size: 150.0,
            ),
          ),
          ElevatedButton(
            child: const Text("顺时针旋转1/5圈"),
            onPressed: () {
              setState(() {
                _turns += .2;
              });
            },
          ),
          const SizedBox(height: 10,),
          ElevatedButton(
            child: const Text("逆时针旋转1/5圈"),
            onPressed: () {
              setState(() {
                _turns -= .2;
              });
            },
          )
        ],
      ),
    );
  }
}

在这里插入图片描述
这部分内容最常用到的函数就是didUpdateWidget(),其在传入参数发生变化时调用。例如我们要实现一个解析url链接的富文本文件,那么在一开始要对传入的文本进行解析,而后才能生成对应的Widget。解析的过程与构建过程分开较为合适,可以保证UI发生变化时,所需的文本不会被反复解析,以减少不必要的耗时,因此放在initState()中是一个不错的选择。但是,当传入的参数发生变化时(组件树结构改变),initState()并不执行,文本内容不会更新。因此,可以将解析过程放在didUpdateWidget()中,这样当参数变化时,能够及时对UI进行重构:

class _MyRichTextState extends State<MyRichText> {

  TextSpan _textSpan;

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

  TextSpan parseText(String text) {
    // 耗时操作:解析文本字符串,构建出TextSpan。
    // 省略具体实现。
  }

  
  void initState() {
    _textSpan = parseText(widget.text)
    super.initState();
  }
  
  
  void didUpdateWidget(MyRichText oldWidget) {
    if (widget.text != oldWidget.text) {
      _textSpan = parseText(widget.text);
    }
    super.didUpdateWidget(oldWidget);
  }
}

虽然这看起来是一个简单的方式,但是在实际开发过程中很容易被忽略,一定要注意传入参数是否会经常发生改变,及时更新输入状态。

相关推荐

  1. Flutter学习笔记10.1 自定义组件方法简介

    2024-03-23 11:46:04       20 阅读
  2. flutter学习-day10-布局类组件

    2024-03-23 11:46:04       36 阅读
  3. 关于学习flutter笔记

    2024-03-23 11:46:04       7 阅读
  4. flutter学习-day13-功能型组件和状态共享

    2024-03-23 11:46:04       40 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-23 11:46:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-23 11:46:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-23 11:46:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-23 11:46:04       18 阅读

热门阅读

  1. 哈工大sse C语言 困难

    2024-03-23 11:46:04       19 阅读
  2. Sql中如何添加数据

    2024-03-23 11:46:04       19 阅读
  3. 牛客笔试|美团2024春招第一场【测试方向】

    2024-03-23 11:46:04       31 阅读
  4. Learning to summarize from human feedback

    2024-03-23 11:46:04       17 阅读
  5. 红黑树(Red-Black Tree)

    2024-03-23 11:46:04       19 阅读
  6. linux docker镜像初始化

    2024-03-23 11:46:04       22 阅读
  7. THINKPHP仿Word 统计字数的方法

    2024-03-23 11:46:04       17 阅读
  8. Go使用Terraform 库

    2024-03-23 11:46:04       20 阅读
  9. tcp/ip中的粘包问题的处理逻辑

    2024-03-23 11:46:04       19 阅读
  10. 质量模型、软件测试流程和测试用例

    2024-03-23 11:46:04       23 阅读