Reputation: 942
I want to color select value from a ListView like this :
The problem is I print all when I try it , code :
class _ProcedureList extends State<ProcedureList> {
bool isSelected = true;
_isSelected() {
setState(() {
if (isSelected) {
isSelected = false;
} else {
isSelected = true;
}
});
}
@override
Widget build(BuildContext context) {
var procedureList = widget.filteredkits
.where((kit) => kit.brand == widget.brand)
.map((kit) => kit.procedure)
.toSet()
.toList();
return Expanded(
flex: 2,
child: Container(
padding: EdgeInsets.only(left: 35.0),
child: new ListView.builder(
itemCount: procedureList.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: EdgeInsets.all(6.0),
child: Column(
children: <Widget>[
new Container(
width: 300.0,
height: 30.0,
color: Colors.grey[700],
padding: EdgeInsets.all(6.0),
child: new Text(widget.brand),
),
GestureDetector(
onTap: () => widget.getProcedureSelectedandList(procedureList[index].toString()) & _isSelected(),
child: Container(
width: 300.0,
padding: EdgeInsets.all(3.0),
color: !isSelected ? Colors.white : Colors.orange,
child:
new Text(procedureList[index])
),
),
],
),
);
},
),
),
);
}
}
And this is what I achieve , all colored :
I don't know how to only color one item when the event happens and if we can change the text color with the same event better .
Upvotes: 3
Views: 8925
Reputation: 1089
sorry for the late reply. I have a better solution and i have changed in your code
class _ProcedureList extends State<ProcedureList> {
int isSelected = -1; // changed bool to int and set value to -1 on first time if you don't select anything otherwise set 0 to set first one as selected.
_isSelected(int index) { //pass the selected index to here and set to 'isSelected'
setState(() {
isSelected = index;
});
}
@override
Widget build(BuildContext context) {
var procedureList = widget.filteredkits
.where((kit) => kit.brand == widget.brand)
.map((kit) => kit.procedure)
.toSet()
.toList();
return Expanded(
flex: 2,
child: Container(
padding: EdgeInsets.only(left: 35.0),
child: new ListView.builder(
itemCount: procedureList.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: EdgeInsets.all(6.0),
child: Column(
children: <Widget>[
new Container(
width: 300.0,
height: 30.0,
color: Colors.grey[700],
padding: EdgeInsets.all(6.0),
child: new Text(widget.brand),
),
GestureDetector(
onTap: () => widget.getProcedureSelectedandList(procedureList[index].toString()) & _isSelected(index), //pass index value to '_isSelected'
child: Container(
width: 300.0,
padding: EdgeInsets.all(3.0),
color: isSelected != null && isSelected == index //set condition like this. voila! if isSelected and list index matches it will colored as white else orange.
? Colors.white
: Colors.orange,
child:
new Text(procedureList[index])
),
),
],
),
);
},
),
),
);
}
}
Still Confused check this blog
Upvotes: 1
Reputation: 2375
you should have a isSelected value per every single item you have in the list an then when the user clicks on the one of the items in the list you will change the isSelected value just for taped item index and in the build statement you should do the action base on the isSelected value of the passed in index
here is example :
class MyListWidgetState extends State<MyListWidget> {
List<String> items = ["A", "B", "C", "D", "E", "F"];
Map<int, bool> itemsSelectedValue = Map();
@override
Widget build(BuildContext context) {
return new ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
bool isCurrentIndexSelected = itemsSelectedValue[index] == null
? false
: itemsSelectedValue[index];
Container contianer;
if (isCurrentIndexSelected) {
contianer = new Container(
alignment: Alignment.center,
height: 100.0,
color: Colors.blue,
child: new Text(
items[index],
style: new TextStyle(color: Colors.red, fontSize: 18.0),
textAlign: TextAlign.center,
),
);
} else {
contianer = new Container(
alignment: Alignment.center,
height: 100.0,
color: Colors.red,
child: new Text(
items[index],
style: new TextStyle(color: Colors.blue, fontSize: 18.0),
textAlign: TextAlign.center,
),
);
}
return GestureDetector(
onTap: () {
print("${!isCurrentIndexSelected}");
itemsSelectedValue[index] = !isCurrentIndexSelected;
setState(() {
print("OnClick : $index + ${itemsSelectedValue[index]}");
});
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: contianer,
),
);
});
}
}
or you can create StateFull widget which keeps own isSelected Value for every item of your list like the below example :
List<String> items = [
"A",
"B",
"C",
"D",
"E",
"F",
"D",
"J",
"K",
"L",
"M",
"P"
];
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: new Scaffold(
body: new ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return new SelectableWidget(
new SelectableWidgetViewModel(
items[index],
isSelected: false,
),
);
},
)),
);
}
}
class SelectableWidget extends StatefulWidget {
final SelectableWidgetViewModel viewModel;
SelectableWidget(this.viewModel);
@override
State<StatefulWidget> createState() {
return SelectableWidgetState();
}
}
class SelectableWidgetState extends State<SelectableWidget> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
Container container;
if (widget.viewModel.isSelected) {
container = new Container(
alignment: Alignment.center,
height: 100.0,
color: Colors.blue,
child: new Text(
widget.viewModel.title,
style: new TextStyle(color: Colors.red, fontSize: 18.0),
textAlign: TextAlign.center,
),
);
} else {
container = new Container(
alignment: Alignment.center,
height: 100.0,
color: Colors.red,
child: new Text(
widget.viewModel.title,
style: new TextStyle(color: Colors.blue, fontSize: 18.0),
textAlign: TextAlign.center,
),
);
}
return GestureDetector(
onTap: () {
setState(() {
widget.viewModel.isSelected = !widget.viewModel.isSelected;
});
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: container,
),
);
}
}
class SelectableWidgetViewModel {
bool isSelected;
String title;
SelectableWidgetViewModel(this.title, {this.isSelected = false});
}
i think second option is better for performance reasons
Upvotes: 5
Reputation: 53347
What you need to do is to refactor your code such that the widget that is being passed into the ListView
builder
function have it is own StatefulWidget
. So move the widget tree inside the builder function into its own separate StatefulWidget
and handle the state manipulation logic in there instead of ProcedureList
. The reason you are getting this behavior is because every instance that is generated from the ListView.builder
is being exposed to the same state value of isSelected
by moving your logic into a separate StatefulWidget
each instance will have its own state.
Upvotes: 0