Skip to content

Conversation

MritunjayTiwari14
Copy link

@MritunjayTiwari14 MritunjayTiwari14 commented Aug 27, 2025

Pull Request: [Ink Splash Poll Vote Indicator Implementation](polls: Add UI feedback on vote button while request pending #1808)

Hello Moderators of Zulip Flutter,
This is Mritunjay Tiwari.
This is my first ever contribution to Zulip.


📌 My Solution

Implementation Video and Images

test_video.mp4

After looking at the Suggestion for the issue made by the moderators, implementation of a Ink Splash upon Tap on Poll Vote is successfully done.

So, the implementation of the Ink Splash Widget is done by replacing the container of the Poll Vote button with a Ink Widget and introducing InkWell and Material Widgets as its ancestors. Making the Material as its Ancestors allows the Splash on of the Ink on the Button which eliminate the risk of leaking of the Splash outside the Poll Vote Button.


Thank you for reviewing my contribution!

💻 Code Snippet

(Added Comments here only to explain the changes, Review the diff for the changes without the addition of the comments)

ConstrainedBox(
            constraints: const BoxConstraints(minWidth: 44, minHeight: 44),
            child: Padding(
              padding: const EdgeInsetsDirectional.only(
                end: 5, top: verticalPadding, bottom: verticalPadding),
              child: Material( //Personal Comments for PR: Eliminates the Leaking of the Ink Splash, Ink Splash is limited to the Material
                color: Colors.transparent,
                child: InkWell( //Personal Comments for PR: Indication to Ink To Splash the Ink on the _toggleVote button
                  onTap: () => _toggleVote(option),
                  child: Ink(  //Personal Comments for PR: Here we Replaced the Container with the Ink Widget
                    padding: const EdgeInsets.symmetric(horizontal: 4),
                    decoration: BoxDecoration(
                      color: theme.colorPollVoteCountBackground,
                      border: Border.all(color: theme.colorPollVoteCountBorder),
                      borderRadius: BorderRadius.circular(3)),
                    child: Center(
                      child: Text(option.voters.length.toString(),
                        style: textStyleBold.copyWith(
                          color: theme.colorPollVoteCountText, fontSize: 20))))))))
  }

Copy link
Contributor

@apoorvapendse apoorvapendse left a comment

Choose a reason for hiding this comment

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

Thanks for the PR @MritunjayTiwari14,
Let's fix up the commit message by following the commit style guidelines.

Something like:

poll: Display UI feedback on poll delay.

<description>

Fixes: #1808

Might be a better way to write this.
Feel free to add details and helpful info in the commit description.

Copy link
Contributor

@apoorvapendse apoorvapendse left a comment

Choose a reason for hiding this comment

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

Starting a thread in the #mobile-design channel might be useful to gain initial feedback on this.

@MritunjayTiwari14
Copy link
Author

Thanks for the PR @MritunjayTiwari14, Let's fix up the commit message by following the commit style guidelines.

Something like:

poll: Display UI feedback on poll delay.

<description>

Fixes: #1808

Might be a better way to write this. Feel free to add details and helpful info in the commit description.

Thank you very much for Reviewing! @apoorvapendse
The commit description has been successfully Updated as per commit style guidelines.

@MritunjayTiwari14
Copy link
Author

Starting a thread in the #mobile-design channel might be useful to gain initial feedback on this.

Okay, I will look forward into this Channel for further feedback.

@apoorvapendse
Copy link
Contributor

Cool, let's line wrap the description to <= 70 characters per line.
See formatting guidelines.

@alya
Copy link
Collaborator

alya commented Sep 2, 2025

If we show a spinner, I'd expect it to appear on the poll, not blocking the user from reading their other messages.

@MritunjayTiwari14
Copy link
Author

Cool, let's line wrap the description to <= 70 characters per line. See formatting guidelines.

I will make sure to use line wrap in the Description from Now, thank you.

@MritunjayTiwari14
Copy link
Author

If we show a spinner, I'd expect it to appear on the poll, not blocking the user from reading their other messages.

Okay, I’ll work on implementing a spinner similar to the Zulip web poll system.

@MritunjayTiwari14
Copy link
Author

Improve Poll Vote Handling: Optimistic Updates with Store Synchronization

Video Testing of Solution

record.mp4

Background

While working on poll vote handling, we wanted the UI to feel responsive but also stay consistent with server-confirmed data.
Initially, the _toggleVote implementation in the widget relied only on server confirmation, which introduced noticeable lag before the UI updated.

