I'm getting a renderflex overflow error which displays as the following:
It's coming from the following code:
return Column(
children: [
DropdownButton<String>(
items: Utils.ddsDropdown
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (value) {}),
ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.fromLTRB(0, 16, 0, 16),
itemCount: card.length,
itemBuilder: (context, index) {
return MyCard.buildCard(card[index], context);
},
)
],
)
I also tried to wrap the column in an Expanded widget and in a SingleChildScrollView but still got the same error. I think the column is preventing the page from being fully scrollable. But I need the column to be able to have the DropdownButton and ListView.
I even tried wrapping the entire widget in a SingleChildScrollView as follows:
Widget build(BuildContext context) => SingleChildScrollView(
child: FutureBuilder<List<Map<String, dynamic>>>(
future: MyCard.getData(widget.categoryIndex, widget.subCategories)!
.whenComplete(() => setState(() {
isLoading = false;
})),
builder: ((context, snapshot) {
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
return FutureBuilder<List<MyCard>>(
future: MyCard.readData(snapshot.data),
builder: (context, cards) {
if (cards.hasData) {
final card = cards.data!;
return Column(
children: [
//Text("Hello"),
DropdownButton<String>(
items: Utils.ddsDropdown
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (value) {}),
ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.fromLTRB(0, 16, 0, 16),
itemCount: card.length,
itemBuilder: (context, index) {
return MyCard.buildCard(card[index], context);
},
)
],
);
} else {
return const Text("No data");
}
});
} else {
return isLoading
? Column(
children: const [CircularProgressIndicator()],
)
: const Text("You do not have any workouts yet");
}
}),
));
but it still overflows.
Wrap only the ListView in Expanded as follows:
Column(
children: [
DropdownButton<String>(
...
Expanded(
child: ListView.builder(
...
This other answer will provide you with further details about your question.
Use SingleChildScrollView to use a scrollable area of that column as below:
return SingleChildScrollView(
child: Column(
children: [
DropdownButton<String>(
items: Utils.ddsDropdown
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (value) {}),
ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.fromLTRB(0, 16, 0, 16),
itemCount: card.length,
itemBuilder: (context, index) {
return MyCard.buildCard(card[index], context);
},
)
],
)
I hope this solved your problem.
Related
I want to implement suggestions inside textFormField. same as below
So, I've searched regarding this but with no success. Everywhere I've got is suggestions inside list. Which is easy to do. If you have any suggestions then please add your valuable answer and comment.
Here is my code
Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: controller,
onFieldSubmitted: (value) {},
onChanged: (value) {
displaySuggestionInList(value);
},
),
const SizedBox(height: 30),
ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 100,
maxWidth: 200,
minWidth: 200,
minHeight: 100,
),
child: ListView.builder(
shrinkWrap: true,
itemCount: dashboardLayouts!.length,
itemBuilder: (context, index) {
return Text((dashboardLayouts![index]['dashBoardData']
as DashboardInfo)
.commonName
.toString());
},
),
)
],
),
What you need to create is a Type-Ahead Widget. To do that, you will firstly create the normal List suggestion StatefulWidget. While on the filter function you should update the hint with the first value from the suggestion list. This way you can call the hint value and place it anywhere on your screen. Unfortunately for us, flutter doesn't allow the update showing of hint within the input field while typing.
Although I made an example for you to get the idea.
class AutocompleteExample extends StatefulWidget {
const AutocompleteExample({super.key});
#override
State<AutocompleteExample> createState() => _AutocompleteExampleState();
}
class _AutocompleteExampleState extends State<AutocompleteExample> {
TextEditingController controller = TextEditingController();
List suggestionList = [];
String hint = "";
List<String> nameList = <String>[
'aardvark',
'bobcat',
'chameleon',
'Nathaniel Bond',
'Taylor Story',
'Lamont Padilla',
'Jamia Sun',
'Nikki Reichert',
'Tea Holguin',
'Rafael Meade',
'Mercedez Goad',
'Aileen Foltz',
'Bryant Burt',
];
void typeAheadFilter(String value) {
suggestionList.clear();
if (value.isEmpty) {
setState(() {});
return;
}
for (String name in nameList) {
if (name.toLowerCase().contains(value)) {
suggestionList.add(name);
}
}
if (suggestionList.isNotEmpty) {
var firstSuggestion = suggestionList[0];
setState(() => hint = firstSuggestion);
}
setState(() {});
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: controller,
onFieldSubmitted: (value) {},
onChanged: (value) => typeAheadFilter(value),
decoration: InputDecoration(
hintText: hint,
labelText: hint.isEmpty ? "Search" : hint,
alignLabelWithHint: true,
hintTextDirection: TextDirection.rtl),
),
const SizedBox(height: 10),
if (suggestionList.isNotEmpty || controller.text.isNotEmpty) ...[
Expanded(
child: ListView.separated(
padding: const EdgeInsets.all(10),
shrinkWrap: true,
itemCount: suggestionList.length,
separatorBuilder: (context, index) => const Divider(),
itemBuilder: (context, index) {
return Text((suggestionList[index]));
},
),
)
] else ...[
Expanded(
child: ListView.separated(
padding: const EdgeInsets.all(10),
shrinkWrap: true,
itemCount: nameList.length,
separatorBuilder: (context, index) => const Divider(),
itemBuilder: (context, index) {
return Text((nameList[index]));
},
),
)
]
],
);
}
}
I'm creating a chat feature in flutter but noticed this behavior on IOS that doesnt shrink the list so you can see the last sent message. How can I have the listview builder shrink to show the last message when the keyboard appears?
Note: This issue doesn't happen on Android
Scaffold(
resizeToAvoidBottomInset: true,
body: Stack(
children: <Widget>[
StreamBuilder(
stream: _chats,
builder: (context, snapshot) {
if (!snapshot.hasData) return Container();
return snapshot.hasData
? GestureDetector(
onPanDown: (_) {
FocusScope.of(context).requestFocus(FocusNode());
},
child: ListView.builder(
shrinkWrap: true,
controller: _scrollController,
padding: EdgeInsets.only(top: 10, bottom: 100),
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
return MessageWidget(
tripId: widget.docId,
uid: snapshot.data.docs[index].data()["uid"],
messageId: snapshot.data.docs[index].id,
message: snapshot.data.docs[index].data()["message"],
sender: snapshot.data.docs[index].data()["senderName"],
sentByMe: widget.uid ==
snapshot.data.docs[index].data()["uid"],
mediaFileUrl:
snapshot.data.docs[index].data()["mediaFileUrl"],
);
}),
)
: Container();
},
);
]
)
)
I think you can try the 'reverse' property from the ListView.builder.
Tell me if this example didn't fit your needs, can you share us your code ? (I didn't see why you use a Stack and what could be the issue around that).
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Expanded(
child: Stack(
children: <Widget>[
StreamBuilder<dynamic>(
builder: (context, dynamic snapshot) {
return GestureDetector(
onPanDown: (_) {
FocusScope.of(context).unfocus();
},
child: ListView.builder(
reverse: true,
shrinkWrap: true,
itemCount: 100,
padding: const EdgeInsets.only(top: 10, bottom: 10),
itemBuilder: (context, index) {
return ListTile(title: Text(index.toString()));
},
),
);
},
),
],
),
),
Container(
padding: const EdgeInsets.all(8),
color: Colors.black12,
child: const TextField(),
),
],
),
);
}
}
I'm trying to display items based on the selected category but I'm not finding the right way to do that.
I suppose the id of the category need to match the categoryId of the item but I'm not getting there.
Here the code for the backend_api:
Future<List<Item>> fetchItems(
{int? categoryId,
String? zipcode,
String? searchText,
String? radius}) async {
var path =
categoryId != null ? "/item/list/category/$categoryId" : "/item/list";
path += zipcode != null ? "/zipcode/$zipcode" : "";
path += "?";
if (searchText != null) {
path += "&search=$searchText";
}
if (radius != null) {
path += "&radiusInKm=$radius";
}
final http.Response response = await _httpClient.get(path);
return jsonDecode(utf8.decode(response.bodyBytes))
.map<Item>((json) => Item.fromJson(json))
.toList();
}
Here the code for displaying the items:
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
physics: const ClampingScrollPhysics(),
child: Row(
children: [
BlocBuilder<ItemCategoriesBloc, ItemCategoriesState>(
builder: ((context, state) {
if (state is ItemCategoriesLoadedState) {
List<MapEntry<int, Category>> categoryList =
List.from(state.categories.entries);
return Container(
width: 800,
child: FormBuilderChoiceChip(
decoration: const InputDecoration(border: InputBorder.none),
selectedColor: MyTheme.primary,
alignment: WrapAlignment.spaceEvenly,
direction: Axis.horizontal,
initialValue: categoryList.map((value) => value).toList(),
name: 'filter_category',
options: categoryList
.map(
(category) => FormBuilderFieldOption(
value: category.value.id,
child: Text(category.value.name),
),
)
.toList(),
//onChanged: showFilteredItems(),
),
);
}
return Container();
}),
),
],
),
),
Expanded(
child: RefreshIndicator(
onRefresh: onRefresh ?? () async {},
child: GridView.builder(
shrinkWrap: true,
physics: const AlwaysScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisSpacing: crossAxisSpacing,
childAspectRatio: aspectRatio,
crossAxisCount: crossAxisCount,
),
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return _ItemListView(
onTap: onTap,
item: items[index],
// Todo: add ngos
);
},
),
),
),
],
);
Thank you in advance for your help!
I have an ExpansionPanelList inside a ListView and I'm unable to get it to scroll when trying in the middle of the screen, though it will work on the edges. I've attached the code below. I'm guessing its the ExpansionPanelList that isn't letting me scroll but I'm not sure why because the items are inside a ListView as well.
I've taken a video of the issue https://drive.google.com/file/d/19zM7cfjshcN8OfEbRhvxIgmUp8sq1TP6/view?usp=sharing
_bodyBuilder() {
return
ListView(
shrinkWrap: true,
padding: EdgeInsets.all(15.0),
children: <Widget>[
ExpansionPanelList.radio(
initialOpenPanelValue: 2,
children: dateList.map<ExpansionPanelRadio>((DateTime date) {
return ExpansionPanelRadio(
canTapOnHeader: true,
value: date.day,
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text(DateFormat('dd/MM/yyyy').format(date)),
);
},
body: Container(
//child: ListView.separated(
child: ListView.builder(
shrinkWrap: true,
itemCount: cleanRecords
.where((Log) =>
DateTime.parse(DateFormat('yyyy-MM-dd')
.format(Log.time.toDateTime()))
.toString() ==
date.toString())
.toList()
.length,
itemBuilder: (context, index) {
final records = cleanRecords
.where((Log) =>
DateTime.parse(DateFormat('yyyy-MM-dd')
.format(Log.time.toDateTime()))
.toString() ==
date.toString())
.toList();
final schedule = records[index];
return ListTile(
title: Text(DateFormat('hh:mm').format(schedule.time.toDateTime())),
subtitle: Text(schedule.notes),
onTap: () => _updateRecord(schedule.parentID, schedule.id),
);
},
// separatorBuilder: (context, index) {
// return Divider();
// },
),
),
);
}).toList(),
)
],
);// ListTiles++
}
I want to display values from my API in a PopupMenuItem in PopupMenuButton. I manage to display it but I want it to be dynamic. Currently, I hard-coded the index of each item because it seems that I cannot do looping inside PopupMenuButton.
`Widget _simplePopup4() => PopupMenuButton<int>(
child: Icon(Icons.arrow_drop_down, color: Colors.orangeAccent),
offset: Offset(0, 100),
itemBuilder: (context) => [
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Text(snapshot.data.setTitle[1].title); //index 1
}
}
return CircularProgressIndicator();
})),
),
PopupMenuDivider(),
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Text(snapshot.data.setTitle[2].title); //index 2
}
}
return CircularProgressIndicator();
})),
),
PopupMenuDivider(),
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Text(snapshot.data.setTitle[3].title); //index 3
}
}
return CircularProgressIndicator();
})),
),
],
);`
//First attempt which gives error: RenderShrinkWrappingViewport does not support returning intrinsic dimensions.
Widget _simplePopup5() => PopupMenuButton(
itemBuilder: (context) {
var list = List<PopupMenuEntry<Object>>();
list.add(
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.setTitle.length,
itemBuilder:
(BuildContext context, int index) {
return Text(snapshot.data.setTitle[index].title);
});
}
}
return CircularProgressIndicator();
})),
),
);
list.add(
PopupMenuDivider(
height: 10,
),
);
return list;
},
icon: Icon(
Icons.settings,
size: 50,
color: Colors.white,
),
);
//Second attempt which gives error: Another exception was thrown: A RenderFlex overflowed by 85 pixels on the bottom.
Widget _simplePopup5() => PopupMenuButton(
itemBuilder: (context) {
var list = List<PopupMenuEntry<Object>>();
list.add(
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final listTitle = <Widget>[];
for (var i = 0;
i < snapshot.data.setTitle.length;
i++) {
listTitle.add(SingleChildScrollView(
scrollDirection: Axis.vertical,
child: InkWell(
child:
Text(snapshot.data.setTitle[i].title),
)));
}
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Column(children: listTitle);
}
}
return CircularProgressIndicator();
})),
),
);
list.add(
PopupMenuDivider(
height: 10,
),
);
return list;
},
icon: Icon(
Icons.settings,
size: 50,
color: Colors.white,
),
);
From the screenshot, only one item are clearly displayed which is "MR" while the other item (before item "MR") are displayed in half. Meanwhile, the rest of the item (after item "MR") being replaced with error message.
The screenshot of the second attempt error
The cause of the RenderFlex error is because the child Widget expands beyond the parent Widget. What you can do here is fetch the List of PopupMenu items prior to rendering the PopupMenuButton. With this approach, the List items is ready prior to clicking the PopupMenuButton.