如何更新内置在 FutureBuilder 中的小部件状态
How to update widget state built inside FutureBuilder
之前,我使用 ListBuilder
生成一个包含 70 个数字的列表并且它有效,但是将 70 个数字生成到自定义小部件中需要很长时间,而且当我点击一个数字只是为了更改背景时颜色状态,需要几毫秒才能更改状态。
现在我正在使用 FutureBuilder
来加载屏幕,同时等待生成的 70 个整数。但是当我点击球号时,背景颜色没有更新......这就像 setState()
在 Future ListBuilder 中不起作用。
这个问题:“ ”很像,但是没有解决我的问题
这是我在构建方法中的代码
Flexible(
child:FutureBuilder<List<Widget>>(
future: ballNumbers,
builder: (context, snapshot){
if(snapshot.connectionState != ConnectionState.done){
return Center(child: CircularProgressIndicator());
}
if(snapshot.hasError){
return Center(child: Text("An error has occured"));
}
List<Widget> balls = snapshot.data ?? [];
return GridView.count(
crossAxisCount: 9,
children: balls,
);
}
)
以下是我如何启动函数的状态:
Future<List<Widget>> ballNumbers;
List<int> picks = [];
@override
void initState() {
ballNumbers = getBallNumbers();
});
Future<List<Widget>> getBallNumbers() async {
return List.generate(limitBallNumber,(number){
number = number + 1;
return Padding(
padding:EdgeInsets.all(2.5),
child:Ball(
number : number,
size: ballWidth,
textColor:(picks.contains(number)) ? Colors.black : Colors.white,
ballColor: (picks.contains(number)) ? Style.selectedBallColor : Style.ballColor,
onTap:(){
setState((){
picks.contains(number) ? picks.remove(number) : picks.add(number);
});
}
)
);
});
}
更新:这是 class Ball
小部件
class Ball extends StatelessWidget {
final Color ballColor;
final Color textColor;
final double size;
final double fontSize;
final int number;
final VoidCallback onTap;
Ball({Key key, @required this.number,
this.textColor,
this.ballColor,
this.onTap,
this.size = 55.0,
this.fontSize = 14,
}) : super(key : key);
@override
Widget build(BuildContext context) {
return Container(
height: size,
width: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [
Style.secondaryColor,
ballColor != null ? ballColor : Style.ballColor,
],
begin: Alignment.bottomLeft,
end: Alignment.topRight
)
),
child: FlatButton(
padding: EdgeInsets.all(0),
child: Container(
child: Text(
number.toString().length > 1 ? number.toString() : "0" + number.toString(),
style: TextStyle(
fontSize: fontSize,
color: textColor != null ? textColor : Colors.white
),
),
padding: const EdgeInsets.all(4.0),
decoration:BoxDecoration(
color: Colors.transparent,
border: Border.all(color: textColor != null ? textColor : Colors.white, width: 1),
borderRadius: BorderRadius.circular(32),
)
),
color: Colors.transparent,
onPressed: onTap,
),
);
}
}
问题是 getBallNumbers
仅在 initState
中被调用一次,因此当 picks
更新时,这无关紧要,因为 getBallNumbers
不是再次调用以更新传递给 Ball
小部件的颜色。
一个简单的解决方法是在 build
中用 future: getBallNumbers()
调用 getBallNumbers
,但这会导致 CircularProgressIndicator
在每次点击时显示为 List
再生。
然而,理想情况下,您应该处理每个 Ball
状态内的所有颜色变化,这样您就不会被迫在每次点击时重建 List
。并且要在父小部件的 State
中维护所选数字的 List
,您应该将回调传递给每个球,在父小部件的 List
中添加和删除它们的数字。
粗略示例:
球 class(修改为有状态并删除了不必要的参数;活动状态现在存储在球中,而不是仅存储在父级中):
class Ball extends StatefulWidget {
final double size;
final double fontSize;
final int number;
final VoidCallback toggleBall;
final bool initialActiveState;
Ball({Key key, @required this.number,
this.toggleBall,
this.size = 55.0,
this.fontSize = 14,
this.initialActiveState,
}) : super(key : key);
_BallState createState() => _BallState();
}
class _BallState extends State<Ball> {
bool isActive;
@override
void initState() {
super.initState();
isActive = widget.initialActiveState;
}
@override
Widget build(BuildContext context) {
return Container(
height: widget.size,
width: widget.size,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [
Style.secondaryColor,
isActive ? Style.selectedBallColor : Style.ballColor,
],
begin: Alignment.bottomLeft,
end: Alignment.topRight
)
),
child: FlatButton(
padding: EdgeInsets.all(0),
child: Container(
child: Text(
widget.number.toString().length > 1 ? widget.number.toString() : "0" + widget.number.toString(),
style: TextStyle(
fontSize: widget.fontSize,
color: isActive ? Colors.black : Colors.white,
),
),
padding: const EdgeInsets.all(4.0),
decoration:BoxDecoration(
color: Colors.transparent,
border: Border.all(color: isActive ? Colors.black : Colors.white, width: 1),
borderRadius: BorderRadius.circular(32),
)
),
color: Colors.transparent,
onPressed: () {
if(!isActive && widget.activeBallList.length >= 7) {
return;
}
setState(() {
isActive = !isActive;
});
widget.activeBallList.contains(widget.number) ? widget.activeBallList.remove(widget.number) : widget.activeBallList.add(widget.number);
},
),
);
}
}
Parent class(唯一需要修改的部分是Ball
的参数):
Future<List<Widget>> getBallNumbers() async {
return List.generate(limitBallNumber,(number){
number = number + 1;
return Padding(
padding:EdgeInsets.all(2.5),
child: Ball(
number: number,
size: ballWidth,
initialActiveState: picks.contains(number),
activeBallList: picks,
)
);
});
}
之前,我使用 ListBuilder
生成一个包含 70 个数字的列表并且它有效,但是将 70 个数字生成到自定义小部件中需要很长时间,而且当我点击一个数字只是为了更改背景时颜色状态,需要几毫秒才能更改状态。
现在我正在使用 FutureBuilder
来加载屏幕,同时等待生成的 70 个整数。但是当我点击球号时,背景颜色没有更新......这就像 setState()
在 Future ListBuilder 中不起作用。
这个问题:“
这是我在构建方法中的代码
Flexible(
child:FutureBuilder<List<Widget>>(
future: ballNumbers,
builder: (context, snapshot){
if(snapshot.connectionState != ConnectionState.done){
return Center(child: CircularProgressIndicator());
}
if(snapshot.hasError){
return Center(child: Text("An error has occured"));
}
List<Widget> balls = snapshot.data ?? [];
return GridView.count(
crossAxisCount: 9,
children: balls,
);
}
)
以下是我如何启动函数的状态:
Future<List<Widget>> ballNumbers;
List<int> picks = [];
@override
void initState() {
ballNumbers = getBallNumbers();
});
Future<List<Widget>> getBallNumbers() async {
return List.generate(limitBallNumber,(number){
number = number + 1;
return Padding(
padding:EdgeInsets.all(2.5),
child:Ball(
number : number,
size: ballWidth,
textColor:(picks.contains(number)) ? Colors.black : Colors.white,
ballColor: (picks.contains(number)) ? Style.selectedBallColor : Style.ballColor,
onTap:(){
setState((){
picks.contains(number) ? picks.remove(number) : picks.add(number);
});
}
)
);
});
}
更新:这是 class Ball
小部件
class Ball extends StatelessWidget {
final Color ballColor;
final Color textColor;
final double size;
final double fontSize;
final int number;
final VoidCallback onTap;
Ball({Key key, @required this.number,
this.textColor,
this.ballColor,
this.onTap,
this.size = 55.0,
this.fontSize = 14,
}) : super(key : key);
@override
Widget build(BuildContext context) {
return Container(
height: size,
width: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [
Style.secondaryColor,
ballColor != null ? ballColor : Style.ballColor,
],
begin: Alignment.bottomLeft,
end: Alignment.topRight
)
),
child: FlatButton(
padding: EdgeInsets.all(0),
child: Container(
child: Text(
number.toString().length > 1 ? number.toString() : "0" + number.toString(),
style: TextStyle(
fontSize: fontSize,
color: textColor != null ? textColor : Colors.white
),
),
padding: const EdgeInsets.all(4.0),
decoration:BoxDecoration(
color: Colors.transparent,
border: Border.all(color: textColor != null ? textColor : Colors.white, width: 1),
borderRadius: BorderRadius.circular(32),
)
),
color: Colors.transparent,
onPressed: onTap,
),
);
}
}
问题是 getBallNumbers
仅在 initState
中被调用一次,因此当 picks
更新时,这无关紧要,因为 getBallNumbers
不是再次调用以更新传递给 Ball
小部件的颜色。
一个简单的解决方法是在 build
中用 future: getBallNumbers()
调用 getBallNumbers
,但这会导致 CircularProgressIndicator
在每次点击时显示为 List
再生。
然而,理想情况下,您应该处理每个 Ball
状态内的所有颜色变化,这样您就不会被迫在每次点击时重建 List
。并且要在父小部件的 State
中维护所选数字的 List
,您应该将回调传递给每个球,在父小部件的 List
中添加和删除它们的数字。
粗略示例:
球 class(修改为有状态并删除了不必要的参数;活动状态现在存储在球中,而不是仅存储在父级中):
class Ball extends StatefulWidget {
final double size;
final double fontSize;
final int number;
final VoidCallback toggleBall;
final bool initialActiveState;
Ball({Key key, @required this.number,
this.toggleBall,
this.size = 55.0,
this.fontSize = 14,
this.initialActiveState,
}) : super(key : key);
_BallState createState() => _BallState();
}
class _BallState extends State<Ball> {
bool isActive;
@override
void initState() {
super.initState();
isActive = widget.initialActiveState;
}
@override
Widget build(BuildContext context) {
return Container(
height: widget.size,
width: widget.size,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [
Style.secondaryColor,
isActive ? Style.selectedBallColor : Style.ballColor,
],
begin: Alignment.bottomLeft,
end: Alignment.topRight
)
),
child: FlatButton(
padding: EdgeInsets.all(0),
child: Container(
child: Text(
widget.number.toString().length > 1 ? widget.number.toString() : "0" + widget.number.toString(),
style: TextStyle(
fontSize: widget.fontSize,
color: isActive ? Colors.black : Colors.white,
),
),
padding: const EdgeInsets.all(4.0),
decoration:BoxDecoration(
color: Colors.transparent,
border: Border.all(color: isActive ? Colors.black : Colors.white, width: 1),
borderRadius: BorderRadius.circular(32),
)
),
color: Colors.transparent,
onPressed: () {
if(!isActive && widget.activeBallList.length >= 7) {
return;
}
setState(() {
isActive = !isActive;
});
widget.activeBallList.contains(widget.number) ? widget.activeBallList.remove(widget.number) : widget.activeBallList.add(widget.number);
},
),
);
}
}
Parent class(唯一需要修改的部分是Ball
的参数):
Future<List<Widget>> getBallNumbers() async {
return List.generate(limitBallNumber,(number){
number = number + 1;
return Padding(
padding:EdgeInsets.all(2.5),
child: Ball(
number: number,
size: ballWidth,
initialActiveState: picks.contains(number),
activeBallList: picks,
)
);
});
}