SingleChildScrollView for Column inside Column - flutter

In the picture below, what I'm trying to achieve, is let the Green part Scrollable, since, in case the keyboard pops up, it doesn't give me the render error.
The whole screen is just a Column, where yellow part is a custom widget, and green part another Column inside it.
I've tried different solutions.
Wrap the whole Column into a SingleChildScrollView, but I would like that yellow part would stay fixed at the top.
I've tried also wrapping only green part into a SingleChildScrollView, but it doesn't work (The Render Error still raised).
I've seen I could use SliverAppBar, but I would like to achieve using my custom widget (yellow part).
I am a little bit stuck.
Scaffold(
body: SafeArea(
child: Column(
children: [
AppBarWidget(height: size.height * 0.15),
Container(
height: size.height - size.height * 0.15 - mediaQuery.padding.top,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
EditableAvatarWidget(
circleRadius: circleRadius,
badge: test,
border: Border.all(color: test.mainColor, width: 5),
),
Column(
children: [
Text(
"Name Surname",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 26,
color: Global.blackGrey),
),
SizedBox(height: 10),
Text(
"mail#mail.com",
style: TextStyle(fontSize: 18, color: Colors.grey),
)
],
),
Padding(
padding: EdgeInsets.symmetric(
horizontal: size.width / 6, vertical: 0),
child: FlatCustomButton(
onPress: () {},
background: Global.editProfileButton,
text: "Edit profile",
textColor: Global.blackGrey,
inkwellColor: Colors.black,
),
)
],
),
),
],
),
),
);
I would maybe also think to implement a ListView (?), but as you can see I've set inside the Column the mainAxisAlignment: MainAxisAlignment.spaceAround to have already my UI preference.
Do you have any idea how I could achieve this?
TL;DR: Let scrollable only GreenPart (Column) that belong to another Column (Whole Screen) and let Yellow Part stay on that fixed position

That's how I fixed.
I've encapsulated the Green Column Part in a Expanded before and then into a SingleChildScrollView.
It works exactly how I wanted.
Now only the green part scroll, and the yellow part stays in a fixed position when keyboard appears.
return Scaffold(
body: SafeArea(
child: Column(
children: [
AppBarWidget(
height: size.height * 0.15,
),
Expanded( //This
child: SingleChildScrollView( // and this fixed my issue
child: Container(
height:
size.height - size.height * 0.15 - mediaQuery.padding.top,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
EditableAvatarWidget(
circleRadius: circleRadius,
badge: test,
border: Border.all(color: test.mainColor, width: 5),
),
Column(
children: [
Text(
"Name Surname",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 26,
color: Global.blackGrey),
),
SizedBox(height: 10),
Text(
"mail#mail.com",
style: TextStyle(fontSize: 18, color: Colors.grey),
)
],
),
Padding(
padding: EdgeInsets.symmetric(
horizontal: size.width / 6, vertical: 0),
child: FlatCustomButton(
onPress: () {},
background: Global.editProfileButton,
text: "Edit profile",
textColor: Global.blackGrey,
inkwellColor: Colors.black,
),
)
],
),
),
),
),
],
),
),
);

You can use SliverAppBar like you have already tried, but inside that you have flexibleSpaceBar which has background property that can accept any kind of Widget.
A sample code is here.

