🦭

【Flutter】Rive アニメーションに Flutter からテキストを入力する

2024/11/09に公開

はじめに

インタラクティブなアニメーションを作る場合、Rive を使いたいですよね?
アプリ内で出入りの多い、テキストをアニメーション内に流し込みたい。
そんなことをやっていきます。

https://rive.app/

実行例
サンプルはテキストを Flutter で入力し、Update ボタン で Rive のテキストを更新します。
PUSH(Rive 内のボタン)を押してアニメーションする。

1. Rive でアニメーションを作成

Rive でアニメーションを作成する。
PUSH ボタンを押すとアニメーションするのは Rive 側で実装しています。
テキストのウェーブのような動きは下記が参考になると思います。

https://www.youtube.com/watch?v=V-QzXkhMu20

次に、
動的に扱いたいテキストの"Text Run"オブジェクトの名前を任意のもの変更する。
今回は"textInput"とする。
実行例
右クリックから Export Name を選択する。
実行例
表示が"[ ]"形式に変更されれば OK
実行例

下記は、今回作成したサンプルアニメーションです。
https://rive.app/community/files/14274-26871-textinputsample/

2. Flutter で実装

上記で作成したアニメーションをインポートします。
基本的な Rive の Flutter での実装は他に譲ります。

Flutter 内では先程指定した任意の名前"textInput"を使って TextRun オブジェクトを取得します。

artboard.component<TextValueRun>('textInput');

初期化時に取得

TextValueRun? _textRun;

void _onRiveInit(Artboard artboard) {
  _artboard = artboard;
  final controller = StateMachineController.fromArtboard(
    artboard,
    'State Machine 1',
  );
  artboard.addController(controller!);
  _textRun = artboard.component<TextValueRun>('textInput'); // <-
  _resetTrigger = controller.getTriggerInput('reset');
}

あとは任意のテキストを入力して更新するだけです。

void _updateText(String newValue) {
  setState(() {
    if (_textRun != null) {
      _textRun!.text = newValue;
    }
  });
}

最後に

基本的な実装は下記が参考になるかなと思うので、宜しければ。
機能のアップデートも多い Rive ですので古い情報となっている場合はすいません 🙇
2024/11 v0.8 の情報になります。
https://zenn.dev/s134/articles/20240229rive

実行例

コード全文
class RiveTextInputWidget extends StatefulWidget {
  const RiveTextInputWidget({super.key});

  
  _RiveTextInputWidgetState createState() => _RiveTextInputWidgetState();
}

class _RiveTextInputWidgetState extends State<RiveTextInputWidget> {
  TextValueRun? _textRun;
  Artboard? _artboard;
  SMITrigger? _resetTrigger;
  String _setText = '';

  final TextEditingController _textController = TextEditingController();

  void _onRiveInit(Artboard artboard) {
    _artboard = artboard;
    final controller = StateMachineController.fromArtboard(
      artboard,
      'State Machine 1',
    );
    artboard.addController(controller!);
    _textRun = artboard.component<TextValueRun>('textInput');
    _resetTrigger = controller.getTriggerInput('reset');
  }

  void _updateText(String newValue) {
    setState(() {
      if (_textRun != null) {
        _textRun!.text = newValue;
        _setText = newValue;
      }
    });
    _resetTrigger?.fire();
    _textController.clear();
  }

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        Expanded(
          child: RiveAnimation.asset(
            'assets/rive/text_input.riv',
            onInit: _onRiveInit,
            fit: BoxFit.contain,
          ),
        ),
        Container(
          width: double.infinity,
          margin: const EdgeInsets.all(24.0),
          decoration: const BoxDecoration(
              border:
                  Border(bottom: BorderSide(width: 2, color: Colors.white))),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              const Text("Set Text",
                  style: TextStyle(fontSize: 24, color: Colors.white)),
              Text(_setText, style: const TextStyle(fontSize: 24)),
            ],
          ),
        ),
        Padding(
          padding: const EdgeInsets.all(24.0),
          child: TextField(
            controller: _textController,
          ),
        ),
        ElevatedButton(
            onPressed: () => _updateText(_textController.text),
            child: const Text('Update')),
      ],
    );
  }
}


Discussion