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!
Related
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.
I have a text element which should be aligned to the left but for some reason it appears centered and I can't figure out why.
What code should be changed / added to make the result string aligned to the right?
return Positioned(
bottom: 0,
right: 0,
left: 0,
child: Align(
alignment: Alignment.bottomCenter,
child: Container(
margin: EdgeInsets.fromLTRB(10, 0, 10, 30),
padding: EdgeInsets.all(10),
child: Column(
children: <Widget>[
//First row here. The one below is the second row:
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 5.0),
child: Icon(Icons.comment),
),
Expanded(
child: Column(children: [
Padding(
padding: EdgeInsets.only(
top: 10.0, left: 5.0, right: 0),
child: Text(
result, //This text is centered. Why?
maxLines: 7,
overflow: TextOverflow.ellipsis,
)),
]),
)
],
),
],
)
)
)
);
It's because row or column alignment.
By default column is centering children, to change that update:
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
...
If this does not solve your problem check:
Under which circumstances textAlign property works in Flutter?
More info on column/row alignment:
https://medium.com/jlouage/flutter-row-column-cheat-sheet-78c38d242041
You can use direction for this purpose.
For example:
MaterialApp(
builder: (context, child) {
return Directionality(
textDirection: TextDirection.rtl,
child: child,
);
},
);
In accordance to https://api.flutter.dev/flutter/widgets/Column-class.html on Column-Class, the Column widget does not scroll (and in general it is considered an error to have more children in a Column than will fit in the available room). If you have a line of widgets and want them to be able to scroll if there is insufficient room, consider using a ListView. For a horizontal variant, see Row.
Also, if you only have one child, then consider using Align or Center to position the child.
For your case, you could add the property crossAxisAlignment to your column as following
... Column(crossAxisAlignment: CrossAxisAlignment.end, ...
as mentioned by #nuts so the text will be align right. Here is a full example also from the above reference under https://api.flutter.dev/flutter/widgets/Expanded-class.html, that i just modified to illustrate how to do it:
/// Flutter code sample for Expanded
// This example shows how to use an [Expanded] widget in a [Column] so that
// its middle child, a [Container] here, expands to fill the space.
//
// ![This results in two thin blue boxes with a larger amber box in between.](https://flutter.github.io/assets-for-api-docs/assets/widgets/expanded_column.png)
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({
Key ? key
}): super(key: key);
static
const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatelessWidget(),
);
}
}
/// This is the stateless widget that the main application instantiates.
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({
Key ? key
}): super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Expanded Column Sample'),
),
body: Center(
child: Column(
children: < Widget > [
Container(
color: Colors.blue,
height: 100,
width: 100,
),
Expanded(
child: Container(
color: Colors.amber,
width: 100,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: < Widget > [
Text('We moved this text to the right')
],
),
Container(
color: Colors.blue,
height: 100,
width: 100,
),
],
),
),
);
}
}
Hope, this will help!
Sir, In code I am wrapping the only Text widget. After that I used some custom style like text align. But after use I expected to center only the text part because only this widget get wrapped not to the other. But I saw my button also get centered. How it possible? I was wrapped one widget but my other widget get affected. What's logic into there, sir?
You need to set a crossAxisAlignment: CrossAxisAlignment.start, and wrap the headline text with center widget. hope the below code can show you a good example.
class Demo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("title"),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Center(
child: Text("Whats your favourite food?"),
),
RaisedButton(
onPressed: () {},
child: Container(
padding: EdgeInsets.zero,
decoration: BoxDecoration(),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Answer 1"),
),
),
),
RaisedButton(
onPressed: () {},
child: Container(
padding: EdgeInsets.zero,
decoration: BoxDecoration(),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Answer 1"),
),
),
),
RaisedButton(
onPressed: () {},
child: Container(
padding: EdgeInsets.zero,
decoration: BoxDecoration(),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Answer 1"),
),
),
),
],
),
);
}
}
Set column widget's mainaxisalignement to start, or wrap all widget that you don't want to center in a column
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'm building a flutter app with a Login Screen. On focus on the text field(s), the screen is overflowed and i cannot scroll. I've tried using a ListView.builder, but that just gives a renderBox error, and the regular ListView doesn't work
The widget structure is like this
-scafold
- body
- container
- column
- form
- column
- textInput
- textInput
- container
- container
- row
- raisedButton
Thank You in advance !!
The ListView solution should work, but at the time of writing, it suffers from the crash listed here. Another way to achieve the same thing without this crash is to use a SingleChildScrollView:
return new Container(
child: new SingleChildScrollView(
child: new Column(
children: <Widget>[
_showChild1(),
_showChild2(),
...
_showChildN()
]
)
)
);
try this Code: Its Using ListView
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: Center(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.all(15.0),
children: <Widget>[
Center(
child: Card(
elevation: 8.0,
child: Container(
padding: EdgeInsets.all(10.0),
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(
prefixIcon: Icon(Icons.person),
labelText: "Username or Email",
),
),
SizedBox(
height: 15.0,
),
TextField(
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock),
labelText: "Password",
),
),
SizedBox(
height: 15.0,
),
Material(
borderRadius: BorderRadius.circular(30.0),
//elevation: 5.0,
child: MaterialButton(
onPressed: () => {},
minWidth: 150.0,
height: 50.0,
color: Color(0xFF179CDF),
child: Text(
"LOGIN",
style: TextStyle(
fontSize: 16.0,
color: Colors.white,
),
),
),
)
],
),
),
),
),
SizedBox(
height: 25.0,
),
Row(
children: <Widget>[
Expanded(child: Text("Don't Have a Account?")),
Text("Sign Up",
style: TextStyle(
color: Colors.blue,
)),
],
),
],
),
),
bottomNavigationBar: Padding(
padding: EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: RaisedButton(
padding: EdgeInsets.all(15.0),
onPressed: () {},
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
32.0,
),
side: BorderSide(color: Color(0xFF179CDF))),
child: Text(
"SKIP SIGN UP FOR NOW",
style:
TextStyle(fontSize: 18.0, color: Color(0xFF179CDF)),
),
)),
],
),
),
);
}
}
There are two easy ways of doing it.
Wrap your Column in a SingleChildScrollView
SingleChildScrollView(
child: Column(
children: [
Text('First'),
//... other children
Text('Last'),
],
),
)
Use ListView instead of Column.
ListView(
children: [
Text('First'),
//... other children
Text('Last'),
],
)
This approach is simple to use, but you lose features like crossAxisAlignment and other benefits provided by Column, in that case, you can wrap your children widget inside Align and set alignment property.
Now you may have nested children which are further scrollable, in that case, you need to provide a fixed height to your children, you may use SizedBox for that, that's it.
For example:
ListView(
children: [
Text('First'),
SizedBox(
height: 100,
child: ListView(...), // nested ScrollView
),
Text('Last'),
],
)
We can use the Expanded widget. It will add scrolling.
return new Expanded(
child: new SingleChildScrollView(
child: new Column(
children: <Widget>[
_showChild1(),
_showChild2(),
...
_showChildN()
]
)
)
);
Wrap your column in SingleChildScrollView and then wrap SingleChildScrollView in a Center widget to center the widgets in the column.
Center(
child: SingleChildScrollView(
child: Column(
children: [
Text('First'),
//... other children
Text('Last'),
],
),
)