My `GridView` breaks or is not scrollable - flutter

I am pulling my hair trying to make my grid view displays within the user's profile page, which is basically a Column Widget.
Either I get error regarding the "unbounceness" of the widget or other error like RenderFlex children have non-zero flex but incoming height constraints are unbounded..
If I set the shrinkWrap to true the grid is there, but unscrollable.
I tried many solution such as adding a Flexible or Expanded parent with a mainAxisSize to min for the Column.
My grid view code is a as follows:
/// A grid view of the user's ads
class UserAdWidget extends StatelessWidget {
final String userId;
const UserAdWidget({super.key, required this.userId});
#override
Widget build(BuildContext context) {
final query = ClassifiedAd.getQueryFromUserId(userId);
return FirestoreQueryBuilder<ClassifiedAd>(
query: query,
builder: (context, snapshot, _) => Flexible(
child: GridView.builder(
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
crossAxisSpacing: 2,
mainAxisSpacing: 20),
itemCount: snapshot.docs.length,
itemBuilder: (context, index) {
// if we reached the end of the current items, get more
if (snapshot.hasMore && index + 1 == snapshot.docs.length) {
snapshot.fetchMore();
}
final ad = snapshot.docs[index].data();
return ad.galleryWidget(context,
withAvatar: false, onTap: () {});
})));
}
}
The component code where the grid is rendered within a Column is as follows:
#override
Widget build(BuildContext context) {
return Consumer<StateModel>(builder: (context, appState, child) {
final selfProfile = appState.loggedUser?.id == widget.user.id;
return Scaffold(
appBar: AppBar(
title:
Text(selfProfile ? "Votre profil" : widget.user.displayName)),
body: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(mainAxisSize: MainAxisSize.min, children: [
// === User's profile widget
UserProfileWidget(user: widget.user),
const Divider(),
// == Grid of user's ads
const Text("The ads:"),
const Text(""),
UserAdWidget(userId: widget.user.id),
])));
});
}
The current code renders like so but does not scroll:

Related

Flutter List View Scrolling Only by Clicking on Edges

I'm Having a List view in my app, and the problem is it does not scroll when touching the middle of the list view but only when touching the edges of the list.
and here is my View Code.
Widget build(BuildContext context) {
const horizontalPadding = EdgeInsets.symmetric(horizontal: 10);
final controller = Get.put(UnitsListController());
return Scaffold(
appBar: AppBar(
),
body: Padding(
padding: horizontalPadding,
child: GetX<UnitsListController>(
builder: (controller) {
return controller.isBusy.value
?const Center(
child: CircularProgressIndicator(
backgroundColor: Colors.grey,
color: Colors.blue,
strokeWidth: 5,
),
): ListView.builder(
shrinkWrap: true,
itemCount: controller.unitsList.length,
itemBuilder: (context, index) {
var item = controller.unitsList[index];
String address ='${item.country},${item.state},${item.area},${item.block},${item.plot},'
'${item.lane},${item.buildingName},${item.buildingNumber}';
return GestureDetector(
onTap: () {
controller.selectedUnit = item;
controller.onUnitTap();
},
child: AppUnitCard(
type: item.type,
address: address,
rooms: item.roomsNum??0,
rent: item.rent,
bathrooms: item.bathsNum,
space: item.unitSpace,
),
);
},
);
}),
),
);
}
Note
that i was wrapping the Scaffold body with a SingleChildScrollView and removed it both ways it didn't work.
Wrap the listview with a SingleChildScrollView and add NeverScrollableScrollPhysics to the listview
SingleChildScrollView (
child : ListView.builder(
shrinkWrap: true,
physics : NeverScrollableScrollPhysics()
)
),
ListView doesn't scroll when wrapped by Column & SingleChildScrollView on all the browsers on Android. For further details and code examples check the link:
https://github.com/flutter/flutter/issues/80794#issuecomment-823961805
If this link didn't help you can find more information on this link:
All solutions for the problem

Go to the end of the list as soon as you create it, Flutter

I read many great answers about a ScrollController in the ListView. Then when a button is clicked we do scrollController.jumpTo(scrollController.position.maxScrollExtent);
My question is, can we go to the bottom of a List, but at the creation of it ?
return ListView.builder(
itemCount: data.length,
shrinkWrap: true,
// CAN WE DO SOMETHING HERE FOR EXAMPLE ?
physics: const ScrollPhysics(),
itemBuilder: (context, index)
{
return Item(data[index]);
}
);
(For me, it's a chat so the reverse option doesn't look good)
If, as you said, you are using this list for a chat log, then the reverse options does work for you. You just have to also invert your chat log data:
data.reversed.toList()
There is not direct option on a ListView to go to the end of the list.
For a more complete example from one of my applications:
class MessageList extends StatelessWidget {
const MessageList(this.displayMessages);
final List<Map> displayMessages;
#override
Widget build(BuildContext context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
reverse: true,
children: displayMessages.map((message) {
return ChatMessage(message);
}).toList(),
),
),
);
}
}
displayMessages = List<Map>.from(clientMessages.data).reversed.toList();

