If i select the 1st index RadioListTile, then all 1st index in the expansion Tile is selected this is my problem, Here i want to select particular radio list and add the index to list
class Addon extends StatefulWidget {
#override
_AddonState createState() => _AddonState();
} class _AddonState extends State<Addon> {
List addonItem = ["Cool Drinks", "Extra Sauce"];
int dummy;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: ListView.builder(
dragStartBehavior: DragStartBehavior.down,
physics: ScrollPhysics(),
shrinkWrap: true,
itemCount: addonItem.length,
itemBuilder: (context, index) {
return ExpansionTile(
initiallyExpanded: false,
title: Text(
addonItem[index],
),
children: [
for (int i = 0; i < 3; i++)
RadioListTile(
value: i,
title: Text("item $i"),
groupValue: dummy,
activeColor: Theme.of(context).primaryColor,
onChanged: (val) {
print(val);
setState(() {
dummy = val;
});
},
)
],
);
}),
),
);
}
}
The problem is that you are using int dummy; as the selected value for both lists of buttons you can use two dummy values and add a condition to the rendering of the children of the ExpansionTile like this:
class Addon extends StatefulWidget {
#override
_AddonState createState() => _AddonState();
}
class _AddonState extends State<Addon> {
List addonItem = ["Cool Drinks", "Extra Sauce"];
int dummy1;
int dummy2;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: ListView.builder(
physics: ScrollPhysics(),
shrinkWrap: true,
itemCount: addonItem.length,
itemBuilder: (context, index) {
return ExpansionTile(
initiallyExpanded: false,
title: Text(
addonItem[index],
),
children: [
if (addonItem[index] == 'Cool Drinks')
for (int i = 0; i < 3; i++)
RadioListTile(
value: i,
title: Text("item $i"),
groupValue: dummy1,
activeColor: Theme.of(context).primaryColor,
onChanged: (val) {
print(val);
setState(
() {
dummy1 = val;
},
);
},
),
if (addonItem[index] == 'Extra Sauce')
for (int i = 0; i < 3; i++)
RadioListTile(
value: i,
title: Text("item $i"),
groupValue: dummy2,
activeColor: Theme.of(context).primaryColor,
onChanged: (val) {
print(val);
setState(
() {
dummy2 = val;
},
);
},
)
],
);
},
),
),
);
}
}
This checks if the value if the index you are accessing in addonItem and if the value is 'Cool Drinks' you use one of the dummy values, if the index is 'Extra Sauce' you use the other value, this makes sure you dont share the same value for both lists.
This only works if you are not adding values dynamically to the addonItem list.
Related
Here's my code
DropdownButton<int>(
value: map['completedVersion'].toInt(), //selected
elevation: 16,
style: TextStyle(color: Theme.of(context).accentColor),
underline: Container(
height: 2,
color: Colors.blueAccent,
),
onChanged: (int? newValue) {
versionInput.text = newValue.toString();
},
items: [for (var i = map['completedVersion'].toInt() as int; i <= map['requiredVersion']; i++) i]
.map<DropdownMenuItem<int>>((int value) {
return DropdownMenuItem<int>(
value: value,
child: Text(value.toString()),
);
}).toList(),
)
Class level declaration
TextEditingController versionInput = TextEditingController();
#override
void initState() {
versionInput.text = map['completedVersion'].toString(); //set the initial value of text field
super.initState();
}
Here's the behavior
It doesn't let me select any other value (eg: 4,5,6). I do see that he onChanged() method is hit when I put in a break point. But I'm not sure why the selection goes back to the original value.
TextEditingValue
class Example extends StatelessWidget {
Example({Key? key}) : super(key: key);
TextEditingController versionInput = TextEditingController(text: "2");
#override
Widget build(BuildContext context) {
return Column(
children: [
ValueListenableBuilder(
valueListenable: versionInput,
builder: (BuildContext context, TextEditingValue selectedValue, Widget? child) {
return DropdownButton<int>(
value: int.parse(selectedValue.text),
elevation: 16,
style: TextStyle(color: Theme.of(context).accentColor),
underline: Container(
height: 2,
color: Colors.blueAccent,
),
onChanged: (value) {},
items: [for (var i = 0 as int; i <= 6; i++) i].map<DropdownMenuItem<int>>((int value) {
return DropdownMenuItem<int>(
value: value,
child: Text(value.toString()),
onTap: () {
versionInput.text = value.toString();
},
);
}).toList(),
);
},
),
],
);
}
}
If you really use the TextEditingController without a text field and you don't need it, you can use it directly in the ValueNotifier.
ValueNotifier
class Example extends StatelessWidget {
Example({Key? key}) : super(key: key);
ValueNotifier<int> versionInput = ValueNotifier<int>(2); // initialValue
#override
Widget build(BuildContext context) {
return Column(
children: [
ValueListenableBuilder(
valueListenable: versionInput,
builder: (BuildContext context, int selectedValue, Widget? child) {
return DropdownButton<int>(
value: selectedValue,
elevation: 16,
style: TextStyle(color: Theme.of(context).accentColor),
underline: Container(
height: 2,
color: Colors.blueAccent,
),
onChanged: (value) {},
items: [for (var i = 0 as int; i <= 6; i++) i].map<DropdownMenuItem<int>>((int value) {
return DropdownMenuItem<int>(
value: value,
child: Text(value.toString()),
onTap: () {
versionInput.value = value;
},
);
}).toList(),
);
},
),
],
);
}
}
You have to change two things :
1- value: map['completedVersion'].toInt(), ==> value: versionInput.text,
2-
onChanged: (newValue) {
setState(() {
versionInput.text = newValue.toString();
});
},
this should work:
#override
Widget build(BuildContext context) {
return Column(
children: [
ValueListenableBuilder(
valueListenable: versionInput,
builder: (BuildContext context, TextEditingValue selectedValue, Widget? child) {
return DropdownButton<int>(
hint: Text(
int.parse(selectedValue.text) ?? '3'
),
elevation: 16,
style: TextStyle(color: Theme.of(context).accentColor),
underline: Container(
height: 2,
color: Colors.blueAccent,
),
onChanged: (int? newValue) {
if(newValue != null){
setState(()=> versionInput.text = newValue.toString());
}
},
items: [for (var i = 0; i <= 6; i++) i].map<DropdownMenuItem<int>>((int value) {
return DropdownMenuItem<int>(
value: value,
child: Text(value.toString()),
onTap: () {
versionInput.text = value.toString();
},
);
}).toList(),
);
},
),
],
);
}
}
I am trying to make an age selection screen. Below is a screenshot.
As you can see there is no color filled in the radio list tile. I don't know what I am doing wrong. Please do let me know.
final ageRange = ["18 - 21", "21 - 26", "26+"];
var selectedIndex = 0;
ListView.builder(
shrinkWrap: true,
itemCount: ageRange.length,
itemBuilder: (context, index) {
return RadioListTile(
value: index,
groupValue: registerController.userAgeRange,
selected: index == selectedIndex,
onChanged: changeSelectedIndex,
title: Text(ageRange[index]),
);
},
),
Others answer like #masum billah sanjid will work, But I think it will be better to provide data type on RadioListTile.
While the value will be string, I prefer
final ageRange = ["18 - 21", "21 - 26", "26+"];
String? selectedValue;
changeSelectedIndex(v) {
setState(() {
selectedValue = v;
});
}
///....
return RadioListTile<String>(
value: ageRange[index],
groupValue: selectedValue,
selected: selectedValue == ageRange[index],
onChanged: changeSelectedIndex,
title: Text(ageRange[index]),
);
Change groupValue property with selectedIndex
Here is my example
final ageRange = ["18 - 21", "21 - 26", "26+"];
var selectedIndex = 0;
changeSelectedIndex(v) {
selectedIndex = v;
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
shrinkWrap: true,
itemCount: ageRange.length,
itemBuilder: (context, index) {
return RadioListTile(
value: index,
groupValue: selectedIndex,
selected: index == selectedIndex,
onChanged: changeSelectedIndex,
title: Text(ageRange[index]),
);
},
),
);
}
you can try this:
ListView.builder(
shrinkWrap: true,
itemCount: ageRange.length,
itemBuilder: (context, index) {
return RadioListTile(
value: index,
groupValue: selectedIndex,
selected: selectedIndex == index,
onChanged: changeSelectedIndex,
title: Text(ageRange[index]),
);
},
),
Wrap your RadioListTile inside a Container:
return Container(
color: Colors.blue,
child: RadioListTile(
value: index,
groupValue: registerController.userAgeRange,
selected: index == selectedIndex,
onChanged: changeSelectedIndex,
title: Text(ageRange[index]),
),
);
i have a project about basic market app
first screen adds item to market when clicked on add button adds to some collection and passes to market cart page
in cart page lists the collection (item name,item price,quantity) and bottom of the screen shows total price.
im learning flutter and my coding is pretty bad right now
my problem is im dismissing items but items not removing from the screen. i tried unique key(does nothing and not working), i tried give keys manually this time ''A dismissed Dismissible widget is still part of the tree.''
i tried to remove items from lists that containts data but not working.
this is my seperated listview builder
Widget buildList() => ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.black,
),
itemCount: cartItemQuantity.length,
itemBuilder: (context, index) {
return seperated(
context,
cartItemNames[index],
recievedShopItemsList[index],
index,
cartItemQuantity[index],
recievedShopItemKeys[index],
);
},
);
this is my widget (when its dismissed its gonna remove item and update total price from screen(newPrice) )
Widget seperated(BuildContext context, String name, String price, int index, int quantity, String uKey) {
return Dismissible(
key: Key(uKey),
direction: DismissDirection.endToStart,
onDismissed: (direction) {
widget.recievedCart.remove(name);
widget.recievedNewShopItems.remove(name);
setState(() {
newPrice= newPrice- (int.parse(price) * quantity);
});
},
child: ListTile(
title: Text(
name,
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text("${price}.0 TL"),
trailing: Text("Quantiy : ${quantity}")),
);
im so swamped this is my full code of cart page
Map<String, dynamic> recievedCart = Map();
Map<String, dynamic> recievedShopItems = Map();
Map<String, dynamic> recievedNewShopItems = Map();
CartPage(
{required this.recievedCart,
required this.recievedShopItems,
required this.recievedNewShopItems});
#override
_CartPageState createState() => _CartPageState();
}
class _CartPageState extends State<CartPage> {
var cartItemQuantity,
cartItemNames,
recievedShopItemsList,
recievedShopItemKeys;
num newPrice= 0;
num calculate() {
for (int i = 0; i < widget.recievedNewShopItems.length; i++) {
newPrice= newPrice+
(int.parse(recievedShopItemsList[i]) * cartItemQuantity[i]);
}
return newPrice;
}
#override
void initState() {
// TODO: implement initState
cartItemQuantity = widget.recievedCart.values.toList();
cartItemNames = widget.recievedCart.keys.toList();
recievedShopItemsList = widget.recievedNewShopItems.values.toList();
recievedShopItemKeys = widget.recievedNewShopItems.keys.toList();
setState(() {
newPrice = calculate();
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
Navigator.pop(context);
},
),
title: Center(
child: Text("Cart"),
),
),
body: Column(
children: <Widget>[
Expanded(child: buildList()),
Container(
height: MediaQuery.of(context).size.height / 12,
color: Colors.blue,
child: Center(
child: Text(
"TOTAL : ${newPrice}.0 TL",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 25),
),
),
)
],
),
);
}
int a = 0;
int b = 0;
int finalprice= 0;
Widget seperated(BuildContext context, String name, String price, int index,
int quantity, String uKey) {
int qnt = 0;
return Dismissible(
key: Key(uKey), //
direction: DismissDirection.endToStart,
onDismissed: (direction) {
print(uKey);
widget.recievedCart.remove(name);
widget.recievedNewShopItems.remove(name);
setState(() {
newPrice= newPrice- (int.parse(price) * quantity);
});
},
child: ListTile(
title: Text(
name,
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text("${price}.0 TL"),
trailing: Text("Quantiy : ${quantity}")),
);
}
int index = 0;
int itemCount = 0;
Widget buildList() => ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.black,
),
itemCount: cartItemQuantity.length,
itemBuilder: (context, index) {
return seperated(
context,
cartItemNames[index],
recievedShopItemsList[index],
index,
cartItemQuantity[index],
recievedShopItemKeys[index],
);
},
);
}
Change widget.recievedCart.remove(name) to widget.recievedCart.removeAt(index),
remove and removeAt are different:
remove(element)
removes a single element from the list, which is strictly equal to the element passed in.
removeAt(element)
removes the element from the list at the given index, and returns that element
Here is my parent widget.
class AddUserextends StatefulWidget {
final ScrollController controller;
AddUser(this.controller);
#override
_AddUser createState() =>
_AddUser();
}
class _AddUserToManagePropertyState extends State<AddUserToManageProperty> {
late TextEditingController _firstNameCtrl;
late TextEditingController _lastNameCtrl;
late TextEditingController _phoneNoCtrl;
late ValueNotifier<num?> _category;
final _scaffoldKey = GlobalKey<ScaffoldState>();
final _formKey = GlobalKey<FormState>();
final ValueNotifier<bool> _formStateEmitter = ValueNotifier(false);
final ValueNotifier<bool> isSelected = ValueNotifier(false);
#override
void initState() {
super.initState();
_firstNameCtrl = TextEditingController(text: '');
_lastNameCtrl = TextEditingController(text: '');
_phoneNoCtrl = TextEditingController(text: '');
_firstNameCtrl.addListener(() {
_formStateEmitter.value = _fieldsStatus();
});
_lastNameCtrl.addListener(() {
_formStateEmitter.value = _fieldsStatus();
});
_phoneNoCtrl.addListener(() {
_formStateEmitter.value = _fieldsStatus();
});
}
bool _fieldsStatus() {
return HwValidators.required(_firstNameCtrl.text) == null &&
HwValidators.required(_lastNameCtrl.text) == null &&
HwValidators.required(_phoneNoCtrl.text) == null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: HwAppBar(
title: 'Add team member',
),
body: Form(
key: _formKey,
child: FormWidget(
maintainSafeArea: false,
showBackBtn: false,
fields: [
HwTextField(
label: 'FIRST NAME',
controller: _firstNameCtrl,
validator: HwValidators.nameValidator,
keyboardType: TextInputType.text,
),
HwSizedBox(height: 4),
HwTextField(
label: 'LAST NAME',
controller: _lastNameCtrl,
validator: HwValidators.nameValidator,
keyboardType: TextInputType.text,
),
HwSizedBox(height: 4),
HwTextField(
label: 'PHONE NO',
controller: _phoneNoCtrl,
validator: HwValidators.phoneValidator,
keyboardType: TextInputType.phone,
),
HwSizedBox(height: 4),
HwSizedBox(height: 4),
HwText('ACCESS LEVEL'),
UserAccessListTiles(),
HwSizedBox(
height: 4,
),
],
),
),
);
}
}
The second last widget is UserAccessListTiles which is a widget that allows user to choose from two set of widgets like this:
class UserAccessListTiles extends StatelessWidget {
List _userAccessListTile = [
SelectableTile(
title: 'Access to all stories',
leading: SvgPicture.asset(HwSvgs.fullAccess),
trailing: null,
),
SelectableTile(
title: 'Custom access',
leading: SvgPicture.asset(HwSvgs.customAccess),
trailing: null),
];
final ValueNotifier<int> _selected = ValueNotifier(0);
#override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: _selected,
builder: (context, int value, child) {
return Column(
children: [
Container(
child: ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: 2,
itemBuilder: (context, index) {
return Column(
children: [
HwSizedBox(
height: 3,
),
InkWell(
onTap: () {
_selected.value = index;
},
child: Container(
decoration: BoxDecoration(
border: Border.all(
width: value == index ? 2 : 1,
color: value == index
? HwColors.green
: HwColors.divider,
),
borderRadius: BorderRadius.circular(8.0)),
child: SelectableTile(
title: _userAccessListTile[index].title,
leading: _userAccessListTile[index].leading,
trailing: value == index
? SvgPicture.asset(HwSvgs.greenCheck)
: null,
),
),
),
],
);
},
),
),
_selected.value == 0 ? UserAccessListTilesRadio() : CustomAccess(),
],
);
});
}
}
And finally both these widgets UserAccessListTilesRadio() and CustomAccess() have a set of RadioListTile that user can choose from and I want that chosen option to be available in the original parent widget as part of the Form.
How can I do it, please help.
I would recommend you to use state management i.e Bloc or Provider
e.g Provider
Make a model that extends ChangeNotifierProvider
class MyProviderModel extends ChangeNotifierProvider{
int yourChosenValue;
void updateChosenValue() {
// Your logic
notifyListeners();
}
}
Init Provider in your Parent Widget
Provider(
create: (_) => MyProviderModel(),
child: Consumer<MyProviderModel>(
builder: (_, a, child) {
return // Your Form
},
)
)
Update Your MyProviderModel in your child Widget
context.read<MyProviderModel>().updateChosenValue();
Init Provider in your parent Widget
I'm creating an application which requires the user to select a value from the drop-down from every item of grid view. But when I select a value from a drop-down it changes value for other drop-down too.
GridView.builder(
itemCount: 50,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, childAspectRatio: 1),
itemBuilder: (BuildContext context, int index) {
return new Card(
child: Column(
children: [
DropdownButton(
hint: _dropDownValue == null
? Text('Dropdown')
: Text(
_dropDownValue,
style: TextStyle(color: Colors.blue),
),
isExpanded: true,
iconSize: 30.0,
style: TextStyle(color: Colors.blue),
items: ['One', 'Two', 'Three'].map(
(val) {
return DropdownMenuItem<String>(
value: val,
child: Text(val),
);
},
).toList(),
onChanged: (val) {
setState(
() {
_dropDownValue = val;
},
);
},
)
],
),
);
},
),
This is the code I'm using.
Screenshot of Problem I'm Facing
This happens because you retrieve the value for every DropDown using _dropDownValue. In order to make it work, you have to store each selection for each Grid using a e.g. List or Map.
class MyWidget extends StatefulWidget {
const MyWidget({Key key}) : super(key: key);
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
List<String> values = [
"Grid 1 selection",
"Grid 2 selection",
"..."
];
#override
Widget build(BuildContext context) {
return GridView.builder(
itemCount: 50,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, childAspectRatio: 1),
itemBuilder: (BuildContext context, int index) {
return new Card(
child: Column(
children: [
DropdownButton(
hint: values[index] == null
? Text('Dropdown')
: Text(
values[index],
style: TextStyle(color: Colors.blue),
),
isExpanded: true,
iconSize: 30.0,
style: TextStyle(color: Colors.blue),
items: ['One', 'Two', 'Three'].map(
(val) {
return DropdownMenuItem<String>(
value: val,
child: Text(val),
);
},
).toList(),
onChanged: (val) {
setState(
() {
values[index] = val;
},
);
},
)
],
),
);
},
);
}
}