[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fKv5MBt6ZKYcfuz_cwbWuM6dJg5IJeeQFbdwisWoIbUc":3},{"code":4,"message":5,"data":6},200,"成功",{"id":7,"createdAt":8,"title":9,"content":10,"summary":11,"image":12,"uid":13,"user":14,"categoryId":21,"category":22,"subCategoryId":24,"subCategory":25,"comments":27,"status":17,"reason":28,"notice":28,"visitCount":29,"commentCount":30,"keywords":31},144,"2024-12-24T11:51:16.415Z","flutter动画 (一) 从loading的转圈动画说起 - Ticker","## 引言\nflutter中的动画一个重要且必不可少的机制就是`Ticker`，所以要理解flutter的动画，必须先要对Ticker有所理解。\n\n对于Ticker这个概念，可以将其理解为时钟或者刷新信号。意味着，在UI IT开发领域共识中，每秒60帧刷新频率下，Ticker会发出信号去执行动画，并且在这个频率之下，Ticker在每一帧都提供了回调给到开发者去进行处理。\n\n## 常见的`SingleTickerProviderStateMixin`\n以上是对Ticker的简单介绍，下面介绍一个轻量且高效的tiker provider `SingleTickerProviderStateMixin`，说到provider，你应该就知道`SingleTickerProviderStateMixin`是一个提供了`Ticker`的对象，而从其命名中就可以看出，这是一个混入`mixin`对象，而且通常用于处理单独的某一种动画时常用的ticker提供对象【使用多动画则考虑使用`TickerProviderStateMixin`】，咱们看看它的声明：\n```ticker_provider.dart\n/// Provides a single [Ticker] that is configured to only tick while the current\n/// tree is enabled, as defined by [TickerMode].\n///\n/// To create the [AnimationController] in a [State] that only uses a single\n/// [AnimationController], mix in this class, then pass `vsync: this`\n/// to the animation controller constructor.\n///\n/// This mixin only supports vending a single ticker. If you might have multiple\n/// [AnimationController] objects over the lifetime of the [State], use a full\n/// [TickerProviderStateMixin] instead.\n@optionalTypeArgs\nmixin SingleTickerProviderStateMixin\u003CT extends StatefulWidget> on State\u003CT> implements TickerProvider {\n  Ticker? _ticker;\n...\n}\n```\n\n所以它可以被状态组件`StatefulWidget`的`State`进行混入处理。而且在`SingleTickerProviderStateMixin`中已经包含`Ticker`对象，在dispose中也会处理`removeListener`,\n```ticker_provider.dart\n...\n@override\n  void dispose() {\n    assert(() {\n      if (_ticker == null || !_ticker!.isActive) {\n        return true;\n      }\n      throw FlutterError.fromParts(\u003CDiagnosticsNode>[\n        ErrorSummary('$this was disposed with an active Ticker.'),\n        ErrorDescription(\n          '$runtimeType created a Ticker via its SingleTickerProviderStateMixin, but at the time '\n          'dispose() was called on the mixin, that Ticker was still active. The Ticker must '\n          'be disposed before calling super.dispose().',\n        ),\n        ErrorHint(\n          'Tickers used by AnimationControllers '\n          'should be disposed by calling dispose() on the AnimationController itself. '\n          'Otherwise, the ticker will leak.',\n        ),\n        _ticker!.describeForError('The offending ticker was'),\n      ]);\n    }());\n    _tickerModeNotifier?.removeListener(_updateTicker);\n    _tickerModeNotifier = null;\n    super.dispose();\n  }\n...\n```\n所以在使用`SingleTickerProviderStateMixin`时，你不必担心`Ticker`的回收内存泄漏问题。\n\n## loading动画案例\n这里我们写一个简单的转圈动画，代码如下：\n```loading.dart\nimport 'package:flutter/cupertino.dart';\n\nclass LoadingWidget extends StatefulWidget {\n\n  late final Widget? loadingWidget;\n\n  LoadingWidget({ super.key, this.loadingWidget });\n\n  @override\n  _LoadingWidgetWidgetState createState() => _LoadingWidgetWidgetState();\n}\n\nclass _LoadingWidgetWidgetState extends State\u003CLoadingWidget>\n    with SingleTickerProviderStateMixin {\n  late AnimationController _controller;\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = AnimationController(\n      duration: const Duration(seconds: 1),\n      vsync: this,\n    )..repeat(); // 持续动画\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AnimatedBuilder(\n      animation: _controller,\n      builder: (context, child) {\n        return Transform.rotate(\n          angle: _controller.value * 2.0 * 3.141592653589793,\n          child: child,\n        );\n      },\n      child: widget.loadingWidget != null ? widget.loadingWidget : Image.asset(\n        'assets/images/loading.png', // 转圈图片\n        width: 24,\n        height: 24,\n      ),\n    );\n  }\n}\n```\n示例图片：\n![loading flutter](https://image.xinwei.ltd/private_media_unlock_loading1735040143331.png)\n\n上面一段代码通过使用`AnimationController`动画控制器，在参数`vsync`传入了当前的state作为ticker provider，动画时长duration为1秒，在`AnimatedBuilder`动画生成器中builder下传入了转圈动画`Transform.rotate`，角度是当前动画进度的值，乘以一圈的弧度 2 pi，那么1秒钟一圈的动画，持续不断的repeat重复执行，就组成了loading转圈的效果。\n\n以上就是对loading动画的一个简单解释说明，在这个基础上，如果想进行其他动画，比如：translate、scale、flip等就都可以用这个思路实现。\n\n希望对大家有帮助，也请大家多多关注“新卫网络科技”微信公众号。\n\n\n\n","逐渐分享一些在flutter中展示的动画以及UI效果，供大家参考，希望对大家学习和了解flutter有一些帮助。","https://image.xinwei.ltd/private_media_unlock_loading1735040143331.png",499668042977349,{"phone":15,"userId":13,"nickName":16,"vipType":17,"avatar":18,"sign":19,"createdAt":20},"13121171998","全栈老韩",1,"https://image.xinwei.ltd/images/IMG_5430.JPG","全栈工程师，擅长iOS App开发、前端（vue、react、nuxt、小程序&Taro）开发、Flutter、React Native、后端（midwayjs、golang、express、koa）开发、docker容器、seo优化等。","2024-01-01T16:14:30.305Z",2,{"id":21,"name":23},"IT技术",11,{"id":24,"name":26,"parentId":21},"Flutter",[],"",417,0,"SingleTickerProviderStateMixin,ticker,flutter loading,flutter animation,flutter animate,flutter动画,flutter progress,flutter circle,loading,animation,animate,动画,flutter"]