I have the code below that I'm using to generate a list view
class PostListsWidget extends StatefulWidget {
const PostListsWidget({Key? key}) : super(key: key);
#override
_PostListsWidget createState() => _PostListsWidget();
}
class _PostListsWidget extends State<PostListsWidget> {
static const List<PostListModel> _latestPosts = [
PostListModel(post: 'this is a test as.', votes: 453, like: 1, iconID: 0xe908),
PostListModel(post: 'this is a test as', votes: 2, like: 2, iconID: 0xe904),
PostListModel(post: 'this is a test a', votes: 324, like: 1, iconID: 0xe908),
PostListModel(post: 'this is a test s', votes: 435, like: 3, iconID: 0xe90d),
];
Widget _buildListItem(BuildContext context, PostListModel postList){
return Container(
margin: const EdgeInsets.all(20),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8)
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: Container(
padding: EdgeInsets.only(bottom: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget> [
PentEmotionWidget(iconid: postList.iconID),
Expanded(child: Text(
postList.post,
overflow: TextOverflow.clip,
) )
],
) ,
)
),
Flexible(
child: Container(
child: Row(
children: <Widget> [
Expanded(child:
PentLikesCountWidget(votes: postList.votes),
),
PentLikesActionWidget(like: postList.like)
],
),
)
)
],
),
);
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: _latestPosts.length,
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (context, index) =>
_buildListItem(context, _latestPosts[index]),
);
}
}
The code works but the last tile is cut
I've tried playing around with Expanded and Flexible but that doesn't make a difference. Seems like the problem is the List cannot scroll therefore it cuts the content overflow. Can anybody help?
Have to add random text here because SO won't let me post without adding more 'details'
If your layout is correct, then you only need to wrap you ListView.builder inside a Container/SizedBox and give it to some height
SizedBox(
height: 200,
child: ListView.builder(
...
)
)
If that's not the case, maybe try not using expanded or flexible.
Related
I'm very new to Flutter and frontend design in general. I've tried looking for the answer online and have tried some of the suggestions on other posts, but they don't match my situation exactly and I keep getting confused. If anyone could offer some guidance I would really appreciate it!
I'm trying to make a custom table widget composed of a title, ListView, and a row of IconButtons. I'm having trouble wrapping my head around how to limit to and fit to containers. I keep getting a error stating RenderFlex children have non-zero flex but incoming height constraints are unbounded. I know it has something to do with the boundaries and I need to use either Flexible or Expanded to fix it, but I've been at it for a while and am not getting anywhere.
#override
Widget build(BuildContext context) {
return Focus(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).colorScheme.tertiary)),
padding: const EdgeInsets.fromLTRB(0, 0, 0, 2),
margin: const EdgeInsets.fromLTRB(13, 2, 13, 2),
clipBehavior: Clip.antiAlias,
child: Column(children: [
Text(
widget.title,
style: Theme.of(context).textTheme.bodyLarge,
),
ListView(
shrinkWrap: true,
children: widget.children,
),
Flexible(
child: Row(
children: [
//PLUS BUTTON
Expanded(
child: IconButton(
onPressed: () {
setState(() {
// updating the state
widget.children.add(ReportInputTableRow(
rowIndex: widget.children.isNotEmpty
? widget.children.length - 1
: 0,
onFocus: (row, column) {
updateCurrent(row, column);
},
));
});
},
icon: Icon(
Icons.plus_one_sharp,
color: Theme.of(context).colorScheme.secondary,
),
splashRadius: 15.0,
),
)
//PLUS BUTTON
],
))
]),
));
}
EDIT:
As requested, here is the code for ReportTableInputRow
class _ReportInputTableRowState extends State<ReportInputTableRow> {
#override
Widget build(BuildContext context) {
return Focus(
child: Row(
children: [
Focus(
child: const Expanded(
child: TextInputField(
text: "Original",
size: 13,
padded: false,
),
),
onFocusChange: (hasFocus) {
if (hasFocus) widget.columnIndex = 0;
}),
Focus(
child: const Expanded(
child: TextInputField(
text: "Note",
size: 13,
padded: false,
)),
onFocusChange: (hasFocus) {
if (hasFocus) widget.columnIndex = 1;
}),
],
),
onFocusChange: (hasFocus) {
widget.onFocus != null
? widget.onFocus!(widget.rowIndex, widget.columnIndex)
: null;
},
);
}
}
EDIT:
The solution was to swap Expanded with Focus in ReportInputTableRowState.
Maybe this one can help you:
Put the ListView inside Expanded and remove the Flexible which wraps the Row beneath the ListView.
child: Column(children: [
Text(
widget.title,
style: Theme.of(context).textTheme.bodyLarge,
),
ListView(
shrinkWrap: true,
children: children,
),
Flexible(
child: Row(
children: [
//PLUS BUTTON
Expanded(
child: IconButton(
onPressed: () {
...
You should also avoid changing widget variables, because they are considered to be immutable. If you want to mutate variables, put them inside the state.
Instead of widget.children.add(...) you should call children.add(...) with children being a state variable.
Actually you may not need Expanded or Flex, since you icons should have a defined size, you can wrap it using a SizedBox with defined height.
SizedBox(
height: 32, // It can be the height you desire, no need to define width
child: Row(
children: [
... // The icons that are children of the row go here
],
),
),
How can I print this list of widgets using the printing package?
I get an Unhandled Exception: type List<Widget> is not a subtype of type List<Widget> in type cast
The error I keep getting is that the packages do not match. I even tried casting the getBarcodes() as List<pw.Widget> to see if it would work.
In the end, what I want is a sheet of barcodes that I can print out. I think the problem is the SinglechildScrollview but I'm not certain of that.
class _BarcodeScreenState extends State<BarcodeScreen> {
final doc = pw.Document();
List<Widget> getBarcodes() {
List<Widget> barcodeList = List.generate(
3,
(index) => SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(
3,
(index) => Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black)),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: BarcodeWidget(
data:
'${DateFormat('MM d y').format(DateTime.now())} ${nanoid(5)}',
barcode: Barcode.code128(),
),
),
),
],
),
),
),
)));
return barcodeList;
}
Above is my list of widgets and below is the code I use to try and print it.
#override
Widget build(BuildContext context) {
return SteriCodeBoiler(
appBarTitle: "Barcodes",
print: IconButton(
icon: Icon(Icons.print),
onPressed:() async{
doc.addPage(pw.Page(
pageFormat: PdfPageFormat.a4,
build: (pw.Context context) {
return pw.Container(
child: pw.ListView(
children: getBarcodes()
)
);
}));
await Printing.layoutPdf(
onLayout: (PdfPageFormat format) async => doc.save());
},
),
screenContent: Container(
child: ListView(
children: getBarcodes(),
)),
);
}
}
I'm using the PieChart of fl_chart to display the distribution of locally saved documents. The percentages displayed in the chart are the result of the length of the two document type lists (See image below).
But when one List is empty I have a weird bug were my custom Legend gets pushed downwards. The PieChart and the Legend are positioned inside of a Row with flex factors on each children (2 for the PieChart and 4 for the Legend).
I really don't understand what pushes the Legend downwards because my Expanded widgets are always positioned inside of Rows so that the PieChart and Legend only take up the available, horizontal space and not the vertical space which happens in the bug (image 2).
PieChart widget:
class PersonalFilesCircularGraph extends StatefulWidget {
const PersonalFilesCircularGraph();
#override
_PersonalFilesCircularGraphState createState() =>
_PersonalFilesCircularGraphState();
}
class _PersonalFilesCircularGraphState
extends State<PersonalFilesCircularGraph> {
late List<FileTypeData> data;
List<PieChartSectionData> getSections() => data
.asMap()
.map<int, PieChartSectionData>((index, data) {
final value = PieChartSectionData(
color: data.color,
value: data.percent,
showTitle: false,
radius: 3,
);
return MapEntry(index, value);
})
.values
.toList();
#override
void initState() {
/* Example getFileTypeData result
[
FileTypeData(
"Patient Questionnaire",
patientQuestionnaires.length /
(patientQuestionnaires.length +
receivedPatientQuestionnaires.length) *
100,
const Color(0xFF3861FB),
),
FileTypeData(
"Received Patient Questionnaire",
receivedPatientQuestionnaires.length /
(receivedPatientQuestionnaires.length +
patientQuestionnaires.length) *
100,
Colors.teal.shade400,
),
];
*/
data = context.read<SessionBloc>().state.getFileTypeData;
super.initState();
}
#override
Widget build(BuildContext context) {
return BlocConsumer<SessionBloc, SessionState>(
listenWhen: (previous, current) {
final bool listenWhen = previous.patientQuestionnaires.length !=
current.patientQuestionnaires.length ||
previous.receivedPatientQuestionnaires.length !=
current.receivedPatientQuestionnaires.length;
return !listenWhen;
},
listener: (context, state) {
data = context.read<SessionBloc>().state.getFileTypeData;
},
builder: (context, state) {
return Row(
children: [
Expanded(
flex: 2,
child: Container(
constraints: const BoxConstraints(
maxWidth: 60,
maxHeight: 60,
),
child: PieChart(
PieChartData(
sections: getSections(),
),
),
),
),
const SizedBox(
width: kMediumPadding,
),
Expanded(
flex: 4,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: data
.map(
(data) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: buildLegend(
percent: data.percent,
text: data.fileName == "Patient Questionnaire"
? L.of(context).patientQuestionnaires
: L.of(context).receivedPatientQuestionnaire,
color: data.color,
),
),
)
.toList(),
),
),
],
);
},
);
}
Widget buildLegend({
required double percent,
required String text,
required Color color,
}) =>
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Row(
children: [
Container(
width: 10,
height: 10,
color: color,
),
const SizedBox(
width: kSmallPadding,
),
Expanded(
child: Text(
text,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
Text(
"${percent.toStringAsFixed(0)}%",
overflow: TextOverflow.ellipsis,
)
],
);
}
I display the chart widget inside a CustomScrollView, wrapped with a SliverToBoxAdapter inside of my home screen:
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
SliverAppBar(
elevation: 0.0,
floating: true,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
title: Text(
"Home",
style: Theme.of(context).textTheme.headline5,
),
centerTitle: true,
),
const SliverPadding(
padding: EdgeInsets.symmetric(
vertical: kSmallPadding,
horizontal: kMediumPadding,
),
sliver: SliverToBoxAdapter(
child: PersonalFilesCircularGraph(),
),
)
],
);
}
}
EDIT:
I just did some more investigation on this bug and placed a colored Container in my CustomScrollView, below the SliverPadding of the CircularGraph to check if the Column of labels expands downwards. But as you can see the colored Container is not effected. It just looks like the Legend is inside a Stack and positioned without effecting other widgets above and below.
const SliverPadding(
padding: EdgeInsets.symmetric(
vertical: kSmallPadding,
horizontal: kMediumPadding,
),
sliver: SliverToBoxAdapter(
child: PersonalFilesCircularGraph(),
),
),
SliverToBoxAdapter(
child: Container(
width: double.infinity,
height: 60,
color: Colors.green,
),
)
I have a parent widget that draws multiple child widgets using a listview. There is a checkbox within each of these child widgets. I am trying to implement a "select all" button in the parent widget which checks all of the children's checkboxes, but I'm having a hard time figuring out how to accomplish this.
Here is my parent widget:
class OrderDisplay extends StatefulWidget {
static const routeName = '/orderDisplay';
//final Order order;
//const OrderDisplay(this.order);
#override
OrderDisplayState createState() {
return OrderDisplayState();
}
}
class OrderDisplayState extends State<OrderDisplay> {
bool preChecked = false;
double total = 0;
List<OrderedItem> itemsToPayFor = [];
#override
Widget build(BuildContext context) {
final OrderDisplayArguments args =
ModalRoute.of(context).settings.arguments;
return Scaffold(
backgroundColor: MyColors.backgroundColor,
body: SafeArea(
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
physics: ScrollPhysics(),
child: Container(
padding: EdgeInsets.only(top: 10),
child: Column(
children: [
Text(args.order.restaurantName,
style: MyTextStyles.headingStyle),
ListView.separated(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: args.order.orderedItems.length,
itemBuilder: (context, index) {
return FoodOrderNode(
preChecked, args.order.orderedItems[index],
onCheckedChanged: (isChecked) {
isChecked
? setState(() {
itemsToPayFor.add(
args.order.orderedItems[index]);
})
: setState(() {
itemsToPayFor.remove(
args.order.orderedItems[index]);
});
});
},
separatorBuilder: (context, index) =>
MyDividers.MyDivider)
],
)),
),
),
MyDividers.MyDivider,
Container(
height: 140,
color: MyColors.backgroundColor,
child: Row(children: [
Expanded(
flex: 5,
child: Column(
children: [
Expanded(flex: 2, child: SizedBox()),
Expanded(
flex: 6,
child: SelectAllButton(() {
print("SELECT ALL");
setState(() {
preChecked = true;
});
})),
Expanded(flex: 2, child: SizedBox())
],
)),
Expanded(
flex: 5,
child: Column(
children: [
Expanded(flex: 1, child: SizedBox()),
Expanded(
flex: 8,
child: PayNowButton(() {
print("PAY NOW");
},
double.parse(itemsToPayFor
.fold(0, (t, e) => t + e.itemPrice)
.toStringAsFixed(
2)))),
Expanded(flex: 1, child: SizedBox())
],
))
]))
],
)));
}
}
And here is FoodOrderNode:
typedef void SelectedCallback(bool isChecked);
class FoodOrderNode extends StatefulWidget {
final bool preChecked;
final OrderedItem item;
final SelectedCallback onCheckedChanged;
const FoodOrderNode(this.preChecked, this.item,
{#required this.onCheckedChanged});
#override
FoodOrderNodeState createState() {
return FoodOrderNodeState();
}
}
class FoodOrderNodeState extends State<FoodOrderNode> {
bool isChecked = false;
bool isSplitSelected = false;
#override
Widget build(BuildContext context) {
isChecked = widget.preChecked;
return Container(
height: 80,
padding: EdgeInsets.only(left: 15, right: 15),
decoration: BoxDecoration(
color: MyColors.nodeBackgroundColor,
),
child: Row(
children: [
Expanded(
flex: 1,
child: CircularCheckBox(
value: isChecked,
checkColor: Colors.white,
activeColor: Colors.blue,
autofocus: false,
onChanged: (bool value) {
print("Change to val: $value");
widget.onCheckedChanged(value);
setState(() {
isChecked = value;
});
},
)),
Expanded(
flex: 7,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.only(bottom: 5, left: 40),
child: Text(
widget.item.itemName,
style: TextStyle(fontSize: 18, color: Colors.black),
textAlign: TextAlign.left,
maxLines: 2,
overflow: TextOverflow.ellipsis,
)),
Container(
padding: EdgeInsets.only(left: 40),
child: Text(
"\$${widget.item.itemPrice}",
style:
TextStyle(fontSize: 16, color: MyColors.labelColor),
))
],
),
),
Expanded(
flex: 2,
child: isSplitSelected
? SplitButtonSelected(() {
setState(() {
isSplitSelected = false;
});
})
: SplitButtonUnselected(() {
setState(() {
isSplitSelected = true;
});
}))
],
),
);
}
}
I have tried creating a "preChecked" argument for FoodOrderNode and then using setState from the parent widget, however, that hasn't worked out. I have also tried using keys, but I couldn't figure out how to get those working for this either. Thank you, and let me know if you'd like any more relevant code.
Just put a global checkbox above the list items and give it isAllChecked (bool) on its value so when it will be checked set the state to isAllChecked => true and then in child checkboxes check for condition if isAllChecked is true then mark as true or checked.
GlobalCheckbox(
onChanged(value){
setState(()
{
isAllChecked==value;
});
}
);
ChildCheckBox(
value: isAllChecked ? true : false
)
this might help you:)
I have a page that divides the screen into left (CheckOutPage) and right (MyFoodOrder()):
class TakeOrderPage extends StatefulWidget {
#override
_TakeOrderPageState createState() => _TakeOrderPageState();
}
class _TakeOrderPageState extends State<TakeOrderPage> {
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Expanded(flex: 4, child: CheckOutPage()),
VerticalDivider(),
Expanded(flex: 6, child: MyFoodOrder()),
],
);
}
}
In MyFoodOrder, I have a widget that builds the food items using FoodCard:
Widget buildFoodList() {
return Expanded(
child: GridView.count(
//itemCount: foods.length,
childAspectRatio: 3.0,
mainAxisSpacing: 4,
crossAxisSpacing: 4,
crossAxisCount: 2,
controller: _controller,
physics: BouncingScrollPhysics(),
//children: foods.map((food) {
// return FoodCard(food);
//}).toList(),
children: [for (var food in Level1) if ((food.foodType == MyFoodTypes[value])) FoodCard(food)].toList(),
),
);
}
Inside FoodCard, I have a widget that has an InkWell that can move to another page when tapped for selecting options. At the moment, the new page ChooseOptions() will occupy the whole screen:
Widget buildPriceInfo() {
ConfirmAction action;
return Padding(
padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'\$ ${food.price}',
style: titleStyle,
),
Card(
margin: EdgeInsets.only(right: 0),
shape: roundedRectangle4,
color: mainColor,
child: InkWell(
onTap: IsAvailable() ? () async {
remark = ''; //cancel any selected taste
if (food.options.length != 0) {
if (food.options.containsKey('2')) {
action = await _showTasteDialog(food.index);
}
if (food.options.containsKey('1')) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChooseOptions(food)),
);
}
else
addItemToCard();
}
else
addItemToCard();
} : (){},
splashColor: Colors.white70,
customBorder: roundedRectangle4,
child: Icon(Icons.add, size: 30,),
),
)
],
),
);
}
I want to modify it so that the new page of ChooseOptions only occupies the area of MyFoodOrder() instead of the whole screen. I read that nested navigator is the solution but I couldn't work it out after reading some of the examples online. Grateful if more explicit guidance or help can be provided.
Many thanks!
Wrap your MyFoodOrder with Navigator, set the routes, and assign a Navigation Key to it.
static final navigatorKey = GlobalKey<NavigatorState>();
Then use the Navigation Key to changing the routing.
navigatorKey.currentState.pushNamed("Your route");