SoFunction
Updated on 2025-04-10

5 Rules to Make You Stand Out in Flutter onTap

introduction

Small things determine your proficiency, and the interesting thing about these small details is their richness. You will encounter the onTap event at hundreds of locations in the code base. Enhanced them can have a significant positive impact on the maintainability and end-user experience of your code.

onTapIt's such a tiny but rich thing - we use it on every screen. It's purely about those onTap events: what to do and what not to do.

Rule 1: Widgets should not implement onTap logic

As the name implies, a widget is a piece of UI drawn on the screen, which should know nothing about business logic. It can pass events to its parent if needed. The best way to do this is to use a function as a constructor parameter.

Important: Don't create anonymous functions within widgets to write business logic. Instead, you can even pass onTap outside the widget and let the parent handle it.

// DON'T
class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) => GestureDetector(
    onTap: () {
      debugPrint('MyWidget onTap called');
      fetchFromServer();
    },
    child: Container(
      width: 100,
      height: 100,
      color: ,
    ),
  );
}
// DO
class MyWidget extends StatelessWidget {
  const MyWidget({});
  final void Function()? onTap;
  @override
  Widget build(BuildContext context) => GestureDetector(
    onTap: onTap,
    child: Container(
      width: 100,
      height: 100,
      color: ,
    ),
  );
}

Rule 2: The onTap function should be null

As part of the UI, widgets should be reusable. Different use cases may or may not require its onTap functionality. Because the main reason for its existence is to draw on the screen rather than sending events to its parent or controller, it should be able to exist with or without the onTap event.

Important: Make all event functions from widgets null.

// DON'T
class MyWidget extends StatelessWidget {
  const MyWidget({required });
  final void Function() onTap;
  @override
  Widget build(BuildContext context) => GestureDetector(
    onTap: onTap,
    child: Container(
      ...
    ),
  );
}
// DO
class MyWidget extends StatelessWidget {
  const MyWidget({});
  final void Function()? onTap;
  @override
  Widget build(BuildContext context) => GestureDetector(
    onTap: onTap,
    child: Container(
      ...
    ),
  );
}

Rule 3: The UI must know nothing about logic, even in the indicative sense

Even after using best practices, a common mistake is to name UI event functions that indicate business logic. A widget in a banking application should not know that it belongs to a banking application and only has enough data to plot itself and its children. In other words, a widget should be beautiful and stupid.

Important: Name the UI event function to indicate events, rather than the underlying business logic. Name such a functiononTapRegisterButton()Compareregister()Better.

// DON'T
class MyScreen extends StatelessWidget {
  ...
  @override
  Widget build(BuildContext context) => Scaffold(
    body: MyWidget(
      onTap: ,
    );
  );
}
// DO
class MyScreen extends StatelessWidget {
  ...
  @override
  Widget build(BuildContext context) => Scaffold(
    body: MyWidget(
      onTap: ,
    );
  );
}
class MyScreenController {
  ...
  void onTapMyWidget() {
    _fetchData()
  }
  void _fetchData() {
    ...
  }
}

Rule 4: Pass the model as much as possible

This is not limited to the UI, but also applies to all functions. Whenever you need to pass some data as a parameter, try to pass the entire model as much as possible, not just an ID or name. This is a good practice to minimize code changes when business logic is expanded or changed in the future.

Important: Pass the model to the function as a parameter instead of an ID.

// DON'T
void onTapMyWidget(int subjectId) {
  ...
}
// DO
void onTapMyWidget(Subject subject) {
  ...
}

Rule 5: Always specify HitTestBehavior

When clicking with GestureDetector, don't forget to putbehaviorAdd to your widget. This property specifies how clicks (clicks) propagate to the child widget. In most cases, you will use, but I suggest you should check out the short description on this for the situation.

Important: Absolute control of the widget's child parts by specifying the click behavior of the widget.

class MyWidget extends StatelessWidget {
  ...
  @override
  Widget build(BuildContext context) => GestureDetector(
    onTap: onTap,
    behavior: ,
    child: Container(
      ...
    ),
  );
}

The above is the detailed content of the 5 rules that make you stand out in Flutter onTap. For more information about Flutter onTap rules, please follow my other related articles!