当前位置:   article > 正文

Rive动画使用介绍(Flutter)

rive动画

感觉官方的案例对我这种level的开发者来说感觉的真的很晦涩,也没什么像样的中文文档,所以做了些尝试,大致理解了一些基本的使用方式。有些较为复杂的素材琢磨不明白,然后把自己所知的做一个总结吧。

首先是通过RiveAnimation组件加载的三种方式,分别可以通过asset资源文件、网络url和本地文件进行动画加载。

以及通过RiveAnimationController控制动画的暂停与播放。

还有一种加载方法是通过直接加载文件并解析转化成RivaFile类型,获取一个所谓artboard的画板,然后通过这个画板获取一个StateMachineController的状态器,接下来在状态其中通过findSMI和findInput方法获取到可执行的动画。动画的名称通过rive网站的editor打开自己查看,这里接似懂非懂了,见得的动画基本按照编辑器里的名称来就可以,有些复杂类型的小游戏实在是没明白怎么加载,按常规动画那套搞我没搞出来。有明白的可以指教下。主要还是findSMI可以找到SMITrigger这种类型的动画控制器,过过调用其实例的fire方法执行动画,还有findInput方法获得的SMIInput实例,通过传入泛型,double、bool等控制数值发生变化来触动动画的执行。具体的可以看下下面的一些案例,效果和代码都贴出来了,仅供参考,互相学习。

Flitter rive加载组件 rive: ^0.9.1

 rive | Flutter Package

Rive素材

Rive - Community

首先是Rive动画的基本使用

下面的代码分别演示Rive加载动画文件的三种方式。

  1. import 'dart:io';
  2. import 'dart:typed_data';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter/services.dart';
  5. import 'package:path_provider/path_provider.dart';
  6. import 'package:rive/rive.dart';
  7. ///author:          wangzhong
  8. ///create:          2022-11-23 23:19
  9. ///Description:     Rive动画的基本本使用,三种加载方式asset加载、network网络url加载、file文件加载
  10. class SimpleAnimationExample extends StatefulWidget {
  11.   @override
  12.   State<StatefulWidget> createState() {
  13.     return _SimepleAnimationExampleState();
  14.   }
  15. }
  16. class _SimepleAnimationExampleState extends State<SimpleAnimationExample> {
  17.   String pathRive = '';
  18.   @override
  19.   void initState() {
  20.     super.initState();
  21.     getFile().then((value) {
  22.       setState(() {
  23.         pathRive = value;
  24.       });
  25.     });
  26.   }
  27.   Future<String> getFile() async {
  28.     ByteData bytes = await rootBundle.load("asset/rive/ferris-wheel.riv");
  29.     String fileName = "ferris-wheel.riv";
  30.     String dir = (await getApplicationSupportDirectory()).path;
  31.     String filePath = "$dir/$fileName";
  32.     ByteBuffer buffer = bytes.buffer;
  33.     File file = await File(filePath).writeAsBytes(buffer.asUint8List());
  34.     return file.path;
  35.   }
  36.   @override
  37.   Widget build(BuildContext context) {
  38.     return Scaffold(
  39.       appBar: AppBar(
  40.         title: Text('SimpleAnimationExample'),
  41.       ),
  42.       body: Column(
  43.         children: [
  44.           Expanded(
  45.             child: RiveAnimation.asset(
  46.               'asset/rive/halloween-ghost.riv',
  47.               fit: BoxFit.cover,
  48.             ),
  49.           ),
  50.           Expanded(
  51.             child: RiveAnimation.network(
  52.                 'https://public.rive.app/community/runtime-files/3605-7541-payfit-summit-2022.riv'),
  53.           ),
  54.           Expanded(
  55.             child: Center(
  56.               child: pathRive.isEmpty
  57.                   ? Icon(Icons.confirmation_num_outlined)
  58.                   : RiveAnimation.file(
  59.                       pathRive,
  60.                       fit: BoxFit.cover,
  61.                     ),
  62.             ),
  63.           )
  64.         ],
  65.       ),
  66.     );
  67.   }
  68. }

