当前,当我单击添加按钮时,我正在尝试在添加和减少图标按钮之间的弹出菜单中更新文本。值增加,但是当我设置它时它不会更新。我必须关闭popupmenu并重新打开它才能看到新的更新值。任何改进我的代码的技巧将不胜感激。谢谢您的帮助。
这是我的代码:
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});
}
答案 0 :(得分:11)
您可以在下面复制粘贴运行完整代码
您可以将StatefulBuilder
用作PopupMenuItem
和return
Row
代码段
PopupMenuItem(
value: item.cartItem,
child: StatefulBuilder(builder:
(BuildContext context, StateSetter setState) {
return Row(
mainAxisSize: MainAxisSize.min,
工作演示
完整代码
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});
}
答案 1 :(得分:1)
弹出菜单本身没有状态,因此setState
不会影响它。最简单的解决方法是将菜单中的每个项目都设置为有状态的小部件。像这样:
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,
),
),
],
),
);
}
}
然后将其替换为PopMenu
构建器。
return totalItem.map((item) => PopupMenuItem(
value: item.cartItem,
child: CartSelector(item: item),
)).toList();
此代码可能需要改进,但我认为它将完成工作。希望这会有所帮助。
答案 2 :(得分:0)
您必须分配PopupMenuButton.initialValue
,
查看简单示例:
PopupMenuButton<int>(
itemBuilder: (context) => [
PopupMenuItem(
value: 1,
child: Text("First"),
),
PopupMenuItem(
value: 2,
child: Text("Second"),
),
],
initialValue: 2,
onCanceled: () {
print("You have canceled the menu.");
},
onSelected: (value) {
print("value:$value");
},
icon: Icon(Icons.list),
);
有关更多信息,请单击此链接: Widgets 14 | PopupMenuButton