Flutter Expanded widget content won't scroll - flutter

When my Expanded widget has expanded there are 20 items, which cause a vertical overflow issue. How do I get the contents within Expanded widget to scroll? I've tried wrapping the content in a SinglechildScrollView but it still didn't allow scrolling.
Scaffold(
key: key,
appBar: customappbar,
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
ExpansionTile(
title: Text(
"${_getcurrentselection(context)}:",
style: const TextStyle(fontSize: 16),
),
children: <Widget>[
for (Map<String, dynamic> submenuitemdata in subMenuItems)
Padding(
padding: const EdgeInsets.only(bottom:10.0),
child: SubmenuItem(submenuitem: submenuitemdata),
),
Expanded(
child: WebViewWidget(controller: controllerGlobal),
),
],
),
],
),
),

Try putting your widgets inside a ListView which should be inside the Expanded.

I couldn't find a suitable solution sa the Expanded widget below the ExpansionTile widget kept throwing errors no matter what I tried. Instead I placed both inside a Stack, so that the ExpansionTile expands over the top of any other widgets.
Also, minus 2 was a bit harsh

Related

i need to remove this text over flow in flutter. how can i do that?

I am facing error in the Text widget which is in Column.
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.songModel.displayNameWOExt, <------- here is my text
),
Text(
widget.songModel.artist.toString(),
overflow: TextOverflow.fade,
),
],
),
],
), ), );
This is what happened to me
This is what i need look like
Wrap the Text that is inside a Row widget with an Expanded widget:
Expanded(child: Text(/* Here the text*/),
wrap the text with the container and give a specific size to the container after that use overflow: TextOverflow.fade or any other property of "overflow", because these properties only work when you give a size to textfield but you cant give size to textfield so wrap it with container.
try embedding the Text() widget inside a Expanded() widget expanded documentation
Expanded is a widget that expands a child of a Row, Column, or Flex so that the child fills the available space.

Making a container scrollable

I have this view in my app
and this is the code for it:
return Container(
color: Colors.black,
child: ListView(
children: <Widget>[
Column(
children: <Widget>[
...
],
)
],
),
);
But if I click on my form I get stuck and the list of items is hidden by the keyboard, so I'd like to make my Container scrollable.
How can I do?
Instead of wrapping your Column with a ListView try with a SingleChildScrollView instead. You can also try adding a bottom padding equal to keyboard size MediaQuery.of(context).viewInsets.bottom.

Widget flickers and disappears when scrolling

I'm already losing sleep over this.
I'm trying to display a chart inside a ListView (for scrolling). For some reason the contents of the Card flickers when scrolling and randomly completely disappears (the Card itself stays visible though).
Any idea why would that happen?
(...) ListView (...)
children: [Row ( children: [buildChartBox()] )] (...)
Expanded buildChartBox() {
return Expanded(
child: Card(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
chartTitles(
title: 'Items',
subtitle: 'by value'),
SizedBox(
height: 300,
child: ValuesChart(data: calculateValues(items)))
],
),
],
),
),
),
);
}
Row chartTitles({String title = '', String subtitle = ''}) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: text_charttitle),
Text(subtitle, style: text_chartsubtitle),
],
)
],
);
}
Things tried:
Both of these were originally Stateless Widgets; I changed to simple
methods to simplify but it didn't change the weird behaviour.
Replacing the chartTitles return with an empty Container (i.e. removing the titles) does mitigate the issue. The chart then stays displayed but also flickers slightly.
Replacing the ListView with a SingleChildScrollView doesn't change anything.
EDIT: Code for the ValuesChart:
import 'package:fl_chart/fl_chart.dart';
class ValuesChart extends StatelessWidget {
final Map<String, int> data;
const ValuesChart({required this.data});
#override
Widget build(BuildContext context) {
return Container(
child: PieChart(
_theData(data),
));
}
}
Note I'm using a package called 'fl_chart'. _theData just returns various parameters for the chart, I don't think it's relevant.
Try to replace ListView with SingleChildScrollView
ListViews in flutter by default using what it is called in Android RecyclerView to efficiently use render resources.
If you are interested here an article
https://medium.com/1mgofficial/how-recyclerview-works-internally-71290de5d2c4

How to make a Widget to be as small as it can (wrap_content in Android)?

