Skip to content

[Scribe] Add animated icon#4309

Open
zatteo wants to merge 4 commits intomasterfrom
feat/scribe-lottie-icon
Open

[Scribe] Add animated icon#4309
zatteo wants to merge 4 commits intomasterfrom
feat/scribe-lottie-icon

Conversation

@zatteo
Copy link
Member

@zatteo zatteo commented Feb 4, 2026

  • Added lottie package to display animation in web
  • Moved the previous static icon in its own component and created a component that manage the loading of the appropriate icon (static or dynamic)

Animated icon is displayed in desktop only on hover for the Scribe tab button.

Demo

Screen.Recording.2026-02-04.at.13.37.25.mov

Summary by CodeRabbit

  • New Features
    • Animated AI assistant button on desktop web with hover-driven playback; platform-adaptive static button on other platforms.
    • New Lottie animation asset added for the assistant, enabling smoother, richer motion feedback.
  • Chores
    • Enabled Lottie support so animations play correctly in the app.

To display lottie animation in web
I moved the previous static icon in its own component and created a component that manage the loading of the appropriate icon: static or dynamic.
@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

Walkthrough

Adds a new Lottie animation asset at assets/animations/lottie-scribe.json and exposes it via a new ImagePaths getter animLottieScribe. Introduces two new widgets: AiAssistantAnimatedButton (hover-driven Lottie playback with an AnimationController) and AiAssistantStaticButton (stateless icon button). Updates AiAssistantButton to choose the animated widget on web desktop and the static widget on other platforms. Adds the lottie: 3.3.1 dependency.

Possibly related PRs

  • AI scribe UI #4197 — Implements additional AI Scribe UI components, assets, and wiring that overlap the new scribe widgets and the lottie asset added here.
  • AI scribe data layer and requirements #4196 — Modifies ImagePaths by adding new getters and touches scribe-related resources, creating a direct code-level overlap with this PR’s animLottieScribe addition.

Suggested reviewers

  • hoangdat
  • dab246
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '[Scribe] Add animated icon' directly reflects the main change: adding an animated icon (Lottie animation) for the Scribe AI assistant button on web desktop.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/scribe-lottie-icon

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@scribe/pubspec.yaml`:
- Around line 37-38: Update the lottie dependency version in pubspec.yaml:
locate the dependency entry "lottie: 3.3.1" and change it to "lottie: 3.3.2" so
the project uses the latest patch release; after updating, run pub get (or
flutter pub get) to refresh packages and verify there are no dependency
conflicts.
🧹 Nitpick comments (2)
scribe/lib/scribe/ai/presentation/widgets/button/ai_assistant_animated_button.dart (2)

54-55: Consider using ImagePaths for the Lottie asset path for consistency.

The asset path is hardcoded here, but ImagePaths.animLottieScribe was added in this PR specifically for this purpose. Using it would maintain consistency with how other assets are referenced throughout the codebase.

♻️ Suggested refactor

This would require passing ImagePaths to the widget similar to AiAssistantStaticButton:

 class AiAssistantAnimatedButton extends StatefulWidget {
   final void Function() onTapActionCallback;
   final EdgeInsetsGeometry? margin;
+  final ImagePaths imagePaths;

   const AiAssistantAnimatedButton({
     super.key,
     required this.onTapActionCallback,
+    required this.imagePaths,
     this.margin,
   });

Then use it in the build method:

         child: Lottie.asset(
-          'assets/animations/lottie-scribe.json',
+          widget.imagePaths.animLottieScribe,
           width: AIScribeSizes.aiAssistantIcon,

21-22: Consider using SingleTickerProviderStateMixin instead of TickerProviderStateMixin.

Since this widget only uses a single AnimationController, SingleTickerProviderStateMixin is more appropriate and slightly more efficient than TickerProviderStateMixin.

♻️ Suggested refactor
 class _AiAssistantAnimatedButtonState extends State<AiAssistantAnimatedButton>
-    with TickerProviderStateMixin {
+    with SingleTickerProviderStateMixin {

@zatteo zatteo requested review from dab246 and hoangdat February 4, 2026 12:52
@github-actions
Copy link

github-actions bot commented Feb 4, 2026

This PR has been deployed to https://linagora.github.io/tmail-flutter/4309.

_animationController.reset();
},
child: Lottie.asset(
'assets/animations/lottie-scribe.json',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use path from ImagePaths imagePaths.animLottieScribe

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I don't know why I did like this 🤔

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@scribe/lib/scribe/ai/presentation/widgets/button/ai_assistant_animated_button.dart`:
- Around line 49-56: Replace the MouseRegion usage that currently calls
_animationController.forward() in the onHover callback with onEnter so the
animation only triggers once on pointer entry; similarly swap onExit handling
remains the same to reset the controller. Locate the MouseRegion widget (where
_animationController.forward() and _animationController.reset() are referenced)
and change the callback name from onHover to onEnter, keeping the same event
parameter and behavior.
🧹 Nitpick comments (1)
scribe/lib/scribe/ai/presentation/widgets/button/ai_assistant_animated_button.dart (1)

