How to fix a widget to the right side of a ListView on Flutter Web - flutter

I have a Flutter Web application where I need to show a widget on the right side of a ListView when I click an item and this widget should always be visible on screen. I can achieve my objective puting both on a Row and using a scrollable only for the ListView, but that requires the ListView to be wrapped by a widget with defined height.
Defining a container with height to wrap the ListView breaks the responsiveness when I resize the browser, as the container doesn't fit the height of the screen.
I thought of using the shrinkWrap property of the ListView so I don't have to wrap it in a widget with predefined height, but that makes the whole Row scrollable vertically, eventually causing the widget to leave the viewport.
I would appreciate if somebody knows how could I keep this right side widget fixed on screen so I can achieve my objective without losing responsiveness.
Here's something similitar to what I've got so far:
class PageLayout extends StatefulWidget {
const PageLayout({Key? key, required this.items}) : super(key: key);
final List<String> items;
#override
State<PageLayout> createState() => _PageLayoutState();
}
class _PageLayoutState extends State<PageLayout> {
final rightSideWidget = Container(
decoration: BoxDecoration(
color: Colors.red,
border: Border.all(color: Colors.white, width: 2),
),
height: 200);
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.49,
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (context, index) => Container(
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(color: Colors.white, width: 2),
),
height: 200,
child: Center(
child: Text(
widget.items[index],
style: const TextStyle(color: Colors.white),
),
),
),
itemCount: widget.items.length,
),
),
Expanded(child: rightSideWidget),
],
),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
I want rightSideWidget to be always centered on screen or follow the scroll.

You can divide your screen into two sections, right section and left section; thereby being able to control behaviour of widgets in both sections.
Divide the overall screen into 2 proportional sections using a Row
widget
Put this Row widget inside a Container with height equal to screen height for preserving responsiveness | Use MediaQuery to get current height of page
Now left hand section can individually scroll, and on click of any option from this section you can define behaviour for right section; while keeping the left section constant throughout page lifecycle

Related

How to control size of a Column inside a SizedBox

I am building a widget to display a chart/graph inside a fixed size window. The chart will be bigger than the window so the solution includes the user being able to scroll the graph widgets around inside a window (you can find details about this in an earlier question I asked about this here).
Part of the layout includes a fixed sized panel created using a SizedBox and, within that, a Column containing rows of widgets that make up the graph. I need the Column to fit its contents tightly so that I can track it's size and, for example, stop the user scrolling up when the last row is visible at the bottom of the SizedBox.
When the size of the children in the Column should make the Column smaller than the SizedBox, the Column is still being forced to be the size of the SizedBox. This is explained in the Flutter documentation here.
According to the Flutter documentation, the solution is:
This can be remedied by wrapping the child SizedBox in a widget that
does permit it to be any size up to the size of the parent, such as
Center or Align.
I have tried this and it doesn't seem to work. Below is a test app I wrote on DartPad to check this out. If I use either Align or Center as child of SizedBox and parent of the Column widget, the Column is still the same size as the SizedBox. I have also added MainAxisSize.min to the Column, but this doesn't appear to make any difference.
I have considered doing this using a Stack so that the Column is displayed over the SizedBox, rather than as a child of it, but that feels like a bit of a hacky workaround given that the documentation suggests you can control the size of a Column inside a SizedBox.
Does anyone know how I force the Column to be the smallest size it can be inside a SizedBox of fixed size?
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
List<Widget> graphWidgets = const [
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: SizedBox(
// This is the window the chart is displayed in
height: 200,
width: 400,
child: DecoratedBox(
decoration: BoxDecoration(
border: Border.all(),
),
child: OverflowBox(
// Used to prevent graph rows from wrapping in the window
maxHeight: double.infinity,
maxWidth: double.infinity,
alignment: Alignment.topLeft,
child: DecoratedBox(
// Debug widget to show extent of child column
decoration: BoxDecoration(
border: Border.all(),
color: Colors.amber,
),
child: Center(
// This should allow the Column to be smaller than the SizedBox?
child: Column(
// This holds the widgets that make up the graph
mainAxisSize: MainAxisSize.min,
children: graphWidgets,
),
),
),
),
),
),
),
),
);
}
}
As per SayyidJ's reply, the answer is to use a ConstrainedBox rather than a SizedBox. The Column can shrink to fit the contents when it is the child of a ConstrainedBox.
Here is the revised code, which you can run in DartPad, showing this working.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
List<Widget> graphWidgets = const [
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: ConstrainedBox(
// This is the window the chart is displayed in
constraints: BoxConstraints(
maxHeight: 200,
maxWidth: 400,
),
child: DecoratedBox(
decoration: BoxDecoration(
border: Border.all(),
),
child: OverflowBox(
// Used to prevent graph rows from wrapping in the window
maxHeight: double.infinity,
maxWidth: double.infinity,
alignment: Alignment.topLeft,
child: DecoratedBox(
// Debug widget to show extent of child column
decoration: BoxDecoration(
border: Border.all(),
color: Colors.amber,
),
child: Column(
// This holds the widgets that make up the graph
mainAxisSize: MainAxisSize.min,
children: graphWidgets,
),
),
),
),
),
),
),
);
}
}

