I have encountered some problems with event handling when using ScrollView
and Transform
. The layout structure is like this, ScrollView
and Transform
exist inside Stack
.
I want the ScrollView
to scroll when scrolling outside the FlatButton
in Container(Colors.cyan)
, event can penetrate to ScrollView.
Click FlatButton
onPress to work. In fact, after clicking FlatButton
twice, it will no longer move whether you click the initial position or the current position. The FlatButton
control moves away from the initial position within the size range, the click event is no longer detected, but I did not understand. the code is as follows:
class EventListener extends StatefulWidget {
@override
_EventListenerState createState() => _EventListenerState();
}
class _EventListenerState extends State<EventListener> {
Offset offset = Offset(0, 0);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("EventListener"),
),
body: Stack(
children: <Widget>[
SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
color: Colors.red,
height: 200,
),
Container(
color: Colors.teal,
height: 300,
),
Container(
color: Colors.orange,
height: 400,
)
],
),
),
Container(
color: Colors.cyan,
width: double.infinity,
height: 400,
alignment: Alignment.center,
child: SizedBox(
width: 100,
height: 100,
child: Transform.translate(
offset: offset,
child: FlatButton(
color: Colors.orange,
onPressed: () {
setState(() {
offset += Offset(50, 50);
});
print('click !');
},
child: Text("translate"),
),
),
),
)
],
),
);
}
}
This is a known difficulty with Buttons and Stacks and I would advise anyone with this kind of problem to look at this discussion on Github.
When translating a widget, the area which you can tap is made of two things:
See the picture below:
Expanding the size of the parent.
Which gives us something like this:
Container(
width: double.infinity,
height: 400,
child: Transform.translate(
offset: offset,
child: SizedBox(
width: 100,
height: 100,
child: FlatButton(
onPressed: () => print('tap button'),
child: Text("translate"),
),
),
),
),
Here you can tap anywhere in the parent Container.
You actually wanted something a bit different: That anything BUT the button is clickable. For that you need:
So here is the final code if we only want the blue container to be clickable:
import 'package:flutter/material.dart';
main() => runApp(MaterialApp(
home: EventListener(),
));
class EventListener extends StatefulWidget {
@override
_EventListenerState createState() => _EventListenerState();
}
class _EventListenerState extends State<EventListener> {
Offset offset = Offset(0, 0);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("EventListener"),
),
body: Stack(
children: <Widget>[
SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
color: Colors.red,
height: 200,
),
Container(
color: Colors.teal,
height: 300,
),
Container(
color: Colors.orange,
height: 400,
)
],
),
),
GestureDetector(
onTap: () {
setState(() {
offset += Offset(50, 50);
});
},
child: Container(
width: double.infinity,
height: 400,
color: Colors.cyan,
alignment: Alignment.center,
child: Transform.translate(
offset: offset,
child: SizedBox(
width: 100,
height: 100,
child: FlatButton(
color: Colors.orange,
onPressed: () {},
child: Text("translate"),
),
),
),
),
)
],
),
);
}
}
As explained previously, the parent being the cyan Container, any area in this container will make the button clickable.
Furthermore adding a GestureDetector on top of this Container allow us to capture any tap within this Container.
So finally here is what happens when you click, if you click:
Hope this helps you and other understand the tricky way all of this works. Once you embrace it it's kinda beautiful though ;)