Skip to content

feat: Multi-frame UI input for Animation transition - #1835

Draft
xar43 wants to merge 7 commits into
fossasia:developmentfrom
xar43:feature/animation-multi-frame
Draft

xar43 wants to merge 7 commits into
fossasia:developmentfrom
xar43:feature/animation-multi-frame

Conversation

@xar43

@xar43 xar43 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Fix #1736

Description

This draft PR updates the Animation UI to strictly match the standard textfield styling, ensuring a consistent and intuitive experience across the app.

When the "Animation" transition is selected, it introduces a frame-by-frame (flipbook style) input mechanism while retaining the look and feel of the primary textfield.


Key Changes Introduced

  • Unified Input Decoration: Applied the exact same InputDecoration as the main textfield for visual consistency.
  • Integrated Controls: Positioned the Emoji picker button on the left and the Font dropdown on the right of the input area.
  • Frame UI Enhancement: Frame boxes now render inside the textfield with clean visual boundaries and clear labels (e.g., Frame 1, Frame 2, etc.).
  • Scroll & Boundary Management: Implemented smooth horizontal scrolling so frame boxes clip cleanly when multiple frames are added.

Why is this a Draft PR?

This PR is opened in Draft status so core maintainers and mentors can review the architectural approach and UI layout early on.

Pending Work & Next Steps:

  • Perform final state-management tests to prevent data loss when switching between animation tabs.
  • Code refactor
video_2026-08-02_12-48-39.mp4

ing and incorporating initial feedback from @ctrlVnt and mentors.

CC: @ctrlVnt

Summary by Sourcery

Introduce a multi-frame text input experience for the Animation (Splitting) transition while aligning the animation textfield with the primary input styling and ensuring correct frame alignment and state handling across animations.

New Features:

  • Add a multi-frame input widget that lets users edit animation content frame by frame while sharing the main badge text controller.
  • Support frame separators in badge text using a control character so animations can treat each frame as a distinct screen segment.
  • Preserve and restore multi-frame content and the active frame index when switching between the Splitting transition and other animations, keeping edits intact.
  • Route clipart insertion into the currently focused frame when using the multi-frame input, rather than always targeting the global message field.

Bug Fixes:

  • Prevent fixed-position animations from using a negative horizontal offset when content is wider than the badge, avoiding misaligned rendering.
  • Stop ongoing animations and reset the active animation index when non-special animations receive an empty message, avoiding stale animation states.

Enhancements:

  • Refactor the homescreen animation input to use a shared textfield decoration, keeping emoji and font controls consistent between normal and splitting modes.
  • Update converter logic to treat frame breaks as explicit segments and pad the underlying LED matrix to badge-width boundaries for clean frame transitions.
  • Implement equality and hash semantics for badge animation types to make animation identity checks more reliable.
  • Adjust animation index handling to return null when no animation is active and add logging for easier debugging of selected animations.

Build:

  • Enable additional Gradle/Flutter migration flags in android/gradle.properties for Kotlin and the new Android DSL.

@sourcery-ai

sourcery-ai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Implements a multi-frame (flipbook-style) input experience for the Animation/Splitting transition, reusing the primary textfield decoration while wiring it into animation, converters, and image providers so each frame maps cleanly to badge frames and clipart insertion.

Sequence diagram for switching between Animation Splitting and other transitions

sequenceDiagram
  actor User
  participant AniContainer
  participant AnimationBadgeProvider
  participant InlineImageProvider
  participant TextEditingController as globalController

  User ->> AniContainer: onTap(index)
  AniContainer ->> AnimationBadgeProvider: getAnimationIndex()
  AnimationBadgeProvider -->> AniContainer: currentIndex

  alt leaving_splitting [currentIndex == 5 and index != 5]
    AniContainer ->> InlineImageProvider: getController()
    InlineImageProvider -->> AniContainer: globalController
    AniContainer ->> InlineImageProvider: savedMultiFrameText = globalController.text
    AniContainer ->> InlineImageProvider: savedActiveFrameIndex = activeFrameIndex
    AniContainer ->> globalController: text = activeFrameText
    AniContainer ->> InlineImageProvider: activeFrameController = null
  else entering_splitting [currentIndex != 5 and index == 5]
    AniContainer ->> InlineImageProvider: getController()
    InlineImageProvider -->> AniContainer: globalController
    AniContainer ->> InlineImageProvider: read savedMultiFrameText, savedActiveFrameIndex
    AniContainer ->> globalController: text = updatedFrames.join('\f')
    AniContainer ->> InlineImageProvider: savedMultiFrameText = null
    AniContainer ->> InlineImageProvider: savedActiveFrameIndex = null
  end

  opt special_animation [index >= 9]
    AniContainer ->> TextEditingController: text.trim()
    AniContainer ->> AniContainer: showSpecialAnimationDialog()
  end
Loading

File-Level Changes