I have a rendering issue with render tree similar to Column>PageView>Column, where the last Column is inside a page of the PageView.
The PageView isn't being rendered correctly, so I get an exception (Horizontal viewport was given unbounded height.) as the framework can't calculate its sizes, I can fix it by wrapping it into a Flexible or an Expanded, but I don't want the PageView to take the whole screen, I want it to be as small as possible and on the center on the screen.
Here's a representation of my problem:
// This code throws an exception:
class Widget_1_Has_Problem extends StatelessWidget {
final PageController pageController = PageController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.done,size: 112),
SizedBox(height: 32),
PageView(
physics: NeverScrollableScrollPhysics(),
controller: pageController,
children: <Widget>[
// Many widgets go here, I am just simplifying with a Column.
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text('TEXT TEXT TEXT TEXT TEXT TEXT'),
SizedBox(height: 16),
Text('TEXT2 TEXT2 TEXT2 TEXT2 TEXT2 TEXT2'),
],
),
],
),
],
),
);
}
}
This is what I wanted to achieve (I removed the PageView in order to show it):
class Widget_2_No_PageView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.done,size: 112),
SizedBox(height: 32),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text('TEXT TEXT TEXT TEXT TEXT TEXT'),
SizedBox(height: 16),
Text('TEXT2 TEXT2 TEXT2 TEXT2 TEXT2 TEXT2'),
],
),
],
),
);
}
}
And this is how I can fix it, but it's not perfect, I will show later:
class Widget_3_With_Flex_Not_Perfect_Solution extends StatelessWidget {
final PageController pageController = PageController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Spacer(flex: 2,),
Icon(Icons.done,size: 112),
SizedBox(height: 32),
Flexible(
flex: 1,
child: PageView(
physics: NeverScrollableScrollPhysics(),
controller: pageController,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text('TEXT TEXT TEXT TEXT TEXT TEXT'),
SizedBox(height: 16),
Text('TEXT2 TEXT2 TEXT2 TEXT2 TEXT2 TEXT2'),
],
),
],
),
),
Spacer(flex: 2,),
],
),
);
}
}
This solution isn't perfect because Spacer and Flexible or Expanded always try to maintain it's proportionality of the flex weight, which means that for smaller displays the Spacer won't vanish, it will remain there, whereas the first and desired image won't have such a void space. As we can see in the below image, the Spacer is always there. Also I have to calculate the flex factor for every change in this design, whereas in my first code example the widget will size itself to the center of the screen automatically.
How can I instruct PageView then to be as small as it can be, instead of expand as much as it wants, as that's the only solution I can find online?
The PageView uses SliverFillViewport to wrap around given children.
This SliverFillViewport doesn't care about how small the children are. It only cares how much space it can occupy from its parent. So the Flex and Align widget are useless.
Currently with PageView, it most likely not possible unless we do some calculation and use SizedBox or ConstrainedBox.
I also found this discussion
This can be done, but not with PageView. I had to use SingleChildScrollView with a Row. The reason for that is: if you want to size your PageView properly, you need to build and layout every child it has. PageView does not do that, but Row does, you can even decide how smaller elements should be positioned. PageController also works if you properly set widths of children in the row.
Note that this will only work nicely if you have small amount of pages.
class SizingPageView extends StatelessWidget {
final ScrollPhysics physics;
final PageController controller;
final List<Widget> children;
const SizingPageView({Key key, this.physics, this.controller, this.children})
: super(key: key);
#override
Widget build(BuildContext context) {
Widget transform(Widget input) {
return SizedBox(
width: MediaQuery.of(context).size.width,
child: input,
);
}
return SingleChildScrollView(
physics: physics ?? PageScrollPhysics(),
controller: controller,
scrollDirection: Axis.horizontal,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: children.map(transform).toList(),
),
);
}
}
class Solution extends StatelessWidget {
final PageController pageController = PageController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Spacer(
flex: 2,
),
Icon(Icons.done, size: 112),
SizedBox(height: 32),
Text('BEFORE SCROLL'),
SizingPageView(
physics: NeverScrollableScrollPhysics(),
controller: pageController,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text('TEXT TEXT TEXT TEXT TEXT TEXT'),
SizedBox(height: 16),
Text('TEXT2 TEXT2 TEXT2 TEXT2 TEXT2 TEXT2'),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text('TEXT TEXT TEXT TEXT TEXT TEXT'),
],
),
Text('Third'),
],
),
Text('AFTER SCROLL'),
Spacer(
flex: 2,
),
FlatButton(
child: Text('Next'),
onPressed: () {
var next = pageController.page.round() + 1;
if (next >= 3) {
next = 0;
}
pageController.animateToPage(
next,
duration: Duration(seconds: 1),
curve: Curves.easeInOut,
);
},
)
],
),
);
}
}
You are asking for items inside of the PageView to set the size of PageView itself. This is not going to work with the standard class. If you look at its source, you'll find that uses Viewport inside Scrollable. Viewport grabs maximum size and ignores the size of the children (Slivers). You'll need to use a different approach or maybe create a custom PageView, perhaps one that uses ShrinkWrappingViewport.
https://api.flutter.dev/flutter/widgets/ShrinkWrappingViewport-class.html
By wrapping PageView inside SizedBox, we can get rid of the exception you are getting. (Horizontal viewport was given unbounded height.).
SizedBox(height: 32, child:
PageView(
physics: NeverScrollableScrollPhysics(),
controller: pageController,
children: <Widget>[
// Many widgets go here, I am just simplifying with a Column.
Column(
...
But that will give you a bottom overflow exception of 16 pixels since you have another SizedBox with fixed height inside Column in PageView.
In order to resolve this issue, let's just for time being replace top SizedBox with Container and add color property to it to see how much space and height is available to us.
So, instead of hardcoding the sizedbox height to 32, we can make use of MediaQuery to calculate height for us.
If you print height of the top SizedBox, it will come out to be 597
SizedBox(height: MediaQuery.of(context).size.height,
I for demo, divided the height with 5 to get 119.4 height of the SizedBox widget that contains PageView, which resolves the renderflow exception as well as maintains pageview's location at center of the screen.
If we again use Container to see now how much space and height is available to us, it will look like:
So with this approach, you will not be required to use Expanded or Flexible widgets as you mentioned and won't need to change much of your code.
The working code is as below:
h = MediaQuery.of(context).size.height / 5;
print(h); // 119.4
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.done,size: 112),
SizedBox(height: h, child:
PageView(
physics: NeverScrollableScrollPhysics(),
controller: pageController,
children: <Widget>[
// Many widgets go here, I am just simplifying with a Column.
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text('TEXT TEXT TEXT TEXT TEXT TEXT'),
SizedBox(height: 16),
Text('TEXT2 TEXT2 TEXT2 TEXT2 TEXT2 TEXT2'),
],
),
],
),
),
],
),
);
You might need to adjust height per your need, but I hope this resolves your issue and answers your question.
You can simply wrap your widget with Flexible widget https://api.flutter.dev/flutter/widgets/Flexible-class.html