How to keep wrap content height for flutter listview with horizontal scrolling? [duplicate]

This question already has answers here:
Horizontal ListView flutter WITHOUT explicit height
(2 answers)
Closed 6 months ago.
I made list view with horizontal scrolling. And it is not working with out setting an height.
My code is
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text(""),
),
body: Column(
children: [
Container(
height: 100,
color: Colors.red,
child: ListView.builder(
itemCount: 5,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Card(
color: Colors.amberAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("$index"),
),
);
}),
),
const Expanded(child: Text("Some other views")),
],
),
),
);
}
}
How can I leave the listview height as wrap content?
That's one of the things you will have to live with. If there is a list of 1 million items and each one is wrap_content and somehow one of the list item is going to be 1000px in height, flutter has no way of knowing this as it only lays out items which are going to be visible (or has potential to be visible on user interaction immediately). It doesn't build all the million items at once so we need to provide some height.
This is true for vertical lists as well. We usually don't pay attention as most apps in portrait mode have not much width so it matches parent's width without any issue.

I want to use remaining available space on my page in Flutter

In the screen, I have a Column, it has a cusotm made widget of specific height. Then, I have Expanded, in which I have a TabBar which has three tabs.
In one of those tabs, I want to show a list. First, I have a padding, which contains column. The column has some text, which should remain at top and the list should be shown in the space which is remaining. I am using Expanded for that, but it is not working.
I can't use ListView directly, and also can't use expanded. It is only working when I am giving it a container of fix size. Now, in different screens, it will look different. So, I want to take all of the remaining space and build the list there. Code for reference -
Here is the doubts screen, which is one of the tabs of main screen -
import 'package:flutter/material.dart';
import 'package:my_board_plus/size_config.dart';
import 'package:my_board_plus/styles.dart';
import '../../api_handling/api_fetch/fetch_doubt_questions.dart';
import '../../data_models/doubt_question_model.dart';
class NewDoubtsScreen extends StatefulWidget {
const NewDoubtsScreen({Key? key}) : super(key: key);
#override
State<NewDoubtsScreen> createState() => _NewDoubtsScreenState();
}
class _NewDoubtsScreenState extends State<NewDoubtsScreen> {
late Future<List<DoubtQuestionModel>> doubtQuestionsList;
#override
void initState() {
doubtQuestionsList = fetchDoubtQuestion();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: backgroundColor2,
floatingActionButton: Container(
width: getProportionateScreenWidth(130),
height: getProportionateScreenHeight(50),
decoration: BoxDecoration(
color: brandPurple,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Center(
child: Text(
'? My Doubts',
style: TextStyle(
color: Colors.white,
fontSize: 15,
),
),
),
),
body: Padding(
padding: EdgeInsets.only(top: 8.0, left: 5),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Trending Doubts',
style: TextStyle(
color: Colors.white,
),
),
Text(
'View all',
style: TextStyle(
color: brandYellow,
decoration: TextDecoration.underline
),
),
],
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Container(
height: getProportionateScreenHeight(530),
width: double.infinity,
color: Colors.red,
),
),
],
),
),
);
}
}
The red area that you are seeing is the one. I want it to occupy whole area available in the phone screen, so I can show list in it which should be scrollable. In this case, it is occupying all, but in different screens, it might not. So, please give me some suggestions.
You can try to give the height of the container like
height: double.infinity
Or you can give the height of it with substracting the other height from the screen size like
height: MediaQuery.of(context).size.height - getProportionateScreenHeight(50) //the heigth size that you give the other widget that top of it
try to wrap your Padding widget with the Expanded widget,
Expanded widget in column will take the rest of the space of the screen.
also no need to give height to Container widget, so you can remove getProportionateScreenHeight(530) this one

In Flutter, how can I have an adjusting vertical layout, a mix between column and listview behavior?

