Flutter GridView.Builder isn't scrolling (height issues) - flutter

I'm trying to make a grid of products using GridView.Builder but it gives error :
Vertical viewport was given unbounded height.
I tried to use flexible on GridView it worked but I need to use GridView.Builder Specifically
and if I tried to wrap it with Flexible or specific height container it doesn't scroll ,any tips?
import 'package:flutter/material.dart';
class Products extends StatefulWidget {
#override
_ProductsState createState() => _ProductsState();
}
class _ProductsState extends State<Products> {
var productList=[
{
"name":"Blazer",
"picture":"images/products/blazer1.jpeg",
"oldPrice":120,
"price":100
},
{
"name":"Dress",
"picture":"images/products/dress1.jpeg",
"oldPrice":120,
"price":100
},
{
"name":"hills",
"picture":"images/products/hills1.jpeg",
"oldPrice":11,
"price":10
},
{
"name":"pants",
"picture":"images/products/pants2.jpeg",
"oldPrice":12,
"price":200,
}
];
#override
Widget build(BuildContext context) {
return GridView.builder(
scrollDirection: Axis.vertical,
itemCount: productList.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (context,index){
return SingalProduct(
name: productList[index]['name'],
picture: productList[index]['picture'],
oldPrice: productList[index]['oldPrice'],
price: productList[index]['price'],
);
},
);
}
}
class SingalProduct extends StatelessWidget {
final name,picture,oldPrice,price;
SingalProduct({this.name,this.picture,this.oldPrice,this.price});
#override
Widget build(BuildContext context) {
return Card(
child: Hero(
tag: name,
child: InkWell(
onTap: (){},
child: GridTile(
footer: Container(
height: 40,
color: Colors.white,
child: Padding(
padding: EdgeInsets.fromLTRB(8, 12, 0, 0),
child: Text(name,textAlign: TextAlign.start,style: TextStyle(fontWeight: FontWeight.bold,fontSize: 16),),
),
),
child: Image.asset(picture,fit: BoxFit.cover, ),
),
),
),
);
}
}

Related

Flutter - make ListTile fill remaining screen space