播放暂停的控制

  1. import 'package:flutter/material.dart';
  2. import 'package:rive/rive.dart';
  3. ///author:          wangzhong
  4. ///create:          2022-11-24 08:50
  5. ///Description:     动画播放暂停控制
  6. class PlayPauseAnimationExample extends StatefulWidget {
  7.   PlayPauseAnimationExample({Key? key}) : super(key: key);
  8.   @override
  9.   _PlayPauseAnimationExampleState createState() =>
  10.       _PlayPauseAnimationExampleState();
  11. }
  12. class _PlayPauseAnimationExampleState extends State<PlayPauseAnimationExample> {
  13.   late RiveAnimationController _controller;
  14.   /// Toggles between play and pause animation states
  15.   void _togglePlay() =>
  16.       setState(() => _controller.isActive = !_controller.isActive);
  17.   /// Tracks if the animation is playing by whethe r controller is running
  18.   bool get isPlaying => _controller.isActive;
  19.   @override
  20.   void initState() {
  21.     // TODO: implement initState
  22.     _controller = SimpleAnimation('Timeline 1');
  23.     super.initState();
  24.   }
  25.   @override
  26.   void dispose() {
  27.     // TODO: implement dispose
  28.     _controller.dispose();
  29.     super.dispose();
  30.   }
  31.   @override
  32.   Widget build(BuildContext context) {
  33.     return Scaffold(
  34.       appBar: AppBar(
  35.         title: Text('PlayPauseAnimationExample'),
  36.       ),
  37.       body: Center(
  38.           child: RiveAnimation.asset(
  39.         'asset/rive/ferris-wheel.riv',
  40.         controllers: [_controller],
  41.         onInit: (state) => setState(() => print(state)),
  42.       )),
  43.       floatingActionButton: FloatingActionButton(
  44.         onPressed: () => _togglePlay(),
  45.         tooltip: isPlaying ? 'Pause' : 'Play',
  46.         child: Icon(
  47.           isPlaying ? Icons.pause : Icons.play_arrow,
  48.         ),
  49.       ),
  50.     );
  51.   }
  52. }

动画中执行单次操作

  1. import 'package:flutter/material.dart';
  2. import 'package:rive/rive.dart';
  3. ///author:          wangzhong
  4. ///create:          2022-11-24 20:59
  5. ///Description:     动画中执行单次动作
  6. class PlayOneShotExample extends StatefulWidget {
  7.   PlayOneShotExample({Key? key}) : super(key: key);
  8.   @override
  9.   _PlayOneShotExampleState createState() => _PlayOneShotExampleState();
  10. }
  11. class _PlayOneShotExampleState extends State<PlayOneShotExample> {
  12.   late RiveAnimationController _controller;
  13.   /// Is the animation currently playing?
  14.   bool _isPlaying = false;
  15.   @override
  16.   void initState() {
  17.     super.initState();
  18.     _controller = OneShotAnimation(
  19.       'bounce',
  20.       autoplay: false,
  21.       onStop: () => setState(() => _isPlaying = false),
  22.       onStart: () => setState(() => _isPlaying = true),
  23.     );
  24.   }
  25.   @override
  26.   Widget build(BuildContext context) {
  27.     return Scaffold(
  28.       appBar: AppBar(
  29.         title: Text('PlayOneShotExample'),
  30.         // titleTextStyle: TextStyle(fontSize: 20,color: Colors.black),
  31.       ),
  32.       body: Center(
  33.         child: RiveAnimation.network(
  34.           'https://cdn.rive.app/animations/vehicles.riv',
  35.           animations: const ['idle', 'curves'],
  36.           controllers: [_controller],
  37.         ),
  38.       ),
  39.       floatingActionButton: FloatingActionButton(
  40.         // disable the button while playing the animation
  41.         onPressed: () => _isPlaying ? null : _controller.isActive = true,
  42.         tooltip: 'Bounce',
  43.         child: const Icon(Icons.arrow_upward),
  44.       ),
  45.     );
  46.   }
  47. }

