Reputation: 1208
I use PopupMenuButton to edit the listView. For every action PopupMenuButton (remove item, add item, edit item in listView) trying to do Callback. For example, the simplest case is to delete
//_editorChannels - data with which I fill in the list
delete(dynamic val) {
setState(() => _editorChannels.removeWhere((data) => data == val));
}
But since I pass the callback function delete
to the widget, when I create it I get error setState() or markNeedsBuild called during build. If change and remove the setState()
, then no error occurs, but of course the list will not be updated when the item is deleted via callback.
delete(dynamic val) {
_editorChannels.removeWhere((data) => data == val);
}
My PopupMenuButton Widget
class PopMenuWidget2 extends StatelessWidget {
final VoidCallback onDelete;
const PopMenuWidget2({Key key, this.onDelete}) : super(key: key);
@override
Widget build(BuildContext context) => PopupMenuButton<int>(
onSelected: (result) {
//On View
if (result == 0) {}
//On Edit
if (result == 1) { }
//OnDelete Callback run
if (result == 2) {
onDelete();
}
},
itemBuilder: (context) => [
PopupMenuItem(
value: 0,
child: Row(
children: <Widget>[
Icon(
Icons.remove_red_eye_rounded,
color: Colors.black38,
),
Text(' View Option', style: TextStyle(color: Colors.black38)),
],
),
),
PopupMenuItem(
value: 1,
child: Row(
children: <Widget>[
Icon(
Icons.create,
color: Colors.black38,
),
Text(' Edit Option', style: TextStyle(color: Colors.black38)),
],
),
),
PopupMenuItem(
value: 2,
child: Row(
children: <Widget>[
Icon(
Icons.delete,
color: Colors.black38,
),
Text(' Delete Option',
style: TextStyle(color: Colors.black38)),
],
),
),
],
);
}
Main Widget
class EditorPage extends StatefulWidget {
EditorPage({Key key, this.Channels}) : super(key: key);
final List<Channel> Channels;
static const String routeName = "/EditorPage";
@override
_EditorPageState createState() => new _EditorPageState();
}
class _EditorPageState extends State<EditorPage> {
Stream<Mock> _result;
final _coreObj = new Core();
List<Channel> _editorChannels;
//callback onDelete
delete(dynamic val) {
setState(() => _editorChannels.removeWhere((data) => data == val));
}
@override
void initState() {
_editorChannels = widget.Channels;
super.initState();
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: new Scaffold(
appBar: AppBar(
title: Text(''),
bottom: TabBar(tabs: [
Tab(icon: FaIcon(FontAwesomeIcons.calendarCheck), text: "One"),
Tab(icon: FaIcon(FontAwesomeIcons.tasks), text: "Two"),
]),
),
body: SafeArea(
child: TabBarView(children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Scaffold(
body: ListView.builder(
itemCount: _editorChannels == null
? 0
: _editorChannels.length,
itemBuilder: (context, index) {
final item = _editorChannels[index];
return Card(
shadowColor: Colors.black26,
margin: EdgeInsets.all(3.0),
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(2),
),
child: ListTile(
title: Container(
child: Text(
item.Name != null ? item.Name : '',
style: new TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0),
)),
subtitle: Text(item.Url),
onTap: () => {},
isThreeLine: false,
leading: getIconbyId(item.Status),
//Pass callback
trailing: PopMenuWidget2(
onDelete: delete(item),
)),
);
}),
),
)
],
),
]))));
}
}
Upvotes: 0
Views: 918
Reputation: 20118
Instead of using VoidCallback
you have to define custom type:
typedef MyCustomCallback<T> = void Function(T value);
and change onDelete definition to:
final MyCustomCallback onDelete;
finally fix the callback invocation to:
if (result == 2) {
onDelete(value);
}
// ...
trailing: PopMenuWidget2(
onDelete: (item) => delete(item),
)),
Upvotes: 1
Reputation:
For setState()
to work you need to have PopMenuWidget2
as StatefulWidget
. Change
class PopMenuWidget2 extends StatelessWidget {
to
class PopMenuWidget2 extends StatefulWidget {
Also
Pass you callback function like link on it, not like result of function execution
onDelete: () => onDelete(item)
trailing: PopMenuWidget(
onDelete: () => onDelete(item),
)),
Full Code:
class PopMenuWidget2 extends StatefulWidget {
final VoidCallback onDelete;
const PopMenuWidget2({Key key, this.onDelete}) : super(key: key);
@override
_PopMenuWidget2State createState() => _PopMenuWidget2State();
}
class _PopMenuWidget2State extends State<PopMenuWidget2> {
@override
Widget build(BuildContext context) => PopupMenuButton<int>(
onSelected: (result) {
//On View
if (result == 0) {}
//On Edit
if (result == 1) { }
//OnDelete Callback run
if (result == 2) {
onDelete();
}
},
itemBuilder: (context) => [
PopupMenuItem(
value: 0,
child: Row(
children: <Widget>[
Icon(
Icons.remove_red_eye_rounded,
color: Colors.black38,
),
Text(' View Option', style: TextStyle(color: Colors.black38)),
],
),
),
PopupMenuItem(
value: 1,
child: Row(
children: <Widget>[
Icon(
Icons.create,
color: Colors.black38,
),
Text(' Edit Option', style: TextStyle(color: Colors.black38)),
],
),
),
PopupMenuItem(
value: 2,
child: Row(
children: <Widget>[
Icon(
Icons.delete,
color: Colors.black38,
),
Text(' Delete Option',
style: TextStyle(color: Colors.black38)),
],
),
),
],
);
}
Upvotes: 1