My Flutter app displays a list of events. When there are no events to display, a message is displayed saying "no events to display":
Center(child: Text("No Events to Display"))
This resulted in the following which is what is required:
However, this means the users cannot pull down to refresh (the Center widget is not in a ListView). So I added a ListView to hold the Center widget so the list of events could be refreshed:
Widget buildNoEvents() {
final noEventMessage = ListView.builder(
padding: const EdgeInsets.all(10),
physics: const AlwaysScrollableScrollPhysics(),
itemCount: 1,
itemBuilder: (context, index) =>
Center(child: Text("No Events to Display")));
return noEventMessage;
}
This results in the following, where the Center widget is positioned at the top of the screen:
The message needs to be in the centre of the screen.
Additionally, to complicate matters, there is also a requirement to display urgent messages at the top of the screen, above the list of events:
Widget buildNoEvents() {
Bulletin bulletin = getBulletin();
final noEventMessage = ListView.builder(
padding: const EdgeInsets.all(10),
physics: const AlwaysScrollableScrollPhysics(),
itemCount: 1,
itemBuilder: (context, index) =>
Center(child: Text("No Events to Display")));
if (bulletin.showBulletin) {
return Column(
children: [
BulletinView(bulletin: bulletin),
Expanded(child: noEventMessage)
],
);
} else {
return noEventMessage;
}
}
class BulletinView extends StatelessWidget {
final Bulletin bulletin;
const BulletinView({super.key, required this.bulletin});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 10, left: 10, right: 10),
child: ListTile(
tileColor: const Color.fromARGB(255, 244, 232, 232),
leading: const CircleAvatar(
backgroundColor: Colors.red,
child: Text(
"!",
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 244, 232, 232),
),
)),
title: Text(bulletin.title),
subtitle: Text("Corner of Eden Avenue")));
}
}
Note the Expanded widget - if I don't use Expanded to wrap the ListView I get the following exception:
════════ Exception caught by rendering library ═════════════════════════════════
The following assertion was thrown during performResize():
Vertical viewport was given unbounded height.
This results in the following UI:
The "bulletin" is correctly positioned at the top of the screen, above the ListView, but the "no events..." message is not correctly positioned in the centre of the screen. The ListView is correctly taking up the whole of the screen below the bulletin and responds to pull to refresh.
How can I force the ListView element for "no events to display" to fill the screen and therefore centre the "no events..." text?
STRIPPED DOWN CODE
class EventListScreen extends StatefulWidget {
#override
_EventListScreenState createState() => _EventListScreenState();
const EventListScreen({Key? key}) : super(key: key);
}
class _EventListScreenState extends State<EventListScreen> {
List<Event> events = [];
Future<List<Event>> getData() async {
events = await Network.getUsers(context);
return events;
}
Future<void> refreshData() async {
await Network.getUsers(context);
setState(() {});
}
#override
build(context) {
return PlatformScaffold(
body: RefreshIndicator(
onRefresh: refreshData,
child: FutureBuilder<List<Event>>(
future: getData(),
builder: (context, snapshot) {
return buildNoEvents();
},
),
));
}
Widget buildNoEvents() {
final noEventMessage = ListView.builder(
padding: const EdgeInsets.all(10),
physics: const AlwaysScrollableScrollPhysics(),
itemCount: 1,
itemBuilder: (context, index) =>
const Center(child: Text("No Events to Display")));
if (getBulletin().showBulletin) {
return Column(
children: [
BulletinView(bulletin: getBulletin()),
Expanded(child: noEventMessage)
],
);
} else {
return noEventMessage;
}
}
Bulletin getBulletin() {
return const Bulletin(title: "WARNING!", message: "News...", showBulletin: true); // dummy for demo purposes
}
}
class Bulletin {
final bool showBulletin;
final String title;
final String message;
const Bulletin({required this.title, required this.message, required this.showBulletin});
}
class BulletinView extends StatelessWidget {
final Bulletin bulletin;
const BulletinView({super.key, required this.bulletin});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 10, left: 10, right: 10),
child: ListTile(
tileColor: const Color.fromARGB(255, 244, 232, 232),
leading: const CircleAvatar(
backgroundColor: Colors.red,
child: Text(
"!",
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 244, 232, 232),
),
)),
title: Text(bulletin.title),
subtitle: Text(bulletin.message)));
}
}
You can wrap the noEventMessage in Center widget and add shrink-wrap:true
Widget buildNoEvents() {
Bulletin bulletin = getBulletin();
final noEventMessage = ListView.builder(
shrinkWrap: true, //<---add this
padding: const EdgeInsets.all(10),
physics: const AlwaysScrollableScrollPhysics(),
itemCount: 1,
itemBuilder: (context, index) =>
Center(child: Text("No Events to Display")));
if (bulletin.showBulletin) {
return Column(
children: [
BulletinView(bulletin: bulletin),
Expanded(child: noEventMessage)
],
);
} else {
return Center(child:noEventMessage); //<--add center widget here
}
}
However pull to refresh doesn't require you to add these elements in Listview. You can still wrap the main Column widget with a Refresh indicator and it will still work
Edit
Widget noEventMessage = SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Container(
child: Center(
child: Text('Hello World'),
),
height: MediaQuery.of(context).size.height,
)
);
// And just return the Widget without the center
if (bulletin.showBulletin) {
return Column(
children: [
BulletinView(bulletin: bulletin),
Expanded(child: noEventMessage)
],
);
} else {
return noEventMessage;
}