控制动画速度

  1. import 'package:flutter/material.dart';
  2. import 'package:rive/rive.dart';
  3. import 'package:yhm_app/utils/rive_speed_controller.dart';
  4. ///author:          wangzhong
  5. ///create:          2022-11-25 11:40
  6. ///Description:     动画速度控制
  7. class SpeedControllExample extends StatefulWidget {
  8.   SpeedControllExample({Key? key}) : super(key: key);
  9.   @override
  10.   _SpeedControllExampleState createState() => _SpeedControllExampleState();
  11. }
  12. class _SpeedControllExampleState extends State<SpeedControllExample> {
  13.   late RiveSpeedController speedController;
  14.   @override
  15.   void initState() {
  16.     // TODO: implement initState
  17.     speedController = RiveSpeedController('Timeline 1', speedMultiplier: 1);
  18.     super.initState();
  19.   }
  20.   @override
  21.   Widget build(BuildContext context) {
  22.     return Scaffold(
  23.       appBar: AppBar(
  24.         title: Text('SpeedControllExample'),
  25.         // titleTextStyle: TextStyle(fontSize: 20,color: Colors.black),
  26.       ),
  27.       body: Container(
  28.         child: Column(
  29.           children: [
  30.             Expanded(
  31.                 child: RiveAnimation.asset(
  32.               'asset/rive/ferris-wheel.riv',
  33.               fit: BoxFit.cover,
  34.               // animations: const ['Timeline 1'],
  35.               controllers: [speedController],
  36.             )),
  37.           ],
  38.         ),
  39.       ),
  40.       floatingActionButton: FloatingActionButton(
  41.         onPressed: () {
  42.           setState(() {
  43.             if (speedController.speedMultiplier > 9) {
  44.               speedController.speedMultiplier = 0;
  45.             }
  46.             speedController.speedMultiplier++;
  47.           });
  48.         },
  49.         child: Text('x${speedController.speedMultiplier}'),
  50.       ),
  51.     );
  52.   }
  53. }
  54. // class RiveSpeedController extends SimpleAnimation {
  55. //   final double speedMultiplier;
  56. //
  57. //   RiveSpeedController(
  58. //     String animationName, {
  59. //     double mix = 1,
  60. //     this.speedMultiplier = 1,
  61. //   }) : super(animationName, mix: mix);
  62. //
  63. //   @override
  64. //   void apply(RuntimeArtboard artboard, double elapsedSeconds) {
  65. //     if (instance == null || !instance!.keepGoing) {
  66. //       isActive = false;
  67. //     }
  68. //     instance!
  69. //       ..animation.apply(instance!.time, coreContext: artboard, mix: mix)
  70. //       ..advance(elapsedSeconds * speedMultiplier);
  71. //   }
  72. // }

