I'm trying to build a system just like tinder where there is a stack of cards a user can swipe and the next few cards are always preloaded.
To do that, I have a Stack
widget that build a series of cards child widgets and give them the id of the content to load:
class PostCardStack extends StatefulWidget {
const PostCardStack({Key key, this.postIds, this.onCardDismissed})
: super(key: key);
final List<String> postIds;
final Function onCardDismissed;
@override
_PostCardStackState createState() => _PostCardStackState();
}
class _PostCardStackState extends State<PostCardStack> {
ValueNotifier<double> _notifier = ValueNotifier<double>(0.0);
_buildCardStack() {
List<Widget> cards = [];
for (String postId in widget.postIds) {
int idx = widget.postIds.indexOf(postId);
if (postId == widget.postIds.first) {
cards.add(CustomDismissible(
resizeDuration: null,
dismissThresholds: {CustomDismissDirection.horizontal: 0.2},
notifier: _notifier,
key: Key(postId),
onDismissed: (direction) {
_notifier.value = 0.0;
widget.onCardDismissed(postId);
},
child: SlidablePanel(
panel: AnimatedBuilder(
animation: _notifier,
child: PostCard(
postId: postId,
),
builder: (context, _) {
return Opacity(
opacity: 1 - _notifier.value,
child: PostCard(
postId: postId,
));
}),
)));
} else {
cards.add(AnimatedBuilder(
animation: _notifier,
child: PostCard(
postId: postId,
),
builder: (context, _) {
return Opacity(
opacity: lerpDouble(1 - (0.1 * idx), 1 - ((0.1 * idx) - 0.1),
_notifier.value),
child: Transform(
origin: null,
alignment: Alignment.bottomCenter,
transform: Matrix4.translationValues(
0.0,
lerpDouble(
-idx * 35, (-idx * 35 + 35), _notifier.value),
0.0)
..scale(
lerpDouble(1 - (0.1 * idx), 1 - ((0.1 * idx) - 0.1),
_notifier.value),
lerpDouble(1 - (0.1 * idx), 1 - ((0.1 * idx) - 0.1),
_notifier.value),
1),
child: PostCard(
postId: postId,
)));
}));
}
}
return cards.reversed.toList();
}
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.bottomCenter, children: _buildCardStack());
}
}
In the PostCard
widget, I use the postId
param to fetch the card information and build it when it is ready.
class PostCard extends StatefulWidget {
const PostCard({Key key, this.postId}) : super(key: key);
final String postId;
@override
_PostCardState createState() => _PostCardState();
}
class _PostCardState extends State<PostCard> {
PostModel post;
@override
void initState() {
super.initState();
print("${widget.postId} mounted");
_fetchPost(widget.postId);
}
@override
void dispose() {
print("${widget.postId} disposed");
super.dispose();
}
_fetchPost(String postId) async {
PostModel fullPost = await blablaFirestore(postId);
setState(() {
post = fullPost;
});
}
@override
Widget build(BuildContext context) {
return Container(
height: 300,
child: Align(
alignment: Alignment.topCenter,
child: Text(post == null ? "Loading..." : post.text),
),
);
}
}
Everything works fine except when any element of the list of id in the Stack component changes, all the child cards gets rebuilt, therefore loosing their previously loaded state and have to fetch the data again.
If only one element of the list of id change, why are every cards rebuilding? Am I missing something here? :)
Edit: In the question marked as duplicate, the problem is the build method having side effects. I don't believe that this is the case here. In my case, the problem is that state of the PostCards widget is not kept even tho they rebuild with the exact same param (postId)
Cheers!
When setState
is called the build
function is called again, and all the PostCard
widget will be recreated since they are created in the build
function.
This said, if PostCard
is a Stateful
widget the state should not be destroyed (eg initState will not be called on them). But you will likely loose the reference to them, which might be why your code is not behaving as you expect (hard to tell from your code).
Maybe you should make the widget where this piece of code is called a Stateful
widget and create a List<PostCard> postCards
variable to store your cards, this way you can initialize postCards
in your initState
function and they will not be recreated when you call setState
, thus preserving the reference.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.