Flutter. GridView inside Container

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'Login.dart';
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
image:DecorationImage(
image: AssetImage("images/black_background_logo.png"),
fit: BoxFit.cover,
)
),
child: Column(
children: [
CarouselDemo(),
HomePanel()
],
),
);
}
}
List<String> images = [
'https://skalka-app.ru/banners/1.png',
'https://skalka-app.ru/banners/2.png',
'https://skalka-app.ru/banners/3.png',
] ;
class CarouselDemo extends StatelessWidget {
CarouselController buttonCarouselController = CarouselController();
#override
Widget build(BuildContext context) => CarouselSlider(
options: CarouselOptions(
height: MediaQuery.of(context).size.height*0.7,
viewportFraction: 1.0,
enableInfiniteScroll: true,
reverse: false,
autoPlay: true,
autoPlayInterval: Duration(seconds: 8),
autoPlayAnimationDuration: Duration(milliseconds: 800),
autoPlayCurve: Curves.fastOutSlowIn,
),
items: images.map((i) {
return Builder(
builder: (BuildContext context) {
return Container(
//width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height*0.7,
decoration: BoxDecoration(
color: Colors.amber
),
child: Image.network(i,fit: BoxFit.cover, height: MediaQuery.of(context).size.height*0.7,)
);
},
);
}).toList(),
);
}
class HomePanel extends StatelessWidget {
#override
Widget build(BuildContext context) {
final double height = MediaQuery.of(context).size.height;
List<String> data = <String>["Twitter", "Reddit", "YouTube", "Facebook",
"Vimeo", "GitHub", "GitLab", "BitBucket", "LinkedIn", "Medium",
"Tumblr", "Instagram", "Pinterest"];
List<RaisedButton> myWidgets = data.map((item) {
return new RaisedButton(
child: new Text(item),
onPressed: () async {
}
);
}).toList();
GridView myGrid = GridView.count(
crossAxisCount: 3,
children: myWidgets
);
return Container(
height: height*0.3,
width: MediaQuery.of(context).size.width,
color: Colors.red,
child: myGrid
);
}
}
I'm trying to add a GridView to a Container, but an indent appears at the top. Please tell me how to fix this?
I painted the Container red to show that there is a padding on top. I could not find a solution to this problem on the Internet. I'm new to Flutter, maybe I missed an important point in building this widget.
You can try wrap GridView with a MediaQuery.removePadding() then set removeTop property to True.
MediaQuery.removePadding(
context: context,
removeTop: true,
child: GridView(
.......
)
);
I have used your code pretty much, just for the Carousel, I have used the ListView.builder(). Rest is fine.
The catch is to use Expanded class inside your Column() to take the height automatically for the Carousel
Follow the code along, and see the result as well, no extra space in the UI in the GridView
class _MyHomePageState extends State<MyHomePage> {
List<String> images = [
'https://skalka-app.ru/banners/1.png',
'https://skalka-app.ru/banners/2.png',
'https://skalka-app.ru/banners/3.png',
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
height: double.infinity,
child: Column(
children: [
// Expanded used to take up the space
Expanded(
// ListView.builder, use your carousel here
child: ListView.builder(
shrinkWrap: true,
itemCount: images.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index){
// look at this as well, no height, only width
// given for the image
return Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(images[index])
)
)
);
}
)
),
HomePanel()
],
),
)
);
}
}
class HomePanel extends StatelessWidget {
#override
Widget build(BuildContext context) {
final double height = MediaQuery.of(context).size.height;
List<String> data = <String>["Twitter", "Reddit", "YouTube", "Facebook",
"Vimeo", "GitHub", "GitLab", "BitBucket", "LinkedIn", "Medium",
"Tumblr", "Instagram", "Pinterest"];
List<RaisedButton> myWidgets = data.map((item) {
return new RaisedButton(
child: new Text(item),
onPressed: () async {
}
);
}).toList();
GridView myGrid = GridView.count(
crossAxisCount: 3,
children: myWidgets
);
return Container(
height: height*0.3,
width: MediaQuery.of(context).size.width,
color: Colors.red,
child: myGrid
);
}
}
Result
Look at the design closely in the result, no extra spacing or padding

ListView.builder doesn't work in alertDialog

I use ListView.builder to show image. it is works when i show it in main page but when i use it in alertDialog it doesn't work at all. This is my code for alertDialog.
void rateVideo(BuildContext context){
var alertDialog = AlertDialog(
title: Text("video rate"),
content: RateClip(),
actions: <Widget>[
FlatButton(
child: Text('ok'),
onPressed: () {
//rateVideo(context);
Navigator.of(context).pop();
}),
],
);
showDialog(context: context,
builder: (BuildContext context){
return alertDialog;
}
);
}
and this code is about ListView.builder that i try to show images
class RateClip extends StatelessWidget {
//const ActivityClip ({Key key}) : super(key: key);
final List<String> pic = [
'assets/images/LG5.png',
'assets/images/stress.png',
'assets/images/stress.png',
'assets/images/normal.png',
'assets/images/happy.png'
];
#override
Widget build(BuildContext context) {
return Container(
height: 60,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: pic.length,
itemBuilder: (context, index) {
return Container(
width: 30,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
InkWell(
onTap: () {
},
child: Image.asset(pic[index].toString(), height: 30, width: 30),
)]));
}));
}
}
As can be seen from error it is assertion error of alert dialog. Element in alert dialog must be specific width, which you can see in log. it change based on device.
So to solve your error you have to provide specific width of container, which is above list view. To be more specific it is because your listview is horizontal and set width to infinity and that’s why it is throwing assertion error. If is was vertical listview then you have to provide height of container.
#override
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width*0.75 // here i set width of container to 75% of screen
child: ListView.builder(
scrollDirection: Axis.horizontal,
here is my new code
class RateClip extends StatelessWidget {
//const ActivityClip ({Key key}) : super(key: key);
final List<String> pic = [
'assets/images/LG5.png',
'assets/images/stress.png',
'assets/images/stress.png',
'assets/images/normal.png',
'assets/images/happy.png'
];
#override
Widget build(BuildContext context) {
return Container(
//margin: const EdgeInsets.only(right: 10, left: 10, top: 200),
height: 60,
//width: 40,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: pic.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
},
child: Image.asset(pic[index].toString(), height: 30, width: 30),
);
}));
}
}

Dynamic staggered grid with same cell height in flutter

