Flutter ListTile leading height - flutter

I'd like to have a listtile where the leading widget is simply a half-transparent white container. Let me clarify it with an example
I managed to create the layout above (2 ListTile's). But as you can see, when the title property contains a large text as it is doing in the first (the green) tile, the height of the leading widget is not following as it should. The below code returns a ListTile given a Label which is a class of my own that simply contains text, textcolor and backgroundcolor.
ListTile(
contentPadding: EdgeInsets.zero,
dense: true,
minLeadingWidth: 15,
tileColor: label.bgColor,
textColor: label.textColor,
horizontalTitleGap: 0,
minVerticalPadding: 0,
trailing: getPopUpMenuButton(label),
leading: Container(
height: double.maxFinite,
width: 15,
color: Colors.white54,
),
title: Padding(
padding: EdgeInsets.all(4),
child: Text(
label.title,
),
),
)
So to summarize: how to make the leading height follow the height of the ListTile?

ListTile provide the UI with specific rules. As for leading this Size is defined within the source code as
final BoxConstraints maxIconHeightConstraint = BoxConstraints(
//...
maxHeight: (isDense ? 48.0 : 56.0) + densityAdjustment.dy,
);
final BoxConstraints looseConstraints = constraints.loosen();
final BoxConstraints iconConstraints = looseConstraints.enforce(maxIconHeightConstraint);
final double tileWidth = looseConstraints.maxWidth;
final Size leadingSize = _layoutBox(leading, iconConstraints);
final Size trailingSize = _layoutBox(trailing, iconConstraints);
The height is coming from
maxHeight: (isDense ? 48.0 : 56.0) + densityAdjustment.dy,
We can create the custom widget using rows and column,
body: Center(
child: ListView.builder(
itemCount: 33,
itemBuilder: (context, index) => Padding( //use separate builder to and remove the padding
padding: const EdgeInsets.all(8.0),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.green,
borderRadius:
BorderRadius.circular(defaultBorderRadiusCircular),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: 20,
),
Expanded(
child: Container(
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.cyanAccent,
borderRadius: BorderRadius.only(
bottomRight:
Radius.circular(defaultBorderRadiusCircular),
topRight:
Radius.circular(defaultBorderRadiusCircular),
)),
child: Row(
children: [
Expanded(
child: Text(
"I managed to create the layout above (2 ListTile's). But as you can see, when the title property contains a large text as it is doing in the first (the green) tile, the height of the leading widget is not following as it should. The below code returns a ListTile given a Label which is a class of my own that simply contains text, textcolor and backgroundcolor.",
softWrap: true,
maxLines: 13,
style: TextStyle(),
),
),
Icon(Icons.menu),
],
),
),
),
],
),
),
),
),
),

Related

ListTile inside ListView is causing an error

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/weather_provider.dart';
class BottomListView extends StatelessWidget {
const BottomListView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final weatherData = Provider.of<WeatherProvider>(context).weatherData;
final isLandscape =
MediaQuery.of(context).orientation == Orientation.landscape;
final height = (MediaQuery.of(context).size.height -
50 -
MediaQuery.of(context).padding.top);
return Container(
decoration: const BoxDecoration(
border: Border(
top: BorderSide(width: 0.3, color: Colors.white),
),
color: Color.fromRGBO(255, 255, 255, 0.2),
),
height: isLandscape ? height * 0.35 : null,
child: Row(
children: [
Expanded(
child: SizedBox(
width: 200,
child: ListView(
scrollDirection: Axis.horizontal,
children: [ListTile(title: Text('Hello'))]),
),
),
],
));
}
}
I want to have horizontal scrollable listView with ListTiles. But without ListTile it works fine . With ListTile it is causing an error.How to solve it? I tried giving it the width but didn't work.
Error:
BoxConstraints forces an infinite width.
These invalid constraints were provided to RenderParagraph's layout() function by the following function, which probably computed the invalid constraints in question:
ListTiles needs to be defined with width params explicitly using SizedBox or Container.
If you dont define width, it will throw infinite width error.
So Wrap your ListTile inside a SizedBox or Container.
Since, ListView's scrollDirection is set to Horizontal, you dont need to place it inside of a row i.e. it will show children horizontally.
If scrollDirection is set to vertical, it will show children in a Column i.e. Vertically
Also you can't do both, Use expanded and give width to a child of a row.
Using Expanded means the child will take maximum size available to it w.r.t to its parent.
Try the below code snippet
Container(
decoration: const BoxDecoration(
border: Border(
top: BorderSide(width: 0.3, color: Colors.white),
),
color: Color.fromRGBO(255, 255, 255, 0.2),
),
// height: isLandscape ? height * 0.35 : null,
height: 100,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
SizedBox(
width: 200,
child: ListTile(
title: Text('Hello'),
),
),
SizedBox(
width: 200,
child: ListTile(
title: Text('Hello'),
),
),
SizedBox(
width: 200,
child: ListTile(
title: Text('Hello'),
),
),
SizedBox(
width: 200,
child: ListTile(
title: Text('Hello'),
),
),
],
),
),

