I am trying to use spacer inside a column that is wrapped with singlechildscrollview and it gives me an error.
RenderFlex children have non-zero flex but incoming height constraints are unbounded.
here is my widget
class Test extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Form(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
TextFormField(),
TextFormField(),
//Spacer()
Divider(),
Icon(Icons.block)
],
),
));
}
}
what should i use? your help is appreciated
You can try this out, I added an example using your code, it works:
class Test extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(body: LayoutBuilder(
builder: (context, constraint) {
return Form(
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraint.maxHeight),
child: IntrinsicHeight(
child: Column(
children: <Widget>[
TextFormField(),
TextFormField(),
Spacer(),
Divider(),
Icon(Icons.block)
],
),
),
),
),
);
},
));
}
}
Check this Github issue for much details on the solution above: Wrapping a Column with an expanded in a SingleChildScrollView throws an exception
Related
I am trying to create a nested list view each wrapped by a column. The parent widget (widget 1) has a column with a vertical list view and each list view item (widget 2) is a column with a horizontal list view. So far I am able to get it to render with the following code where in widget 2 I wrap the horizontal list view with a Container and a specified height. I am trying to use not use a fixed height, however, so I have tried using Flexible and Expanded instead of Container but both of these result in the unbounded height constraints error.
class Widget1State extends State<Widget1> {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(
child: Scrollbar(
child: ListView.builder(
padding: const EdgeInsets.all(8.0),
itemCount: getWidgets().length,
itemBuilder: (BuildContext context, int index) {
return Widget2();
},
),
),
),
],
),
);
}
}
class Widget2State extends State<Widget2> {
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
height: 30,
child: Scrollbar(
child: ListView.builder(
padding: const EdgeInsets.all(8.0),
scrollDirection: Axis.horizontal,
itemCount: getWidgets2().length,
itemBuilder: (BuildContext context, int index) {
return Text('widget');
},
),
),
),
],
);
}
}
As you can see below this is how it currently works where the exercises is the parent list view and the sets are the child list view. Currently because the sets list is in a Container it takes up space when it's empty and also doesn't size to whatever makes up the list item. I want to change the sets list view so that it only takes up the space is needed by the list item.
Given that Listview takes all avaible height if you dont provide one it will result in failure.
In order to proovide alternative solutions I need you especify how is the design that you want. Coudl you give more details?
---- Edited:
This solution was found here: Flutter: Minimum height on horizontal list view
You can change the widget 2 from Listview to SingleChildScrollView:
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [
const Padding(
padding: EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(label: Text('Workout Name')),
),
),
...List.generate(
exercises, // number of exercises
(index) => Column(
mainAxisSize: MainAxisSize.min,
children: [
DropdownButton<String>(items: const [
DropdownMenuItem(child: Text('Select Exercise'))
], onChanged: (value) {}),
SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Row(
children: List.generate(
sets, //number of sets
(index) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Set $index'),
Text('reps'),
Text('rest')
],
)),
)),
TextButton.icon(
onPressed: () {
addSet();
},
icon: const Icon(Icons.add),
label: Text('Set'))
],
))
],
),
bottomNavigationBar: IconButton(
onPressed: () {
addExercise();
},
icon: const Icon(Icons.add)),
);
}
I've tried this code and works as you need, without setting the height of the 'Sets' scrollview.
I want to point the use of the bottomNavigationBar to the AddExercise button, instead of a Column>Listview structure. Separating the button in the bottomNavBar you can use the body atribute freely.
You want to use Flutter default widgets when posible.
does anyone know how to solve it so that there is no border between two elements in a column in the website? Thanks for any advice
Image
Code Here
class DesktopScreen extends StatelessWidget {
const DesktopScreen({super.key});
#override
Widget build(BuildContext context) {
return Sizer(
builder: (context, orientation, deviceType) {
return Scaffold(
body: Column(
children: [
Container(color: HexColor("#111340"), height: 5.h, child: Row(children: [],),),
Container(color: HexColor("#111340"), height: 95.h,)
],
)
);
}
);
}
}
I'm a newbie, so I apologize for any mistakes in the post
I don't think there's any error in the code you posted, the little space between the elements should not be there.
Here's your code running, there's no space between the containers:
https://zapp.run/edit/flutter-z8206198306?entry=lib/main.dart&file=lib/main.dart
I think the problem could be related to the extension you are using for providing height, or to the Sizer widget.
If you want to handle responsiveness, I suggest to look at this package: https://pub.dev/packages/layout
Use Flexible
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Flexible(
child: Container(
color: Colors.red,
),
),
Flexible(
flex: 4,
child: Container(
color: Colors.green,
),
),
],
),
);}
Totally I need a scrollable DataTable with refresh possibility on pull down between header and footer fixed width widgets.
I have a widget, using on every page in my app. THis widget return a Scaffold with appbar and body like column (). This widget used like CommonPage(title, widgetsArrayForBodyColumn) from other widgets.
For now I need to use a follow widgets in that body:
Lookup (for filtering or for example, any fixed height widget)
DataTable (for show data)
Card (to show another data)
DataTable should be scrollable and refreshable on pull down.
I tried many different ways, but still has no success. My last solution is using stack, but list hidden beside the card.
I can't understand how to achieve my goal: have refreshing datatable between non scrollable widgets in a column.
class CommonPage extends StatefulWidget {
final String title;
final List<Widget> children;
const CommonPage({
Key? key,
required this.title,
required this.children,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
...
body: Column(children: [
FixedHeightWidget(),
PageScreen(),
]);
}
}
class PageScreen {
#override
Widget build(BuildContext context) {
return CommonPage(
title: Title,
children: [
FixedHeightWidget(),
PageList(),
],
);
}
}
class PageList {
Widget build(BuildContext context) {
//TODO Return fixedWidthWidget at top, expandedRefreshableScrollableDataTable, fixedWidthWidget at bottom
}
}
My current solution: (almost achieves my goal, but as mentioned card hides part of list and no ways to scroll and check bottom of the list):
return Expanded(
child: Stack(
children: [
RefreshIndicator(
onRefresh: onRefresh,
child: ListView(
children: [
listItems.isNotEmpty
? DataTable(...)
: Container(),
],
),
),
Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
alignment: Alignment.bottomCenter,
child: Column(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
child: Card(...),
),
),
),
],
),
],
),
);
I found the solution:
return Expanded(
child: Column(
children: [
Expanded(
child: RefreshIndicator(
onRefresh: onRefresh,
child: ListView(
children: [listItems.isNotEmpty
? DataTable(...)
: Container(),
],
),
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
child: Card(...),
),
],
),
);
I have a fairly simple flutter app. It has a chat feature.
However, I have a problem with the chat feature.
It's made up of a widget does Scaffold and in it SingleChildScrollView - which has a message-list (container) and input-area (container). Code is attached.
Problem is: if I click on the input box, the keyboard opens and it pushes the message-list.
Pushing the message-list is an acceptable thing if you are already at the bottom of the message-list.
However, if the user scrolled up and saw some old messages, I don't want the message-list widget to be pushed up.
Also, I don't want the message-list to be pushed up if I have only a handful of messages (because that just makes the messages disappear when keyboard opens, and then I need to go and scroll to the messages that have been pushed [user is left with 0 visible messages until they scroll]).
I tried different approaches - like
resizeToAvoidBottomInset: false
But nothing seems to work for me, and this seems like it should be a straightforward behavior (for example, whatsapp act like the desired behavior).
My only option I fear is to listen to keyboard opening event, but I was hoping for a more elegant solution.
Here's my code:
#override
Widget build(BuildContext context) {
height = MediaQuery.of(context).size.height;
width = MediaQuery.of(context).size.width;
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
SizedBox(height: height * 0.1),
buildMessageList(), // container
buildInputArea(context), // container
],
),
),
);
Widget buildInputArea(BuildContext context) {
return Container(
height: height * 0.1,
width: width,
child: Row(
children: <Widget>[
buildChatInput(),
buildSendButton(context),
],
),
);
}
Widget buildMessageList() {
return Container(
height: height * 0.8,
width: width,
child: ListView.builder(
controller: scrollController,
itemCount: messages.length,
itemBuilder: (BuildContext context, int index) {
return buildSingleMessage(index);
},
),
);
}
This seems to work for me:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ScrollController _controller = ScrollController();
#override
Widget build(BuildContext context) {
SystemChrome.setEnabledSystemUIOverlays([]);
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
buildMessageList(),
buildInputArea(context),
],
),
),
);
}
Widget buildInputArea(BuildContext context) {
return Row(
children: <Widget>[
Expanded(
child: TextField(),
),
RaisedButton(
onPressed: null,
child: Icon(Icons.send),
),
],
);
}
Widget buildMessageList() {
return Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: 50,
controller: _controller,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: EdgeInsets.all(10),
child: Container(
color: Colors.red,
height: 20,
child: Text(index.toString()),
),
);
},
),
);
}
}
I think the problem is that you are using fixed sizes for all widgets. In this case it is better to use Expanded for the ListView and removing the SingleChildScrollView. That way the whole Column won't scroll, but only the ListView.
Try to use Stack:
#override
Widget build(BuildContext context) {
height = MediaQuery.of(context).size.height;
width = MediaQuery.of(context).size.width;
return Scaffold(
body: Stack(
children: <Widget>[
SingleChildScrollView(
child: Column(
children: <Widget>[
SizedBox(height: height * 0.1),
buildMessageList(),
],
),
),
Positioned(
bottom: 0.0,
child: buildInputArea(context),
),
],
),
);
}
Setting resizeToAvoidBottomInset property to false in your Scaffold should work.
You can use NotificationListener to listen to scroll notifications to detect that user is at the bottom of the message-list. If you are at the bottom you can then set resizeToAvoidBottomInset to true.
Something like this should work
final resizeToAvoidBottomInset = true;
_onScrollNotification (BuildContext context, ScrollNotification scrollNotification) {
if (scrollNotification is ScrollUpdateNotification) {
// detect scroll position here
// and set resizeToAvoidBottomInset to false if needed
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: this.resizeToAvoidBottomInset,
body: NotificationListener<ScrollNotification>(
onNotification: (scrollNotification) {
return _onScrollNotification(context, scrollNotification);
},
child: SingleChildScrollView(
child: Column(
children: <Widget>[
buildMessageList(), // container
buildInputArea(context), // container
],
),
),
),
);
}
this is technically already answered, and the answer is almost correct. However, I have found a better solution to this. Previously the author mentions that he wants to have a similar experience to WhatsApp. By using the previous solution, the listview would not be able to scrolldown to maxExtent when the sent button is pressed. To fix this I implemented Flex instead of Expanded, and use a singlechildscrollview for the input area
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ScrollController _controller = ScrollController();
TextEditingController _textcontroller=TextEditingController();
List<String> messages=[];
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Flexible(
child: ListView.builder(
shrinkWrap: true,
itemCount: messages.length,
controller: _controller,
itemBuilder: (BuildContext context, int index) {
print("From listviewbuilder: ${messages[index]}");
return Padding(
padding: EdgeInsets.all(10),
child: Container(
color: Colors.red,
height: 20,
child: Text(messages[index])
),
);
},
),
),
SingleChildScrollView(
child: Row(
children: <Widget>[
Expanded(
child: TextField(controller: _textcontroller),
),
RaisedButton(
onPressed: (){
Timer(Duration(milliseconds: 100), () {
_controller.animateTo(
_controller.position.maxScrollExtent,
//scroll the listview to the very bottom everytime the user inputs a message
curve: Curves.easeOut,
duration: const Duration(milliseconds: 300),
);
});
setState(() {
messages.add(_textcontroller.text);
});
print(messages);
},
child: Icon(Icons.send),
),
],
),
),
],
),
),
);
}
}
It's better to use flex because expanded as the documentation says, expands over available space, whereas flex would resize to the appropriate proportion. This way if you are going for the "WhatsApp experience" in which the listview scrolls down once you sent a message. The listview would resize when the keyboard pops up and you will get to the bottom, instead of it not going fully to the bottom.
I want to add onTap to each category in my horizontal list view so how can I do it?
class HorizontalList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 80.0,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Category(
image_location: "catogories/name.png",
),
],
),
);
}}
You can wrap your Category widget with GestureDetector widget.
class HorizontalList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 80.0,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
GestureDetector(
onTap: () {
//This will be called on tap
},
child:Category(
image_location: "catogories/name.png",
),
),
],
),
);
}}
If you are using small sized images, you can use IconButton().
class HorizontalList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 80.0,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
IconButton(icon: Image.asset(<name_here(or any other optoin such as Image.network()>)), onPressed: <function_here:fox example clickEvent()>)
],
),
);
}
}
You can also use
icon: Icon(icon: Icons.<icon_name>, color: Colors.<colors_name>)
, if you're using Icons.
And if you're trying to do something else, comment below !