How to create a horizontally scrolling table with fixed column in Flutter?

I would like to create a series of tables that you can scroll through vertically, each of which may have a different number of rows/columns from each other.
Within each table, I would like to have the leftmost column frozen in place, and the remaining columns in that table to be horizontally scrollable, in case there are a number of columns that do not fit in the width of the screen. See screenshot:
My initial plan was to use a ListView for the page-level vertical scrolling between tables, and within each table, there is a Row of Columns, where the first column is a static width, and the remaining columns are enclosed within a horizontally scrolling ListView. The error I'm getting from Flutter is not helping me determine what I need to do, but it clearly has to do with having to set bounds on child Widgets.
Error: (Fixed 7/9/19 by wrapping horizontal ListView with a fixed height container and shrinkwrapping the ListView)
The following assertion was thrown during performResize():
Horizontal viewport was given unbounded width.
Viewports expand in the scrolling direction to fill their container.In this case, a horizontal
viewport was given an unlimited amount of horizontal space in which to expand. This situation
typically happens when a scrollable widget is nested inside another scrollable widget.
If this widget is always nested in a scrollable widget there is no need to use a viewport because
there will always be enough horizontal space for the children. In this case, consider using a Row
instead. Otherwise, consider using the "shrinkWrap" property (or a ShrinkWrappingViewport) to size
the width of the viewport to the sum of the widths of its children.
New Error 7/9/19:
The following message was thrown during layout:
A RenderFlex overflowed by 74 pixels on the right.
The overflowing RenderFlex has an orientation of Axis.horizontal.
The edge of the RenderFlex that is overflowing has been marked in the rendering with a yellow and
black striped pattern. This is usually caused by the contents being too big for the RenderFlex.
Consider applying a flex factor (e.g. using an Expanded widget) to force the children of the
RenderFlex to fit within the available space instead of being sized to their natural size.
This is considered an error condition because it indicates that there is content that cannot be
seen. If the content is legitimately bigger than the available space, consider clipping it with a
ClipRect widget before putting it in the flex, or using a scrollable container rather than a Flex,
like a ListView.
The specific RenderFlex in question is:
RenderFlex#9bf67 relayoutBoundary=up5 OVERFLOWING
creator: Row ← RepaintBoundary-[<0>] ← IndexedSemantics ←
NotificationListener ← KeepAlive ← AutomaticKeepAlive ← SliverList ←
SliverPadding ← Viewport ← IgnorePointer-[GlobalKey#74513] ← Semantics ← Listener ← ⋯
parentData: (can use size)
constraints: BoxConstraints(w=404.0, 0.0<=h<=Infinity)
size: Size(404.0, 300.0)
direction: horizontal
mainAxisAlignment: start
mainAxisSize: max
crossAxisAlignment: center
textDirection: ltr
This was the issue I ran into originally before getting side-tracked with the first issue reported; I can't understand why my ListView is not creating a scrollable container.
Code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: Scaffold(
appBar: AppBar(
title: Text('My App'),
backgroundColor: Colors.teal[400],
),
body: MyClass(),
),
);
}
}
const double headerCellWidth = 108.0;
const double cellPadding = 8.0;
const double focusedColumnWidth = 185.0;
const double rowHeight = 36.0;
class MyClass extends StatefulWidget {
#override
_MyClassState createState() => _MyClassState();
}
class _MyClassState extends State<MyClass> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return ListView(
padding: EdgeInsets.all(5.0),
children: <Widget>[
Row(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
color: Colors.grey,
padding: EdgeInsets.all(cellPadding),
width: headerCellWidth,
),
HeaderCell('ABC'),
HeaderCell('123'),
HeaderCell('XYZ'),
],
),
Container(
height: 300.0, // Could compute height with fixed rows and known number of rows in advance
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
children: <Widget>[
Column(
children: <Widget>[
Container(
color: Colors.grey[300],
padding: EdgeInsets.all(cellPadding),
height: rowHeight,
width: focusedColumnWidth,
),
NumberCell('89'),
NumberCell('92'),
NumberCell('91'),
NumberCell('90'),
NumberCell('91'),
NumberCell('89'),
],
),
Column(
children: <Widget>[
Container(
color: Colors.grey[300],
padding: EdgeInsets.all(cellPadding),
height: rowHeight,
width: focusedColumnWidth,
),
NumberCell('89'),
NumberCell('92'),
NumberCell('91'),
NumberCell('90'),
NumberCell('91'),
NumberCell('89'),
],
),
],
),
),
],
),
],
);
}
}
class HeaderCell extends StatelessWidget {
HeaderCell(this.text);
final String text;
#override
Widget build(BuildContext context) {
return Container(
height: rowHeight,
padding: EdgeInsets.all(cellPadding),
width: headerCellWidth,
child: Text(
text,
textAlign: TextAlign.left,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
);
}
}
class NumberCell extends StatelessWidget {
NumberCell(this.text);
final String text;
#override
Widget build(BuildContext context) {
return Container(
height: rowHeight,
width: focusedColumnWidth,
padding: EdgeInsets.all(cellPadding),
child: Text(
text,
),
);
}
}
Here is a quick example and this would be the result: Video
List<Widget> _buildCells(int count) {
return List.generate(
count,
(index) => Container(
alignment: Alignment.center,
width: 120.0,
height: 60.0,
color: Colors.white,
margin: EdgeInsets.all(4.0),
child: Text("${index + 1}", style: Theme.of(context).textTheme.title),
),
);
}
List<Widget> _buildRows(int count) {
return List.generate(
count,
(index) => Row(
children: _buildCells(10),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SingleChildScrollView(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _buildCells(20),
),
Flexible(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _buildRows(20),
),
),
)
],
),
),
);
}
So I tried to produce a minimum working bit of code, and ended up with a workable solution (even if all the details aren't ironed out, like the first locked column being of flexible width instead of a fixed width as desired). Hopefully this will help others trying to produce something similar. What's interesting is that the Table construct is needed here, because replacing the TableRow (wrapped by Table) with just a Row causes an overflow error. I would still be interested in understanding why that is since it seems crucial to the layout engine.
#override
Widget build(BuildContext context) {
return ListView(
padding: EdgeInsets.all(5.0),
children: <Widget>[
Table(
children: <TableRow>[
TableRow(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// first locked column items
],
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
// table header items
],
),
Row(
children: <Widget>[
// data cells
],
),
Row(
children: <Widget>[
// data cells
],
),
],
),
),
],
),
],
),
],
);
}
If there is not much customization needed, for those needed a fixed header and first column table may also consider to use the horizontal_data_table package:
https://pub.dev/packages/horizontal_data_table
It basically is using the two list view approach.