Closed jonfrank closed 5 years ago
I think you can using setState() for refresh your list. Or you can post your code here.
@jonfrank if you have not resolved please post your code here.
Sorry for the delay.
I'm holding a singleton object called TagList which has an app-wide set of tags. And then in this particular screen I want to show all those tags with only some of them selected (the ones which apply to the particular object being displayed).
In my build method I create a list of tags to feed into SelectableTags which has the full list from TagList() but some are active and some are not. Debug shows that this gets created correctly each time there's a build - but the new set of tags doesn't get displayed.
So the particular issue for me is when I edit the app-wide tags in TagList() using the TagsPage widget (the last sample of code here) it returns to the main screen, the list of tags gets correctly recreated, but no display update. If I back up one level further, and then back into the screen, it refreshes OK.
[It is of course entirely possible that I'm doing something stupid which has nothing to do with flutter_tags... I am a flutter novice so apologies if so.]
Here's my TagList object:
class TagList {
// singleton creation
static final TagList _tagslist = new TagList._internal();
factory TagList() {
return _tagslist;
}
TagList._internal();
List<Tag> tags;
int nextId = 1;
int nextUnusedIdFromTags(List<Tag> tags) {
int result = 0;
for (Tag t in tags) {
if (t.id > result) {
result = t.id;
}
}
return ++result;
}
TagList setTags(List<Tag> tags) {
this.tags = tags;
this.nextId = nextUnusedIdFromTags(tags);
return this;
}
TagList setTagsFromObject(TagList tags) {
this.tags = tags.getTagsList();
this.nextId = nextUnusedIdFromTags(this.tags);
return this;
}
List<Tag> getTagsList() {
return this.tags;
}
List<String> getStringTagsList() {
final List<String> result = [];
for (Tag t in this.tags) {
result.add(t.title);
}
return result;
}
Tag tagAtIndex(int index) {
return this.tags[index];
}
int numberOfTags() {
if (tags == null) {
return 0;
} else {
return tags.length;
}
}
List<Tag> listWithTheseTagsActivated(List<String> aList) {
// this returns the tag list but activates only those which are listed in the parameter
// so we preserve proper ids but update active flag in the list copy
List<Tag> result = [];
for (Tag t in this.tags) {
result.add(Tag(
title: t.title,
id: t.id,
active: aList.contains(t.title),
));
}
return result;
}
void addStringTag(String toAdd) {
Tag newTag = Tag(title: toAdd, active: true, id: nextId++);
tags.add(newTag);
}
bool deleteTag(Tag toDel) {
int index = this.tags.indexOf(toDel);
if (index == -1) {
return false;
} else {
this.tags.removeRange(index, index + 1);
return true;
}
}
bool deleteStringTag(String toDel) {
Tag tagToDelete;
for (Tag t in this.tags) {
if (t.title == toDel) {
tagToDelete = t;
break;
}
}
if (tagToDelete == null) {
return false;
} else {
return deleteTag(tagToDelete);
}
}
}
And then my display widget which doesn't get refreshed properly looks like this:
@override
Widget build(BuildContext context) {
tagSet = TagList().listWithTheseTagsActivated(tags); // tags comes from the object being displayed
return ListView(
children: [
Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
...
Padding(
padding:
const EdgeInsets.symmetric(vertical: 8.0, horizontal: 25.0),
child: SelectableTags(
tags: tagSet,
columns: 2,
fontSize: 12,
symmetry: true,
activeColor: Colors.greenAccent,
textActiveColor: Colors.black,
backgroundContainer: Colors.transparent,
alignment: MainAxisAlignment.start,
onPressed: (Tag tag) {
for (Tag t in tagSet) {
if (t.title == tag.title) {
t.active = !(t.active);
}
}
},
),
),
],
),
),
],
);
}
TagsPage widget:
import 'package:flutter_tags/input_tags.dart';
import 'package:flutter/material.dart';
import 'package:casebook/model/case.dart';
class TagsPage extends StatefulWidget {
@override
_TagsPageState createState() => _TagsPageState();
}
class _TagsPageState extends State<TagsPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.chevron_left),
onPressed: () => Navigator.of(context).pop({'data': 'test'}),
),
title: Text("Tags"),
),
body: Container(
color: Colors.lime,
child: Center(
child: Container(
padding: const EdgeInsets.all(32.0),
child: ListView(
children: [
Text(
"Delete or insert tags",
),
InputTags(
tags: TagList().getStringTagsList(),
columns: 2,
fontSize: 16,
symmetry: true,
backgroundContainer: Colors.transparent,
alignment: MainAxisAlignment.start,
onDelete: (tag) {
setState(() {
if (!CaseList().tagIsUsed(tag)) {
TagList().deleteStringTag(tag);
}
});
},
onInsert: (tag) {
setState(() {
TagList().addStringTag(tag);
});
},
)
],
),
),
),
));
}
}
@jonfrank are you referring to this method?
onPressed: (Tag tag) {
for (Tag t in tagSet) {
if (t.title == tag.title) {
t.active = !(t.active);
}
}
},
I think the problem is due to the loss of the reference to the List
List<Tag> listWithTheseTagsActivated(List<String> aList) {
// this returns the tag list but activates only those which are listed in the parameter
// so we preserve proper ids but update active flag in the list copy
List<Tag> result = [];
for (Tag t in this.tags) {
result.add(Tag(
title: t.title,
id: t.id,
active: aList.contains(t.title),
));
}
return result;
}
I have a SelectableTags widget inside my build method. I also give the user the option to go to a page and edit tags using InputTags before returning to this main screen to select tags.
I can see it’s calling my method to rebuild the SelectableTags widget but what’s displayed doesn’t change. I’ve got it to print how many tags it is using when it rebuilds and so I know it’s picking up the updated tag list successfully, but what’s displayed doesn’t update.
Is there something I need to do to force the display to refresh??