If you can set a fixed height to the Column inside the SingleChildScrollView,
that's probably the best, even if it involves a bit of "hacking" as in the fix you provided.
However, when you need flexible/expanding content in the Column, you can consider using a ConstrainedBox with IntrinsicHeight inside the SingleChildScrollView as the SingleChildScrollView's documentation describes.
I've found that for relatively simple screens this works well. E.g. where you really only need scrolling if you have an input field and the keyboard pops up, as in your code.
LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: IntrinsicHeight(
child: Column(
children: <Widget>[
...
Note: with more complex screens, IntrinsicHeight might be too costly, see its docs for more info.

Related

How to set width as much as I need, but not more

In appBar I have a container which is responsible for displaying text (it could be 5 chars or even 100). And I want to adjust width of container based on text. For example for 5 chars container should show text and should be a little bigger than text (I will add paddings later), but for 100 chars container should take all possible space and show text with dots.
Currently I have two solutions:
The first one (Without spacer() in tree) is expanding container always to the biggest possible width (like I said, I don't need this big container for short texts)
The second one (with spacer() in tree) is always displaying container with 1/3 width of appbar (Did someone know why it's always the same width? It's kind of interesting).
return Scaffold(
appBar: AppBar(
titleSpacing: 10,
centerTitle: false,
automaticallyImplyLeading: false,
title: SizedBox(
width: MediaQuery.of(context).size.width,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(height: 40, width: 40, color: Colors.amberAccent),
const SizedBox(width: 10),
const Text("Some Text"),
const SizedBox(width: 10),
Expanded(
child: Container(
color: Colors.purpleAccent,
height: 40,
child: const Center(
child: Text(
"text",
overflow: TextOverflow.ellipsis,
softWrap: true,
),
),
),
),
],
),
),
),
body: Container(),
);
So, how can I adjust width of this container to text inside?
Update:
I don't know if I explained it correctly, because your answers weren't related with my question 😁 So I will try again:
I want the last one container to take full possible width if text is long and for shorten texts just width to cover background behind text.
Here is long text: (It should be like that)
Here is short text: (Container should be smaller)
You should use constraints: BoxConstraints(maxWidth:), property of Container.
Please go with below code.
Container(
color: Colors.red,
padding: const EdgeInsets.all(20.0),
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.7,
),
child: Text('Hello', style: TextStyle(color: Colors.black)),
),
Spacer(),
Flexible(
child: Container(
color: Colors.purpleAccent,
height: 40,
child: const Center(
child: Text(
"Long Textttttttttttttttttttt",
overflow: TextOverflow.ellipsis,
softWrap: true,
),
),
),
),
Your result will be like below screenshot.
I implement another text for checking dynamic width of container.
Container(
color: Colors.red,
padding: const EdgeInsets.all(20.0),
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.7,
),
child: Text('Hello World', style: TextStyle(color: Colors.black)),
),
Spacer(),
Flexible(
child: Container(
color: Colors.purpleAccent,
height: 40,
child: const Center(
child: Text(
"Long Textttttttttttttttttttt",
overflow: TextOverflow.ellipsis,
softWrap: true,
),
),
),
),
For This You can Use :
Flexible Widget:
Example:
Flexible(
child: Container(
color: Colors.green,
)
),
or Wrap Widget :
Example :
Card(
margin: const EdgeInsets.only(top: 20.0),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Text(
'Categories',
style: TextStyle(fontFamily: 'MonteSerrat', fontSize: 16.0),
),
Wrap(
children: <Widget>[
_checkBox('Gaming'),
_checkBox('Sports'),
_checkBox('Casual'),
_checkBox('21 +'),
_checkBox('Adult'),
_checkBox('Food'),
_checkBox('Club'),
_checkBox('Activities'),
_checkBox('Shopping')
],
)
],
),
));

How can I change the width of a container/card depending on screen size?

I tried adding a width to the container but it didn't do anything. By default it fills the page? Not sure what's happening. Do I have to use MediaQuery somewhere? I want the post to be the same size on both the phone and the tablet. I'm not great with explaining so I added a photo to help.
Here's my code:
body: ListView(
children: [
Container(
height: 132,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15)),
child: Card(
margin: EdgeInsets.all(10),
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15)),
child: Column(
children: <Widget>[
ListTile(
leading: CircleAvatar(
backgroundColor: Color(0xFF192A4F),
),
title: SizedBox(
height: 39,
child: TextButton(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AutoSizeText(
'Username',
style: TextStyle(
color: Color(0xFF192A4F),
fontSize: 16,
fontWeight: FontWeight.bold),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: Size(50, 30),
alignment: Alignment.centerLeft),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SeventhRoute(),
),
);
},
),
),
),
Divider(
height: 10,
color: Colors.black26,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Row(
children: <Widget>[
Icon(Icons.comment_outlined,
color: Colors.black),
SizedBox(width: 8.0),
Text(
'Comment',
style: TextStyle(color: Colors.black),
),
],
),
Row(
children: <Widget>[
Icon(Icons.bookmark_outline, color: Colors.black,),
SizedBox(width: 8.0),
Text('Bookmark', style: TextStyle(color: Colors.black),),
],
),
],
),
SizedBox(
height: 12.0,
)
],
),
),
),
],
),
double width = MediaQuery.of(context).size.width;
you can get screen width from here. Then add a logic
As example if screen width of right phone is 500 and left is 1000,
then you can set
if the screen width is <= 500 ,
set the container width as screen width
otherwise
set the container width as 500.
---Additionally---
You can get screen orientation like this
var isPortrait = MediaQuery.of(context).orientation == Orientation.portrait
The reason specifying the width is ignored is because you didn't specify what should happen with the smaller card. The framework doesn't know if it should be aligned left, center, right, etc. To fix this, you can wrap your Card, Container, or even the whole ListView in a Center (or Align) widget.
Based on your images i can see that only the width is changing, height is constant, so i'd suggest in your container give width: MediaQuery.of(context).size.width;
And of course some padding to that container.
It looks like you want that widget to take as much width as it can, but no more than 500px wide. Trivial:
ConstrainedBox(
child: YourChild(...),
constraints: BoxConstraints(maxWidth: 500),
),