下载进度动画

  1. import 'dart:math';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/services.dart';
  4. import 'package:rive/rive.dart';
  5. ///author:          wangzhong
  6. ///create:          2022-11-26 17:40
  7. ///Description:     xxxx
  8. class DownloadProgressExample extends StatefulWidget {
  9.   DownloadProgressExample({Key? key}) : super(key: key);
  10.   @override
  11.   _DownloadProgressExampleState createState() =>
  12.       _DownloadProgressExampleState();
  13. }
  14. class _DownloadProgressExampleState extends State<DownloadProgressExample> {
  15.   // SMIInput<bool> start;
  16.   // SMIInput<double> progeress;
  17.   Artboard? _riveArtboard;
  18.   // SMIInput<bool>? _start;
  19.   SMITrigger? _start;
  20.   SMIInput<double>? _progress;
  21.   @override
  22.   void initState() {
  23.     rootBundle.load('asset/rive/liquid_download.riv').then(
  24.       (data) async {
  25.         // Load the RiveFile from the binary data.
  26.         final file = RiveFile.import(data);
  27.         // The artboard is the root of the animation and gets drawn in the
  28.         // Rive widget.
  29.         final artboard = file.mainArtboard;
  30.         var controller =
  31.             StateMachineController.fromArtboard(artboard, 'Download');
  32.         if (controller != null) {
  33.           artboard.addController(controller);
  34.           _start = controller.findSMI('Download');
  35.           _progress = controller.findInput('Progress');
  36.           print(_start);
  37.           print(_progress);
  38.         }
  39.         setState(() => _riveArtboard = artboard);
  40.       },
  41.     );
  42.     super.initState();
  43.   }
  44.   @override
  45.   Widget build(BuildContext context) {
  46.     final ThemeData theme = Theme.of(context);
  47.     return Scaffold(
  48.       appBar: AppBar(
  49.         title: Text('DownloadProgressExample'),
  50.         // titleTextStyle: TextStyle(fontSize: 20,color: Colors.black),
  51.       ),
  52.       body: Container(
  53.         child: Column(
  54.           children: [
  55.             Expanded(
  56.                 child: _riveArtboard == null
  57.                     ? SizedBox()
  58.                     : GestureDetector(
  59.                         onTap: () {
  60.                           _start?.fire();
  61.                           // _start?.value = true;
  62.                         },
  63.                         child: Rive(artboard: _riveArtboard!))),
  64.             _riveArtboard == null
  65.                 ? SizedBox()
  66.                 : SliderTheme(
  67.                     data: theme.sliderTheme.copyWith(
  68.                       activeTrackColor: Colors.deepPurple,
  69.                       inactiveTrackColor: Colors.blue.withAlpha(55),
  70.                       activeTickMarkColor:
  71.                           theme.colorScheme.onSurface.withOpacity(0.7),
  72.                       inactiveTickMarkColor:
  73.                           theme.colorScheme.surface.withOpacity(0.7),
  74.                       overlayColor:
  75.                           theme.colorScheme.onSurface.withOpacity(0.12),
  76.                       thumbColor: Colors.deepPurple,
  77.                       valueIndicatorColor: Colors.deepPurpleAccent,
  78.                       thumbShape: _CustomThumbShape(),
  79.                       valueIndicatorShape: _CustomValueIndicatorShape(),
  80.                       valueIndicatorTextStyle: theme.accentTextTheme.bodyText2!
  81.                           .copyWith(color: theme.colorScheme.onSurface),
  82.                     ),
  83.                     child: Slider(
  84.                         value: _progress!.value,
  85.                         min: 0,
  86.                         max: 100,
  87.                         divisions: 10,//加上这个属性才会显示label
  88.                         activeColor: Colors.orangeAccent,
  89.                         inactiveColor: Colors.green.withAlpha(99),
  90.                         thumbColor: Colors.deepPurpleAccent,
  91.                         label: _progress!.value.toStringAsFixed(2),
  92.                         onChanged: (value) {
  93.                           setState(() {
  94.                             _progress?.value = value;
  95.                           });
  96.                         })),
  97.             SizedBox(
  98.               height: 30,
  99.             )
  100.           ],
  101.         ),
  102.       ),
  103.     );
  104.   }
  105. }
  106. class _CustomThumbShape extends SliderComponentShape {
  107.   static const double _thumbSize = 4.0;
  108.   static const double _disabledThumbSize = 3.0;
  109.   @override
  110.   Size getPreferredSize(bool isEnabled, bool isDiscrete) {
  111.     return isEnabled
  112.         ? const Size.fromRadius(_thumbSize)
  113.         : const Size.fromRadius(_disabledThumbSize);
  114.   }
  115.   static final Animatable<double> sizeTween = Tween<double>(
  116.     begin: _disabledThumbSize,
  117.     end: _thumbSize,
  118.   );
  119.   @override
  120.   void paint(PaintingContext context, Offset center,
  121.       {required Animation<double> activationAnimation,
  122.       required Animation<double> enableAnimation,
  123.       required bool isDiscrete,
  124.       required TextPainter labelPainter,
  125.       required RenderBox parentBox,
  126.       required SliderThemeData sliderTheme,
  127.       required TextDirection textDirection,
  128.       required double value,
  129.       required double textScaleFactor,
  130.       required Size sizeWithOverflow}) {
  131.     final Canvas canvas = context.canvas;
  132.     final ColorTween colorTween = ColorTween(
  133.       begin: sliderTheme.disabledThumbColor,
  134.       end: sliderTheme.thumbColor,
  135.     );
  136.     final double size = _thumbSize * sizeTween.evaluate(enableAnimation);
  137.     final Path thumbPath = _downTriangle(size, center);
  138.     canvas.drawPath(thumbPath,
  139.         Paint()..color = colorTween.evaluate(enableAnimation) ?? Colors.blue);
  140.   }
  141. }
  142. Path _upTriangle(double size, Offset thumbCenter) =>
  143.     _downTriangle(size, thumbCenter, invert: true);
  144. Path _downTriangle(double size, Offset thumbCenter, {bool invert = false}) {
  145.   final Path thumbPath = Path();
  146.   final double height = sqrt(3.0) / 2.0;
  147.   final double centerHeight = size * height / 3.0;
  148.   final double halfSize = size / 2.0;
  149.   final double sign = invert ? -1.0 : 1.0;
  150.   thumbPath.moveTo(
  151.       thumbCenter.dx - halfSize, thumbCenter.dy + sign * centerHeight);
  152.   thumbPath.lineTo(thumbCenter.dx, thumbCenter.dy - 2.0 * sign * centerHeight);
  153.   thumbPath.lineTo(
  154.       thumbCenter.dx + halfSize, thumbCenter.dy + sign * centerHeight);
  155.   thumbPath.close();
  156.   return thumbPath;
  157. }
  158. class _CustomValueIndicatorShape extends SliderComponentShape {
  159.   static const double _indicatorSize = 4.0;
  160.   static const double _disabledIndicatorSize = 3.0;
  161.   static const double _slideUpHeight = 30.0;
  162.   @override
  163.   Size getPreferredSize(bool isEnabled, bool isDiscrete) {
  164.     return Size.fromRadius(isEnabled ? _indicatorSize : _disabledIndicatorSize);
  165.   }
  166.   static final Animatable<double> sizeTween = Tween<double>(
  167.     begin: _disabledIndicatorSize,
  168.     end: _indicatorSize,
  169.   );
  170.   @override
  171.   void paint(PaintingContext context, Offset center,
  172.       {required Animation<double> activationAnimation,
  173.       required Animation<double> enableAnimation,
  174.       required bool isDiscrete,
  175.       required TextPainter labelPainter,
  176.       required RenderBox parentBox,
  177.       required SliderThemeData sliderTheme,
  178.       required TextDirection textDirection,
  179.       required double value,
  180.       required double textScaleFactor,
  181.       required Size sizeWithOverflow}) {
  182.     final Canvas canvas = context.canvas;
  183.     final ColorTween enableColor = ColorTween(
  184.       begin: sliderTheme.disabledThumbColor,
  185.       end: sliderTheme.valueIndicatorColor,
  186.     );
  187.     final Tween<double> slideUpTween = Tween<double>(
  188.       begin: 0.0,
  189.       end: _slideUpHeight,
  190.     );
  191.     final double size = _indicatorSize * sizeTween.evaluate(enableAnimation);
  192.     final Offset slideUpOffset =
  193.         Offset(0.0, -slideUpTween.evaluate(activationAnimation));
  194.     final Path thumbPath = _upTriangle(size, center + slideUpOffset);
  195.     final Color paintColor = enableColor
  196.             .evaluate(enableAnimation)
  197.             ?.withAlpha((255.0 * activationAnimation.value).round()) ??
  198.         Colors.black;
  199.     canvas.drawPath(
  200.       thumbPath,
  201.       Paint()..color = paintColor,
  202.     );
  203.     canvas.drawLine(
  204.         center,
  205.         center + slideUpOffset,
  206.         Paint()
  207.           ..color = paintColor
  208.           ..style = PaintingStyle.stroke
  209.           ..strokeWidth = 2.0);
  210.     labelPainter.paint(
  211.         canvas,
  212.         center +
  213.             slideUpOffset +
  214.             Offset(-labelPainter.width / 2.0, -labelPainter.height - 4.0));
  215.   }
  216. }