24-25: Consider using SingleTickerProviderStateMixin.

Since there's only one AnimationController in this state class, SingleTickerProviderStateMixin is more appropriate and slightly more efficient than TickerProviderStateMixin.

♻️ Suggested change
-class _AiAssistantAnimatedButtonState extends State<AiAssistantAnimatedButton>
-    with TickerProviderStateMixin {
+class _AiAssistantAnimatedButtonState extends State<AiAssistantAnimatedButton>
+    with SingleTickerProviderStateMixin {

Avoid calling repeatly the forward method that leads to start and stop of Lottie animation
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@scribe/lib/scribe/ai/presentation/widgets/button/ai_assistant_animated_button.dart`:
- Around line 51-52: Guard the calls to _animationController.forward() and
_animationController.reverse() in the onEnter/onExit handlers so they only run
after the Lottie composition has set a duration (i.e. when the async onLoaded
callback has completed). Concretely, update the onEnter and onExit handlers in
ai_assistant_animated_button.dart to check a safe condition (for example
_animationController.duration != null or a boolean set when the onLoaded
callback runs, or that the Lottie composition/_lottieComposition is non-null)
before calling _animationController.forward()/reverse(); leave the onLoaded
callback that sets the controller duration unchanged.
🧹 Nitpick comments (1)
scribe/lib/scribe/ai/presentation/widgets/button/ai_assistant_animated_button.dart (1)

24-25: Prefer SingleTickerProviderStateMixin when using a single AnimationController.

Since this state class manages only one AnimationController, using SingleTickerProviderStateMixin is more appropriate and slightly more efficient than TickerProviderStateMixin.

♻️ Proposed change
 class _AiAssistantAnimatedButtonState extends State<AiAssistantAnimatedButton>
-    with TickerProviderStateMixin {
+    with SingleTickerProviderStateMixin {

Comment on lines +51 to +52
onEnter: (event) {
_animationController.forward();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against calling forward() before the Lottie composition is loaded.

AnimationController.forward() asserts that duration is not null. Since the duration is only set in the onLoaded callback (which fires asynchronously), a quick hover before the composition finishes loading will trigger an assertion failure in debug mode.

🐛 Proposed fix to guard against null duration
         onEnter: (event) {
-          _animationController.forward();
+          if (_animationController.duration != null) {
+            _animationController.forward();
+          }
         },

Also applies to: 63-64

🤖 Prompt for AI Agents
In
`@scribe/lib/scribe/ai/presentation/widgets/button/ai_assistant_animated_button.dart`
around lines 51 - 52, Guard the calls to _animationController.forward() and
_animationController.reverse() in the onEnter/onExit handlers so they only run
after the Lottie composition has set a duration (i.e. when the async onLoaded
callback has completed). Concretely, update the onEnter and onExit handlers in
ai_assistant_animated_button.dart to check a safe condition (for example
_animationController.duration != null or a boolean set when the onLoaded
callback runs, or that the Lottie composition/_lottieComposition is non-null)
before calling _animationController.forward()/reverse(); leave the onLoaded
callback that sets the controller duration unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants