Flutter GestureDetector, how to implement onTap Function? - flutter

I want to implement the OnTap in the Gesture Detector, i just cant figure out how... Originally i had the OnTap in the Inkwell, but it didnt work, since it didnt "entered" to that function according to Debugger, so i wanted to try out if a Gesture Detector could help...
Widget _buildCardDeck() {
return Container(
child: Row(
children: <Widget>[
InkWell(
child: cardDeckClosed.isNotEmpty
? Padding(
padding: const EdgeInsets.all(2.0),
child: TransformedCard(
playingCard: cardDeckClosed.last, attachedCards: [], columnIndex: 0,
),
)
: Opacity(
opacity: 1.0,
child: Center(
widthFactor: 5.0,
heightFactor: 7.0,
child: GestureDetector(
child: Container(
padding: const EdgeInsets.all(5.0),
color: Colors.white,
width: 70.5,
height: 100.0,
),
),
),
),
onTap: () {
setState(() {
if (cardDeckClosed.isEmpty) {
cardDeckClosed.addAll(cardDeckOpened.map((card) {
return card
..opened = false
..faceUp = false;
}));
cardDeckOpened.clear();
} else {
cardDeckOpened.add(
cardDeckClosed.removeLast()
..faceUp = true
..opened = true,
);
}
});
},
),

Try below code, In your above code you write your onTap:(){} function inside InkWell
Using GestureDetector
GestureDetector(
onTap: () {
print('Container Pressed');
},
child: Container(
alignment: Alignment.center,
color: Colors.blue,
height: 50,
width: 150,
child: Text('Ontap Using GestureDetector'),
),
),
Using InkWell
InkWell(
onTap: () {
print('Container Pressed');
},
child: Container(
alignment: Alignment.center,
color: Colors.blue,
height: 50,
width: 150,
child: Text('Ontap Using InkWell'),
),
),

it's a pretty straight forward widget, this is a starter code that might help you:
GestureDetector(
onTap: (){
// this will fire when you tap
},
child: yourChildWidget(),
)

Related

Is it possible to pass a tap event to multiple widgets at the same time in Flutter?

When I tap a widget, I want that widget and the widget behind it to recognize my tap at the same time. But what happens is only the widget at forefront recognizes the tap.
I've tried this in several ways, with a simple parent/child relation with all the combinations of HitTestBehavior:
Center(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => print('red'),
child: Container(
color: Colors.red,
width: 200,
height: 200,
child: Center(
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () => print('green'),
child: Container(
color: Colors.green,
width: 100,
height: 100,
),
),
),
),
),
);
Also tried using a Stack. Again, with all possible behaviors:
Stack(
alignment: Alignment.center,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => print('red'),
child: Container(
color: Colors.red,
width: 200,
height: 200,
),
),
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () => print('green'),
child: Container(
color: Colors.green,
width: 100,
height: 100,
),
),
],
);
I know I can simply use a mutual function for both of the detectors, but the main reason I want to achieve this is when using nested widgets, it is required to add unnecessary implementations to access that functionality.
You can use onPanDown
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () => print('green'),//doesnt work
onPanDown: (details) => print('tdgreen'),
child: Container(
color: Colors.green,
width: 100,
height: 100,
),
),
You can check this thread for more.

How to get an image above background while shadowing background in flutter

I have an image and I want it to appear on my screen, above my background, with keeping background a bit dark type like in the image. I am thinking on using AlertDialog to display the image, but if you think there is a better way of doing it or a specific widget for this, please do tell me. Also please tell me what do we name this kind of image which hovers over background and focusing itself in UI.
enter code here
Just for trying out, I used this in my screen's initstate, as I want it to appear as soon as my screen appears -
super.initState();
showDialog(
context: context,
builder: (BuildContext buildContext) {
return AlertDialog(
content: Center(
child: Container(
color: Colors.red,
width: 200,
height: 200,
),
),
);
}
);
initState doesn't contain context you can get context after first frame.
You can use addPostFrameCallback
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
showDialog(..);}
Or Future.delayed
Future.delayed(Duration.zero).then((value) {
showDialog(..); }
The dialog construction is :
showDialog(
context: context,
builder: (BuildContext buildContext) {
const double iconSize = 48;
return LayoutBuilder(
builder: (context, constraints) => AlertDialog(
backgroundColor: Colors.transparent,
elevation: 0,
contentPadding: EdgeInsets.zero,
content: SizedBox(
width: constraints.maxWidth * .75,
height: constraints.maxHeight * .75,
child: Stack(
children: [
///background image with fit Cover
Align(
alignment: Alignment.center,
child: Container(
width: constraints.maxWidth * .75 - iconSize,
height: constraints.maxHeight * .75 - iconSize,
color: Colors.cyanAccent,
),
),
Align(
alignment: Alignment.topRight,
child: GestureDetector(
onTap: () {
Navigator.of(context).pop();
},
child: Container(
decoration: const BoxDecoration(
color: Colors.white, shape: BoxShape.circle),
child: const Icon(
Icons.close,
size: iconSize,
),
),
),
)
],
),
),
),
);
},
);
You can use Stack widget withe three widget as children.
first children widget is your background screen.
second children widget is a Container widget with color .withOpacity().
third children widget is the image you are trying to show on top.
Like this:
return Scaffold(
body: Stack(
children: [
Container(
color: Colors.white,
),
Container(
color: Colors.black.withOpacity(0.5),
),
Center(
child: Container(
height: MediaQuery.of(context).size.height * 70 / 100,
width: MediaQuery.of(context).size.width * 80 / 100,
color: Colors.purple,
),
),
],
),
);
for Visibility issue you can use this.
bool isVisible = false;
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: Stack(
children: [
Container(
color: Colors.red,
child: Center(
child: Container(
child: TextButton(
onPressed: () {
setState(() {
isVisible = true;
});
},
child: Text('Show Widget'),
),
),
),
),
Visibility(
visible: isVisible,
child: GestureDetector(
onTap: () {
setState(() {
isVisible = false;
});
},
child: Container(
color: Colors.white.withOpacity(0.5),
),
),
),
Visibility(
visible: isVisible,
child: Center(
child: Container(
height: MediaQuery.of(context).size.height * 70 / 100,
width: MediaQuery.of(context).size.width * 80 / 100,
color: Colors.purple,
child: Stack(
children: [
Positioned(
top: 0.0,
right: 0.0,
child: IconButton(
icon: const Icon(
Icons.close,
color: Colors.white,
),
onPressed: () {
setState(() {
isVisible = false;
});
},
),
),
],
),
),
),
),
],
),
);
}

Updating single item in GridView.Builder() without rebuilding whole tree

I am trying to update a single image in my gridview of images when a user taps on the image but whenever I update a single element of array, it updates the entire gridview. This itself is not bad but since user will be selecting multiple images, it does not look ideal or good. I would appreciate it if someone can guide me to update single element of gridview without updating the entire widget
I am using provider for my state management but I have also tried it with setState. I have include all the code of the gridview file but let me know if you need for info.
Thanks
return Consumer<RemoverList>(
builder: (context, data, child) {
return WillPopScope(
onWillPop: () async {
data.remover.clear();
data.removerBool.clear();
// Provider.of<RemoverList>(context).remover.clear();
return true;
},
child: Scaffold(
body: Container(
height: height,
width: width,
color: Theme.of(context).backgroundColor,
child: SafeArea(
child: Column(
children: [
GestureDetector(
onTap: () {
Navigator.pushNamed(context, 'options');
},
child: Container(
height: height * 0.05,
width: width * 0.95,
margin: const EdgeInsets.symmetric(vertical: 20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2),
border: Border.all(color: Colors.white)),
child: Center(
child: Text(
widget.album.name,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 25),
),
),
),
),
Expanded(
child: Container(
padding: const EdgeInsets.fromLTRB(0, 5, 0, 5),
// color: Colors.green,
width: width * 0.95,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(5)),
child: GridView.builder(
itemCount: widget.album.images.length,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
mainAxisSpacing: 5,
crossAxisSpacing: 5,
crossAxisCount: 3),
itemBuilder: (context, i) {
data.generateRemoverList(widget.album.images);
var image = Uint8List.fromList(widget
.album.images[i].image['data']
.cast<int>());
return GestureDetector(
onLongPress: () {
// data.tapped(i);
if (data.removerBool.isNotEmpty) {
if (data.removerBool[i] == true) {
setState(() {
data.removerBool[i] = false;
});
} else {
setState(() {
data.removerBool[i] = true;
});
}
}
print(data.removerBool[i]);
print(data.remover);
setState(() {
active = true;
});
data.addToList(
widget.album.images[i].originalName);
},
onTap: () {
if (active == false) {
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return OpenImage(unit: image);
}));
} else if (active == true) {
data.addToList(
widget.album.images[i].originalName);
print(data.remover);
data.removerBool[i] = !data.removerBool[i];
// data.tapped(i);
}
// data.tapped(i);
// print(data.removerBool[i]);
},
child: Container(
decoration: BoxDecoration(
color: Colors.black,
image: DecorationImage(
image: MemoryImage(image),
fit: BoxFit.cover)),
child: Center(
child: data.remover.contains(
widget.album.images[i].originalName)
? const Icon(
Icons.check_box_rounded,
color: Colors.black,
)
: Container(),
),
));
}),
)),
Container(
width: width * 0.95,
margin: const EdgeInsets.symmetric(vertical: 10),
alignment: Alignment.bottomCenter,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: width * 0.45,
child: ElevatedButton(
onPressed: () {},
child: const Text('Add Photos')),
),
Container(
width: width * 0.45,
child: ElevatedButton(
onPressed: () {},
child: const Text('Remove Seleted')),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: width * 0.45,
child: ElevatedButton(
onPressed: () {},
child: const Text('Save')),
),
Container(
width: width * 0.45,
child: ElevatedButton(
onPressed: () {},
child: const Text('Delete Album')),
)
],
)
],
),
)
],
),
),
),
),
);
},
);
}
}

