gpt4 book ai didi

flutter - 为什么我的 StatefulWidget 会丢失状态?

转载 作者:行者123 更新时间:2023-12-02 12:13:12 32 4
gpt4 key购买 nike

我正在构建一个类似tinder的卡片刷卡机制,其中我在Stack小部件中有一个卡片小部件列表。第一张卡被包装在一个 Dismissible 小部件中,在关闭时,关闭顶部的卡并在底部添加一个新的卡。

我的问题是,当第一张卡被关闭并且第二张卡成为第一张卡时,该卡会立即被处理并再次调用initState

但是,正如 Linus Torvalds 曾经说过的,

"Talk is cheap, show me the code"

所以我整理了一个例子:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Container(color: Colors.white, child: CardStack()),
);
}
}

class CardStack extends StatefulWidget {
@override
_CardStackState createState() => _CardStackState();
}

class _CardStackState extends State<CardStack> {
final List<String> _postIds = ["1", "2", "3"];
int _nextId = 4;

_updatePosts(DismissDirection dir) {
setState(() {
_postIds.removeAt(0);
_postIds.add((_nextId++).toString());
});
}

List<Widget> _buildCards() {
List<Widget> cards = [];
for (int i = 0; i < _postIds.length; ++i) {
if (i == 0) {
cards.insert(
0,
Dismissible(
key: UniqueKey(),
onDismissed: _updatePosts,
child: Card(key: ValueKey(_postIds[i]), postId: _postIds[i]),
));
} else {
cards.insert(0, Card(key: ValueKey(_postIds[i]), postId: _postIds[i]));
}
}
return cards;
}

@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: _buildCards(),
);
}
}

class Card extends StatefulWidget {
Card({Key key, this.postId}) : super(key: key);

final String postId;

@override
_CardState createState() => _CardState();
}

class _CardState extends State<Card> {
@override
void initState() {
super.initState();
print("Init state card with id ${widget.postId}");
}

@override
Widget build(BuildContext context) {
return Container(
height: 200,
width: 200,
color: Colors.blueAccent,
child: Center(
child: Text(widget.postId),
),
);
}
}

如果我运行它会记录

I/flutter (26740): Init state card with id 3
I/flutter (26740): Init state card with id 2
I/flutter (26740): Init state card with id 1
// swipe
I/flutter (26740): Init state card with id 4
I/flutter (26740): Init state card with id 2 <- unwanted rebuild

我认为第一张卡片的重建是因为它现在被包装在 Dismissible 小部件中,因此 flutter 不知道如何重用底层的 Card 小部件。

有没有办法阻止这种不必要的重建?

最佳答案

问题是,您的 StatefulWidget 的位置正在小部件树内发生变化。

您的 StatefulWidget 可能有条件地包装在特定的小部件内,这样您的小部件树就会在以下之间变化:

SomeWidget
MyStatefulWidget

和:

SomeWidget
AnotherWidget
MyStatefulWidget

默认情况下,Flutter 不会理解 MyStatefulWidget 在 widget 树中移动

相反,它会认为之前的 MyStatefulWidget 已被删除,并且新的与原始的完全无关。

这会导致在之前的 State 上调用 dispose,并在新的 State 上调用 initState .

<小时/>

为了防止这种情况发生,您有一些解决方案。

不要有条件地将您的小部件包装在另一个小部件中

如果可以的话,这是最好的解决方案:重构您的布局,使小部件树始终相同。

例如,如果您只想将 ListView 中的第一个小部件关闭,则可以:

ListView.builder(
itemBuilder: (_, index) {
final child = MyStatefulWidget();
if (index == 0) {
return Dismissible(key: ValueKey(index), onDismissed: ..., child: child);
}
}
)

做:

ListView.builder(
itemBuilder: (_, index) {
return Dismissible(
key: ValueKey(someValue),
onDismissed: ...,
confirmDismiss: (_) async => index == 0, // only first item can be dismissed
child: MyStatefulWidget();
);
}
)

这样,树不会有条件地改变,但行为仍然是有条件的

使用全局 key

不太理想,因为成本更高,但更灵活。

您可以使用 GlobalKey 告诉 Flutter 特定的 widget 可以从一个位置移动到另一个位置。

因此,我们可以写:

Widget build(BuildContext context) {
if (something) {
return SomeTree(
child: MyStatefulWidget(key: GlobalObjectKey('my widget'));
);
} else {
return AnotherTree(
child: MyStatefulWidget(key: GlobalObjectKey('my widget'));
);
}
}

这样,当从 SomeTree 转换到 AnotherTree 时,小部件 MyStatefulWidget 将保留其状态。

作为一个小提醒,如果可能的话,请避免使用它。这是相对昂贵的。

关于flutter - 为什么我的 StatefulWidget 会丢失状态?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59338209/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com