Flutter Overlapped InkWells Gesture Detection - dart

While working on an application, we have an instance where we want a card to have an inkwell as well as a button on the card (also with an inkwell). However, I have been unable to determine a way to separate the gestures such that only the inkwell directly under the user's tap is invoked. As it is today, it appears that the tap 'bleeds through' to the next inkwell such that both splash effects are invoked. This is undesirable behavior, the application appears to be selecting the card not the invokable item on the card (note: actual application is much different but the same issue is present). I have reproduced this in a simple application to demonstrate the bleed through when the user pressed the button in the bottom right of the card. Is there something I am missing which can prevent this behavior? Thanks
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: Card(
color: Colors.blue,
child: InkWell(
onTap: () { },
child: Container(
height: 150.0,
child: Align(
alignment: Alignment.bottomRight,
child: RaisedButton(
color: Colors.red,
onPressed: () { },
),
),
),
),
),
);
}
}

This is the normal expected InkWell behavior as most of the time you want to use it's tap feature for every widget in it's tree. So what you can do is to define a Stack and set the button in the z-axis absolute over the InkWell:
Widget build(BuildContext context) {
return new Scaffold(
body: Center(
child: Card(
color: Colors.blue,
child: Stack(
children: <Widget>[
InkWell(
onTap: () {
print("inkwell");
},
child: Container(
height: 150.0,
),
),
RaisedButton(
color: Colors.red,
onPressed: () {
print("button");
},
),
],
),
),
),
);
}
If you would want to set the button in the bottom right corner again you can set a Row and Colum around it and assign it's alignment:
#override
Widget build(BuildContext context) {
return new Scaffold(
body: Center(
child: Container(
height: 150.0,
child: Card(
color: Colors.blue,
child: Stack(
children: <Widget>[
InkWell(
onTap: () {
print("inkwell");
},
child: Container(
height: 150.0,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
RaisedButton(
color: Colors.red,
onPressed: () {
print("button");
},
),
],
),
],
),
],
),
),
),
),
);
}
Upper code would result in seperated widgets:

Related

click on image that image style should change or image should be change in flutter

I have 4 images in 2 columns, when I clicked on one image its style should change like color, shadow should change or that image should be replaced by other image. Once click on that image, other images should remain same. It should work like radio buttons. How to do that? Please help me, thanks in advance.
final img_rowi= Center(child:
new Container(
color: Colors.transparent,
padding: const EdgeInsets.all(5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(padding: const EdgeInsets.all(3.0),child: Stack(
alignment: Alignment.center,
children: <Widget>[
svgIcon,new GestureDetector(
onTap: (){
setState(() {
pressed = !pressed;
});
},
child:
Container(
child: new Column(
children: <Widget>[
new Container(
child: new Image.asset(
'images/sheep_female.png',
height: 50.0,
fit: BoxFit.cover,
),
),
new Container(
child: new Text('Sheep',style: pressed
? TextStyle(color: const Color(0xFFCDCDCD),fontFamily: 'Montserrat',
)
: TextStyle(color:Colors.black,fontFamily: 'Montserrat',
),),
),
],
),
),
),
],
),),
Padding(padding: const EdgeInsets.all(3.0),child:
Stack(
alignment: Alignment.center,
children: <Widget>[
svgIcon,new GestureDetector(
onTap: (){
setState(() {
pressed1 = !pressed1;
});
},
child:
Container(
child: new Column(
children: <Widget>[
new Container(
child: new Image.asset(
'images/biily_doe.png',
height: 50.0,
fit: BoxFit.cover,
),
),
new Container(
child: new Text('Billy Doe',style: pressed1
? TextStyle(color: const Color(0xFFCDCDCD),fontFamily: 'Montserrat',
)
: TextStyle(color:Colors.black,fontFamily: 'Montserrat',
),),
),
],
),
),
),
],
),),
],
),
),
);
Store initial properties of Image in variables. For example if I want to set initial color of FlutterLogo widget to Colors.blue then declare a state in the class. Then wrap your Image with GestureDetector widget and set onTap property. Now call setState method and change all the variables (properties of Image) inside it.
Below is an example where there is one FlutterLogo widget where I've set initial color of that widget to be Colors.blue and when I tap on it, color of FlutterLogo widget is changed to Colors.green. If I again tap on it and if color is Colors.green then it changes color to Colors.yellow and so on. You can do similar thing with your Image and change it's size, visibility and other properties.
There is also imagePath variable which stores path of initial asset and when user taps on second widget (Image.asset) in Column, value of variable imagePath is changed and build method get called again and image is replaced.
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: MyApp()));
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool visibility;
Color colorOfFlutterLogo = Colors.blue;
String imagePath1 = "assets/initial-path-of-image-1";
String imagePath2 = "assets/initial-path-of-image-2";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
),
body: Column(
children: <Widget>[
GestureDetector(
onTap: () => setState(() {
if (colorOfFlutterLogo == Colors.blue)
colorOfFlutterLogo = Colors.green;
else if (colorOfFlutterLogo == Colors.green)
colorOfFlutterLogo = Colors.yellow;
else if (colorOfFlutterLogo == Colors.yellow)
colorOfFlutterLogo = Colors.blue;
}),
child: FlutterLogo(
size: double.infinity,
colors: colorOfFlutterLogo,
),
),
// Image 1
GestureDetector(
onTap: () => setState(() {
imagePath2 = "assets/new-path-for-image-2";
}),
child: Image.asset(imagePath1),
),
// Image 2
GestureDetector(
onTap: () => setState(() {
imagePath1 = "assets/new-path-for-image-1";
}),
child: Image.asset(imagePath2),
)
],
));
}
}

