import 'dart:math';
import 'package:flutter/material.dart';
import 'package:rive/math.dart';
import 'package:rive/rive.dart';
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: MyRiveWidget(),
    );
  }
}
class MyRiveWidget extends StatefulWidget {
  const MyRiveWidget({Key? key}) : super(key: key);
  @override
  State<MyRiveWidget> createState() => _MyRiveWidgetState();
}
class _MyRiveWidgetState extends State<MyRiveWidget>
    with SingleTickerProviderStateMixin {
  late final AnimationController _animationController =
      AnimationController(vsync: this, duration: const Duration(seconds: 10));
  RiveArtboardRenderer? _artboardRenderer;
  Future<void> _load() async {
    // You need to manage adding the controller to the artboard yourself,
    // unlike with the RiveAnimation widget that handles a lot of this logic
    // for you by simply providing the state machine (or animation) name.
    final file = await RiveFile.asset('assets/little_machine.riv');
    final artboard = file.mainArtboard.instance();
    final controller = StateMachineController.fromArtboard(
      artboard,
      'State Machine 1',
    );
    artboard.addController(controller!);
    setState(
      () => _artboardRenderer = RiveArtboardRenderer(
        antialiasing: true,
        fit: BoxFit.cover,
        alignment: Alignment.center,
        artboard: artboard,
      ),
    );
  }
  @override
  void initState() {
    super.initState();
    _animationController.repeat();
    _load();
  }
  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: _artboardRenderer == null
            ? const SizedBox()
            : CustomPaint(
                painter: RiveCustomPainter(
                  _artboardRenderer!,
                  repaint: _animationController,
                ),
                child: const SizedBox.expand(), // use all the size available
              ),
      ),
    );
  }
}
class RiveCustomPainter extends CustomPainter {
  final RiveArtboardRenderer artboardRenderer;
  RiveCustomPainter(this.artboardRenderer, {super.repaint}) {
    _lastTickTime = DateTime.now();
    _elapsedTime = Duration.zero;
  }
  late DateTime _lastTickTime;
  late Duration _elapsedTime;
  void _calculateElapsedTime() {
    final currentTime = DateTime.now();
    _elapsedTime = currentTime.difference(_lastTickTime);
    _lastTickTime = currentTime;
  }
  @override
  void paint(Canvas canvas, Size size) {
    _calculateElapsedTime(); // Calculate elapsed time since last tick.
    // Advance the artboard by the elapsed time.
    artboardRenderer.advance(_elapsedTime.inMicroseconds / 1000000);
    final width = size.width / 3;
    final height = size.height / 2;
    final artboardSize = Size(width, height);
    // First row
    canvas.save();
    artboardRenderer.render(canvas, artboardSize);
    canvas.restore();
    canvas.save();
    canvas.translate(width, 0);
    artboardRenderer.render(canvas, artboardSize);
    canvas.restore();
    canvas.save();
    canvas.translate(width * 2, 0);
    artboardRenderer.render(canvas, artboardSize);
    canvas.restore();
    // Second row
    canvas.save();
    canvas.translate(0, height);
    artboardRenderer.render(canvas, artboardSize);
    canvas.restore();
    canvas.save();
    canvas.translate(width, height);
    artboardRenderer.render(canvas, artboardSize);
    canvas.restore();
    canvas.save();
    canvas.translate(width * 2, height);
    artboardRenderer.render(canvas, artboardSize);
    canvas.restore();
    // Paint the full canvas size
    // artboardRenderer.render(canvas, size);
  }
  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}
/// Keeps an `Artboard` instance and renders it to a `Canvas`.
///
/// This is a simplified version of the `RiveAnimation` widget and its
/// RenderObject
///
/// This accounts for the `fit` and `alignment` properties, similar to how
/// `RiveAnimation` works.
class RiveArtboardRenderer {
  final Artboard artboard;
  final bool antialiasing;
  final BoxFit fit;
  final Alignment alignment;
  RiveArtboardRenderer({
    required this.antialiasing,
    required this.fit,
    required this.alignment,
    required this.artboard,
  }) {
    artboard.antialiasing = antialiasing;
  }
  void advance(double dt) {
    artboard.advance(dt, nested: true);
  }
  late final aabb = AABB.fromValues(0, 0, artboard.width, artboard.height);
  void render(Canvas canvas, Size size) {
    _paint(canvas, aabb, size);
  }
  final _transform = Mat2D();
  final _center = Mat2D();
  void _paint(Canvas canvas, AABB bounds, Size size) {
    const position = Offset.zero;
    final contentWidth = bounds[2] - bounds[0];
    final contentHeight = bounds[3] - bounds[1];
    if (contentWidth == 0 || contentHeight == 0) {
      return;
    }
    final x = -1 * bounds[0] -
        contentWidth / 2.0 -
        (alignment.x * contentWidth / 2.0);
    final y = -1 * bounds[1] -
        contentHeight / 2.0 -
        (alignment.y * contentHeight / 2.0);
    var scaleX = 1.0;
    var scaleY = 1.0;
    canvas.save();
    canvas.clipRect(position & size);
    switch (fit) {
      case BoxFit.fill:
        scaleX = size.width / contentWidth;
        scaleY = size.height / contentHeight;
        break;
      case BoxFit.contain:
        final minScale =
            min(size.width / contentWidth, size.height / contentHeight);
        scaleX = scaleY = minScale;
        break;
      case BoxFit.cover:
        final maxScale =
            max(size.width / contentWidth, size.height / contentHeight);
        scaleX = scaleY = maxScale;
        break;
      case BoxFit.fitHeight:
        final minScale = size.height / contentHeight;
        scaleX = scaleY = minScale;
        break;
      case BoxFit.fitWidth:
        final minScale = size.width / contentWidth;
        scaleX = scaleY = minScale;
        break;
      case BoxFit.none:
        scaleX = scaleY = 1.0;
        break;
      case BoxFit.scaleDown:
        final minScale =
            min(size.width / contentWidth, size.height / contentHeight);
        scaleX = scaleY = minScale < 1.0 ? minScale : 1.0;
        break;
    }
    Mat2D.setIdentity(_transform);
    _transform[4] = size.width / 2.0 + (alignment.x * size.width / 2.0);
    _transform[5] = size.height / 2.0 + (alignment.y * size.height / 2.0);
    Mat2D.scale(_transform, _transform, Vec2D.fromValues(scaleX, scaleY));
    Mat2D.setIdentity(_center);
    _center[4] = x;
    _center[5] = y;
    Mat2D.multiply(_transform, _transform, _center);
    canvas.translate(
      size.width / 2.0 + (alignment.x * size.width / 2.0),
      size.height / 2.0 + (alignment.y * size.height / 2.0),
    );
    canvas.scale(scaleX, scaleY);
    canvas.translate(x, y);
    artboard.draw(canvas);
    canvas.restore();
  }
}