GridView List
I'm trying to make Staggered Grid List with same image cell height
I found a plugin https://pub.dev/packages/flutter_staggered_grid_view but it didn't work for me,
I need 2nd column to get down with some space and column height should be same, what should I do?
flutter_staggered_grid_view Plugin.
Try this:
class MyHomeScreen extends StatefulWidget {
#override
_MyHomeScreenState createState() => _MyHomeScreenState();
}
class _MyHomeScreenState extends State<MyHomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Staggered Grid View with image demo"),),
body: Center(
child: sliverGridWidget(context),
),
);
}
Widget sliverGridWidget(BuildContext context){
return StaggeredGridView.countBuilder(
padding: const EdgeInsets.all(8.0),
crossAxisCount: 4,
itemCount: 10, //staticData.length,
itemBuilder: (context, index){
return Card(
elevation: 8.0,
child:InkWell(
child: Hero(
tag: index,// staticData[index].images,
child: new FadeInImage(
width: MediaQuery.of(context).size.width,
image: NetworkImage("https://images.unsplash.com/photo-1468327768560-75b778cbb551?ixlib=rb-1.2.1&w=1000&q=80"), // NetworkImage(staticData[index].images),
fit: BoxFit.cover,
placeholder: AssetImage("assets/images/app_logo.png"),
),
),
onTap: (){
//
}
)
);
},
staggeredTileBuilder: (index) => StaggeredTile.count(2,index.isEven ? 2: 3),
mainAxisSpacing: 8.0,
crossAxisSpacing: 8.0,
);
}
}
Output:

How to give some space (margin/padding) between pages in PageView?

I am using PageView.builder to create pages.
PageView.builder(
itemCount: _pagesList.length,
itemBuilder: (BuildContext context, int index) {
return Container(
color: _pagesList[index],
);
}
)
What I currently have:
What I want:
i.e. I want to provide some Padding between pages (when they are being scrolled)
Reason: I will display Images in these pages, and since the Images will cover the full width of each page, it doesn't look nice when we scroll pages, since they are knitted together, like this:
How can I solve this?
PageController imagesController =
PageController(initialPage: 0, viewportFraction: 1.1);
PageView(
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: EdgeInsets.only(left: 10, right: 10),
child: Container(
color: _pagesList[index],
),
);
}
),
If you want to add padding and still have your pages as wide as the screen:
I needed this exact same thing, also for displaying images. I wanted to add padding but at the same time have each image take up the entire screen width. I figured I could use Fahad Javed's technique and tweaking it a little bit by calculating the viewPortFraction based on the screen width and padding.
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width; // screen width
double screenPad = 16.0; // screen padding for swiping between pages
int _currentPosition = 0;
return PageView.builder(
scrollDirection: Axis.horizontal,
itemCount: data.length,
controller: PageController(
initialPage: _currentPosition,
viewportFraction:
1 + (screenPad * 2 / screenWidth)), // calculate viewPortFraction
onPageChanged: (int value) {
_currentPosition = value;
},
itemBuilder: (BuildContext context, int position) {
return Padding(
padding: EdgeInsets.only(left: screenPad, right: screenPad),
child: Text('YOUR PAGE CONTENT'),
);
},
);
}
This answer from on the question asked by Amon Kataria Github
final pageController = PageController(viewportFraction: 1.1);
PageView.builder(
controller: pageController,
itemCount: _pagesList.length,
itemBuilder: (BuildContext context, int index) {
return FractionallySizedBox(
widthFactor: 1 / pageController.viewportFraction,
child: Container(
color: _pagesList[index],
),
);
},
);
Thanks #mono0926
Best effort:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: MyPageView()
)
);
}
}
class MyPageView extends StatefulWidget {
MyPageView({Key key}) : super(key: key);
_MyPageViewState createState() => _MyPageViewState();
}
class _MyPageViewState extends State<MyPageView> {
#override
Widget build(BuildContext context) {
return PageView(
children: <Widget>[
Container(
color: Colors.black,
child: Card(
color: Colors.red,
)
),
Container(
color: Colors.black,
child: Card(
color: Colors.blue,
),
),
Container(
color: Colors.black,
child: Card(
color: Colors.green,
),
),
],
);
}
}
You just need to add some padding around each page and the width of the page view must be at least the 'card width + the padding from both sides'. This worked for me:
class MyWidget extends StatelessWidget {
final _CARD_WIDTH = 220.0;
final PageController _controller = PageController(initialPage: 0);
#override
Widget build(BuildContext context) {
return Container(
height: _CARD_WIDTH,
width: _CARD_WIDTH + 32,
child: PageView(
scrollDirection: Axis.horizontal,
controller: _controller,
children: <Widget>[
_buildImageCard("1"),
_buildImageCard("2"),
_buildImageCard("3"),
],
),
);
}
Widget _buildImageCard(String text) {
return Padding(
padding: const EdgeInsets.only(left: 16.0, right: 16),
child: Container(
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(15),
),
width: _CARD_WIDTH,
height: _CARD_WIDTH,
child: Center(
child: Text(text),
),
),
);
}
}