How to make onTap only respond on Actual Text not whole ListTile width - flutter

Illustrations
I have the following problem:
The title and subtitle components of ListTile expand fully even when there only is text in some smaller area.
Usually, this is not a problem as you could just color the background of the title the same as the ListTie.
However, I have a problem when trying to get an onTap event working on a Row in title or subtitle:
As you can see, I have a Row in the title component with an Icon and a Text. That works fine, however, trying to add an onTap to this title will produce unwanted behavior.
I only want the onTap to trigger when I actually tap the text or icon and not when I tap somewhere else to the right. I am clueless how I would constrain the width of the title to the width of the text plus icon. It seems like nothing works. What is going on here?
Code
I created a Gist with the source code (minimal reproducible example) for both illustrations. This should help to give you an idea of the setup.
How do I only trigger onTap when the actual children of the Row are tapped and not the whole title area that apparently expands no matter what I do?
Notes
I could probably just add an InkWell to both the Text and the Icon individually, however, that is not at all what I want because I want a splash (InkWell's splash) to cover the title and icon together and they should not be visually seperated.

Please, try this code it works for me
import 'package:flutter/material.dart';
main() {
runApp(
MaterialApp(
home: Scaffold(
body: Center(
child: Container(
color: Colors.greenAccent,
child: ListTile(
dense: true,
leading: Material(child: const Text('leading')),
title: Material(
child: Row(
children: <Widget>[
InkWell(
onTap: () {},
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(Icons.format_align_left),
Text(
'title',
),
],
),
),
Container(
height: 0.0,
),
// Expanded(child: Container(
// height: 10.0,
// ),)
],
),
),
subtitle: Material(child: const Text('subtitle')),
trailing: Material(child: const Text('trailing')),
),
),
),
),
),
);
}

Use FlatButton.icon instead of using InkWell and Row.
FlatButton.icon solves your mentioned problem and serves both of your purpose to have splash effect and a Title with Icon. It reduces your code too. See below for example
title: Align(
alignment: Alignment.centerLeft,
heightFactor: 1.0,
child: FlatButton.icon(
splashColor: Colors.white,
onPressed: () {},
icon: Icon(Icons.format_align_left),
label: Text('title'))),
Full code
main() {
runApp(
MaterialApp(
home: Scaffold(
body: Center(
child: Container(
color: Colors.greenAccent,
child: ListTile(
dense: true,
leading: Material(child: const Text('leading')),
title: Material(
child: Align(
alignment: Alignment.centerLeft,
heightFactor: 1.0,
child: FlatButton.icon(
splashColor: Colors.white,
onPressed: () {},
icon: Icon(Icons.format_align_left),
label: Text('title'))),
),
subtitle: Material(child: const Text('subtitle')),
trailing: Material(child: const Text('trailing')),
),
),
),
),
),
);
}

Related

Bottom overflowed

I have a code that is responsible for displaying the characteristics of the device (the code is presented below. I have shortened it for readability.)
My problem:
in the body: if there are long names, then the display of elements is confused (as in the photo). How can I get the elements to display correctly even with long titles?
in appbar: how can I make the text size of the device name change depending on the size of the text itself (so that the name of the device can be seen in full)
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(device.name),
leading: IconButton(
icon: Icon(Icons.favorite_border),
color: Colors.black,
iconSize: 40.0,
onPressed: () {}
),
iconTheme: const IconThemeData(color: Colors.white, size: 40),
actions: [
IconButton(
icon: const Icon(Icons.phonelink_setup, size: 40.0,),
color: Colors.black,
onPressed: () {}),],
backgroundColor: Colors.white,
centerTitle: true,
),
body: SingleChildScrollView(
child: Align(
alignment: Alignment.topCenter,
child: Column(
children: [
Container(
constraints: const BoxConstraints(maxWidth: 800, maxHeight: 400),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(5.0),),
child: Card(
child: Column(
children: [
ListTile(
title: const Text('Brand:'),
trailing: Text('${device.brand}')),
const Divider(color: Colors.black, endIndent: 10, indent: 10),
],
),
),
),
]
)
)
)
);
}
}
You can use FittedBox widget! For your example, if you want to resize your AppBar text based on width.
appBar: AppBar(
title: const FittedBox(
fit: BoxFit.fitWidth,
child: Text('Very very veryveryvery veryveryvery longname'),
),
),
Then, your text size will be adjusted accordingly. You can do the same with the device name.
Another option for you for the device name might be using overflow and softwrap to make it multiple lines without adjusting the font size.
appBar: AppBar(
title: const Text(
'Very very veryveryvery veryveryvery longname',
overflow: TextOverflow.visible,
softWrap: true,
),
),
You may look at the BoxFit options here.
There is another option that you can use which is Auto_Text_Size. It's a very nice package and works very well!
you can fix the overflow by wrapping the listTile inside another single scroll

Adding multiple rows or column in body