The requirement was:

  • Keep the data layer (PerAccountStore) responsible for state updates.
  • Keep the UI layer focused only on rendering.
  • Provide fast feedback to the user without waiting for server round trips.

Difficulties Faced

  1. Event Timing Uncertainty

    • We initially explored detecting the exact time a vote change arrived from the backend using _modelChanged.
    • This was difficult to reason about, fragile to implement, and created unnecessary complexity.
  2. Lag Without Optimistic Updates

    • If we waited only for server events, the user would experience noticeable delays before their vote appeared.
    • This degraded the user experience and made the interface feel unresponsive.
  3. Navigation & State Sync Issues

    • At one point, we experimented with forcing UI rebuilds via navigation as a workaround to sync state.
    • This was brittle and highlighted the need for a cleaner architecture.
  4. Text Lifting Problem

    • When votes were updated, the vote count text would sometimes “jump” or lift visually due to layout changes.
    • This was solved by changing the alignment of the vote count to center, stabilizing its position during updates.

Resolution

After carefully studying the structure of the Poll model, the Model layer, and the API contract, we realized that a clean and reliable solution could be achieved by adopting optimistic updates with store synchronization:

  • When the user votes:

    1. PerAccountStore immediately updates the local poll model (optimistic update).
    2. A spinner or temporary UI state shows that the vote is “in progress.”
    3. The submessage is sent to the server.
  • When the server confirmation (SubmessageEvent) arrives:

    • The store reconciles the local state with the authoritative server state.
    • Listeners are notified again, ensuring the UI reflects the final, confirmed state.

Outcome

  • Fast feedback: Users see their vote reflected instantly, avoiding lag.
  • Cleaner UI: Alignment changes and loading indicators make the vote count stable and visually consistent during updates.
  • Cleaner architecture: Store handles all state and reconciliation, while the widget focuses only on rendering.
  • Technical debt cleared: Earlier issues around timing, _modelChanged misuse, and navigation hacks were fully resolved.
  • Edge case tested: Added testing to handle abrupt or multiple simultaneous vote updates to ensure the UI and store remain consistent.
  • Informed design: The final solution was arrived at only after a thorough study of the Poll structure, model APIs, and event flow in the codebase.

@MritunjayTiwari14
Copy link
Author

MritunjayTiwari14 commented Sep 12, 2025

Extremely sorry for not removing the import that i used in my previous commit but then refactored my code so that it was not required which caused CI error. I have fixed that in the latest commit.

Copy link
Contributor

@apoorvapendse apoorvapendse left a comment

Choose a reason for hiding this comment

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

We don't want to keep merge commits in the PR branch.
Can you rebase this branch with upstream/main?

@MritunjayTiwari14
Copy link
Author

We don't want to keep merge commits in the PR branch. Can you rebase this branch with upstream/main?

Sure!

@MritunjayTiwari14
Copy link
Author

MritunjayTiwari14 commented Sep 17, 2025

@apoorvapendse Extremely Sorry for the inconvenience caused, when i tried to clean my commit history to start a fresh rebase. Git automatically closed this issue because it didn't found any commit from my origin/main.

I have reopen the PR after rebasing upstream/main.

In this commit, we added Material, InkWell and Ink
Widget to account for the Ink Splash effect for UI feedback.

Fixes: zulip#1808
@MritunjayTiwari14
Copy link
Author

@gnprice It will be really helpful if I could get your feedback on the Updated Description of the Pull Request.

Copy link
Member

@gnprice gnprice left a comment

Choose a reason for hiding this comment

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

Thanks @MritunjayTiwari14, and thanks @apoorvapendse for the previous reviews! A couple of comments below.

Linking also the #mobile-design thread here: #mobile-design > loading indicator on voting in poll

Comment on lines -96 to -101
child: Padding(
// For accessibility, the touch target is padded to be larger
// than the vote count box. Still, we avoid padding at the
// start because we want to align all the poll options to the
// surrounding messages.
padding: const EdgeInsetsDirectional.only(
Copy link
Member

Choose a reason for hiding this comment

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

What's the reason for deleting this comment? Seems like it's still equally relevant.

color: Colors.transparent,
child: InkWell(
onTap: () => _toggleVote(option),
child: Ink(
Copy link
Member

Choose a reason for hiding this comment

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

What's the effect of this Ink widget? Does the UI behave any differently if you take it out?

This catches my eye because we don't currently use an Ink widget anywhere else in the app (while we use Material and InkWell all the time).

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.

4 participants