状态器

  1. import 'package:flutter/material.dart';
  2. import 'package:flutter/services.dart';
  3. import 'package:rive/rive.dart';
  4. ///author:          wangzhong
  5. ///create:          2022-11-26 17:51
  6. ///Description:     xxxx
  7. class MoreTypeStateChangeExample extends StatefulWidget {
  8.   MoreTypeStateChangeExample({Key? key}) : super(key: key);
  9.   @override
  10.   _MoreTypeStateChangeExampleState createState() =>
  11.       _MoreTypeStateChangeExampleState();
  12. }
  13. class _MoreTypeStateChangeExampleState
  14.     extends State<MoreTypeStateChangeExample> {
  15.   Artboard? _riveWomenArtboard;
  16.   SMIInput<double>? _levelInput;
  17.   Artboard? _riveMenArtboard;
  18.   SMITrigger? _crossInput;
  19.   SMITrigger? _jabInput;
  20.   SMITrigger? _kickInput;
  21.   SMIInput<bool>? _runInput;
  22.   SMIInput<double>? _turnInput;
  23.   @override
  24.   void initState() {
  25.     rootBundle.load('asset/rive/skills.riv').then(
  26.       (data) async {
  27.         // Load the RiveFile from the binary data.
  28.         final file = RiveFile.import(data);
  29.         // The artboard is the root of the animation and gets drawn in the
  30.         // Rive widget.
  31.         final artboard = file.mainArtboard;
  32.         var controller =
  33.             StateMachineController.fromArtboard(artboard, 'Designer\'s Test');
  34.         if (controller != null) {
  35.           artboard.addController(controller);
  36.           _levelInput = controller.findInput('Level');
  37.         }
  38.         setState(() => _riveWomenArtboard = artboard);
  39.       },
  40.     );
  41.     rootBundle.load('asset/rive/character-controller.riv').then(
  42.       (data) async {
  43.         // Load the RiveFile from the binary data.
  44.         final file = RiveFile.import(data);
  45.         // The artboard is the root of the animation and gets drawn in the
  46.         // Rive widget.
  47.         final artboard = file.mainArtboard;
  48.         var controller =
  49.             StateMachineController.fromArtboard(artboard, 'State Machine 1');
  50.         if (controller != null) {
  51.           artboard.addController(controller);
  52.           _crossInput = controller.findSMI('crossPunch');
  53.           _jabInput = controller.findSMI('jabPunch');
  54.           _kickInput = controller.findSMI('sideKick');
  55.           _runInput = controller.findInput('isRunning');
  56.           _turnInput = controller.findInput('xDir');
  57.         }
  58.         setState(() => _riveMenArtboard = artboard);
  59.       },
  60.     );
  61.     super.initState();
  62.   }
  63.   @override
  64.   Widget build(BuildContext context) {
  65.     return Scaffold(
  66.       appBar: AppBar(
  67.         title: Text('MoreTypeStateChangeExample'),
  68.         // titleTextStyle: TextStyle(fontSize: 20,color: Colors.black),
  69.       ),
  70.       body: Container(
  71.         child: Column(
  72.           children: [
  73.             Expanded(
  74.                 child: _riveWomenArtboard == null
  75.                     ? SizedBox()
  76.                     : Rive(
  77.                         artboard: _riveWomenArtboard!,
  78.                       )),
  79.             Padding(
  80.               padding: const EdgeInsets.all(20),
  81.               child: Row(
  82.                 children: [
  83.                   Expanded(
  84.                     child: ElevatedButton(
  85.                         onPressed: () {
  86.                           _levelInput?.change(0);
  87.                         },
  88.                         child: Text('baby')),
  89.                   ),
  90.                   Padding(
  91.                     padding: const EdgeInsets.symmetric(
  92.                         horizontal: 10, vertical: 20),
  93.                     child: ElevatedButton(
  94.                         onPressed: () {
  95.                           _levelInput?.change(1);
  96.                         },
  97.                         child: Text('younger')),
  98.                   ),
  99.                   Expanded(
  100.                     child: ElevatedButton(
  101.                         onPressed: () {
  102.                           _levelInput?.change(2);
  103.                         },
  104.                         child: Text('old people')),
  105.                   ),
  106.                 ],
  107.               ),
  108.             ),
  109.             Expanded(
  110.               child: _riveMenArtboard == null
  111.                   ? SizedBox()
  112.                   : Rive(
  113.                       artboard: _riveMenArtboard!,
  114.                     ),
  115.             ),
  116.             Row(
  117.               children: [
  118.                 Expanded(
  119.                   child: ElevatedButton(
  120.                       onPressed: () {
  121.                         _crossInput?.fire();
  122.                       },
  123.                       child: Text('cross')),
  124.                 ),
  125.                 Expanded(
  126.                   child: Padding(
  127.                     padding: const EdgeInsets.symmetric(horizontal: 10),
  128.                     child: ElevatedButton(
  129.                         onPressed: () {
  130.                           _jabInput?.fire();
  131.                         },
  132.                         child: Text('jab')),
  133.                   ),
  134.                 ),
  135.                 Expanded(
  136.                   child: ElevatedButton(
  137.                       onPressed: () {
  138.                         _kickInput?.fire();
  139.                       },
  140.                       child: Text('kick')),
  141.                 ),
  142.                 Expanded(
  143.                   child: Padding(
  144.                     padding: const EdgeInsets.symmetric(horizontal: 10),
  145.                     child: ElevatedButton(
  146.                         onPressed: () {
  147.                           bool? flag = _runInput?.value;
  148.                           _runInput?.value = !flag!;
  149.                         },
  150.                         child: Text('run')),
  151.                   ),
  152.                 ),
  153.               ],
  154.             ),
  155.             Padding(
  156.               padding: const EdgeInsets.only(bottom: 20),
  157.               child: Row(
  158.                 children: [
  159.                   Expanded(
  160.                     child: ElevatedButton(
  161.                         onPressed: () {
  162.                           print(_turnInput);
  163.                           _turnInput?.change(-1);
  164.                         },
  165.                         child: Text('turn left')),
  166.                   ),
  167.                   Expanded(
  168.                     child: Padding(
  169.                       padding: const EdgeInsets.symmetric(horizontal: 10),
  170.                       child: ElevatedButton(
  171.                           onPressed: () {
  172.                             _turnInput?.value = 1;
  173.                           },
  174.                           child: Text('turn right')),
  175.                     ),
  176.                   ),
  177.                 ],
  178.               ),
  179.             ),
  180.           ],
  181.         ),
  182.       ),
  183.     );
  184.   }
  185. }

如上图所示,状态切换,点击方块时,圆圈会做出一个亮暗的变化,模仿了一个开关灯的操作。

在rive平台编辑器上看到的在这组动画中有Inputs中有开关,Listener中有监听

  1. Expanded(
  2.   child: GestureDetector(
  3.     // onTap: _hitSwitch,
  4.     child: RiveAnimation.asset(
  5.       'asset/rive/light_switch.riv',
  6.       stateMachines: ['Switch'],
  7.     ),
  8.   ),
  9. ),

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/973756
推荐阅读
相关标签
  

闽ICP备14008679号