Efficient way to make container take remaining width in a Row widget in Flutter

I am new to Flutter and was practicing its UI where I came across a situation where I had a list where each list element have an image on the left and some text on right.
Below is my approach to that
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 20),
children: [
const SizedBox(height: 5),
Row(
children: [
Container(
height: 80,
width: 80,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
image: const DecorationImage(
image: NetworkImage('http://images.unsplash.com/photo-1555010133-d883506aedee?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max'),
fit: BoxFit.cover
)
),
),
const SizedBox(width: 10),
Container(
color: Colors.green,
height: 80,
width: 280,
)
],
),
],
),
Here I am specifying width individually for both containers which is not an efficient way to do this since phone sizes may vary.
Below is the result for above block of code
Screenshot of the app screen
I tried specifying crossAxisAlignment: CrossAxisAlignment.stretch to the Row() but it throws an error as below
The following assertion was thrown during performLayout():
BoxConstraints forces an infinite height.
How can I achieve this? Please assist
Thank you
Wrap the widget with an Expanded inside the row:
Row(
children: [
...otherChildren,
Expanded(
child: Container(
color: Colors.green,
height: 80,
),
),
],
),
Use ListView.builder with ListTile widgets. It has a leading widget (normally an icon or an avatar) to the left, text in the middle and trailing widget (normally an icon), each of which is optional.
ListView.builder(
itemCount: ...,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: const CircleAvatar(
radius: 20.0,
foregroundImage: NetworkImage(...),
),
title: Text(...),
subtitle: Text(...),
trailing: ...,
onTap: () => ...,
);
},
)

how to Make whole page scrollable instead to GridView Builder in flutter?

I am new to flutter and creating a screen with following code:-
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:wallpaper/ui/widgets/card_wallpaper.dart';
import '../../providers/anime_provider.dart';
import '../../models/wallpaper.dart';
import '../../providers/wallpaper_provider.dart';
class AnimeDetail extends StatelessWidget {
#override
Widget build(BuildContext context) {
final String id = ModalRoute.of(context).settings.arguments;
final selectedAnime = Provider.of<AnimeProvider>(context).findById(id);
final selectedWallPaper =Provider.of<WallpaperProvider>(context).getByAnime(id);
final appBar = AppBar(
leading: BackButton(
color: Theme.of(context).primaryColor,
),
elevation: 0,
backgroundColor: Colors.transparent,
title: Text(
selectedAnime.title,
style: TextStyle(
color: Theme.of(context).primaryColor,
fontFamily: 'Righteous',
),
),
);
final mediaQuery = MediaQuery.of(context);
final double totalHeight = mediaQuery.size.height -appBar.preferredSize.height -mediaQuery.padding.top -335;
return Scaffold(
appBar: appBar,
body: SingleChildScrollView(
child: Column(
children: [
Container(
width: double.infinity,
height: 300,
child: Card(
elevation: 3.1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
child: Stack(
children: [
Container(
height: 300,
width: double.infinity,
child: ClipRRect(
child: Image.asset(
selectedAnime.imageUrl,
fit: BoxFit.cover,
),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20),
),
),
),
],
),
),
),
SizedBox(height: 10),
Row(
children: [
Container(
height: 25,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 0),
child: Text(
'WallPapers from ${selectedAnime.title}',
style: TextStyle(
fontFamily: 'Righteous',
fontSize: 16,
color: Theme.of(context).primaryColor,
),
),
),
),
],
),
Container(
width: double.infinity,
height: totalHeight,
child: GridView.builder(
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.5,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
value: selectedWallPaper[i],
child: CardWallpaper(),
),
padding: const EdgeInsets.all(8.0),
itemCount: selectedWallPaper.length,
),
),
],
),
));
}
}
When I run this app, the gridview is scrollable but the image at the top of it does not scroll,but, I want to scroll whole page but only the grid scrolls even though I am using singlechildscrollview. I tried using expanded on gridview builder but it produces error.How can I make whole page scroll instead of just gridview.builder.
![See the screen here]:https://i.stack.imgur.com/ClDZy.jpg
As you can see when you scroll the page only gridview gets scrolled while top image remains there. I want to scroll the page as whole. Is there any way to determine the height of gridtile?
In your gridview, set the parameter scrollable: NeverScrollablePhysics().

