I want to make a nested list view in the following manner
How can I do this? I only want the nested list view for one of the radio tiles not all of them.
I tried including both ListView builder in another List however there was rendering problem.
My code:
Column(
children: <Widget>[
.....
Expanded(
child:
ListView.builder(
padding: EdgeInsets.all(0.0),
itemCount: tasks.length,
itemBuilder: (context, index) {
return RadioListTile<String>(
//contentPadding: EdgeInsets.symmetric(horizontal: 16.0),
title: Text(tasks[index], style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w400)),
value: tasks[index],
groupValue: selectedRadio,
onChanged: (val){
setSelectedRadio(val);
}
);
},
),
),
],
);
You cannot build a ListView inside a ListView as you will confuse the scroll behaviour. You should use List widget that does not scroll, such as Column.
ListView.builder(
padding: EdgeInsets.all(0.0),
itemCount: tasks.length,
itemBuilder: (context, index) {
if (// single RadioListTile) {
return RadioListTile<String>(
title: Text(tasks[index], style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w400)),
value: tasks[index],
groupValue: selectedRadio,
onChanged: (val) => setSelectedRadio(val),
);
}
else if (// nested RadioListTile) {
return Column(
children: <Widget>[
// RadioListTile1,
// RadioListTile2,
// RadioListTile3,
],
);
}
},
),
You can totally include a list view inside of another list view. But the inside list view has to have shrinkWrap set to true
Related
I am building a listview from a Firestore Database. I originally wanted to separate my items by ListTiles since I know they can do separators, but I was not getting the height that I wanted out of the tiles, so I moved to transparent Cards.
Problem is I cannot figure out how to add a separator or divider after each card.
Here is my code so far
Widget build(BuildContext context) {
if (snapshot == null) return CircularProgressIndicator();
return Scaffold(
body: ListView.builder(
itemCount: snapshot.length,
itemBuilder: (context, index){
return Card(
elevation: 0,
color: Colors.transparent,
child: Row(
children: <Widget>[
Padding(padding: EdgeInsets.all(10.0),),
Column(
children: <Widget>[
Padding(padding: EdgeInsets.all(10.0),),
Text(snapshot[index].data["month"], style:
TextStyle(fontSize: 30, fontWeight:
FontWeight.w300),),
Text(snapshot[index].data["day"], style:
TextStyle(fontSize: 20),),
],
)
],
),
);
}
),
);
}
}
Desired
Current
I would think list tiles would work better, but I tried what I knew how to do to build custom list tiles and i could not replicate the results.
Use ListView.separated
ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.black,
),
itemCount: 20,
itemBuilder: (context, index) => Padding(
padding: EdgeInsets.all(8.0),
child: Center(child: Text("Index $index")),
),
)
or divideTiles()
ListView(
children: ListTile.divideTiles(
context: context,
tiles: [
// your widgets here
]
).toList(),
)
I have a card that contains row of items (text and checkbox widgets). The problem is that the card can only fill up limited space each row, but it isn't going to the next line in the row. I tried using the wrap widget but it had no effect. I keep getting this:
As you can see it is not wrapping to the next but trying to fit everything in that one line. Here is my code:
Widget _buildCategories() {
return Card(
margin: const EdgeInsets.only(top: 20.0),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Text(
'Categories',
style: TextStyle(fontFamily: 'MonteSerrat', fontSize: 16.0),
),
Wrap(
children: <Widget>[
Row(
children: <Widget>[
_checkBox('Gaming'),
_checkBox('Sports'),
_checkBox('Casual'),
_checkBox('21 +'),
_checkBox('Adult'),
_checkBox('Food'),
_checkBox('Club'),
_checkBox('Activities'),
_checkBox('Shopping'),
],
)
],
)
],
),
));
}
Widget _checkBox(String category) {
return Expanded(
child: Column(
children: <Widget>[
Text(
'$category',
textAlign: TextAlign.center,
style: TextStyle(fontFamily: 'MonteSerrat'),
),
Checkbox(
value: false,
onChanged: (value) {
// We will update the value to the firebase data here
print('updated value to: $value');
},
)
],
));
}
I fixed your code with the following changes:
Removed Row widget inside Wrap.
Removed Expanded widget.
Add the property maxLines to your Text widget.
Widget _buildCategories() {
return Card(
margin: const EdgeInsets.only(top: 20.0),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Text(
'Categories',
style: TextStyle(fontFamily: 'MonteSerrat', fontSize: 16.0),
),
Wrap(
children: <Widget>[
_checkBox('Gaming'),
_checkBox('Sports'),
_checkBox('Casual'),
_checkBox('21 +'),
_checkBox('Adult'),
_checkBox('Food'),
_checkBox('Club'),
_checkBox('Activities'),
_checkBox('Shopping')
],
)
],
),
));
}
Widget _checkBox(String category) {
return Column(
children: <Widget>[
Text(
'$category',
maxLines: 1,
textAlign: TextAlign.center,
style: TextStyle(fontFamily: 'MonteSerrat'),
),
Checkbox(
value: false,
onChanged: (value) {
// We will update the value to the firebase data here
print('updated value to: $value');
},
)
],
);
}
}
Also you can add the properties runSpacing and spacing to your Wrap widget to give more space between your items in horizontal and vertical.
Wrap(
runSpacing: 5.0,
spacing: 5.0,
More info here: https://api.flutter.dev/flutter/widgets/Wrap-class.html
When I'm, not using the ListView.builder constructor in Flutter, the individual item is shown as expected from the JSON API:
when I use ListView.builder, nothing shows up. no Error!
I also tried a listview with Texts only that doesn't seem to work either.
Here's the code:
#override
Widget build(BuildContext context) {
return Container(
child: new Scaffold(
appBar: AppBar(
title: Text("the title"),//TODO edit this
backgroundColor: Colors.blueAccent),
body:
Column(
children: <Widget>[
FutureBuilder<List<dynamic>>(
future: getPosts2(),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? ListViewPosts(postsFrom: snapshot.data)
: Center(child: CircularProgressIndicator());
},
),
],
),
),
);
}
}
And here is the ListViewPosts Stateless Widget:
class ListViewPosts extends
StatelessWidget {
final List<dynamic> postsFrom;
ListViewPosts({Key key,
this.postsFrom}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
FadeInImage.assetNetwork(
placeholder: 'assets/images/placeholder.png',
image: postsFrom[1]["featured_media"] == 0
? 'assets/images/placeholder.png'
: postsFrom[1]["_embedded"]["wp:featuredmedia"]
[0]["source_url"],
),
FadeInImage.assetNetwork(
placeholder: 'assets/images/placeholder.png',
image: postsFrom[2]["featured_media"] == 0
? 'assets/images/placeholder.png'
: postsFrom[2]["_embedded"]["wp:featuredmedia"]
[0]["source_url"],
),
new Row(
children: <Widget>[
Expanded(
child: Text(
"نووسهر: " +
postsFrom[1]["_embedded"]["author"][0]
["name"],
textAlign: TextAlign.right,
),
),
Expanded(
child: Text(
dateConvertor(
postsFrom[2]["date"].toString()),
textAlign: TextAlign.left,
),
),
],
),
ListView.builder(
itemCount: postsFrom.length, //== null ? 0 : postsFrom.length,
itemBuilder: (context, int index) {
Card(
child: Column(
children: <Widget>[
Text(postsFrom.toString()),
Container(
child: hawalImage(postsFrom, index),
),
new Padding(
padding: EdgeInsets.all(5.0),
child: new ListTile(
title: new Text("whatEver"),
subtitle: new Row(
children: <Widget>[
Expanded(
child: new Text(postsFrom[index]["title"]["rendered"]),
),
Expanded(
child: hawalDate(postsFrom, index),
),
],
),
),
),
new ButtonTheme.bar(
child: hawalBtnBar(),
),
],
),
);
},
),
You have to write return Card at the beginning of the curly brackets in the builder function. Also I would be cautious with using Expanded there, it might cause some errors.
Also you put a ListView inside a Column without defining it's height, so it will take up space indefinitely. Wrap it in a widget that provides height constraints(SizedBox, Expanded, ...)
Inside the Listview.builder() you could try to add the property shrinkWrap: true.
This worked for me. I was facing a similar issue.
Use with Expanded. Hopefully, it will solve your problem.
You have to write return Card at the beginning of the curly brackets in the builder function. Also I would be cautious with using Expanded there, it might cause some errors.
I've added a list element by using the following method:
listToAddTo.add(italianFood());
However, when I try removing the same element by using the method:
listToAddTo.remove(italianFood());
it does not work.
I've tried using the removeWhere method with the parameters item == italianFood(), the retainWhere method with the item != italianFood() parameters, and the removeAt method with the listToAddTo.indexOf(italianFood()) parameters, however, none of those seem to work.
When I try printing the list, I get the following result:`
ListView(
scrollDirection: vertical,
primary: using primary controller,
scrollPhysics: AlwaysScrollableScrollPhysics,
shrinkWrap: shrink-wrapping
)
Above methods with this result also seem to have no effect.
Necessary code is as follows:
List listToAddTo = [];
ListView italianFood() {
return ListView(
shrinkWrap: true,
children: <Widget>[
listEntry(
'Fast Food Nana',
'Mon - Sun 06:00 - 04:00',
Container(
child: IconButton(
icon: Icon(
Icons.motorcycle,
size: 30.0,
),
),
),
IconButton(
icon: Icon(
Icons.navigation,
size: 30.0,
),
),
),
],
);
Container filterItem(String label, value, onChanged) {
return Container(
decoration: BoxDecoration(color: Colors.white),
child: CheckboxListTile(
value: value,
title: Text(label),
onChanged: onChanged,
),
);
bool isTrue = false;
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Container(
margin: const EdgeInsets.only(top: 45.0),
child: ListView.builder(
itemCount: listToAddTo.length,
itemBuilder: (BuildContext context, int Index) {
return (listToAddTo[Index]);
},
),
),
ExpansionTile(
title: Text('Filters'),
children: <Widget>[
filterItem(
'Italian',
isTrue,
(bool value) {
setState(() {
if (isTrue == false) {
listToAddTo.add(italianFood());
isTrue = !isTrue;
} else {
listToAddTo.removeAt(listToAddTo.indexOf(italianFood()));
isTrue = !isTrue;
}
});
print(listToAddTo);
print(italianFood());
},
),
],
),
],
);
You can't remove it because are different objects.
Every time you use italianFood() you create a new instance of the class.
Create a gloval variable :
ListView _myItalianFood;
Instantiate:
in your add method:
_myItalianFood = italianFood();
listToAddTo.add(_myItalianFood);
Remove:
listToAddTo.remove(_myItalianFood);
The problem is that italianFood() returns a new ListView instance on each call. So when you call listToAddTo.add(italianFood()) you are creating a new ListView instance (let's call it LV1) and you end up with listToAddTo being [LV1].
When you call listToAddTo.remove(italianFood()) you are creating a new ListView instance (let's call it LV2) and asking listToAddTo to remove anything that is equal to LV2, so this ends up doing nothing since LV2 is not in listToAddTo.
You could fix this by making italianFood a field.
final italianFood = ListView(shrinkWrap: true, ...);
I am developing a screen where I have to show suggestions list below the textfield.
I want to achieve this
I have developed this so far
Following code shows textfield with suggestions in a list.
#override
Widget build(BuildContext context) {
final header = new Container(
height: 39.0,
padding: const EdgeInsets.only(left: 16.0, right: 2.0),
decoration: _textFieldBorderDecoration,
child: new Row(
children: <Widget>[
new Expanded(
child: new TextField(
maxLines: 1,
controller: _controller,
style: _textFieldTextStyle,
decoration:
const InputDecoration.collapsed(hintText: 'Enter location'),
onChanged: (v) {
_onTextChanged.add(v);
if (widget.onStartTyping != null) {
widget.onStartTyping();
}
},
),
),
new Container(
height: 32.0,
width: 32.0,
child: new InkWell(
child: new Icon(
Icons.clear,
size: 20.0,
color: const Color(0xFF7C7C7C),
),
borderRadius: new BorderRadius.circular(35.0),
onTap: (){
setState(() {
_controller.clear();
_places = [];
if (widget.onClearPressed != null) {
widget.onClearPressed();
}
});
},
),
),
],
),
);
if (_places.length > 0) {
final body = new Material(
elevation: 8.0,
child: new SingleChildScrollView(
child: new ListBody(
children: _places.map((p) {
return new InkWell(
child: new Container(
height: 38.0,
padding: const EdgeInsets.only(left: 16.0, right: 16.0),
alignment: Alignment.centerLeft,
decoration: _suggestionBorderDecoration,
child: new Text(
p.formattedAddress,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: _suggestionTextStyle,
),
),
onTap: () {
_getPlaceDetail(p);
},
);
}).toList(growable: false),
),
),
);
return new Container(
child: new Column(
children: <Widget>[header, body],
),
);
} else {
return new Container(
child: new Column(
children: <Widget>[header],
),
);
}
}
Header(Textfield) and body(Suggestions List - SingleChildScrollView with ListBody) is wrapped inside the Column widget, and column expands based on the total height of the children.
Now the problem is as Column expands, layout system pushes other widgets on screen to the bottom. But I want other widgets to stay on their positions but suggestion list starts to appear on top of other widgets.
How can I show suggestions list on top of other widgets? And the suggestions list is dynamic, as user types I call the Google Places API and update the suggestions list.
I have seen there is showMenu<T>() method with RelativeRect positions but it doesn't fulfills my purpose, my suggestion list is dynamic(changing based on user input) and the styling for each item I have is different from what PopupMenuItem provides.
There is one possibility I can think of using Stack widget as root widget of this screen and arrange everything by absolute position and I put suggestion list as a last child of the stack children list. But it is not the right solution I believe.
What other possibilities I need to look into? What other Widgets can be used here in this use-case?
And again use-case is simple, overlaying suggestion list on other widgets on the screen and when user tap any of the item from the list then hiding this overlaid suggestion list.
The reason why your autocomplete list pushes down the widgets below it is because the List is being expanded on the Container. You can use Flutter's Autocomplete widget and it should inflate the autocomplete list over other widgets.
var fruits = ['Apple', 'Banana', 'Mango', 'Orange'];
_autoCompleteTextField() {
return Autocomplete(
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<String>.empty();
}
return fruits.where((String option) {
return option
.toLowerCase()
.contains(textEditingValue.text.toLowerCase());
});
},
onSelected: (String selection) {
debugPrint('You just selected $selection');
},
);
}