how to create a custom container with three individual buttons inside

I am trying to create a custom widget that is a rounded rectangle containing 3 iconButtons, used for navigation. but, as far as I have seen, iconButtons cannot be used outside of material widgets, and i don't know how to wrap them in a widget that doesn't mess up my UI.
just a container with iconButtons throws "no material widget found, iconButton widgets require material widget"
trying to wrap with a material widget, i get positional arguments, messing up my UI
i have tried wrapping my container in other widgets to no avail.
here is a piece of my code, just one of the icons in the container. the code repeats twice with different icon and onPressed before closing the widget.
i really would like my UI to look how i planned, and for these buttons to work.
#override
Widget build(BuildContext context) {
return Container(
height: 50.0,
width: 200.0,
// color: Colors.grey[800],
decoration: new BoxDecoration(
color: Colors.grey[800],
borderRadius: new BorderRadius.all( Radius.circular(50.0)),
),
child: Stack(
children: <Widget>[
Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Container(
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
icon: Icon(Icons.menu),
color: Colors.white,
onPressed: () {
print('test');
},
) // IconButton
], // <Widget>[]
) //Column
), // Container
Wrap your code in Scaffold and it will work.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
height: 50.0,
width: 200.0,
decoration: BoxDecoration(
color: Colors.grey[800],
borderRadius: new BorderRadius.all(Radius.circular(50.0)),
),
child: Stack(
children: [
Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
IconButton(
icon: Icon(Icons.menu),
color: Colors.white,
onPressed: () {
print('test');
},
)
],
),
)
],
),
)
],
),
),
); // IconButton ], // [] ) //Column ), // Container
}

Expanded widget inside Column not expanded, instead it's got invisible

