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
[Tabs] Fix tab indicator flies off issue #65463
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we still need the first if case to deal with user tap a tab that are more than one tab away?
else if (value == index) | ||
_currentRect = middle; | ||
else if (value < index) | ||
_currentRect = previous == null ? middle : Rect.lerp(middle, previous, index - value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't this assume index - value is always <1.0 ? in case of user tapp on a tab, this value may be >1.0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes you are right. index - value
can be greater than 1.0
. I thought this was the solution for the case of selecting a tab during animation.
I added a gif that the case of user tap a tab that are more than one tab away. It seems no problem, but I'm not sure if this is the case you were talking about or not. If this is not the case of you intended, could you describe more detail situation for reproduce?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see, we are doing a negative lerp and using the wrong previous/next rect. They just so happen to cancel each other out.
for example we move from 0 to 2
it will lerp from -1.0 to 1.0, and from rect =1, to dest =2
so this just cancel each other out.
I think this is a dangerous because the lerp value should be from 0.0 to 1.0. We do not guarantee the behavior outside the range. it just so that our current implementation of Rect.lerp somehow works with this corner case. However, it is not guaranteed that it will still work the same in the future.
The correct logic should be as following, let's take the 0 to 2 example
it should lerp from 0.0 to 1.0 from rect 0 to dest 2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chunhtai When there are three tabs A, B, C, the image below is a scenario in which the user taps C and then taps B after the animation has progressed 25%.
Here you can see a case which the lerp value is greater than 1, and I think it can be relatively greater or less than 1 depending on which point the lerp is based on.
As this document also says that a value greater than 1 or less than 0 is not an invalid value, I think it is just a difference in mathematical expressions and there is no problem.
Nevertheless, if the lerp value should be between 0 and 1, I think that the function should be newly implemented regardless of the tapping problem during animation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to not work correctly if the sizes of the tab are not the same.
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
void main() {
timeDilation = 10.0;
runApp(Test());
}
class Test extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '',
home: Material(
color: Colors.black,
child: DefaultTabController(
length: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Tap 1 then tap 2.', style: TextStyle(color: Colors.white)),
TabBar(
isScrollable: true,
indicatorSize: TabBarIndicatorSize.tab,
tabs: <Widget>[
Text('-', style: TextStyle(fontSize: 48.0)),
Text('1ljkjlkjlk', style: TextStyle(fontSize: 48.0)),
Text('2', style: TextStyle(fontSize: 48.0)),
],
),
],
),
),
),
);
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. It doesn't work properly. It seems my current approach is wrong.
Sorry for wasting your time. 🙏
Hi @nero-angela are you still working on this PR? |
Hi @chunhtai, I'm waitting for your answer about this question. Could you please check my answer? |
@chunhtai At the moment of tabbing, the current indicator's position was recorded in the In this regard, is it better to close the current PR and create a new PR to proceed? |
I wonder whether we can do it in the following way: we have
We can try to grab the current tab index and the index that is one tab ahead. We can just lerp between this two tabs. For example: value = 1.4, index =5 we know the animation is moving to the right, for example value = 2.3, index = 1 we know the animation is moving to the left, For abs(value - index) < 1.0 , we just keep the logic as is. |
Wow, you are really smart 👍👍. I implemented the solution in the way you gave me. All of the cases found so far seem to work fine, but please check the code and let me know if it's not what you intended. One of the previous tests failed because the indicator's speed was changed normally, and I fixed it correctly. Check out the test-related code here. |
@nero-angela |
@nero-angela yes, I think that looks good, can you revise this PR with the change? |
@chunhtai |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Description
I think the code below is not only wrong, but unnecessary.
flutter/packages/flutter/lib/src/material/tabs.dart
Lines 400 to 404 in 54ade88
The reason why I think it's unnecessary is the below code already covers the case when the animation is running.
flutter/packages/flutter/lib/src/material/tabs.dart
Lines 406 to 421 in 54ade88
And as you can see the below code, it is implemented by putting
_currentRect
inRect.lerp()
and putting the result back in_currentRect
. Implemented in this way, the indicator will performs an acceleration motion. I prepared a dart pad to help you understand.flutter/packages/flutter/lib/src/material/tabs.dart
Line 403 in e682ec7
In the test, I think below comment is mentioned because the indicator performs acceleration motion.
flutter/packages/flutter/test/material/tabs_test.dart
Line 1656 in e682ec7
I have also identified the cause of the disappearance of the indicator, but I will skip this because the problematic code is replaced with the existing code.
Related Issues
Fixes #48000
Tests
I added the following test:
I fixed the following test:
flutter/packages/flutter/test/material/tabs_test.dart
Lines 1656 to 1661 in e682ec7
Checklist
Before you create this PR, confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]
). This will ensure a smooth and quick review process.///
).flutter analyze --flutter-repo
) does not report any problems on my PR.Breaking Change
Did any tests fail when you ran them? Please read Handling breaking changes.