Application Design II / Task 3: Micro Interaction Prototype

22/04/2026 - 10/7/2026 (Week 1 - Week 12)

Chang Wing / 0367807 

Application Design II / Bachelors of Design (Honours) in Creative Media / Taylor's University

Task 3: Individual Task – Micro Interaction Prototype


TABLE OF CONTENTS




INSTRUCTIONS



Figure 1.1 Task 3 Project Brief



BUILD PROCESS

Animation

As for the animation, I tried using Google Gemini to generate an animation from an image, but the output doesn't have a transparent background. Another con is, video generation in Google Gemini consumes a lot of usage quota, far more than text or image generation.

Gemini's animation generation also produces mp4 rather than Lottie (.json) files, so the generated video cannot be used directly in FlutterFlow as a Lottie animation. Converting from mp4 to JSON also doesn't help because the generated mp4 does not have a transparent background. 

I also looked into AI tools that generate Lottie JSON animations directly, but most of them require a premium subscription.


Figure 2.1 Google Gemini generated animation from image


So, I use the traditional way instead, I exported the assets as SVGs from Figma, then grouped related layers in Adobe Illustrator to make easier to animate individually in Adobe After Effects. After importing the Illustrator (.ai) file into After Effects, I added subtle animations to the illustration.


Figure 2.2 Animating with Adobe After Effects


Figure 2.3 Animating with Adobe After Effects

Figure 2.4 Animating with Adobe After Effects




Issues when exporting JSON from Adobe After Effects

To export animations as Lottie (.json) files for use in FlutterFlow, I installed the Bodymovin plugin in Adobe After Effects. However, the exported JSON file contained image assets instead of editable vector data. This occurred because the Illustrator (.ai) layers were imported as footage layers rather than native After Effects Shape Layers.

To ensure proper Lottie export, vector artwork needs to be converted using Create → Create Shapes from Vector Layer before exporting through Bodymovin. This allows Bodymovin to convert the artwork into Lottie-supported vector paths and animations.

However, layers containing animations created using the Puppet Pin Tool could not be converted successfully using Create → Create Shapes from Vector Layer. Attempting to convert these layers caused the composition preview to become black, indicating a failed conversion. This occurs because the Puppet Tool relies on mesh-based deformation, which is not supported by the Lottie format used by Bodymovin. As a result, these deformation-based movements cannot be preserved in the exported JSON file.

To maintain compatibility with FlutterFlow, animations need to rely on Lottie-supported properties such as:

  • Position
  • Scale
  • Rotation
  • Opacity
  • Shape Path animation

Therefore, character or object movements created using the Puppet Tool need to be recreated using transform-based animations before exporting through Bodymovin.

To preserve the Puppet Tool animations, Mr. Miguel suggested an alternative workaround which is to export the animation from Adobe After Effects as a transparent GIF using Adobe Media Encoder, then upload to Flutterflow it as an image (but it will play the GIF). However, this approach sacrifices some quality and flexibility compared to Lottie because GIF files have larger file sizes and FlutterFlow provides minimal playback control for GIFs, meaning animations cannot be easily paused, delayed, restarted, or controlled programmatically as they can with Lottie animations, in Flutterflow.

Figure 2.5 Bodymovin Plugin from aescripts.com (Unable to export puppet tool movements)
 


Below are the animations (animated in After Effects) incorporated in my app:

Figure 2.6 Welcome Page Animation exported as Transparent gif and imported into Flutterflow


Figure 2.7 Consent to Verify Page Animation exported as Transparent gif and imported into Flutterflow



Figure 2.8 Account Created Feedback Animation exported as Transparent gif and imported into Flutterflow


Figure 2.9 Job Application Submission Successful Feedback Animation exported as Transparent gif and imported into Flutterflow






Figure 2.10 Stepper Animation exported as JSON and imported into Flutterflow



Error Prevention Feedbacks
  • On the verification screen, use FlutterFlow's Pin Code Field widget set to 6 digits.
  • On the "Continue" button: Action → Custom Action → verifyCode, passing the page parameter email and the Pin Code field's value.
  • Add a Conditional Action: if verifyCode returns true
    • Action: Firebase Auth > Create Account with Email & Password (you'll need a password — either have the user set one on the signup screen, or generate a random one if you're doing passwordless-style access)
    • Then Navigate To your home page.
  • If false → show an error text/snackbar like "Invalid or expired code."

Figure 2.11 Error text implementation

Figure 2.12 Verification code input error prevention



Simple Search

I followed the tutorial below to implement the simple search feature. However, instead of storing the search state as an App State variable as demonstrated in the tutorial, I used a Page State variable instead.