I am trying to expand widget inside of Column widget but not able to make it expended.
When giving constant height to parent widget, the layout will be rendered as expected. But as I remove the constant height layout is not as expected as I want to make Listview with it and I should not give a constant height to the widget which will be used as listview item.
Below is my layout code.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'layout test',
home: Layout_test_class(),
));
}
class Layout_test_class extends StatelessWidget {
Widget cell() {
return Container(
color: Colors.yellow,
// height: 200, after un commenting this will work. but i want to make it without this
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Container(
color: Colors.green,
child: Text('apple z'),
),
),
Container(
color: Colors.red,
child:Text('apple 2'),
)
],
),
),
Column(
children: <Widget>[
Container(
color: Colors.black,
width: 200,
height: 200,
),
],
),
],
),
);
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('title'),
),
body: Center(
child: ListView(
children: <Widget>[
cell(),
],
)
),
);
}
}
Below is my expected output screenshot.
Try to wrap your Container with IntrinsicHeight
return IntrinsicHeight(
Container(
color: Colors.yellow
child: ...
)
)
Your ListView needs to be inside Flexible. Flexible inside Column will set maximum height available to ListView. ListView needs a finite height from parent, Flexible will provide that based on max. space available.
Column(
children: <Widget>[
Flexible(
child: ListView.builder(...)
),
Container(
color: Colors.red,
child:Text('apple 2'),
),
],
)
A nice way of doing this, it's to play with the MediaQuery, heigth and width.
Let me explain, if you want the widget to have the maximum heigth of a decide screen, you can set it like this:
Container(
height: MediaQuery.of(context).size.height // Full screen size
)
You can manipulate it by dividing by 2, 3, 400, the value you want.
The same things works for the width
Container(
width: MediaQuery.of(context).size.width // Can divide by any value you want here
)
Actually quite the opposite, if you're planning to use this as an item in a listViewyou can't let infinite size on the same axis your listView is scrolling.
Let me explain:
Currently you're not defining any height on your cell() widget, which is fine if you're using it alone. like this :
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'layout test',
home: Layout_test_class(),
));
}
class Layout_test_class extends StatelessWidget {
Widget cell() {
return Container(
color: Colors.yellow,
//height: 250, after un commenting this will work. but i want to make it without this
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Container(
color: Colors.green,
child: Text('apple z'),
),
),
Container(
color: Colors.red,
child: Text('apple 2'),
)
],
),
),
Column(
children: <Widget>[
Container(
color: Colors.black,
width: 200,
height: 200,
),
],
),
],
),
);
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('title'),
),
body: cell(),
);
}
}
But using it with a listView you have to define a height. A listView scrolls as long as it have some content to scroll. Right now it just like you're giving it infinite content so it would scroll indefinitely. Instead Flutter is not constructing it.
It's actually quite ok to define a global size for your container (as an item). You can even define a specific size for each using a parameter like this :
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'layout test',
home: Layout_test_class(),
));
}
class Layout_test_class extends StatelessWidget {
Widget cell(double height) {
return Container(
color: Colors.yellow,
height: height, //after un commenting this will work. but i want to make it without this
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Container(
color: Colors.green,
child: Text('apple z'),
),
),
Container(
color: Colors.red,
child: Text('apple 2'),
)
],
),
),
Column(
children: <Widget>[
Container(
color: Colors.black,
width: 200,
height: 200,
),
],
),
],
),
);
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('title'),
),
body: ListView(
children: <Widget>[
cell(250.0),
cell(230.0),
cell(300.0),
],
)
);
}
}

Flutter How to set Title to Show Modal Bottom Sheet?

Is there a possibility to set title and perhaps navigation back button on this showModalBottomSheet?
I expect something like this...
Yes it's possible to do something like that in Flutter. You can use Column Widget and make its first child as a title bar or something like that with title and back arrow icon.
Here's the code for that:
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
canvasColor: Colors.transparent,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget{
#override
HomePageS createState()=> HomePageS();
}
class HomePageS extends State<MyHomePage>{
#override
Widget build(BuildContext context){
return Scaffold(
body: Container(
color: Colors.white,
child: Center(
child: FlatButton(
child: Text("Show BottomSheet"),
onPressed: () async{
showModalBottomSheet(
context: context,
builder: (BuildContext context){
return ClipRRect(
borderRadius: BorderRadius.only(topLeft: Radius.circular(20.0), topRight: Radius.circular(20.0)),
child: Container(
color: Colors.white,
child: Column(
children: [
ListTile(
leading: Material(
color: Colors.transparent,
child: InkWell(
onTap: (){
Navigator.of(context).pop();
},
child: Icon(Icons.arrow_back) // the arrow back icon
),
),
title: Center(
child: Text("My Title") // Your desired title
)
),
]
)
)
);
}
);
}
)
)
)
);
}
}
Here is the output:
If you don't wanna use the InkWell widget, you can use the IconButton widget like this:
...
ListTile(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: (){
Navigator.of(context).pop();
}
),
title: Center(
child: Text("My Title")
)
),
...
But if you noticed, the title text is not really centered. In this case, we can replace the ListTile widget to a Stack widget and do something like this:
child: Column(
children: [
Stack(
children: [
Container(
width: double.infinity,
height: 56.0,
child: Center(
child: Text("My Title") // Your desired title
),
),
Positioned(
left: 0.0,
top: 0.0,
child: IconButton(
icon: Icon(Icons.arrow_back), // Your desired icon
onPressed: (){
Navigator.of(context).pop();
}
)
)
]
),
]
)
...
This is the output:
But what would happen if we have a very long text for our title? It would definitely look like this:
Ugly, right? We can see that our Text widget overlaps with our IconButton widget. To avoid this, we can replace our Stack widget with a Row widget.
Here's the code for that:
...
child: Column(
children: [
Row( // A Row widget
mainAxisAlignment: MainAxisAlignment.spaceBetween, // Free space will be equally divided and will be placed between the children.
children: [
IconButton( // A normal IconButton
icon: Icon(Icons.arrow_back),
onPressed: (){
Navigator.of(context).pop();
}
),
Flexible( // A Flexible widget that will make its child flexible
child: Text(
"My Title is very very very very very very very long", // A very long text
overflow: TextOverflow.ellipsis, // handles overflowing of text
),
),
Opacity( // A Opacity widget
opacity: 0.0, // setting opacity to zero will make its child invisible
child: IconButton(
icon: Icon(Icons.clear), // some random icon
onPressed: null, // making the IconButton disabled
)
),
]
),
]
)
Output would be like this:
And this (if the title is not long):
I guess I have naive solution but this works perfectly for me. You might as well try it.
showModalBottomSheet(
barrierColor: Colors.transparent,
enableDrag: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
context: context,
builder:(context){
return Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8.0, bottom: 8.0),
child: Stack(children: [
Padding(
padding: const EdgeInsets.only(top: 65),
child: SingleChildScrollView(
child:<<Scrollable Wdgets>>,
),
),
Card(
elevation: 3,
color: Colors.grey[850],
child: ListTile(
leading: Text("YOUR TITLE",
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w500)),
trailing: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Icon(
Icons.close,
color: Colors.white,
size: 20,
),
),
),
),
]),
);}