Change Details Files
Refactored the homescreen text input to share decoration and conditionally render a multi-frame input when the Animation transition is active.
  • Wrapped the main text input area in a Consumer and derived an isAnimationMode flag based on animation index 5.
  • Extracted a shared InputDecoration that contains the emoji prefix icon and font dropdown suffix so both single-line and multi-frame modes look identical.
  • In animation mode, replaced ExtendedTextField with an InputDecorator hosting MultiFrameInputWidget wired to the global inlineimagecontroller and badgeAnimation callback; otherwise kept the original ExtendedTextField configuration.
lib/view/homescreen.dart
Introduced multi-frame-specific state and switching logic so entering/leaving the Animation (Splitting) transition preserves per-frame content and active frame selection.
  • In animation_container, added logic to capture the full multi-frame text (with \f separators) and active frame index when leaving splitting, and to restore them when re-entering, merging edits done in non-splitting modes.
  • Updated InlineImageProvider to track an activeFrameController plus savedMultiFrameText and savedActiveFrameIndex, and changed insertInlineImage to target the active frame controller when present.
  • Adjusted AnimationBadgeProvider.getAnimationIndex to return null when no animation matches and tweaked badgeAnimation to cancel timers and reset internal index instead of calling stopAllAnimations when message is empty for non-special animations.
lib/view/widgets/animation_container.dart
lib/providers/imageprovider.dart
lib/providers/animation_badge_provider.dart
Extended converters to understand frame separators and align frames to badge screen boundaries for correct multi-frame rendering.
  • Taught both the main and default-font converters to treat the form feed character (\f) as a frame separator, flushing current text segments and inserting frame_break markers into the segment list.
  • Added a private _padMatrixToNextFrame helper that pads the combined matrix with empty columns until its width is a multiple of the badgeScreenWidth (default 44).
  • Updated the segment-processing loops to handle frame_break by padding to the next frame boundary before processing subsequent text segments, ensuring each frame starts on a fresh 44-column screen.
lib/bademagic_module/utils/converters.dart
Implemented MultiFrameInputWidget to provide a horizontally scrollable, per-frame editor inside the textfield shell, wired to the global controller and image provider.
  • Created a stateful widget that maintains a list of per-frame TextEditingControllers and FocusNodes, initializing them by splitting the global controller text on \f and always ensuring at least one frame.
  • Synced frame edits back to the global controller by joining frame texts with \f and invoking a provided onChanged callback, while also listening to external changes on the global controller to rebuild or update frames without re-entrancy.
  • Designed the UI as a horizontally scrollable row of frame cards plus a separate add-frame button, with per-frame labels (Frame N), focus highlighting, delete controls (keeping at least one frame), and integrated FocusNode listeners that set InlineImageProvider.activeFrameController for clipart insertion.
  • Ensured proper cleanup by detaching listeners, disposing controllers/focus nodes, and clearing activeFrameController from the provider when the widget is disposed.
lib/view/widgets/multi_frame_input_widget.dart
Improved animation and build robustness for fixed animations and Gradle configuration.
  • Modified FixedAnimation to use max(0, (badgeWidth - newWidth) ~/ 2) when computing horizontalOffset to avoid negative offsets when content exceeds badge width.
  • Overrode == and hashCode in BadgeAnimation to compare by runtimeType, simplifying animation equality checks in providers.
  • Updated Android gradle.properties with builtInKotlin and newDsl flags added by Flutter migrator to keep the build configuration aligned with the upgraded toolchain.
lib/badge_animation/ani_fixed.dart
lib/badge_animation/animation_abstract.dart
android/gradle.properties

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@xar43
xar43 force-pushed the feature/animation-multi-frame branch from 8ea4dcd to 0bb6727 Compare August 2, 2026 07:32
@ctrlVnt

ctrlVnt commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Hi @xar43
Nice job!
I have some modification to you:

  • Can you change "frame" to "screen"? (like screen 1, screen 2, etc...)
  • I wanted to propose a different design, like just add a vertical bar "|" at the end of the screen

The implementation works on the badge, we need to adjust the longer of the screen. Now is too long, can you try to reduce every screen at least at "frame" longer please? As you can see frame1 is too long for 1 screen

PXL_20260803_093541516

@xar43

xar43 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Hi @ctrlVnt , thanks for the feedback!

  1. I've updated the labels from "Frame" to "Screen".
  2. I also reduced the width of the screen boxes so they better match the actual badge screen size.
  3. I still feel separate "Screen 1", "Screen 2" boxes are more intuitive for new users than a single text field with |.

Let me know what you think about the updated width. If you still prefer the | approach, I can implement that instead.

@ctrlVnt

ctrlVnt commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Yes, I prefere the | approach please

@xar43

xar43 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Hey @ctrlVnt, I've updated the implementation to use the | separator approach as discussed and resolved all merge conflicts. Ready for final review

WhatsApp.Video.2026-08-05.at.1.14.13.PM.mp4

@ctrlVnt

ctrlVnt commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Hi @xar43
I like your implementation
I'm thinking to find a better solution to tell people this behavior. It is possible also to add a divider after some n° of characters? for example 6?

I'm thinking about add a button near to fonts that tell "insert screen" or "add screen" to add this character

This branch has not been deployed

No deployments
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.

[Bug] "Anmation" transition isn't intuitive

2 participants