-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Fix Scrollable.ensureVisible throws an error in DropdownMenu inside nested scroll views
#147057
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
Conversation
e8bd1ce to
996b013
Compare
Scrollable.ensureVisible throws an error in DropdownMenu inside nested scroll viewsScrollable.ensureVisible throws an error in DropdownMenu inside nested scroll views
Scrollable.ensureVisible throws an error in DropdownMenu inside nested scroll viewsScrollable.ensureVisible throws an error in DropdownMenu inside nested scroll views
…de nested scroll views
996b013 to
f806af2
Compare
Scrollable.ensureVisible throws an error in DropdownMenu inside nested scroll viewsScrollable.ensureVisible throws an error in DropdownMenu inside nested scroll views
| WidgetsBinding.instance.addPostFrameCallback((_) { | ||
| final BuildContext? highlightContext = buttonItemKeys[currentHighlight!].currentContext; | ||
| if (highlightContext != null) { | ||
| Scrollable.ensureVisible(highlightContext); |
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.
shouldn't we fix Scrollable.ensureVisible? Surely it's a bug that calling that doesn't work.
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.
DropdownMenu calls Scrollable.ensureVisible which then calls ScrollPosition.ensureVisible. However, after the OverlayPortal migration, it seems the correct scrollable context isn't passed in nested scroll views.
flutter/packages/flutter/lib/src/widgets/scrollable.dart
Lines 471 to 476 in 1465da4
| RenderObject? targetRenderObject; | |
| ScrollableState? scrollable = Scrollable.maybeOf(context); | |
| while (scrollable != null) { | |
| final List<Future<void>> newFutures; | |
| (newFutures, scrollable) = scrollable._performEnsureVisible( | |
| context.findRenderObject()!, |
| final Future<void> ensureVisibleFuture = position.ensureVisible( |
My fix finds the scrollable and calls ScrollPosition.ensureVisible directly.
I'm not expert in scrollables so I'll defer to @Piinks, if this makes sense.
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 can reproduce the issue in the official OverlayPortal example by nesting scroll views.
Hixie is right it is a bug in Scrollable.ensureVisible. This happens only in OverlayPortal. Scrollable.ensureVisible cannot find the right ScrollableState inside it.
Code sample
expand to view the code sample
import 'package:flutter/material.dart';
/// Flutter code sample for [OverlayPortal].
void main() => runApp(const OverlayPortalExampleApp());
class OverlayPortalExampleApp extends StatelessWidget {
const OverlayPortalExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('OverlayPortal Example')),
body: const Center(child: ClickableTooltipWidget()),
),
);
}
}
class ClickableTooltipWidget extends StatefulWidget {
const ClickableTooltipWidget({super.key});
@override
State<StatefulWidget> createState() => ClickableTooltipWidgetState();
}
class ClickableTooltipWidgetState extends State<ClickableTooltipWidget> {
final OverlayPortalController _tooltipController = OverlayPortalController();
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Card(
child: SingleChildScrollView(
child: TextButton(
onPressed: _tooltipController.toggle,
child: DefaultTextStyle(
style: DefaultTextStyle.of(context).style.copyWith(fontSize: 50),
child: OverlayPortal(
controller: _tooltipController,
overlayChildBuilder: (BuildContext context) {
return Positioned(
right: 50,
bottom: 50,
child: GestureDetector(
onTap: () {
print('tooltip tapped');
Scrollable.ensureVisible(context);
},
child: const ColoredBox(
color: Colors.amberAccent,
child: Text('tooltip'),
),
),
);
},
child: const Text('Press to show/hide tooltip'),
),
),
),
),
),
);
}
}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.
#67773 made similar fix for nested scroll views, it doesn't handle OverlayPortal scenario very well and this is where the error is coming.
I'll close this PR to find out why this fails in an OverlayPortal and try to fix this in ensureVisible instead.
|
Hey, I am running into this problem, too. How can I fix this in user-land or do I have to wait for an upstream fix? |
|
Hello, I would like to know if there is any news about this issue. It seems that I am facing the same error. |
|
This was fixed in #148897. It would be available in the next major stable release. |
fixes DropdownMenu throws exception when nested inside nested SingleChildScrollView
Description
Scrollable.ensureVisibleinDropdownMenuinside nested scroll views after theOverlayPortalmigration throws an error.The error is thrown by
getTransformTowhenobject.parent != nullis not true. This PR fixes the error by gettingScrollableand callensureVisible.Code sample
expand to view the code sample
Before
After
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.