Pin a view to bottom of device

I have a view in a stack and I would like it to stay pinned to the bottom of the phone when it is turned to landscape. Currently i have the following:
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: detectOrientationForAppBar(),
backgroundColor: Colors.black,
key: _scaffoldKey,
body: new Column(
children: <Widget>[
new Expanded(
child: new Stack(
children: <Widget>[
new Container(
height: double.infinity,
width: double.infinity,
child: new Padding(
padding: const EdgeInsets.all(1.0),
child: new Center(
child: _cameraPreviewWidget(),
),
),
),
_captureControlRowWidget()
],
),
),
],
),
);
}
within this i would like the _captureControlRowWidget to stay fixed to the bottom of the device even when it is rotated. Here is my _captureControlRowWidget:
Widget _captureControlRowWidget() {
return new Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
new Container(
color: Colors.black,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new IconButton(
icon: const Icon(
Icons.camera_alt,
color: Colors.white,
),
color: Colors.white,
onPressed: controller != null &&
controller.value.isInitialized &&
!controller.value.isRecordingVideo
? onTakePictureButtonPressed
: null,
),
//showSwapCameraButton(),
new IconButton(
icon: new Icon(
Icons.videocam,
color: isRecording ? Colors.red : Colors.white,
),
color: isRecording ? Colors.red : Colors.white,
onPressed: controller != null &&
controller.value.isInitialized &&
!controller.value.isRecordingVideo
? onVideoRecordButtonPressed
: onStopButtonPressed,
)
],
),
),
],
);
}
Ive tried a few things like using the CrossAxis and MainAxis Alignments but it always sticks to the edge of the phone that is lowest. e.g if im holding the phone in porttrait the widget is at the bottom, if i rotate the phone 90 degrees in either direction the widget should be on the right side of the phone.
At the moment, your main body is a Column, so, as expected your control widget is below your camera preview. In landscape mode you want your control widget to be to the right of the camera preview, which means you want your main body to be a Row.
You can achieve this by changing the top Column to a Flex (which is a switch-selectable Row/Column).
#override
Widget build(BuildContext context) {
bool landscape = MediaQuery.of(context).orientation == Orientation.landscape;
return new Scaffold(
appBar: detectOrientationForAppBar(),
backgroundColor: Colors.black,
key: _scaffoldKey,
body: new Flex(
direction: landscape ? Axis.horizontal : Axis.vertical,
You will have to make the same change in your _captureControlRowWidget, making its Columns and Rows into orientation sensitive Flexes.

Resources