Flutter Listview builder show data in two rows depending upon `index` is odd or event

I am fairly new in Flutter, I am fetching data from api and displaying it in a listview. I have following demo code.
/*Other codes*/
return ListView.builder(
//Other Codes
physics: const AlwaysScrollableScrollPhysics(),
itemCount: item.length,
padding: new EdgeInsets.only(top: 5.0),
itemBuilder: (context, index) {
});
My requirement is to show data in Two Rows, such that if the item in index is even show it in first row and if its odd, show it in another row? More like in the Image below.
Update: There could be many items and can be scrolled horizontally to reveal more items.
Can we achieve this without manipulating the data received from API? From API the data comes in date-wise sorted in descending order.
Thanks
GridView.builder is probably what you are looking for.
Just give it scrollDirection: Axis.horizontal to make it horizontal.
Then give it gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2) to make sure you have only 2 rows.
Then use SizedBox to restrict the height of the GridView to 90 + 90.
Finally, use the childAspectRatio property of the SliverGridDelegateWithFixedCrossAxisCount to specify that you want your aspect ratio to be 90 / 256.
Color randomColor() => Color.fromARGB(255, Random().nextInt(255), Random().nextInt(100), Random().nextInt(200));
class MyApp extends StatelessWidget {
List<String> array = ["0", "1", "2", "3"];
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Example')),
body: SizedBox(
height: 90 + 90,
child: GridView.builder(
itemCount: 4,
scrollDirection: Axis.horizontal,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: 90 / 256,
crossAxisCount: 2,
),
itemBuilder: (context, index) {
return Container(
color: randomColor(), child: Text(array[index])
);
}
),
),
),
);
}
}

Flutter AlwaysScrollableScrollPhysics() not working

Question
Hi, I was searching a solution to allow user scroll on a list even when there is insufficient content.
Looking throght Flutter documentation i found this page https://api.flutter.dev/flutter/widgets/ScrollView/physics.html
As the documentation said
To force the scroll view to always be scrollable even if there is insufficient content, as if primary was true but without necessarily setting it to true, provide an AlwaysScrollableScrollPhysics physics object, as in:
physics: const AlwaysScrollableScrollPhysics(),
so I tried to run a simple code an detect user scroll even when there isn't enough content
code
class Page extends StatefulWidget {
#override
_PageState createState() => _PageState();
}
class _PageState extends State<Page> {
#override
Widget build(BuildContext context) {
final ScrollController scrollController = ScrollController();
#override
void initState(){
scrollController.addListener((){
print('listener called');
});
super.initState();
}
return Scaffold(
body: ListView.builder(
controller: scrollController,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: 5,
itemBuilder: (context, index){
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Container(
color: Colors.black,
height: 50,
),
);
},
),
);
}
}
Why this isn't working?
edit
Here is the design i'm looking forward
I have a list that is dynamically created. I want to be able to detect user vertical swipes on that list even if there is no scroll because there aren't enough element to overflow the screen height.
On a scrollable list I can simply add a scroll Listener and then every time a scroll is detected I can do my logic with scrollController.position info's.
I want scroll listener to be called even when user swipes on list of this type
I do see the effect of scroll with the addition of AlwaysScrollableScrollPhysics so that part seems to be working. Maybe wrapping the scaffold on a NotificationListener can do what you're trying to do:
class _PageState extends State<Page> {
#override
Widget build(BuildContext context) {
final ScrollController scrollController = ScrollController();
return NotificationListener(
child: Scaffold(
body: ListView.builder(
controller: scrollController,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: 5,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Container(
color: Colors.black,
height: 50,
),
);
},
),
),
onNotification: (scrollNotification) {
if (scrollNotification is ScrollStartNotification) {
print('Widget has started scrolling');
}
return true;
},
);
}
}
NotificationListener has a property called onNotification that allows you to check for different kinds of scrollNotifications, you can check more here: NotificationListener Class and ScrollNotification class

How to specify ListTile height in Flutter

