currently I am trying to update a text in my popupmenu, in between the add and reduce icon button, when i click on add button. Value increases but it does not update when i setstate it. I have to close the popupmenu and reopen it to see the new updated value. Any tips to improve my code would be greatly appreciated. Thank you for your help.
This is my code:
class SelectMedicalItems extends StatefulWidget {
final Function callBack;
SelectMedicalItems({this.callBack});
@override
_SelectMedicalItemsState createState() => _SelectMedicalItemsState();
}
class _SelectMedicalItemsState extends State<SelectMedicalItems>
with SingleTickerProviderStateMixin {
AnimationController _animationController;
double _scale;
int tempCount = 0;
String tempName = '';
List<CartItems> totalItem = [];
TextEditingController textController = TextEditingController();
void _onTap() {
_animationController.forward();
}
void _onTapReverse() {
_animationController.reverse();
}
void _onTapUp(TapUpDetails details) {
_animationController.reverse();
}
void _onTapDown(TapDownDetails details) {
_animationController.forward();
}
List items = List<String>();
List<String> tempMedical = medicalItems;
void filteredSearch(String query) {
items = tempMedical
.where((txt) => query.isEmpty || txt.toUpperCase().contains(query))
.toList();
setState(() {});
}
GlobalKey<ScaffoldState> _scaffoldState = GlobalKey<ScaffoldState>();
void _showBar(String newValue) async {
_scaffoldState.currentState.showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
content: Text('You have selected: $newValue'),
),
);
await showDialog(
barrierDismissible: false,
context: context,
child: StatefulBuilder(builder: (context, setState) {
return AlertDialog(
elevation: 30,
backgroundColor: Color(0xFFE6F0F9),
contentPadding: EdgeInsets.all(10),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
title: Text(
'How many do you need?',
style: TextStyle(
color: Color(0xFF67696F),
),
),
content: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: Padding(
padding: EdgeInsets.all(15.0),
child: TextField(
autofocus: true,
keyboardType: TextInputType.numberWithOptions(),
onSubmitted: (value) {
setState(() {
tempCount = int.parse(value);
});
},
onChanged: (value) {
setState(() {
tempCount = int.parse(value);
});
},
decoration: InputDecoration(
hintText: 'e.g 10', suffixText: 'pcs'),
),
),
),
],
),
actions: <Widget>[
FlatButton(
onPressed: () {
setState(() {
tempCount = 0;
tempName = '';
});
Navigator.pop(context);
},
child: Text(
'Cancel',
style: TextStyle(color: Colors.red),
)),
FlatButton(
onPressed: () {
setState(() {
totalItem.add((CartItems(
cartItem: tempName, itemQuantity: tempCount)));
tempName = '';
tempCount = 0;
});
Navigator.pop(context);
},
child: Text(
'Okay',
style: TextStyle(color: Colors.blue),
)),
],
);
}));
setState(() {});
}
@override
void initState() {
items = tempMedical;
_animationController = AnimationController(
vsync: this,
upperBound: 0.1,
lowerBound: 0.0,
duration: Duration(milliseconds: 100))
..addListener(() {
setState(() {});
});
super.initState();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
_scale = 1 - _animationController.value;
return SafeArea(
child: Scaffold(
backgroundColor: Color(0xFFE6F0F9),
resizeToAvoidBottomPadding: false,
key: _scaffoldState,
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: EdgeInsets.all(15.0),
child: Row(
children: <Widget>[
GestureDetector(
onTapUp: _onTapUp,
onTapDown: _onTapDown,
onTap: () {
_onTap();
_onTapReverse();
Future.delayed(Duration(milliseconds: 500), () {
Navigator.pop(context);
});
},
child: Transform.scale(
scale: _scale,
child: Container(
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Color(0xFFE6F0F9),
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
offset: Offset(5, 5),
blurRadius: 8,
),
BoxShadow(
color: Colors.white.withOpacity(0.5),
offset: Offset(-5, -5),
blurRadius: 8,
)
]),
child: Icon(
Icons.arrow_back_ios,
color: Color(0xFF67696F),
),
),
),
),
Spacer(),
Badge(
elevation: 5,
position: BadgePosition.topRight(top: -1, right: -5),
animationDuration: Duration(seconds: 1),
toAnimate: true,
animationType: BadgeAnimationType.slide,
badgeContent: Text(
totalItem.length.toString(),
style: TextStyle(color: Colors.white),
),
child: PopupMenuButton(
color: Color(0xFFE6F0F9),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
itemBuilder: (context) {
return totalItem
.map((item) => PopupMenuItem(
value: item.cartItem,
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Flexible(
child: Text(
'${item.cartItem}',
style: TextStyle(
color: Colors.grey,
fontWeight: FontWeight.w500),
),
),
Spacer(),
GestureDetector(
onTap: () {
setState(() {
item.itemQuantity++;
});
},
child: Icon(
Icons.add_circle,
color: Colors.green,
),
),
Text(
item.itemQuantity.toString(),
style: TextStyle(color: Colors.black),
),
GestureDetector(
onTap: () {
setState(() {
item.itemQuantity--;
});
},
child: Icon(
Icons.remove_circle,
color: Colors.red,
),
),
],
),
))
.toList();
},
child: Container(
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Color(0xFFE6F0F9),
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
offset: Offset(5, 5),
blurRadius: 8,
),
BoxShadow(
color: Colors.white.withOpacity(0.5),
offset: Offset(-5, -5),
blurRadius: 8,
)
]),
child: Icon(
Icons.shopping_cart,
color: Color(0xFF67696F),
)),
),
),
],
),
),
Padding(
padding: EdgeInsets.all(15.0),
child: TextField(
controller: textController,
decoration: InputDecoration(
hintText: 'Search',
labelText: 'Search',
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25.0))),
suffix: GestureDetector(
child: Icon(Icons.clear),
onTap: () {
textController.clear();
setState(() {
items = tempMedical;
});
},
)),
onChanged: (value) {
filteredSearch(value.toUpperCase());
},
),
),
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return Material(
color: Color(0xFFE6F0F9),
child: InkWell(
child: ListTile(
onTap: () {
SystemSound.play(SystemSoundType.click);
_showBar('${items[index]}');
widget.callBack(items[index]);
tempName = '${items[index]}';
},
title: Text(
'${items[index]}',
style: TextStyle(color: Color(0xFF67696F)),
),
trailing: Icon(
Icons.add_circle_outline,
color: Colors.green,
size: 30,
),
),
),
);
})),
],
),
),
);
}
}
class CartItems {
final String cartItem;
int itemQuantity;
CartItems({this.cartItem, this.itemQuantity: 1});
}
You can copy-paste run full code below
You can use StatefulBuilder
as child of PopupMenuItem
and return Row
Code Snippet:
PopupMenuItem(
value: item.cartItem,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Row(
mainAxisSize: MainAxisSize.min,
Working Demo:
Full Code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SelectMedicalItems(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class SelectMedicalItems extends StatefulWidget {
final Function callBack;
SelectMedicalItems({this.callBack});
@override
_SelectMedicalItemsState createState() => _SelectMedicalItemsState();
}
class _SelectMedicalItemsState extends State<SelectMedicalItems>
with SingleTickerProviderStateMixin {
AnimationController _animationController;
double _scale;
int tempCount = 0;
String tempName = '';
List<CartItems> totalItem = [CartItems(cartItem: "apple", itemQuantity: 2)];
TextEditingController textController = TextEditingController();
void _onTap() {
_animationController.forward();
}
void _onTapReverse() {
_animationController.reverse();
}
void _onTapUp(TapUpDetails details) {
_animationController.reverse();
}
void _onTapDown(TapDownDetails details) {
_animationController.forward();
}
List items = List<String>();
List<String> tempMedical = ["medical"]; //medicalItems;
void filteredSearch(String query) {
items = tempMedical
.where((txt) => query.isEmpty || txt.toUpperCase().contains(query))
.toList();
setState(() {});
}
GlobalKey<ScaffoldState> _scaffoldState = GlobalKey<ScaffoldState>();
void _showBar(String newValue) async {
_scaffoldState.currentState.showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
content: Text('You have selected: $newValue'),
),
);
await showDialog(
barrierDismissible: false,
context: context,
child: StatefulBuilder(builder: (context, setState) {
return AlertDialog(
elevation: 30,
backgroundColor: Color(0xFFE6F0F9),
contentPadding: EdgeInsets.all(10),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
title: Text(
'How many do you need?',
style: TextStyle(
color: Color(0xFF67696F),
),
),
content: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: Padding(
padding: EdgeInsets.all(15.0),
child: TextField(
autofocus: true,
keyboardType: TextInputType.numberWithOptions(),
onSubmitted: (value) {
setState(() {
tempCount = int.parse(value);
});
},
onChanged: (value) {
setState(() {
tempCount = int.parse(value);
});
},
decoration: InputDecoration(
hintText: 'e.g 10', suffixText: 'pcs'),
),
),
),
],
),
actions: <Widget>[
FlatButton(
onPressed: () {
setState(() {
tempCount = 0;
tempName = '';
});
Navigator.pop(context);
},
child: Text(
'Cancel',
style: TextStyle(color: Colors.red),
)),
FlatButton(
onPressed: () {
setState(() {
totalItem.add((CartItems(
cartItem: tempName, itemQuantity: tempCount)));
tempName = '';
tempCount = 0;
});
Navigator.pop(context);
},
child: Text(
'Okay',
style: TextStyle(color: Colors.blue),
)),
],
);
}));
setState(() {});
}
@override
void initState() {
items = tempMedical;
_animationController = AnimationController(
vsync: this,
upperBound: 0.1,
lowerBound: 0.0,
duration: Duration(milliseconds: 100))
..addListener(() {
setState(() {});
});
super.initState();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
_scale = 1 - _animationController.value;
return SafeArea(
child: Scaffold(
backgroundColor: Color(0xFFE6F0F9),
resizeToAvoidBottomPadding: false,
key: _scaffoldState,
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: EdgeInsets.all(15.0),
child: Row(
children: <Widget>[
GestureDetector(
onTapUp: _onTapUp,
onTapDown: _onTapDown,
onTap: () {
_onTap();
_onTapReverse();
Future.delayed(Duration(milliseconds: 500), () {
Navigator.pop(context);
});
},
child: Transform.scale(
scale: _scale,
child: Container(
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Color(0xFFE6F0F9),
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
offset: Offset(5, 5),
blurRadius: 8,
),
BoxShadow(
color: Colors.white.withOpacity(0.5),
offset: Offset(-5, -5),
blurRadius: 8,
)
]),
child: Icon(
Icons.arrow_back_ios,
color: Color(0xFF67696F),
),
),
),
),
Spacer(),
Container(
/* elevation: 5,
position: BadgePosition.topRight(top: -1, right: -5),
animationDuration: Duration(seconds: 1),
toAnimate: true,
animationType: BadgeAnimationType.slide,
badgeContent: Text(
totalItem.length.toString(),
style: TextStyle(color: Colors.white),
),*/
child: PopupMenuButton(
color: Color(0xFFE6F0F9),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
itemBuilder: (context) {
return totalItem
.map((item) => PopupMenuItem(
value: item.cartItem,
child: StatefulBuilder(builder:
(BuildContext context, StateSetter setState) {
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Flexible(
child: Text(
'${item.cartItem}',
style: TextStyle(
color: Colors.grey,
fontWeight: FontWeight.w500),
),
),
Spacer(),
GestureDetector(
onTap: () {
setState(() {
item.itemQuantity++;
});
},
child: Icon(
Icons.add_circle,
color: Colors.green,
),
),
Text(
item.itemQuantity.toString(),
style: TextStyle(color: Colors.black),
),
GestureDetector(
onTap: () {
setState(() {
item.itemQuantity--;
});
},
child: Icon(
Icons.remove_circle,
color: Colors.red,
),
),
],
);
})))
.toList();
},
child: Container(
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Color(0xFFE6F0F9),
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
offset: Offset(5, 5),
blurRadius: 8,
),
BoxShadow(
color: Colors.white.withOpacity(0.5),
offset: Offset(-5, -5),
blurRadius: 8,
)
]),
child: Icon(
Icons.shopping_cart,
color: Color(0xFF67696F),
)),
))
],
),
),
Padding(
padding: EdgeInsets.all(15.0),
child: TextField(
controller: textController,
decoration: InputDecoration(
hintText: 'Search',
labelText: 'Search',
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25.0))),
suffix: GestureDetector(
child: Icon(Icons.clear),
onTap: () {
textController.clear();
setState(() {
items = tempMedical;
});
},
)),
onChanged: (value) {
filteredSearch(value.toUpperCase());
},
),
),
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return Material(
color: Color(0xFFE6F0F9),
child: InkWell(
child: ListTile(
onTap: () {
//SystemSound.play(SystemSoundType.click);
_showBar('${items[index]}');
widget.callBack(items[index]);
tempName = '${items[index]}';
},
title: Text(
'${items[index]}',
style: TextStyle(color: Color(0xFF67696F)),
),
trailing: Icon(
Icons.add_circle_outline,
color: Colors.green,
size: 30,
),
),
),
);
})),
],
),
),
);
}
}
class CartItems {
final String cartItem;
int itemQuantity;
CartItems({this.cartItem, this.itemQuantity: 1});
}
The popmenu itself has no state, so the setState
doesn't affect it. The easiest way to fix that is making each item of the menu a Stateful Widget. Something like this:
class CartSelector extends StatefulWidget {
CartSelector({Key key, this.item}) : super(key: key);
final CartItems item;
@override
_CartSelectorState createState() => _CartSelectorState();
}
class _CartSelectorState extends State<CartSelector> {
CartItems item;
@override
void initState() {
super.initState();
item = widget.item;
}
@override
Widget build(BuildContext context) {
return Container(
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Flexible(
child: Text(
'${item.cartItem}',
style: TextStyle(color: Colors.grey, fontWeight: FontWeight.w500),
),
),
Spacer(),
GestureDetector(
onTap: () {
setState(() {
item.itemQuantity++;
});
},
child: Icon(
Icons.add_circle,
color: Colors.green,
),
),
Text(
item.itemQuantity.toString(),
style: TextStyle(color: Colors.black),
),
GestureDetector(
onTap: () {
setState(() {
item.itemQuantity--;
});
},
child: Icon(
Icons.remove_circle,
color: Colors.red,
),
),
],
),
);
}
}
And then replace it in the PopMenu
builder.
return totalItem.map((item) => PopupMenuItem(
value: item.cartItem,
child: CartSelector(item: item),
)).toList();
This code probably need some polish, but I think it will do the work. Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With