footurist
footurist

Reputation: 1609

Flutter: Tap delay with GestureDetector in a Tile of a GridView

Normally, the below behavior is quick in nature. However, inside a GridView (or any ScrollView I assume (tried ListView, too)) the performance seems to be very poor. When I tap on the screen the opacity of the Container increases, but after a delay. Any idea what I am missing?

import "package:flutter/material.dart";
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
    Widget build(BuildContext context) {
      return MaterialApp(
        home: Scaffold(
          body: MyWidget()
        )
      );
    }
}
class MyWidgetState extends State<MyWidget> {
  double opacity = 0.2;
  @override
    Widget build(BuildContext context) {
      return GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 3
        ),
        itemBuilder: (context, index) {
          return GestureDetector(
            onTap: () {
              setState(() {
                opacity = 0.2;                
              });
            },
            onTapDown: (details) {
              setState(() {
                opacity = 1.0;                
              });
            },
            child: Container(
              color: Colors.red.withAlpha((255 * opacity).toInt())
            )
          );
        },
      );
    }
}
class MyWidget extends StatefulWidget {
  @override
    State<StatefulWidget> createState() {
      return MyWidgetState();
    }
}

Upvotes: 6

Views: 5006

Answers (2)

Hashem Aboonajmi
Hashem Aboonajmi

Reputation: 13890

I have created a reusable widget named PressFade you can use it immediately, it clones the iOS button behavior:

enter image description here

import 'package:flutter/material.dart';

class PressFade extends StatefulWidget {
  final Widget child;
  final VoidCallback onPressed;
  const PressFade({
    super.key,
    required this.onPressed,
    required this.child,
  });

  @override
  State<PressFade> createState() => _PressFadeState();
}

class _PressFadeState extends State<PressFade> {
  double opacity = 1.0;
  Duration duration = Duration.zero;
  
  void _onTapDown() {
    setState(() {
      duration = Duration.zero;
      opacity = 0.5;
    });
  }

  void _onTapUp() {
    setState(() {
      duration = const Duration(milliseconds: 150);
      opacity = 1.0;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Listener(
      onPointerDown: (_) => _onTapDown(),
      onPointerUp: (_) => _onTapUp(),
      onPointerCancel: (_) => _onTapUp(),
      child: GestureDetector(
        onTap: widget.onPressed,
        child: AnimatedOpacity(
          duration: duration,
          opacity: opacity,
          child: widget.child,
        ),
      ),
    );
  }
}

Upvotes: 0

Andrii Turkovskyi
Andrii Turkovskyi

Reputation: 29458

Maybe Listener would be faster in your case

Listener(
  onPointerDown: (PointerDownEvent event) {
    setState(() {
      opacity = 1.0;
    });
  },
  onPointerUp: (PointerUpEvent event) {
    setState(() {
      opacity = 0.2;
    });
  },
  child: Container(
      color: Colors.red.withAlpha((255 * opacity).toInt())
  ),
)

Upvotes: 21

Related Questions