Home > Blockchain >  Flutter: Increase hitbox of GestureDetector
Flutter: Increase hitbox of GestureDetector

Time:08-30

I am fairly new to flutter and currently trying to create a NavBar. It looks like this: enter image description here

If I click on the icon, the bar moves to the selected one and the content changes. However, I have to hit the icon perfectly. I would like to have a "box" around it, so I can tap just near it. Basically divide the space into 3.

I tried the following:

  Widget build(BuildContext context) {
    return Container(
      height: 60,
      color: Color(0xff282424),
      child: Stack(
        children: [
          Container(
            child: Row(
              children: items.map((x) => createNavBarItem(x)).toList(),
            ),
          ),
          AnimatedContainer(
            duration: Duration(milliseconds: 200),
            alignment: Alignment(active.offset, 0.7),
            child: AnimatedContainer(
              duration: Duration(milliseconds: 400),
              height: 5,
              width: 50,
              decoration: BoxDecoration(
                  color: active.color,
                  borderRadius: BorderRadius.circular(2.5)),
            ),
          ),
        ],
      ),
    );
  }


  Widget createNavBarItem(MenuItem item) {
    double width = MediaQuery.of(context).size.width;
    return SizedBox(
      width: width / items.length,
      height: 55,
      child: GestureDetector(
        child: Icon(
          Icons.access_time,
          color: item.color,
          size: 30,
        ),
        onTap: () {
          setState(() {
            active = item;
            navBarUpdate(item);
          });
        },
      ),
    );
  }

The items should take 1/3 of the width. It isn't working that way tho. Any idea on how to increase the "tappable" space?

EDIT

Full code:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.\
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.red,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var screens = [Text("Button1"), Text("Button2"), Text("Button3")];
  int currentScreen = 0;

  void changeIndex(int index) => setState(() {
        currentScreen = index;
      });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        color: Colors.orange,
        child: Stack(
          children: [
            SafeArea(child: screens[currentScreen]),
            Container(
                alignment: Alignment.bottomCenter, child: NavBar(changeIndex))
          ],
        ),
      ),
    );
  }
}

class MenuItem {
  final String name;
  final Color color;
  final double offset;

  MenuItem(this.name, this.color, this.offset);
}

class NavBar extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => NavBarState(navBarUpdate);

  late Function(int) navBarUpdate;

  NavBar(this.navBarUpdate);
}

class NavBarState extends State<NavBar> {
  var items = [
    MenuItem("Test", Colors.red, -0.76),
    MenuItem("Test2", Colors.green, 0),
    MenuItem("Test3", Colors.yellow, 0.76)
  ];

  late MenuItem active;
  late Function(MenuItem) navBarUpdate;

  @override
  void initState() {
    super.initState();
    active = items[0];
  }

  NavBarState(Function(int) navBarUpdate) {
    this.navBarUpdate = (item) {
      navBarUpdate(items.indexOf(item));
    };
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 60,
      color: Color(0xff282424),
      child: Stack(
        children: [
          Container(
            child: Row(
              children: items.map((x) => createNavBarItem(x)).toList(),
            ),
          ),
          AnimatedContainer(
            duration: Duration(milliseconds: 200),
            alignment: Alignment(active.offset, 0.7),
            child: AnimatedContainer(
              duration: Duration(milliseconds: 400),
              height: 5,
              width: 50,
              decoration: BoxDecoration(
                  color: active.color,
                  borderRadius: BorderRadius.circular(2.5)),
            ),
          ),
        ],
      ),
    );
  }

  Widget createNavBarItem(MenuItem item) {
    double width = MediaQuery.of(context).size.width;
    return SizedBox(
      width: width / items.length,
      height: 55,
      child: GestureDetector(
        child: Icon(
          Icons.access_time,
          color: item.color,
          size: 30,
        ),
        onTap: () {
          setState(() {
            active = item;
            navBarUpdate(item);
          });
        },
      ),
    );
  }
}

CodePudding user response:

You can use behavior: HitTestBehavior.translucent, or opaque on createNavBarItem

child: GestureDetector(
  behavior: HitTestBehavior.translucent,

You can swap your GestureDetector on top level widget from Icon.

 Widget createNavBarItem(MenuItem item) {
    double width = MediaQuery.of(context).size.width;
    return GestureDetector(
      child: Container(
        color: Colors.transparent,
        width: width / items.length,
        height: 55,
        child: Icon(
          Icons.access_time,
          color: item.color,
          size: 30,
        ),
      ),
      onTap: () {
        setState(() {
          active = item;
          navBarUpdate(item);
        });
      },
    );
  }
  • Related