I am trying to create one Grid View page with Cards as list elements and the last of the cards are getting cut from the bottom. The following are relevant code snippets:
createListBody.dart
List<String> services = [
'Demo1',
'Demo2',
'Demo3',
'Demo4',
'Demo5',
'Demo6',
'Demo7',
'Demo8',
'Demo9',
'Demo10',
];
#override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
child: GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 2.0,
mainAxisSpacing: 2.0,
children: services.map((String serviceName){
return Container(
child:Card(
color: Colors.grey[500],
elevation: 15,
semanticContainer: true,
shadowColor: palletFuchsia,
shape: CircleBorder(),
child: InkWell(
onTap: (){
print("Tapped "+serviceName);
},
child: Center(
child: Text(
serviceName,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25
),
),
),
),
)
);
}).toList(),
),
);
}
listScreen.dart
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: palletWhiteDrawer,
drawer: Theme(
data: Theme.of(context).copyWith(
canvasColor: palletYellowDrawer,
),
child: CreatDrawerWidget(),
),
appBar: CreateAppBar(),
body:GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ServiceListBody()
],
)
),
),
);
}
Screenshot
I am completely new to flutter so, sorry if there is some silly mistake. But any help would be useful.
Thank you for your time!
Just for exploring, you can do height: MediaQuery.of(context).size.height *2, It shouldn't get cut anymore right?
Solution 1
Your telling your container a given height which is fix for the moment, no matter how many items you got inside your ListView.
You can for example use ConstrainedBox with shrinkWrap:true and manage your max height
ConstrainedBox(
constraints: BoxConstraints(maxHeight: 200, minHeight: 56.0),
child: ListView.builder(
shrinkWrap: true,
...
more info: https://api.flutter.dev/flutter/widgets/ConstrainedBox-class.html
Solution 2
use Slivers, they are dynamic.
For this you need to customize your structure a little bit. You wrap everything with a CustomScrollView(), SliverToBoxAdapter() for fixed elements and SliverList() for dynamic ones, makes it work like a charm.
Example using CustomScrollView and SliverToBoxAdapter:
return SafeArea(
child: CustomScrollView(
//scrollDirection: Axis.vertical,
physics: BouncingScrollPhysics(),
slivers: <Widget>[
// Place sliver widgets here
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.fromLTRB(25, 30, 25, 20),
child: SizedBox(
height: 299,
child: Column(children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
...
You can define the height with the SizedBox height. Also you can apply nice effects to your whole View on the CustomScrollView
Example Sliverlist:
SliverList(
delegate: SliverChildBuilderDelegate(
(ctx, index) => ChangeNotifierProvider.value(
value: list[index],
child: GestureDetector(
onTap: () {}),
childCount: list.length,
)),
Infos about slivers: https://medium.com/flutterdevs/explore-slivers-in-flutter-d44073bffdf6
The GridView Widget itself is Scrollable , So you don't have to wrap it with a Column and SingleChildScrollView Widget...
You can now simply scroll down if the Circles goes out of the screen
If you wish all the circles to fit in the screen.. You'll have to use
var mobileHeight = MediaQuery.of(context).size.height
And then the height of each circles will be mobileHeight divided by the no. of circles vertically.
Related
I've very much a beginner in Dart and Flutter, and my project is to learn while building an app. I've learned the basics with an online class and I have basis in Java.
Now, in my homepage, I'm trying to have 4 buttons take the whole screen. My current layout is to have them all take an equal amount of space and be stacked vertically.
Problem is, I can't find a way to make the RaisedButton fill automatically. I've tried retrieving the height of the screen and dividing that between the buttons but it's not working. Here's how I do it :
First, the homescreen layout:
class Homescreen extends StatelessWidget {
Widget build(context) {
double height = MediaQuery.of(context).size.height - 60;
return Scaffold(
body: SafeArea(
child: ListView(
children: [
PlanningButton(height),
Container(margin: EdgeInsets.only(top: 20.0)),
BookingButton(height),
Container(margin: EdgeInsets.only(top: 20.0)),
ModifyButton(height),
Container(margin: EdgeInsets.only(top: 20.0)),
CancelButton(height),
],
),
),
);
}
As you can see, I pass the height argument to my buttons all built around this model:
Widget PlanningButton(double pixel_y) {
return RaisedButton(
padding: EdgeInsets.all(pixel_y / 8.0),
onPressed: () {}, //goes to the planning screen
color: Colors.blue[200],
child: Text("Planning"),
);
}
Yet, it's never quite right. I don't think it's taking into account the notification bar or the navigation bar.
I suspect there is a workaround that involves wrapping the buttons into a container or using a different kind of widget that have button-like properties but I can't find what suits my needs.
Thanks for any help !
edit: I'm basically trying to do this
Try removing the variable height logic, and simply wrap your buttons in Expanded widgets. This is pretty much what it was built for.
Check it out here: https://api.flutter.dev/flutter/widgets/Expanded-class.html
You could use a Column and Expanded to do the job. If you want to make your Column scrollable, you can wrap it with SingleChildScrollView. I used SizedBox instead of Container to make the spacing because it is way more efficient and is designed for it.
class Homescreen extends StatelessWidget {
Widget build(context) {
double height = MediaQuery.of(context).size.height - 60;
return Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: PlanningButton(height)
),
SizedBox(height:20),
Expanded(
child: BookingButton(height),
),
SizedBox(height:20),
Expanded(
child: ModifyButton(height),
),
SizedBox(height:20),
Expanded(
child: CancelButton(height),
),
],
),
),
);
}
If 4 buttons are the only widgets, you dont need to use ListView just use a Column with mainAxisAlignment: MainAxisAlignment.spaceEvenly
class Sample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Container(
color: Colors.red,
child: Center(
child: RaisedButton(
onPressed: () {},
child: Text("button"),
),
),
),
),
Expanded(
child: Container(
color: Colors.blue,
child: Center(
child: RaisedButton(
onPressed: () {},
child: Text("button"),
),
),
),
),
Expanded(
child: Container(
color: Colors.green,
child: Center(
child: RaisedButton(
onPressed: () {},
child: Text("button"),
),
),
),
),
Expanded(
child: Container(
color: Colors.yellow,
child: Center(
child: RaisedButton(
onPressed: () {},
child: Text("button"),
),
),
),
),
],
),
),
);
}
}
Before you read this, it's not the best way to do things but it's one I'm using at least for prototyping. You may find using a container with a list and the argument MainAxisAlignment to work better for you.
A lot of comments here might give a solution that works for you but here's mine. I got it to work like I wanted by using SizedBox.
First, I recovered the width of the screen, then I created the buttons and put them into SizedBox fitting the screen's width, I put them in a Column and I added a few Container for the style points.
Result:
Code :
class Homescreen extends StatelessWidget {
Widget build(context) {
double width = MediaQuery.of(context).size.width; //getting screen width
return Scaffold(
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(margin: EdgeInsets.only(bottom: 20.0)),
PlanningButton(width),
Container(margin: EdgeInsets.only(bottom: 20.0)),
BookingButton(width),
Container(margin: EdgeInsets.only(bottom: 20.0)),
ModifyButton(width),
Container(margin: EdgeInsets.only(bottom: 20.0)),
CancelButton(width),
Container(margin: EdgeInsets.only(bottom: 20.0)),
],
),
),
);
}
Widget PlanningButton(double width) {
return Expanded(
child: SizedBox(
width: width - 20,
child: RaisedButton(
onPressed: () {},
color: Colors.blue,
child: Text(
"planning",
textScaleFactor: 4.0,
),
),
//fit: BoxFit.cover,
),
);
}
The user can tap anywhere on the colored buttons and it will register. Now I'll add images and transparency to make it look good!
I'm trying to display items in the card vertically, but in the card content I'm facing an issue that is, "Bottom overflowed by 52 pixels".
Here, I'm attaching code, my design's screenshot, and the screenshot of what actually I need.
Please help!
Below is my code.
Widget buildRowItemsGrid(BuildContext context, int index) {
FlutterMoneyFormatter formatter =
FlutterMoneyFormatter(amount: items[index].price);
return Container(
child: Card(
elevation: 5.0,
margin: EdgeInsets.all(8.0),
child: Column(
children: [
Container(
child: Image.network(
"https://fiverr-res.cloudinary.com/images/q_auto,f_auto/gigs/118898040/original/870e2763755963f5a300574bbea5977fa8b18460/sell-original-football-and-basketball-teams-jersey.jpg",
width: 100,
height: 100,
fit: BoxFit.fill),
),
Column(
children: [
Container(
child: Text(items[index].title,
style: titleTextStyle,
maxLines: 2,
overflow: TextOverflow.ellipsis),
margin: EdgeInsets.fromLTRB(10.0, 5.0, 10.0, 5.0),
),
Align(
alignment: Alignment.centerLeft,
child: Container(
margin: EdgeInsets.fromLTRB(10.0, 5.0, 10.0, 5.0),
child: Text(items[index].subtitle,
style: subtitleTextStyle)),
),
Container(
margin: EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(formatter.output.symbolOnLeft,
style: priceTextStyle),
Text("ADD TO CART", style: addToCardTextStyle)
],
),
)
],
),
],
),
),
);
Code of Grid View
Widget buildGridView() {
return Expanded(
child: GridView.count(
crossAxisCount: 2,
scrollDirection: Axis.vertical,
physics: PageScrollPhysics(),
shrinkWrap: true,
children: List.generate(items.length, (index) {
return buildRowItemsGrid(context, index);
})),
);
You need to set an appropriate childAspectRatio in the GridView like this.
GridView.count(
crossAxisCount: 2,
childAspectRatio: 2/3, //<-- width/height ratio depending on the child's content. Set accordingly.
//...
)
Edit:
The above part will solve the overflow problem. In addition to that, you can make the size of the Image widget adaptive like this so it can take up more space when available.
Image.network(
"https://fiverr-res.cloudinary.com/images/q_auto,f_auto/gigs/118898040/original/870e2763755963f5a300574bbea5977fa8b18460/sell-original-football-and-basketball-teams-jersey.jpg",
width: MediaQuery.of(context).size.width / 2.8,
height: MediaQuery.of(context).size.width / 2.8,
fit: BoxFit.fill,
)
You can also set the Column's mainAxisAlignment to spaceEvenly so its children can take up the space evenly when more space is available vertically.
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
//...
)
I can't see the ScreenShot, but from the code I suggest you wrap The Second Column with Expanded and then the first column wrap it with SingleChildScrollView, that way it won't overflowed and you can scroll through if the contain inside were higher
I guess you have a fixed size for your Grid item. So it will overflow if the height of the Grid is not enough.
I have two ideas for you:
Use auto_size_text https://pub.dev/packages/auto_size_text
Expanded(
child: AutoSizeText(
'The text to display',
style: TextStyle(fontSize: 20),
maxLines: 2,
minFontSize: 1,
),
)
Increase the height of your Grid.
Would it work? Have you given it some fixed height in its parents? If given remove that, and then try this.
Widget buildRowItemsGrid(BuildContext context, int index) {
FlutterMoneyFormatter formatter =
FlutterMoneyFormatter(amount: items[index].price);
return Container(
child: Card(
elevation: 5.0,
margin: EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
child: Image.network(
"https://fiverr-res.cloudinary.com/images/q_auto,f_auto/gigs/118898040/original/870e2763755963f5a300574bbea5977fa8b18460/sell-original-football-and-basketball-teams-jersey.jpg",
width: 100,
height: 100,
fit: BoxFit.fill),
),
Column(
children: [
Container(
child: Text(items[index].title,
style: titleTextStyle,
maxLines: 2,
overflow: TextOverflow.ellipsis),
margin: EdgeInsets.fromLTRB(10.0, 5.0, 10.0, 5.0),
),
Align(
alignment: Alignment.centerLeft,
child: Container(
margin: EdgeInsets.fromLTRB(10.0, 5.0, 10.0, 5.0),
child: Text(items[index].subtitle,
style: subtitleTextStyle)),
),
Container(
margin: EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(formatter.output.symbolOnLeft,
style: priceTextStyle),
Text("ADD TO CART", style: addToCardTextStyle)
],
),
)
],
),
],
),
),
);
Try wrapping the overflowing widget with a Flexible widget. The child of the Flexible widget will fill the available space in the main axis.
I am trying to place an ExpansionTile with a horizontal GridView child in a container. See below for code. I get the error message 'Horizontal viewport was given unbounded height.'
I don't want to specify the height of ExpansionTile or GridView explicitly, but rather have it calculated from the Expanded widget and the children of GridView.
I've tried setting 'shrinkWrap: true' in GridView as suggested here with no success. I also tried wrapping GridView in Flexible based on this answer which gives me the error 'RenderFlex children have non-zero flex but incoming height constraints are unbounded.'
I think the problem is with how I'm using GridView, but I'm new to Flutter and not really sure. Here is my code:
import 'package:flutter/material.dart';
void main() => runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("Title")),
body: Column(
children: <Widget>[
Expanded(
flex: 3,
child: ExpansionTile(
title: Text("Expandable"),
children: <Widget>[
GridView.count(
scrollDirection: Axis.horizontal,
crossAxisCount: 2,
children: <Widget>[
RawChip(label: Text("Hello")),
RawChip(label: Text("World")),
],
),
],
),
),
Expanded(
flex: 7,
child: Container(
color: Colors.blue
),
),
],
),
),
),
);
You can just add a height constraint to your GridView :
ExpansionTile(
title: Text("Expandable"),
children: <Widget>[
SizedBox(
height: 200.0,
child: GridView.count(
scrollDirection: Axis.horizontal,
crossAxisCount: 2,
children: <Widget>[
RawChip(label: Text("Hello")),
RawChip(label: Text("World")),
],
),
),
],
),
I have a column: header with text, body with image, footer with text, all widgets have transparent backgrounds.
I want to set the background using a blur of the main image but I keep reaching dead ends.
In some situations this would be straight forward but in my scenario the image could be of any size and aspect ratio, and I need the effect to be wrapped with the column.
Here are my two failed attempts:
Method 1
I have a Stack with the image as the first item, then a BackdropFilter with ImageFilter blur, then my Column.
This works but the Image bleeds out from under the Column because of the image size (which can be any size).
I want to constrain it to the height of my Column.
return Container(
child: Stack(
alignment: AlignmentDirectional.topCenter,
children: <Widget>[
Positioned.fill(child: Image.network(_imgUrl)),
BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Container(
decoration: BoxDecoration(color: Colors.white.withOpacity(0.0)),
)),
Container(
child: Column(
mainAxisSize: MainAxisSize.min,
// https://stackoverflow.com/a/41846093/3429021
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Header(),
BodyPhoto(),
Footer()
],
),
),
],
),
Method 2
Putting the column in a Container with a DecorationImage, which sizes the image perfectly, but I have no way of applying the blur effect.
(_streamItem is my Column wrapped in a container)
body: Container(child: _streamItem,
decoration: new BoxDecoration(image: DecorationImage(
image: NetworkImage(_streamItem.imgUrl),
fit: BoxFit.cover)
)
)
Any ideas?
I think you can consider using Positioned widget - it has width and height property. We can combine these widgets: Positioned > ClipRect > BackdropFilter. Please refer to the Gist: https://gist.github.com/anticafe/dc84cf6488d9defea437b702b13e2749#file-blur_multiple_widgets_with_dynamic_region-dart
Let take a look at this article to see more about BackdropFilter.
You can explicitly give a fix height to the container that contains the image, which will even look better.
In this example I have set three images of very different dimensions and has no problem rendering.
Refer the below code:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('So Help'),
),
body: new ListView(
children: <Widget>[
MyContainer(
imageUrl:
'https://i.dailymail.co.uk/i/pix/2017/01/16/20/332EE38400000578-4125738-image-a-132_1484600112489.jpg',
),
MyContainer(
imageUrl:
'http://static.guim.co.uk/sys-images/Guardian/Pix/pictures/2014/4/11/1397210130748/Spring-Lamb.-Image-shot-2-011.jpg',
),
MyContainer(
imageUrl:
'http://wackymania.com/image/2011/6/vertical-panoramic-photography/vertical-panoramic-photography-15.jpg',
),
],
),
);
}
}
class MyContainer extends StatelessWidget {
final String imageUrl;
MyContainer({#required this.imageUrl});
#override
Widget build(BuildContext context) {
return new Container(
height: 300.0,
child: new Stack(
children: <Widget>[
new Container(
child: Image.network(
this.imageUrl,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
),
),
BackdropFilter(
filter: Dui.ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: new Container(
color: Colors.transparent,
),
),
new Container(
height: 300.0,
child: new Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
),
Padding(
padding: const EdgeInsets.all(20.0),
child: new Text(
'Header',
style: new TextStyle(color: Colors.white),
),
),
new Expanded(
child: Image.network(imageUrl),
),
Padding(
padding: const EdgeInsets.all(20.0),
child: new Text(
'Footer',
style: new TextStyle(color: Colors.white),
),
),
],
),
)
],
),
);
}
}
I have a ListView and I need an expanded vertical line on the left of it (See image below). I have achieved the following layout but it expands to infinite.
Here is the code for the above layout:
return Scaffold(
...
body: Column(
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 4.0),
color: Colors.grey,
width: 4.0,
child: SizedBox.expand(),
),
Flexible(
child: StreamBuilder( // Creates the list view
stream: ...,
builder: ...,
),
),
],
),
],
),
};
Line goes to infinite because of SizedBox.expand() but how to fit into the screen. Any suggestions?
Or is there a way to get this same effect from ListView item (Doesn't have a fixed size).
Don't use Column as Parent if you only has one child, this is working for me:
return Container(
child: Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 4.0),
color: Colors.grey,
width: 4.0,
),
Expanded(
child: ListView.builder(
// Creates the list view
shrinkWrap: true,
itemCount: list.length,
itemBuilder: (context, index) {
return ListTile(
contentPadding: EdgeInsets.all(20.0),
title: Text(list[index].toString()),
);
},
),
),
],
),
);