Repository navigation
Conversation
* master: (209 commits) Allow mouse hover to only respond to some mouse events but not all. (flutter#30886) Fix issue 23527: Exception: RenderViewport exceeded its maximum number of layout cycles (flutter#30809) Keep hover annotation layers in sync with the mouse detector. (flutter#30829) Use identical instead of '==' in a constant expression. (flutter#30921) Add toggle for debugProfileWidgetBuilds (flutter#30867) Revert "Manual engine roll with disabled service authentication codes (flutter#30919)" (flutter#30930) Manual engine roll with disabled service authentication codes (flutter#30919) Baseline Aligned Row (flutter#30746) [Material] Fix showDialog crasher caused by old contexts (flutter#30754) Let `sliver.dart` `_createErrorWidget` work with other Widgets (flutter#30880) Add more dialog doc cross-reference (flutter#30887) Allow downloading of desktop embedding artifacts (flutter#30648) CupertinoDatePicker initialDateTime accounts for minuteInterval (flutter#30862) add golden tests for CupertinoDatePicker (flutter#30828) Simplify toImage future handling (flutter#30876) Fixed Table flex column layout error flutter#30437 (flutter#30470) Fix iTunes Transporter quirk (flutter#30883) Bump Android build tools to 28.0.3 in Dockerfile (flutter#30832) Update the upload key which seems to have trouble for some reason (flutter#30871) Check for invalid elevations (flutter#30215) ...
|
CC @justinmc |
justinmc
left a comment
There was a problem hiding this comment.
A few improvements can be made I think, but this is pretty close. I checked out your branch locally and tried this myself and it works great.
|
|
||
| /// The direction of the triangle attached to the toolbar. | ||
| /// | ||
| /// Defaults to showing the triangle downwards if sufficient space is available |
There was a problem hiding this comment.
I think that you shouldn't describe something like a default here where the enum is defined, because it's far from where the code that defines the default is actually located. This enum could be reused somewhere else with a different default, or the default behavior could change below and the author might forget to update this documentation.
| @override | ||
| bool shouldRepaint(_TextSelectionToolbarNotchPainter oldPainter) => false; | ||
| bool shouldRepaint(_TextSelectionToolbarNotchPainter oldPainter) { | ||
| return triangleOffsetX != oldPainter.triangleOffsetX; |
There was a problem hiding this comment.
triangleOffsetX is final, so it can never change, and this check shouldn't be necessary, right? Same for arrowDirection.
|
|
||
| /// If the setState method is called immediately, | ||
| /// the toolbar display position is wrong. | ||
| _timer = Timer(const Duration(milliseconds: 50), () { |
There was a problem hiding this comment.
Can you explain this more? I worry about adding a timer like this.
There was a problem hiding this comment.
This timer workaround looks like a hack to me.
Maybe the toolbar should use multi child layout, so you can have the size of the toolbar ready when you position the triangle?
| } | ||
|
|
||
| class _TextSelectionToolbarState extends State<_TextSelectionToolbar> { | ||
| double _triangleOffsetX = double.nan; |
There was a problem hiding this comment.
Could this be provided in the constructor instead of starting as nan and needing to call updateTriangleOffsetX?
|
|
||
| void _updateTriangleOffsetX(double triangleOffsetX) { | ||
| if (_toolbarStateGlobalKey.currentState != null) { | ||
| _toolbarStateGlobalKey.currentState.updateTriangleOffsetX(triangleOffsetX); |
There was a problem hiding this comment.
This seems to me like it should just be passed into _TextSelectionToolbar like a normal parameter rather than manually updated like this. Maybe you were running into problems that caused you to use this pattern and a timer?
There was a problem hiding this comment.
The exact position of the triangle requires the true width of the toolbar to be determined to prevent the triangle from appearing outside the toolbar.

Within the _TextSelectionToolbar class, we only know the number and type of menus to display but don't know the true width of the toolbar. Only the childSize parameter in the Offset getPositionForChild(Size size, Size childSize) method in _TextSelectionToolbarLayout class is the final true width of the toolbar, so it is necessary in _TextSelectionToolbarLayout class to calculate the true position of the triangle and pass the data out.
As for why a Timer is needed to delay execution of the setState(() {}); method by 50 milliseconds? Immediate execution causes the toolbar to always appear in the upper left corner of the screen. : (

Of course, the current implementation is not a very good solution, I hope we can discuss a better one. : )
There was a problem hiding this comment.
@jiisd I just dug into this and I think I agree with @LongCatIsLooong that MultiChildLayoutDelegate is the way to go. You should be able to position the triangle inside the delegate without having to pass around triangleOffsetX and without having to use a timer.
That will require a bit of refactoring existing code. Let me know if you don't mind taking that on, otherwise I can take over the PR and do it myself if you want.
There was a problem hiding this comment.
Although this solution now achieves this effect, it is very difficult to understand, and the responsibilities of the class are not clear.;(
I think the MultiChildLayoutDelegate approach is better than the current one. Of course, it would be better if you had time to work on it together.🤝
There was a problem hiding this comment.
Hi @jiisd sorry for the late response. That sounds good to me! I'm going to work on this and create a pull request based on your changes. I'll let you know when the PR is open.
| class _TextSelectionToolbarNotchPainter extends CustomPainter { | ||
| const _TextSelectionToolbarNotchPainter( | ||
| this.arrowDirection, | ||
| this.triangleOffsetX, |
There was a problem hiding this comment.
I see 3 names here for the same thing: notch, arrow, and triangle. It looks like we use multiple names in the existing code too though :( Maybe try to choose the most common one and stick with it if you think it will help.
| }); | ||
|
|
||
| testWidgets( | ||
| 'Check the toolbar appears below the TextField when there is not enough space above the TextField to show it', |
There was a problem hiding this comment.
This seems like it's from your other PR (#29809) and it verifies the top/bottom position, but you should write a test that verifies the arrow position.
I think if you just cover the 3 cases it should be fine. Test that selecting in the middle of an input puts the arrow in the middle (as before), selecting the first word puts the arrow left of center, and selecting the last word puts the arrow right of center.
It might be hard to find the arrow in a test, but I think you can do it. Here is a snippet from another test that finds something by name and descendant, it should help here:
Finder findFadeUpwardsPageTransition() {
return find.descendant(
of: find.byType(MaterialApp),
matching: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_FadeUpwardsPageTransition'),
);
}|
Closing this PR as the issue has been (mostly) addressed. |
Fixed an issue described in this link
#11274
#22576
Description
Fix the problem that the triangle attached to the toolbar is always in the middle.
Now looks like iOS, if the toolbar squishes to the side, the arrow attempts to stay as close as possible to the selected text.
However, the problem of dividing the line has not yet found a reasonable solution to solve.
#24784
This PR was based on the #29809 PR.
iPhone X (Flutter, this branch):

GIF example -->