How to overlap button on BottomSheet in Flutter?

In my design, there is a close button in the BottomSheet. I have tried Stack. But it didn't work. Does anyone know how to achieve the result? Thanks :)
modalBottomSheet(context) {
return showModalBottomSheet(
context: context,
builder: (context) {
return Stack(
children: [
Container(
child: Text('Sample Text'),
),
Container(
height: 50,
width: 50,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
),
],
);
}
);
}
So I've been trying around for a bit, and this seems to work as you explained.
modalBottomSheet(context) {
return showModalBottomSheet(
context: context,
builder: (context) {
// using a scaffold helps to more easily position the FAB
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: double.maxFinite,
),
Padding(
padding: EdgeInsets.all(30.0),
child: Text("Text in the sheet"),
),
],
),
// translate the FAB up by 30
floatingActionButton: Container(
transform: Matrix4.translationValues(0.0, -30, 0.0), // translate up by 30
child: FloatingActionButton(
onPressed: () {
// do stuff
print('doing stuff');
},
child: Icon(Icons.add),
),
),
// dock it to the center top (from which it is translated)
floatingActionButtonLocation: FloatingActionButtonLocation.centerTop,
);
});
}
The meat of the solution here is to transform the Container which contains the FAB. I got this idea from this older SO answer to a somewhat related question.
The result looks like this:
You'll probably have to make some more edits to achieve the exact result you desire, but I hope this sets you on the right path.
Edit
When, in the above solution, you want to press the FAB, and you tap the top half, the onPressed handler fires, but the modal also closes. You should probably use a WillPopScope that only pops when the actual button is pressed (and not the area around/above it). If you think it's fine pressing anywhere above it as well, you can just leave it as-is.
showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
builder: (BuildContext context) {
return Stack(clipBehavior: Clip.none, children: [
Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.vertical(top: Radius.circular(20))),
height: 220.h,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0, vertical: 16),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
ElevatedButton(
color: Colors.green,
onPressed: () {},
child: const Center(child: Text('Remove')))
],
),
),
),
),
Positioned(
top: -30.h,
right: 12.w,
child: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.close),
)),
]);
},
)

track which container was clicked on in flutter

I have 3 containers and in each picture, when you click on which an icon appears. How to assign a value to each container, so that when you click it, you can change it too. Or how to track which container was clicked on
class _EditAccountScreenState extends State<EditAccountScreen> {
bool checkboxValue = false;
...
child: GestureDetector(
onTap: () {
setState(() {
checkboxValue = !checkboxValue;
});
},
child: Padding(
child: Row(
children: <Widget> [
Container(
child: Stack(
children: <Widget>[
Image.asset('assets/images/telegram-512.png',fit: BoxFit.fill),
Positioned(
bottom: 0, right: 15, //give the values according to your requirement
child: checkboxValue
? Container(
decoration: BoxDecoration(
color: Colors.green,
borderRadius:BorderRadius.circular(100)
),
child: Icon(
Icons.check,
color: Colors.white,
size: 15,
)
)
: Container(),),],),),
Container(
child: Stack(
children: <Widget>[
Image.asset('assets/images/Viber-Logo.png',fit: BoxFit.fill),
Positioned(
bottom: 0, right: 15, //give the values according to your requirement
child: checkboxValue
? Container(
decoration: BoxDecoration(
color: Colors.green,
borderRadius:BorderRadius.circular(100)
),
child: Icon(
Icons.check,
color: Colors.white,
size: 15,
)
)
: Container(),),],),),
Use Gesture Detector to detect Taps on containers.
Here is an example related to this:
Container(
alignment: FractionalOffset.center,
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
Icons.lightbulb_outline,
color: _lights ? Colors.yellow.shade600 : Colors.black,
size: 60,
),
),
GestureDetector(
onTap: () {
setState(() {
_lights = true;
});
},
child: Container(
color: Colors.yellow.shade600,
padding: const EdgeInsets.all(8),
child: const Text('TURN LIGHTS ON'),
),
),
],
),
)
First of All Make a Integer Variable and then set its value to -1 by default
int selectedContainerIndex = -1;
Now, You Can Wrap each of your Container in seperate GestureDetector, and then on the onTap method you can set the selectedContainerIndex value of your container according to you.
See the below Code
Stack(
children: [
//First Container
GestureDetector(
onTap: () {
selectedContainerIndex = 0;
setState(() {});
},
child: new Container(),
),
//Second Container
GestureDetector(
onTap: () {
selectedContainerIndex = 1;
setState(() {});
},
child: new Container(),
),
//Third Container
GestureDetector(
onTap: () {
selectedContainerIndex = 2;
setState(() {});
},
child: new Container(),
),
],
),
Now To know which Container was tapped you can always use the selectedContainerIndex Value
print(selectedContainerIndex);
NOTE: If your containers are more in numbers say 4 or 5, then I would recommend you to use good practice and show those using some dynamic listview builder, instead of hardcoding them each.