Why is Gridview.count not filling out the space with my dynamic content

I am trying to create a gridview starting from the second column. The first card is just a static card with a button in it. So the second card starting should be dynamic.
All the cards have the same width and height. So basically they all should look like the first card (Add a new dog)
But it's not filling out the space as I expected it would.
Here is part of my code from the body section:
body: Stack(fit: StackFit.expand, children: [
//bg image
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(Images.bgYellow), fit: BoxFit.cover)),
),
//content
SafeArea(
bottom: false,
left: true,
right: true,
top: false,
child: Padding(
padding: EdgeInsets.all(3 * SizeConfig.safeBlockHorizontal),
child: GridView.count(
crossAxisCount: 2,
children: [
//add card
Container(
margin: EdgeInsets.symmetric(
vertical: 1 * SizeConfig.blockSizeVertical,
horizontal: 2 * SizeConfig.blockSizeHorizontal),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 8,
offset: Offset(
0, 2), // changes position of shadow
),
],
),
child: FlatButton(
onPressed: null,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
const IconData(0xe901,
fontFamily: 'icDog'),
color: muddyBrown,
size: 20 * SizeConfig.safeBlockHorizontal,
),
SizedBox(height: 5),
Text(
"ADD A NEW DOG",
style: TextStyle(
color: muddyBrown,
fontWeight: FontWeight.bold,
fontSize: 4 *
SizeConfig.safeBlockHorizontal),
)
],
)),
),
//dynamic content
StateBuilder<PetState>(
observe: () => _petStateRM,
builder: (context, model) {
return Column(
children: [
...model.state.pets.map((pet) =>
GestureDetector(
onTap: () {
Navigator.pushNamed(
context, petDetailRoute);
},
child: Container(
margin: EdgeInsets.symmetric(
vertical: 1 *
SizeConfig
.blockSizeVertical,
horizontal: 2 *
SizeConfig
.blockSizeHorizontal),
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(30),
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey
.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 8,
offset: Offset(0,
2), // changes position of shadow
),
],
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
//dynamic data => Photo + Name
children: [
Container(
width: 100.0,
height: 100.0,
decoration: new BoxDecoration(
color:
const Color(0xff7c94b6),
image: new DecorationImage(
image: new NetworkImage(
"${pet.photo}"),
fit: BoxFit.cover,
),
borderRadius:
new BorderRadius.all(
new Radius.circular(
50.0)),
border: new Border.all(
color: muddyBrown,
width: 4.0,
),
),
),
SizedBox(height: 5),
Text(
"${pet.name}",
style: TextStyle(
fontSize: 4 *
SizeConfig
.safeBlockHorizontal,
color: muddyBrown,
fontWeight:
FontWeight.bold),
)
],
),
),
))
],
);
}),
],
)
)),
]));
What about simply adding the 'static' card to the 'dynamic' ones and then build one GridView with all of them together?
Widget newDogButton = Card(...);
//dynamic content
StateBuilder<PetState>(
observe: () => _petStateRM,
builder: (context, model) {
return Column(
children: [
newDogButton,
...model.state.pets.map((pet) => // ...
That should take care of most of your layout issues automatically.
Because StateBuilder return a widget. How about moving the Whole GridView inside it?
...
child: Padding(
padding: EdgeInsets.all(3 * SizeConfig.safeBlockHorizontal),
child: StateBuilder<PetState>(
observe: () => _petStateRM,
builder: (context, model) {
return GridView.count(
crossAxisCount: 2,
children: [
// The button "ADD A NEW DOG" here,
Container(...),
//dynamic content here
...model.state.pets.map((pet) =>
...
).toList(),
},
),
),
Every grid item in GridView will have same height and width, if you want different dynamic height or width for different items, use flutter_staggered_grid_view, in your case:
StaggeredGridView.countBuilder(
crossAxisCount: 2,
itemCount: 2,
itemBuilder: (BuildContext context, int index) => new Container(
color: Colors.green,
child: new Center(
child: new CircleAvatar(
backgroundColor: Colors.white,
child: new Text('$index'),
),
)),
staggeredTileBuilder: (int index) =>
new StaggeredTile.count(1, index.isEven ? 2 : 1),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
)
The situation:
To me, this problem is not coming from your Dynamic content but from the height allowed to it to display it's content. Inside the GridView.count constructor, the default childAspectRatio value is 1.0, meaning that the default max height of each child of the GridView is deviceWidth / crossAxisCount (2 in your case).
The problem:
In order for each child to display correctly, it's height must not exceed this ratio (causing your overflowed error).
My opinion:
To solve this problem, I will either replace the dynamic content StateBuilder<PetState> with a static Widget which height will not exceed the ratio OR wrap the dynamic content StateBuilder<PetState> in a SingleChildScrollView to ensure that the overflowed error will not happen and the wrapper can produce the scroll effect to see the entire dynamic content.

I can't get CircleAvatar to be anything but an oval when I assigned a backgroundImage

I'm trying to build out a basic ListView filled with ListTiles, and have a CircleAvatar (either leading or trailing) with a photo in it.
My code is:
body: ListView.builder(
itemCount: 18,
itemBuilder: (ctx, i) {
return Padding(
padding: EdgeInsets.all(5),
child: ListTile(
trailing:
CircleAvatar(
radius: 50.0,
// child: Text('hi'),
backgroundImage:
AssetImage(
'assets/images/Survivor-12.jpg',
),
),
title: Text('Name of the Person $i'),
subtitle: Text('Assigned to: Bob'),
leading: Text(i.toString(), style: TextStyle(fontSize: 24)),
),
);
},
The output however is like this:
How can I get the image to be an actual circle? If I remove it and replace it with a Text widget it works.
For the image to be in actual circle, you can wrap your CircleAvatar Widget inside of a Container Widget and use decoration property to define its shape as below.
ListView.builder(
itemCount: 18,
itemBuilder: (ctx, i) {
return Padding(
padding: EdgeInsets.all(5),
child: ListTile(
trailing: Container(
height: 55.0,
width: 55.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
),
child: CircleAvatar(
radius: 30.0,
backgroundImage: NetworkImage(
'https://buildflutter.com/wp-content/uploads/2018/04/buildflutter_255.png'),
),
),
title: Text('Name of the Person $i'),
subtitle: Text('Assigned to: Bob'),
leading: Text(i.toString(), style: TextStyle(fontSize: 24)),
),
);
},
),
ListTile(
trailing: ClipOval(
child: Container(
color: Colors.white,
height: 70,
width: 100,
child: Image.network(
imageUrl[0],
),
),
),
title: Text('Name of the Person $i'),
subtitle: Text('Assigned to: Bob'),
leading: Text(i.toString(), style: TextStyle(fontSize: 24)),
),
It looks like the list-type container changes the rendering: if I change a ListView() for a Column(), the CircleAvatar renders correctly in my application.
There's a hint in the ListView docs:
In the cross axis, the children are required to fill the ListView.
To solve this here I wrapped my CircleAvatar in a Row which will fill the horizontal space of the ListView, and adjusted the contents of the Row accordingly. The ListTile may need further jiggling to get it to correctly emulate a Row.
ListView(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircleAvatar(
radius: 60.0,
backgroundImage: avatarImage ?? avatarImage,
),
],
),
//...
],
);