In this code, I am trying to make a list of buttons or tiles "as buttons do not work well for me " at the very top of the page. Thus, when one is clicked it returns a value in the rest of the page.
The issue is The tile here toke around more than half of the page which makes it looks inconsistent. I want to limit the height of the tile, I have tried putting them in a row and a container and it doesn't work. Any HELP will be appreciated.
the result after running the code is:
this is the error after runing the code :
class HomePage extends StatefulWidget {
// const HomePage({Key key}) : super(key: key);
#override
HomePageState createState() {
return new HomePageState();
}
}
class HomePageState extends State<HomePage> {
List<String> temp=new List();
List<String> temp1=['Nile University', 'Smart Village', 'Zewail'];
Map<String,String> map1={};
#override
void initState() {
super.initState();
getplaces(temp);
getuser(map1,'1jKpg81YCO5PoFOa2wWR');
}
Future<List> getuser(temp,String place) async{
List<String> userids=[];
QuerySnapshot usersubs= await Firestore.instance.collection('tempSubs').getDocuments();
QuerySnapshot userid= await Firestore.instance.collection('users').where('place',isEqualTo: place).getDocuments();
userid.documents.forEach((DocumentSnapshot doc,){
usersubs.documents.forEach((DocumentSnapshot doc1){
if(doc.documentID==doc1.documentID){
doc1.data['products'].forEach((k,v){
if( DateTime.fromMillisecondsSinceEpoch(v).day==DateTime.now().day){
int x= DateTime.fromMillisecondsSinceEpoch(v).day;
print('keey equal $k and v is $x');
print('dy is $x');
userids.add(
doc.documentID);
}
});
}
} ); }
);
print('doc.documentID');
print (userids);
setState(() {});
return userids;
}
Future<List> getplaces(temp) async{
QuerySnapshot place= await Firestore.instance.collection('places').getDocuments();
place.documents.forEach((DocumentSnapshot doc){
temp.add(
doc.data['name']
);
// print(doc.data['name']);
});
// print(temp);
setState(() {});
return temp;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: !temp.isNotEmpty?
CircularProgressIndicator():
Row(mainAxisSize:MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children:<Widget>[
Container(
height: 100.0,
child:
ListView.builder(
scrollDirection: Axis.horizontal,
itemExtent: 100.0,
itemCount:temp.length,
itemBuilder:(BuildContext context, int index) {
return ListTile(title: Text(temp[index]),onTap:
(){
print(temp[index]);
}
);}
),),
Container(child:Text('data'),)
],),
);
}
}
Applying VisualDensity allows you to expand or contract the height of list tile. VisualDensity is compactness of UI elements. Here is an example:
// negative value to contract
ListTile(
title: Text('Tile title'),
dense: true,
visualDensity: VisualDensity(vertical: -3), // to compact
onTap: () {
// tap actions
},
)
// positive value to expand
ListTile(
title: Text('Tile title'),
dense: true,
visualDensity: VisualDensity(vertical: 3), // to expand
onTap: () {
// tap actions
},
)
The values ranges from -4 to 4 and default is 0 as of writing this answer.
However, you cannot use this method for specific width or height size.
Just remove the Expanded Widget to avoid fill the space available and use a parent Container with a fixed height, the same as the itemExtent value:
Column(
children: <Widget>[
Container(
height: 100.0,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemExtent: 100.0,
itemCount: temp.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(temp[index]),
onTap: () {
print(temp[index]);
});
}),
),
Container(
child: Text('data'),
)
],
),
You should use a Container or Padding instead of ListTile if you need more customization.
You cannot set the height, but you can make it smaller by setting the dense property to true:
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: list.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(list[index].name,style: TextStyle(fontSize: 20.0),),
contentPadding: EdgeInsets.symmetric(vertical: 0.0, horizontal: 16.0),
dense:true,
);
},
);
ListTile:
A single fixed-height row that typically contains some text as well as
a leading or trailing icon.
To be accessible, tappable leading and trailing widgets have to be at
least 48x48 in size. However, to adhere to the Material spec, trailing
and leading widgets in one-line ListTiles should visually be at most
32 (dense: true) or 40 (dense: false) in height, which may conflict
with the accessibility requirement.
For this reason, a one-line ListTile allows the height of leading and
trailing widgets to be constrained by the height of the ListTile. This
allows for the creation of tappable leading and trailing widgets that
are large enough, but it is up to the developer to ensure that their
widgets follow the Material spec.
https://api.flutter.dev/flutter/material/ListTile-class.html
Since there's no height property in ListTile you can limit the size of a tile by placing it inside a SizedBox:
SizedBox(
height: 32,
child: ListTile(..))