As far as I see, Column and ListView both have a very distinct usage when used for a base root layouting.
Column is used when the screen has few components (such as login screen). We can add some Expanded components to adjust white spaces in between, so when the keyboard is visible, the screen shrink to keep everything visible.
ListView is used when the screen has many components that potentially need scrolling. We can't use Expanded component in ListView. When using ListView, appearing keyboard does not change the white spaces, only change the size of outer ListView, while the inner content is wrapped in scroll view.
Now the problem is, how if I want to have screen like this:
When all the contents' combined vertical size is not longer than available height quota given from parent (in this case, screen's height), then the components behave like inside Column: expanding or shrinking to fill available white spaces according to rules set by Expanded.
When all the content's combined vertical size is longer than available height quota, then the components behave like inside ListView: all the possible expanding components will shrink into their minimum size (ignoring Expanded), and the screen is scrollable so user can see the rest of the screen below.
Is this possible to be done in Flutter? How?
EDIT: based on Reign's comment, I have isolated some code from SingleChildScrollView manual, but it looks like it still can't handle if its children contains Expanded.
Widget columnRoot({
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.spaceBetween,
AssetImage backgroundImage,
List<Widget> children
}) =>
LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) =>
SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: backgroundImage,
fit: BoxFit.cover),
color: Colors.white
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: mainAxisAlignment,
children: children
),
)
)
)
);
Widget content(BuildContext context) => columnRoot(children: [
Container(color: Colors.red, height: 100.0),
Expanded(Container(color: Colors.green)), // without this line, there's no layout error
Container(color: Colors.blue, height: 100.0),
]);
Error:
RenderFlex children have non-zero flex but incoming height constraints are unbounded.
I added some code you can test with also with some explanation.
Copy paste and run the code
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: SingleChildScrollView( //Since setting it to scrollable, your widget Column with expanded children wont work as it supposed to be because it wont know its parent height
//Since its already scrollable `Expanded` will expand or shrink now based on it child widget (Expanded(child: SomeHeight widget)) refer: #10 example
child: IntrinsicHeight( //This will fix the expanded widget error
child: Container(
//Test remove this height
// height: 400, //But when you set its height before its parent scroll widget, `Expanded` will expand based on its available space
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: [
Container(color: Colors.red, height: 100.0),
//#10
//Experiment with this
Expanded(
child: Container(
color: Colors.purple,
// height: 100.0, //initialized height, remove parent container height: 400
// child: Text("This is also considered as min height"),
),
),
Container(color: Colors.blue, height: 100.0),
],
),
),
),
),
),
);
}
}

Positioned widgets must be placed directly inside Stack widgets

I am trying to implement gridview with image and a text inside it. where i want text at the bottom of image with black background. Here is my code for ListItem
class ListItem extends StatelessWidget {
String url;
String name;
ListItem(this.url, this.name);
#override
Widget build(BuildContext context) {
return new Container(
child: new Column(
children: <Widget>[
new Image.network('${url}', fit: BoxFit.cover),
new Positioned(
child: new Container(
child: new Text('${name}',
style: new TextStyle(fontSize: 20.0, color: Colors.white)),
decoration: new BoxDecoration(color: Colors.black),
padding: new EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 16.0)),
left: 0.0,
bottom: 108.0,
)
],
));
}
}
With this code it is showing error
Positioned widgets must be placed directly inside Stack widgets.
Positioned(no depth, dirty) has a Stack ancestor, but there are other widgets between them:
- Column(direction: vertical, mainAxisAlignment: start, crossAxisAlignment: center)
Issue was with Column, after changing few lines from here and there i finally found that it was because of Column
Once i change Column to Stack, it works fine.
return new Container(
child: new Stack(
We were just discussing this yesterday. Positioned can actually be used in more than just a Stack, so the docs aren't exactly right about that. It can't be used in anything that renders, and the docs are very specific about RenderObjectWidget:
"A Positioned widget must be a descendant of a Stack, and the path from the Positioned widget to its enclosing Stack must contain only StatelessWidgets or StatefulWidgets (not other kinds of widgets, like RenderObjectWidgets).
Source: https://docs.flutter.io/flutter/widgets/Positioned-class.html
Column is descended from RenderObjectWidget:
... Widget > RenderObjectWidget > MultiChildRenderObjectWidget > Flex > Column
Most people starting out in Flutter are only aware of StatelessWidget and StatefulWidget, but there are others and knowing them can be very important at times.
Widget:
StatefulWidget
StatelessWidget
RenderObjectWidget
ProxyWidget
PreferredSizeWidget
More at:
https://docs.flutter.io/flutter/widgets/Widget-class.html