How to make the page scrollable in the following structure (Flutter)

I'm building a product detail page. As the following piece of code and image shown, when there is a lot of content in the description part, the bottom overflow will occur. I'm wondering how to make the page scrollable, I've tried wrapping the Stack with SingleChildScrollView, but this is definitely not working in my case here. Can anyone help me with that? Thank you very much!!!!!
class DetailsScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Scaffold(
backgroundColor: Colors.white,
body: Stack(
children: [
buildBody("path/to/image", size),
],
),
);
}
Positioned buildBody(String imagePath, Size size) {
return Positioned.fill(
child: Column(
children: [
Expanded(
child: Container(
padding: EdgeInsets.symmetric(vertical: 60, horizontal: 30),
color: Colors.green,
child: Stack(
children: [
Align(
alignment: Alignment.center,
child: Hero(
tag: 1,
child: Image.asset(
imagePath,
width: size.width * 0.7,
),
),
),
],
),
),
),
Expanded(
child: Container(
color: Colors.white,
child: Column(
children: [
SizedBox(
height: 100,
),
Container(
margin: EdgeInsets.symmetric(
horizontal: 20,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CircleAvatar(),
SizedBox(
width: 10,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Anvesha Shandilya',
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 16),
),
Text(
'Owner',
style: TextStyle(
color: fadedBlack,
),
),
],
),
Expanded(child: Container()),
Text(
'Dec 16, 2020',
style: TextStyle(
color: fadedBlack,
fontSize: 12,
),
),
],
),
),
SizedBox(
height: 30,
),
Container(
margin: EdgeInsets.symmetric(
horizontal: 20,
),
child: Text(
'A lot of content..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................',
style: TextStyle(
color: fadedBlack,
height: 1.7,
),
),
),
],
),
),
),
],
),
);
}
}
Error: Bottom Overflowed
To reproduce: Change the path/to/image, to this: A image can be used with the code:
tl;dr remove Expanded widgets -> remove Stack -> replace Column with ListView (for scrollability)
Start by removing Expanded widgets from the tree as you don't really need them. If possible, you should use mainAxisAlignment and crossAxisAlignment to position your widgets. That's exactly how you should handle placing the date next to user's name.
Expanded documentation states with which widgets you can use it:
Creates a widget that expands a child of a Row, Column, or Flex so
that the child fills the available space along the flex widget's main
axis.
Then remove Stack widget. If you don't then it will still work as Stack tries to get as big as its positioned children so it will just get as big as the Column you have inside. It's redundant.
Last but not least, replace Column with ListView. Column doesn't really care about whether it's overflowing or if it's being rendered. So if you create a Column that is bigger than the screen, it will simply display it which will cause the overflow. To fix that you could wrap it SingleChildScrollView, but I think it's more appropriate to just use ListView instead.
Here's an amazing explanation of layout system in Flutter: https://flutter.dev/docs/development/ui/layout/constraints

SingleChildScrollView makes the UI white

