Flutter onPressed not works after FlatButton translate

2,033

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.

TL;DR:

When translating a widget, the area which you can tap is made of two things:

  1. The area of the parent widget
  2. The area of the child (the Flatbutton here)

See the picture below:

Child vs parent vs clickable area

The usual solution:

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.

Solution for you

You actually wanted something a bit different: That anything BUT the button is clickable. For that you need:

  1. a GestureDetector being the parent of the clickable area
  2. a FlatButton with a onPressed method which does nothing

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"),
                  ),
                ),
              ),
            ),
          )
        ],
      ),
    );
  }
}

Why this works

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:

  1. Outside the cyan Container, nothing happens.
  2. Inside the cyan Container
    1. Outside the button, the GestureController catch the tap and makes the button move
    2. Inside the button, the Button catches the tap, does nothing with it (empty method), and mark this tap as treated which causes it not to bubble up in the tree and therefore the GestureController gets nothing and nothing happens.

Hope this helps you and other understand the tricky way all of this works. Once you embrace it it's kinda beautiful though ;)

Share:
2,033
Jeong Woo
Author by

Jeong Woo

Updated on December 21, 2022

Comments

  • Jeong Woo
    Jeong Woo over 1 year

    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"),
                    ),
                  ),
                ),
              )
            ],
          ),
        );
      }
    }
    
    • HasilT
      HasilT about 4 years
      Please explain what you are trying to achieve here, or what is your end results should be
    • Jeong Woo
      Jeong Woo about 4 years
      Sorry, it may not be described clearly, I updated the question.
  • Lulupointu
    Lulupointu about 4 years
    @JeongWoo I would recommend you to read my new explanation, it's much better and flexible!