Skip to content

animate cupertino time picker hour label #172850

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

MaironLucas
Copy link
Contributor

@MaironLucas MaironLucas commented Jul 28, 2025

This PR adds the buildAnimatedLabel function to the CupertinoTimePicker widget, allowing the creation of a label with a FadeTransition in this component. It also uses this new function to build de hour label, because this label has this animation in the native implementation, as can be seen in the below visual reference:

trim.D05A047A-23D4-482B-A85D-A84FE2FD725F.MOV

Closes #27515

Before After
time-picker-before.mp4
time-picker-after.mp4

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@github-actions github-actions bot added framework flutter/packages/flutter repository. See also f: labels. f: cupertino flutter/packages/flutter/cupertino repository labels Jul 28, 2025
@MaironLucas
Copy link
Contributor Author

Some time ago, I opened a PR related to this(#164292), but my implementation differed from the native one. Now I'm bringing a new implementation that replicates the native behavior and will work for any suffix, supporting languages beyond English. I still need to make a few adjustments to the golden test, but overall, I believe it's ready for review.

@MaironLucas MaironLucas marked this pull request as ready for review July 30, 2025 19:21
@dkwingsmt dkwingsmt self-requested a review August 6, 2025 18:29
@dkwingsmt
Copy link
Contributor

Awesome! I'll take a look.

@dkwingsmt dkwingsmt requested a review from victorsanni August 6, 2025 18:30
Copy link
Contributor

@victorsanni victorsanni left a comment

Choose a reason for hiding this comment

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

Hi, @MaironLucas, thanks for the PR. Can you run dart format packages/flutter/lib/src/cupertino/date_picker.dart to fix the CI failures?

@MaironLucas MaironLucas force-pushed the 27515 branch 2 times, most recently from d7712fb to 1b990f0 Compare August 13, 2025 20:40
@flutter-dashboard
Copy link

Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change).

If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

Changes reported for pull request #172850 at sha 1b990f0

@flutter-dashboard flutter-dashboard bot added the will affect goldens Changes to golden files label Aug 13, 2025
@@ -53,6 +53,10 @@ const double _kTimerPickerLabelFontSize = 17.0;
// The width of each column of the countdown time picker.
const double _kTimerPickerColumnIntrinsicWidth = 106;

// Value derived using a video captured from the Clock app and a video editor to
// analyze animation duration.
const Duration _kTimePickerLabelAnimationDuration = Duration(milliseconds: 200);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you specify what iOS device and version this value was observed on?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dkwingsmt, could you help me with that? I used the video that you put in this comment to find this value.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure! It's an iPhone 16 pro running iOS 18.

- Add AnimatedSwitcher to animate the label of hour when the picker
changes

ref flutter#27515
@flutter-dashboard
Copy link

Golden file changes are available for triage from new commit, Click here to view.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

Changes reported for pull request #172850 at sha 025b118

Copy link
Contributor

@dkwingsmt dkwingsmt left a comment

Choose a reason for hiding this comment

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

Sorry for the late review!

In general looks great (I love the findAnimatedLabelWithText util function and how the text is shown after splitting).

@@ -53,6 +53,10 @@ const double _kTimerPickerLabelFontSize = 17.0;
// The width of each column of the countdown time picker.
const double _kTimerPickerColumnIntrinsicWidth = 106;

// Value derived using a video captured from the Clock app and a video editor to
// analyze animation duration.
const Duration _kTimePickerLabelAnimationDuration = Duration(milliseconds: 200);
Copy link
Contributor

Choose a reason for hiding this comment

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

Sure! It's an iPhone 16 pro running iOS 18.

///
/// [labelBuilder] is a function that takes a value and returns the full label text.
/// [currentValue] is the current value to display.
/// [baseValue] is the value used to split the label into prefix and suffix.
Copy link
Contributor

Choose a reason for hiding this comment

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

Since baseValue is always 1, i recommend removing it from _buildAnimatedLabel's parameters and just say final String baseLabel = labelBuilder(1); with a short comment.

}) {
final String baseLabel = labelBuilder(baseValue);
final String currentLabel = labelBuilder(currentValue);
final List<String> parts = currentLabel.split(baseLabel);
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of split, use currentLabel.substring(baseLabel.length), which is IMO clearer and also avoids a (not-yet-existing) problem where baseLabel appears for multiple times.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, add a comment that we only support the case where baseLabel is a prefix of (or equal to) currentLabel, since we haven't observed other cases.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, if you want to be super safe, instead of showing baseLabel as the prefix, show currentLabel.substring(0, baseLabel.length) to avoid the (not-yet-existing) problem where baseLabel is not a prefix.

child: AnimatedSwitcher(
duration: _kTimePickerLabelAnimationDuration,
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition(opacity: animation, child: child);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain why you need the custom transition builder to remove the key from the FadeTransition?

@@ -2636,6 +2657,66 @@ void main() {
lastOffset = tester.getTopLeft(find.text('11'));
expect(tester.getTopLeft(find.text('2022')).dy, lastOffset.dy);
});

group('CupertinoTimerPicker hour label animation', () {
testWidgets('animates from plural to singular when scrolling from 2 to 1', (
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems to me that these tests would also pass if the text is not animated. If so, can you think of a way to test it? Maybe find the FadeTransition / RenderAnimatedOpacity around the s text and verify its opacity?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
f: cupertino flutter/packages/flutter/cupertino repository framework flutter/packages/flutter repository. See also f: labels. will affect goldens Changes to golden files
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cupertino Time picker hour plural label should fade in and out
3 participants