I am trying to make my UI scrollable but when i add the SingleChildScrollView i get this white screen not showing anything at all.I should erase the Column from the begining?Use a container?I already search on internet but i don't know what to add.
Here is my code, please tell me what i can erase or add to make it work ..
class _UserProfilesState extends State<UserProfiles> {
#override
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Expanded(
child: Stack(
children: <Widget>[
Padding(
padding:
EdgeInsets.symmetric(horizontal: 10.0, vertical: 40.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(Icons.arrow_back),
iconSize: 30.0,
color: Colors.black,
onPressed: () => Navigator.pop(context),
),
])),
Positioned(
left: 24,
top: MediaQuery.of(context).size.height / 6.5 - 28,
child: Container(
height: 84,
width: 84,
//profilepic
child: CircleAvatar(
radius: 10,
backgroundImage: NetworkImage(widget.avatarUrl != null
? widget.avatarUrl
: "https://icon-library.com/images/add-image-icon/add-image-icon-14.jpg"),
),
),
),
Positioned(
right: 24,
top: MediaQuery.of(context).size.height / 6.5 + 16,
child: Row(
children: <Widget>[
Container(
height: 32,
width: 100,
child: RaisedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ChatScreen(
serviceProviderId:
widget.serviceProviderId,
userName: widget.userName,
avatarUrl: widget.avatarUrl,
)));
},
color: Colors.black,
textColor: Colors.white,
child: Text(
"Message",
style: TextStyle(fontWeight: FontWeight.bold),
)),
),
SizedBox(
width: 16,
),
Container(
height: 32,
width: 32,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
"https://lh3.googleusercontent.com/Kf8WTct65hFJxBUDm5E-EpYsiDoLQiGGbnuyP6HBNax43YShXti9THPon1YKB6zPYpA"),
fit: BoxFit.cover),
shape: BoxShape.circle,
color: Colors.blue),
),
],
),
),
Positioned(
left: 24,
top: MediaQuery.of(context).size.height / 4.3,
bottom: 0,
right: 0,
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(widget.userName,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 22,
)),
SizedBox(
height: 4,
),
Padding(
padding: const EdgeInsets.all(1.0),
child: Text(
widget.address == "default"
? "No Address yet"
: "${widget.address}",
style: TextStyle(
color: Colors.black,
fontSize: 12,
),
)),
Padding(
padding: const EdgeInsets.all(1.0),
child: Text(
"${widget.categoryName}",
style: TextStyle(
color: Colors.black,
fontSize: 12,
),
)),
],
),
))
],
))
],
),
),
);
}
}
Using Spacer() might also cause this issue
The reason why your UI became white is that SingleChildScrollView allows it child to take as much space as possible. In your Column, one of your children is Expanded, Expanded tries to take as much space as possible. You are making your UI take an infinite amount of space because of that. That's the reason why you are seeing a white screen. The solution here would be to remove Expanded.
according to me and the knowledge I have this happened because the singlechildscrollview here tries to take as much height as it can take so It is creating an issue to solve this wrap the singlechildscrollview in a container and give height&width to the container it solved in my case
Thanks...
I faced similar issues, but after much research I got it working, by ensuring that the body of the scaffold be a Container that defines it's height and width, or Column. After which you can wrapped your any of these with a SingleChildScrollView.
However, in an attempt to insert an Expanded widget as child element, the expanded widget will look for bounded heights of it's parents/root element. And if it doesn't found any, it will thrown an error. A good example is if the root element is a column or a container with an unbounded height. Which is your case, looking at your code

Auto expanding Container in flutter -- for all devices

I need a Container with some text in to auto expand. I have an API call, which can be anything from 5 words to 500 words. I don't want to just have 1 fixed size that's huge, but contains 10 words.
I have tried Expanded() and SizedBox.Expand(), but I might be using them wrong
Card(
elevation: defaultTargetPlatform ==
TargetPlatform.android ? 5.0 : 0.0,
child: Column(
children: <Widget>[
Container(
margin: const EdgeInsets.all(0.0),
padding: const EdgeInsets.all(2.0),
decoration: BoxDecoration(color: Colors.black),
width: _screenSize.width,
height: 250,
child: Column(
children: <Widget>[
Container(
color: Colors.black,
width: _screenSize.width,
height: 35,
child: Padding(
padding: const EdgeInsets.only(
left: 15, top: 11),
child: Text("Title".toUpperCase(),
style: TextStyle(
color: Colors.white
),
),
),
),
Container(
color: Colors.white,
width: _screenSize.width,
height: 210,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 8, bottom: 5),
child: Text("Title of expanding text", style: TextStyle(
fontSize: 25,
),
),
),
Text("Expanding text", style: TextStyle(
fontSize: 35,
fontWeight: FontWeight.w800
),),
],
),
),
],
),
),
],
),
),
I just need the Container to expand, but stay small/get bigger
Have you tried not specifying height at all? The Container should wrap according to the child in this case.
Otherwise, the widget has a child but no height, no width, no
constraints, and no alignment, and the Container passes the
constraints from the parent to the child and sizes itself to match the
child.
Above is an extract from the official flutter documentation for Container.
Here is the official flutter documentation link.
You can use FittedBox, this will resize your text according to available area.
You can use it like this :
FittedBox(child: Text('...............Your text...............'));
I would suggest you to use Constraints...this will set Container height according to the Text child's requirement. Please see the example...
Container(
constraints: BoxConstraints(
maxHeight: double.infinity,
),
child: Column(
children: [
Text(
'Hello flutter...i like flutter...i like google...',
softWrap: true,
style: TextStyle(
color: Colors.white, fontSize: 20 , ),
),],),)
we just neeed to add mainAxisSize: MainAxisSize.min, properties inside child Column or Row where the child is set to Container
for example
AnythingYourWidget(
child: Container(
child: Column( // For Example Column
mainAxisSize: MainAxisSize.min, // these properties following the children content height available.
children: [
// YourWidget
]
)
)
),
I too had a container with a text widget inside that would not scale as the text increased in character count. Make the widget tree Container -> IntrinsicWidth -> Text/TextField and it seems to play nicely for me.
IntrinsicWidth will scale the size of the container to its child.