This is because I wanted each search bar to manage its own active state within the page. Whenever a user taps one of the search bars, the corresponding Page State variable (e.g., searchIsActive) is updated, while the other two search bars are set to false. This ensures that only one search bar is active at a time, allowing the correct GridView to be displayed through Conditional Visibility (i.e., searchIsActive == false) without conflicting with the other search sections.




Figure 2.14 Inputting jobposts datas 



Like Button

I didn't want likes to disappear every time the app restarted, so instead of storing "liked" in the app's temporary state, I saved it straight to Firestore as a field on the post itself.

Each job post has an isLiked field. Tapping the heart updates that exact post's document (true or false), not the whole list. Two heart icons are stacked on top of each other, and I just show whichever one matches the post's current isLiked value.

So when the app reopens, it's not "remembering" anything on its own, it's just reading the same data it already saved. 


Figure 2.15 Like btn demo in my app

This is how I implemented the like button that remember itself:

1. A boolean field on the data itself

Each jobposts document has an isLiked field (Boolean, default false). When the like button's clicked, value will be set to 'true' or 'false' and gets stored straight into Firebase.


Figure 2.14 Heart icon (outline/unfilled) → on tap → Update Document → sets isLiked = true

2. Two tap actions tied to one specific document

  • Heart icon (outline/unfilled) → on tap → Update Document → sets isLiked = true on that exact post's Firestore reference
  • Heart icon (filled) → on tap → Update Document → sets isLiked = false on the same reference

Figure 2.16 Heart icon (outline/unfilled) → on tap → Update Document → sets isLiked = true

Because the action references the specific document (jobpostsItem.reference), only the post that was tapped gets updated — not the whole collection.

3. Conditional visibility, driven by live data
The two heart icons are stacked, each visible only when:

  • isLiked == false → show outline heart
  • isLiked == true → show filled heart

Figure 2.17 Icon conditional visibility (isLiked == false)

Why it survives app restart

Because the "source of truth" for the like status was never stored in the app's memory, it was always stored in Firestore. When the app reopens and re-queries the jobposts collection, it pulls the current isLiked value straight from the database, so the heart renders in its correct (already-saved) state automatically.


Tooltips

I added tooltips to help users better understand the three available options: creating a resume within the app, uploading an existing resume, or proceeding without a resume (optional) to reduce confusion around these three buttons and make it clear that users only need to choose one of the available actions.

I followed the tutorial: Tooltip | Flutterflow to add the tooltips.

Figure 2.14 Tooltip on Edit button

Figure 2.14 Tooltip on Upload button


Figure 2.18 Tooltip on Delete / Do not include button



Animated Page Transition vs Instant Page Transition

I used different page transition styles based on the user's navigation intent.

Animated Sliding Page Transition is used when users move through a sequence of related screens, such as progressing through onboarding, forms, or multi-step flows. The sliding animation provides a clear sense of direction and continuity, helping users understand that they are moving forward or backward within the same journey.

In contrast, Instant Page Transition is used when switching between main sections of the app through the navigation bar. Since these pages represent independent destinations rather than sequential steps, displaying them immediately makes navigation feel faster and more responsive. This aligns with users' expectations for bottom navigation, where they can quickly jump between sections without waiting for transition animations.

Figure 2.19 Animated Sliding Page Transition

Figure 2.20 Instant Page Transition



Hover, Default, Active States


Figure 2.21 Switch toggle on / off states


Figure 2.22 Btn active / inactive states


Figure 2.23 Btn active / inactive states


Figure 2.24 Loading indicator


Pop Up Confirmation for Error Prevention


Figure 2.25 Pop Up Confirmation Message


Action Success Feedback

Figure 2.26 Success Feedback Pop Up


Image Posts Scrolling


Figure 2.27 Snap scrolling with Flutterflow's Page View widget




REFLECTION

This task exposed me to converting animations into suitable file formats for FlutterFlow, particularly JSON files, which was a completely new file type that I had not encountered before. Through this process, I learned that JSON-based animations, such as Lottie files, are more suitable for applications because they are lightweight, scalable, and can maintain animation quality across different screen sizes without significantly increasing the app file size.

Throughout this task, I explored different approaches to creating animations for applications, including experimenting with AI tools to generate and assist with animation production. Although I eventually switched back to the traditional animation workflow, this exploration helped me better understand the advantages and limitations of different animation methods. It also allowed me to discover how animations can be optimised and integrated into applications more effectively.

Overall, this task expanded my understanding that both macro and micro-interactions are not only visual enhancements but also important tools for improving usability and guiding the user through the app. For example, different animation states and colour changes can provide immediate feedback to users, helping them understand whether an action is successful, in progress or requires attention. This showed me that well-designed motion plays an important role in making UIs feel more responsive and engaging, while ensuring that animations serve a functional purpose rather than being purely decorative.





Comments