Hi trying to figure out why my hamburger is getting misaligned when i add another Expanded() code within the body.
I tried to follow this url to develop a hamburger menu and responsive for all platforms at once.
Flutter Blog
Below is the code that i tried where i added Expanded() below the Row()
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
return Scaffold(
key: _scaffoldKey,
drawer: AppDrawer(),
body: Row(
children: [
Row(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: IconButton(
icon: Icon(Icons.menu, size: 30),
onPressed: () {
_scaffoldKey.currentState.openDrawer();
}),
),
],
),
Expanded(
child: Container(
width: width,
child: Column(
children: [
Expanded(
child: Lottie.network(
'https://assets2.lottiefiles.com/datafiles/6WfDdm3ooQTEs1L/data.json'),
),
Expanded(
child: Column(
children: [
Flexible(
child: Text(
"Multivariate Testing",
style: GoogleFonts.montserrat(
fontSize: 50,
),
),
),
SizedBox(
height: 10,
),
Flexible(
child: Text(
'Run experiments to make key flows more effective.',
style: GoogleFonts.montserrat(
fontSize: 25,
),
),
),
SizedBox(
height: 35,
),
// ignore: deprecated_member_use
TextButton(
onPressed: () {},
child: Text(
'Create Experiment',
style: GoogleFonts.montserrat(
fontSize: 20,
),
),
)
],
),
),
],
),
),
)
],
),
);
}
}
and the output that i am seeing from mobile view. Appreciate any help. Sorry for too big screenshot!
This is because of the default functionality of Row.
So change your first child for your top level Row to this,
Scaffold(
key: _scaffoldKey,
drawer: AppDrawer(),
body: Row(
children: [
Align(
alignment: Alignment.topCenter,
child: Padding(
padding: const EdgeInsets.all(16),
child: IconButton(
icon: Icon(Icons.menu, size: 30),
onPressed: () {
_scaffoldKey.currentState.openDrawer();
})),
),
Expanded( ...... rest of your code
Here, we are using the Align widget with alignment: Alignment.topCenter.
Align widget takes the entire available space in the parent and then depending on the alignment property, aligns it't child inside it.
So, saying Alignment.topCenter will place it at the top vertically and middle horizontally.
Using an AppBar will unnecessarily complicate your situation since now, the AppBar is going to take up space a the top, but in your case, you want your Lottie view to be visible at the top, so it will overlap.
Because Expanded fills up the whole space and that's why your hamburger is getting misaligned.
Why don't you try using appbar for the hamburger?

how to put icons in drawer flutter

i want to put icons in the left hand side of drawer's children like this photo
is there any specific widgets to do something like this?
my drawer code is this
Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('forms'),
onTap: () { //and rest of the code
Use leading property of ListTile widget to add an Icon.
ListTile(
onTap: () {},
leading: Icon(Icons.add),
title: Text('Add'),
);

Flutter Scrollable navigation drawer with one menu item aligned to the bottom

I have a navigation drawer with a bunch of items and I want the logout item aligned to the very bottom of the drawer.
return Drawer(
child: Column(
children: [
DrawerAccountHeader(UserType.worker),
DrawerNavigationItem(
"Home",
Icons.home,
),
new ListTile(
title: new Text('Jobs', style: TextStyle(color: Colors.black54),),
dense: true,
),
DrawerNavigationItem(
"Nearby",
Icons.location_on,
),
DrawerNavigationItem(
"Applied",
Icons.check_circle_outline,
),
DrawerNavigationItem(
"Hired",
Icons.check_circle,
),
Expanded(child: Container()),
Divider(),
DrawerNavigationItem(
"Logout",
Icons.exit_to_app,
)
],
),
);
But on smaller screen I get overflow error because that topmost column height is bigger than screen height. So I tried changing the column to a ListBox and then I get an exception "Vertical viewport was given unbounded height", because I have the Expanded() in between the items to make that gap so that Login buttons sticks to the bottom of the list.
Is there any way to achieve below?
Have the login button (and possibly a group of buttons) stick to the bottom of the screen when there's vertical space.
Make the drawer item scrollable when there's not enough vertical space.
(nice to have) The scrollable, the entire drawer can be scrolled. I.e. on a small screen the user will need to scroll the drawer to get to the logout option.
Try this,
Drawer(
child: LayoutBuilder(
builder: (context, constraint) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraint.maxHeight),
child: IntrinsicHeight(
child: Column(
children: <Widget>[
DrawerHeader(
margin: EdgeInsets.all(0.0),
child: Center(
child: Text("Header"),
),
),
ListTile(
leading: Icon(Icons.home),
title: Text("Home"),
onTap: () {},
),
new ListTile(
title: new Text(
'Jobs',
style: TextStyle(color: Colors.black54),
),
dense: true,
),
ListTile(
leading: Icon(Icons.location_on),
title: Text("Nearby"),
onTap: () {},
),
ListTile(
leading: Icon(Icons.check_circle_outline),
title: Text("Applied"),
onTap: () {},
),
ListTile(
leading: Icon(Icons.check_circle),
title: Text("Hired"),
onTap: () {},
),
const Expanded(child: SizedBox()),
const Divider(height: 1.0, color: Colors.grey),
ListTile(
leading: Icon(Icons.exit_to_app),
title: Text("Logout"),
onTap: () {},
)
],
),
),
),
);
},
),
)
Use the Align widget to position bottom
Align(
alignment: Alignment.bottomCenter, //you can set it as per your requirement
child: Container(
child: Image.asset(
"assets/drawerCycleImage.png", //or Text widget or More Any
)),
#override
Widget build(BuildContext context) {
return Scaffold(
appBar:AppBar(
title:Text('Title')),
drawer:Drawer(
child:Stack(
children:[
ListView(
shrinkWrap:true,
children: List.generate(25,(ind){
//SizedBox is to remain space for logout button !
return ind==25 ?
SizedBox(height:60)
: ListTile(title:Text('Item $ind'));
})
),
Align(
alignment:Alignment.bottomCenter,
child: Container(
height:60,
color:Colors.black,
child: ListTile(
leading:Icon(Icons.exit_to_app),
title:Text('Logout')
),
),
)
]
)
),
body: Container(
alignment:Alignment.center,
child: Text('Hello !')
),
);
}

Resize leading widget in flutter AppBar

I am making a custom AppBar that has a larger height than the typical AppBar. I would like to resize the leading widget/icon as well, and take advantage of the automaticallyImplyLeading default behaviors (so the menu icons and back icons are automatically implemented).
This is the solution I thought I would implement:
class AppAppBar extends PreferredSize{
AppAppBar(String title) : super(
preferredSize: Size.fromHeight(56.0),
child: AppBar(
centerTitle: true,
title: Text(title, style: textStyle)
)) {
(child as AppBar).leading =
SizedBox(width: 30.0, height: 30.0, child: (child as AppBar).leading);
}
static const textStyle = TextStyle(fontSize: 32.0);
}
But of course this won't work because (child as AppBar).leading is final.
So in the AppBar below (text size made dramatically larger for illustration purposes), I would like to make the automatically added hamburger icon larger in comparison.
What do you think? Are there solutions for this or should I give up on the automatic icons and add them myself?
Edit: Added an image to show what I mean
You cant because it is a predefined widget.
You can work around it with a Row widget:
Scaffold(
key:_scaffoldKey,
drawer: Drawer(),
appBar: AppBar(
automaticallyImplyLeading: false
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 20, // Your Height
width: 20, // Your width
child: IconButton( // Your drawer Icon
onPressed: () => _scaffoldKey.currentState.openDrawer()),
icon: Icon(Icons.arrow_back, color: Colors.white),
),)
// Your widgets here
],
),
),
)
You need the Key to open the drawer with _scaffoldKey.currentState.openDrawer().
automaticallyImplyLeading: false will prevent the default drawer Icon.
A simple example to demonstrate Raffi Jonas answer
AppBar(
automaticallyImplyLeading: false,
title: Row(
children: [
Expanded(
child: Text('One'),
),
Center(
child: Text('Two'),
),
Expanded(
child: Align(
alignment: Alignment.centerRight,
child: Text('Three'),
),
),
],
),
),
The leading Widget width of AppBar in Flutter can be resized using the leadingWidth property.
For Example :
AppBar(
title: const Text('Title'),
leadingWidth: 50,
leading: Container()
)
What you want is a custom app bar in Flutter. Most people try giving their own widgets in the title argument of an AppBar. But let me show you how to properly do it.
#override
Widget build(BuildContext context) => Scaffold(
appBar: _appBar(),
body: _body(),
);
//Custom AppBar
_appBar() => PreferredSize(
//kToolBarHeight: Default size used by all AppBar widgets in Flutter.
//MediaQuery...: viewPadding.top is StatusBar area. viewPadding.bottom is iPhone bottom bar.
preferredSize: PreferredSize.fromHeight(kToolBarHeight + MediaQuery.of(context).viewPadding.top),
child: Container(
child: Row(
//This will spread Row content evenly across the screen.
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
//Leading Widget
Icon(Icons.home),
//Title
Text("Hello World!"),
//Trailing Widget / Actions
Icon(Icons.home),
],
),
),
);
Widget _body() => Container(
color: Colors.blue,
);
You can set a margin for height or width.
AppBar(
leading: Container(
margin: EdgeInsets.symmetric(vertical: 15),
child: IconButton(
icon: Icon(CupertinoIcons.chevron_left),
onPressed: () {},
),
),
To use a custom appBar leading button, here is the code.
appBar: AppBar(
title: Text('hello'),
// automaticallyImplyLeading: false,
elevation: 0,
leadingWidth: 58,
actions: [
ProfileBar(),
],
leading: Container(
width: 14,
//color: Colors.yellow,
child: Row( // <--- Using row to avoid force resizing of leading
children: [
Padding( // <--- Padding to make it look more nicer
padding: const EdgeInsets.only(left: 24.0),
child: GestureDetector(
onTap: () {
Navigator.of(context).pop();
},
child: SvgPicture.asset( // <-- Using SvgPicture package
'assets/svg/icons/backbtn.svg',
width: 24,
height: 24,
),
),
),
],
),
),
),