in this widget the on tap gesture detector fails although the button is correctly positioned and on the most top layer , the functionality intended is the button to render only after the animation is completed and react to on tap events i made some tests to check if the animation might not end and be the culprit but that wasnt the case here is the widget that contains it , also notice that the button is directly positioned above a matching image
this is the widget that contains gesture detector
Widget buildImageWithTextAndLine(String imagePath, String text, int index) {
bool isTapped = _tappedButtons[index] ?? false; // Safeguard against null
return Column(
children: [
Stack(
children: [
Image.asset(imagePath, width: 319, height: 111.5),
// Your positioning code remains unchanged
if (_animationComplete)
Positioned(
top: buttonTopOffset,
left: buttonLeftOffset,
child: GestureDetector(
onTap: () {
setState(() {
_tappedButtons[index] = !isTapped; // Toggle the tapped state
print("Button $index tapped state: ${_tappedButtons[index]}");
});
},
child: Opacity(
opacity: buttonOpacity,
child: Container(
width: buttonWidth,
height: buttonHeight,
// Dynamically change color based on tap
color: isTapped ? Colors.red : const Color.fromARGB(255, 0, 0, 0),
),
),
),
),
],
),
SizedBox(height: widget.spaceBetweenImages),
],
);
}
and this is the whole context file
import 'package:flutter/material.dart';
class Era extends StatefulWidget {
final double topSpacing;
final double spaceBetweenImages;
final double spaceBetweenRows;
const Era({
Key? key,
this.topSpacing = 35.0,
this.spaceBetweenImages = 8,
this.spaceBetweenRows = 60.0,
}) : super(key: key);
@override
_EraState createState() => _EraState();
}
class _EraState extends State<Era> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Offset> _leftColumnAnimation;
late Animation<Offset> _rightColumnAnimation;
bool _animationComplete = false;
Map<int, bool> _tappedButtons = {};
// Configurable variables for line and text
final double lineWidth = 280.0;
final double lineHeight = 0.5;
final double lineTopOffset = 30.0;
final double lineLeftOffset = 10.0;
final double fontSize = 35.0;
final double textTopOffset = 25.0;
final double textLeftOffset = 10.0;
// Button customization variables
final double buttonWidth = 319.0; // You can adjust this
final double buttonHeight = 111.5; // You can adjust this
final double buttonTopOffset = 0.0; // You can adjust this
final double buttonLeftOffset = 0.0; // You can adjust this
final double buttonOpacity = 1; // Opacity of the button
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: this,
);
_controller.addListener(() {
if (_controller.isCompleted) {
setState(() {
_animationComplete = true;
});
}
});
_leftColumnAnimation = Tween<Offset>(
begin: const Offset(-1.5, 0.0),
end: Offset.zero,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeOut,
));
_rightColumnAnimation = Tween<Offset>(
begin: const Offset(1.5, 0.0),
end: Offset.zero,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeOut,
));
_controller.forward();
// Assuming the number of images/buttons is known and fixed
int numberOfButtons = 6; // Adjust this based on your actual number of images/buttons
for (int i = 0; i < numberOfButtons; i++) {
_tappedButtons[i] = false;
}
}
Widget buildImageWithTextAndLine(String imagePath, String text, int index) {
bool isTapped = _tappedButtons[index] ?? false; // Safeguard against null
return Column(
children: [
Stack(
children: [
Image.asset(imagePath, width: 319, height: 111.5),
// Your positioning code remains unchanged
if (_animationComplete)
Positioned(
top: buttonTopOffset,
left: buttonLeftOffset,
child: GestureDetector(
onTap: () {
setState(() {
_tappedButtons[index] = !isTapped; // Toggle the tapped state
print("Button $index tapped state: ${_tappedButtons[index]}");
});
},
child: Opacity(
opacity: buttonOpacity,
child: Container(
width: buttonWidth,
height: buttonHeight,
// Dynamically change color based on tap
color: isTapped ? Colors.red : const Color.fromARGB(255, 0, 0, 0),
),
),
),
),
],
),
SizedBox(height: widget.spaceBetweenImages),
],
);
}
@override
Widget build(BuildContext context) {
return Positioned(
left: 0,
right: 0,
top: widget.topSpacing,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SlideTransition(
position: _leftColumnAnimation,
child: Column(
children: [
buildImageWithTextAndLine('assets/conquest/swedishphase.png', "The Swedish Phase 1630", 0),
buildImageWithTextAndLine('assets/conquest/frenchphase.png', "The French Phase 1635", 1),
buildImageWithTextAndLine('assets/conquest/dutchship.png', "Cuba 1628", 2),
],
),
),
SizedBox(width: widget.spaceBetweenRows),
SlideTransition(
position: _rightColumnAnimation,
child: Column(
children: [
buildImageWithTextAndLine('assets/conquest/bohemianphase.png', "The Bohemian Phase 1618", 3),
buildImageWithTextAndLine('assets/conquest/danishphase.png', "The Danish Phase 1625", 4),
buildImageWithTextAndLine('assets/conquest/brazil.png', "Brazil 1624", 5),
],
),
),
],
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
i expect an answer to why gesture detector is blocked thats not because of the animation set to true i tested that and not because the gesture doesnt work in general it simply doesnt work in this file with that particular stack where the button is positioned over the img thats what might create this problem