我有一个 Stateful 主页,其中有一个 Stateful widget 子组件的列表。当我点击一个孩子时,我会调用它的 setState()
来向它添加一个 CircularProgressIndicator。这一切都很好。点击一个孩子只会重建那个孩子。
但是,我的主页也包含在 AbsorbPointer
中,并且我想在单击子小部件时设置 absorbing = true
。目标是阻止用户在应用程序在后台执行一些异步工作时四处点击。现在的问题是,如果我在主页中调用 setState()
将“吸收”设置为 true,它将重建所有子小部件。
我可以将一些参数传递给子小部件,这样只有我点击的小部件才会有一个 CircularProgressIndicator,但即便如此,所有其他小部件仍将被重建。
我想这归结为这样一个事实,即我无法在不重建所有子级的情况下在父级小部件上调用 setState(),即使我传递给该 setState() (absorbing
) 的参数没有任何内容与那些孩子一起做。
是否有解决方法?
谢谢!
// home_screen.dart
class HomeScreen extends StatefulWidget {
static const String routeName = "homeScreen";
final MyUser? user;
const HomeScreen({Key? key, required this.user}) : super(key: key);
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
final MyDatabase _db = MyDatabase();
MyUser? _me;
int _currentPage = -1;
bool _isLoading = false;
...
@override
Widget build(BuildContext context) {
return AbsorbPointer(
absorbing: _isLoading,
child: Container(
decoration: BoxDecoration(
// color: Color(0xFF0d0717),
image: DecorationImage(
image: Image.asset(
'assets/background.png',
).image,
fit: BoxFit.cover,
),
),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: ...,
bottomNavigationBar: ...,
body: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 12.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StreamBuilder<QuerySnapshot>(
stream: _db.getLiveChannels(),
builder: (_, snapshot) {
if (!snapshot.hasData) {
// print("Has no data");
return Center(
child: CircularProgressIndicator(),
);
}
_channels.addAll(List.generate(
snapshot.data!.docs.length,
(index) => Channel.fromSnapshot(
snapshot.data!.docs[index])));
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'Now Playing',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.w700,
),
),
SizedBox(width: 8.0),
LiveIndicator(),
],
),
SizedBox(height: 8.0),
Container(
height: 250,
child: PageView.builder(
physics: PageScrollPhysics(),
scrollDirection: Axis.horizontal,
controller: PageController(
viewportFraction: .9,
),
itemCount: _channels.length,
itemBuilder: (context, index) {
Channel channel =
_channels[_channels.length - 1 - index];
return ChildWidget(
callback: _callback;
loading: (_isLoading && _currentPage == index),
key: UniqueKey(),
);
},
),
),
...,
],
);
},
),
...,
],
),
),
),
),
),
);
}
Future<void> _callback(params) async {
if (_isLoading == false) {
setState(() {
_isLoading = true;
_currentPage = index;
});
}
someAsyncMethod().then((_) => setState(() {
_isLoading = false;
_currentPage = -1;
}));
}
}
// child_widget.dart
class ChildWidget extends StatefulWidget {
final Future<void> Function(params) callback;
final bool loading;
const ChildWidget({
Key? key,
required this.callback,
required this.loading,
}) : super(key: key);
@override
_ChildWidgetState createState() => _ChildWidgetState();
}
class _ChildWidgetState extends State<ChildWidget> {
late Future<void> Function(params) callback;
late bool loading;
@override
void initState() {
super.initState();
callback = widget.callback;
loading = widget.loading;
}
@override
Widget build(BuildContext context) {
return Padding(
child: Column(
children: [
Expanded(
child: CustomClickableWidget(
onPressed: callback,
child: Expanded(
child: Container(
child: Stack(
children: [
...,
if (loading) ...[
Container(
alignment: Alignment.center,
child: CircularProgressIndicator(),
),
],
],
),
),
),
),
),
...,
],
),
);
}
}
答案 0 :(得分:0)
SetState 函数会触发 Build() 函数,因此 Build() 函数中存在的所有代码都将再次执行。我不太明白为什么这对你来说是个问题?
另一方面,在您的代码中,我看到您为您的孩子定义了一个键:UniqueKey ()。当 build 函数在 SetState() 之后运行时,它会创建一个新的孩子,而不保留前一个孩子的状态。你不应该在你的函数中定义 UniqueKey () 而是作为你状态的实例变量
ChildWidget(callback: _callback;
loading: (_isLoading && _currentPage == index),
key: UniqueKey(),
)
你应该在这里定义你的密钥
class _ChildWidgetState extends State<ChildWidget> {
UniqueKey myKey = UniqueKey();
你的功能
ChildWidget(callback: _callback;
loading: (_isLoading && _currentPage == index),
key: myKey,
)