Flutter: How to make a ListView transparent to pointer events (but not its non-transparent contents)? - flutter

I have a Scrollable (ListView or any other) and it contains a transparent widget, say Container(height:200). I can see through both the widget and the scrollable (in other words I can see the widgets behind the scrollable).
How can I be able to click through the transparent widget and the scrollable, so that I reach the widgets behind the scrollable?
ListView(
children: [
Container(height: 200), // Transparent.
Container(color: Colors.red, height: 200),
],
);
Note, I cannot wrap the scrollable with IgnorePointer, because I still want to click the non-transparent widgets in the scrollable.

The only reasonable solution I can think of is to have a GestureDetector on the transparent container, which will give you the global position of the taps:
GestureDetector(
onTapUp: (TapUpDetails tapUpDetails) {
print("onTapUp global: " + tapUpDetails.globalPosition.toString());
},
And then add a Key to the widget behind the list, and use it to get the global position of the top left corner of the widget's rectangle, as well as the size of the widget, and use those to get the rectangle of the widget:
RenderBox renderBox = _key.currentContext.findRenderObject();
Offset topLeftCorner = renderBox.localToGlobal(Offset.zero);
Size size = renderBox.size;
Rect rectangle = topLeftCorner & size;
If the background widget does not move, you can do it within initState on the very next frame using WidgetsBinding.instance.addPostFrameCallback (the render object will be null if do it synchronously within initState) and save the Rect - otherwise you will have to recalculate it on every tap.
And then finally on each tap on the transparent container you can calculate whether the tap's position is within the boundaries of the background widget, and invoke the corresponding code:
// if tap is within boundaries of the background widget
if (rectangle.contains(tapUpDetails.globalPosition)) {
// your code here
}

Related

How can I draw a widget on top of a widget in flutter?

I want to draw a yellow arrow over the 9 blue squares as shown in the picture below.
But I don't know which widget to use to draw arrows freely on each rectangle. I want to draw an arrow like a screen lock pattern. I would like to know the different ways in which this arrow can be implemented.
You can put the whole Containers inside a Stack widget and then paint the line by using the CustomPaint widget shown in this question Draw lines with flutter.
Stack(
children: [
Arrow(child: Container()),
ContainerWidgets(),
],
)
And for the painter:
class Arrow extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
// Add your paint logic here
}
}

Flutter Horizontal PageView with Child Width Larger Screen Width

How to implement PageView width child have width larger screen width.
PageView(
controller: controller,
children: const <Widget>[
Image('img1.jpg'),
Image('img2.jpg'), // large image one
Image('img3.jpg')
],
)
https://i.imgur.com/jvowQd2.mp4
try using InteractiveViewer. It lets you have a bigger size image or widget in it and the user can zoom or zoom out from it.
InteractiveViewer documentation.

Using GestureDetector inside InteractiveViewer

Is it possible to use GestureDetector — specifically the onTapUp gesture — inside of an InteractiveViewer?
I know InteractiveViewer uses a GestureDetector itself and overrides the onScaleEnd, onScaleStart, and onScaleUpdate methods to implement panning. However, onTapUp is not overriden, which makes me think there's potential to use it.
I did some digging around and found this in the InteractiveViewer's TransformationController documentation for the toScene() method:
Return the scene point at the given viewport point.
A viewport point is relative to the parent while a scene point is
relative to the child, regardless of transformation. Calling toScene
with a viewport point essentially returns the scene coordinate that
lies underneath the viewport point given the transform.
The viewport transforms as the inverse of the child (i.e. moving the
child left is equivalent to moving the viewport right).
This method is often useful when determining where an event on the
parent occurs on the child. This example shows how to determine where
a tap on the parent occurred on the child.
GestureDetector(
onTapUp: (TapUpDetails details) {
_childWasTappedAt = _transformationController.toScene(
details.localPosition,
);
},
child: InteractiveViewer(
transformationController: _transformationController,
child: child,
), ); }
So it seems that rather than having a GestureDetector as a child, the recommended solution is to wrap the InteractiveViewer with a GestureDetector, use a custom TransformationController, and get the position relative to the viewer viewport from this controller.

Flutter Markdown "Show more" button

I use MarkdownBody from flutter_markdown inside a LimitedBox. When pressing a "Show more" button the maxHeight is set to double.infinity and the full text is shown:
LimitedBox(
maxHeight:
showMoreCommentsIds
.contains(
commentId)
? double.infinity
: 100,
child: Wrap(
direction:
Axis.horizontal,
children: <Widget>[
MarkdownBody(
data: showList[index]
.comment
.comment,
)
],
),
),
But how can I find out the height of the text and only display the "Show more" button, if it is necessary?
You can find the length of the text using text.length and based on that information determine if the "Show more" button is needed. For example:
if(text.length > 60) {
_showButton();
}
You may need to do a little testing with the length of text in order to find out which length you want as the threshold. I just chose 60 as an arbitrary number.
I struggled a little bit with a similar problem because I had to know the size of a widget to apply some logic.
I learned that you can prerender the widget in an Offstage widget, determine it's size with the widget key once it is mounted.
You should wait for the rebuild, so you can force it and then you get the size with a function like this:
Future.delayed(Duration(milliseconds:100)).then(
final widgetSize = getWidgetSize(widgetKey);
// You can make logic here to remove the Offstage, variables, and free its space.
// Todo: apply the size in your code after this
)
...
Size getWidgetSize(GlobalKey key) {
final RenderBox renderBox = key.currentContext.findRenderObject();
return renderBox.size;
}
In my case, I needed it after tapping somewhere, so I used it inside a function, but maybe you will apply it directly on the initState(), inside a post frame callback.
initState(){
super.initState();
ServicesBinding.instance.addPostFrameCallback((_) async {
// here
});
}

Flutter Center align widget to specific coordinate

Ive create a popup menu widget which takes two arguments :a button widget, and a menu widget . When the button is pressed the menu widget is passed to an OverlayEntry as the child which is then added to the Overlay.of(context)
I'd like the resulting display to be as such:
O (Button)
____|____
| MENU |
|_______|
I can place the menu widget inside a Positioned Widget to move it freely around the Overlay, but I dont know how to get the correct coordinates. I can get the center of the Button by finding it's Renderbox, but I cant know the size, and therefore the correct position, of the Menu until after it's draw (as it can be any widget).
Is there some soft of layout option to tell a Widget of any size to vertically or horzontally align itself with a given coordinate?
To get the size of a widget:
First give your Widget a key:
key: _key,
Then you can get the size(and position) like this:
_getSize() {
final RenderBox renderBox = _key.currentContext.findRenderObject();
final size = renderBox.size;
final position = renderBox.localToGlobal(Offset.zero);
final height = size.height; //same for width
}