Flutter构建玩家大厅时触发StackOverflowError问题求助
问题
我希望在游戏启动前构建玩家大厅,为此定义了8个包含slot_number和player属性的PlayerSlot对象,预期游戏创建者会显示在1号槽位,但运行时触发了StackOverflowError。相关代码如下:
class WaitingPlayersPage extends Stateful Widget { final Player creatorOfLobby; WaitingPlayersPage(this.creatorOfLobby); @override State<WaitingPlayersPage> createState() => WaitingPlayersPageState(); } class WaitingPlayersPageState extends State<WaitingPlayersPage> { double rangeSlider = 1; PlayerSlot _playerSlot1 = PlayerSlot(1 ,false, null); PlayerSlot _playerSlot2 = PlayerSlot(2 ,false, null); PlayerSlot _playerSlot3 = PlayerSlot(3 ,false, null); PlayerSlot _playerSlot4 = PlayerSlot(4 ,false, null); PlayerSlot _playerSlot5 = PlayerSlot(5 ,false, null); PlayerSlot _playerSlot6 = PlayerSlot(6 ,false, null); PlayerSlot _playerSlot7 = PlayerSlot(7 ,false, null); PlayerSlot _playerSlot8 = PlayerSlot(8 ,false, null); @override void initState() { _playerSlot1.fillSlotWithPlayer(widget.creatorOfLobby); } @override Widget build(BuildContext context) { List<PlayerSlot> multiPlayerSlot = [ _playerSlot1, _playerSlot2, _playerSlot3,_playerSlot4, _playerSlot5, _playerSlot6, _playerSlot7, _playerSlot8 ]; return Scaffold( body: Container( // WHOLE SCREEN alignment: Alignment.center, color: Color.fromARGB(255, 236, 131, 166), child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ SizedBox(height: 30,), Container( // BOX FOR PLAYERS decoration: BoxDecoration( borderRadius: BorderRadius.circular(10.0), color: Colors.pink, ), width: 340, height: 500, child: Column( children: [ const Padding(padding: EdgeInsets.symmetric(vertical: 12.0)), _playerSlot1, const Padding(padding: EdgeInsets.symmetric(vertical: 8.0)), _playerSlot2, const Padding(padding: EdgeInsets.symmetric(vertical: 8.0)), _playerSlot3, const Padding(padding: EdgeInsets.symmetric(vertical: 8.0)), _playerSlot4, const Padding(padding: EdgeInsets.symmetric(vertical: 8.0)), _playerSlot5, const Padding(padding: EdgeInsets.symmetric(vertical: 8.0)), _playerSlot6, const Padding(padding: EdgeInsets.symmetric(vertical: 8.0)), _playerSlot7, const Padding(padding: EdgeInsets.symmetric(vertical: 8.0)), _playerSlot8, ], ), ), Padding(padding: EdgeInsets.symmetric(vertical: 8.0)), Container( width: 1000, child: Slider( label: 'Number of round(s) : $rangeSlider', value: rangeSlider, onChanged: (double newRangeSlider) { setState(() { rangeSlider = newRangeSlider; }); }, min: 1, max: 10, divisions: 9, ), ), Padding(padding: EdgeInsets.symmetric(vertical: 8.0)), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: () {}, child: Container( padding: EdgeInsets.all(16.0), width: 320, alignment: Alignment.center, child: Text('CANCEL', style: TextStyle(color: Color.fromARGB(255, 236, 131, 166), decoration: TextDecoration.none),), ), ), Padding(padding: EdgeInsets.symmetric(vertical: 8.0)), ElevatedButton( onPressed: () { GlobalKey gameGlobalkey = GlobalKey(); //initGame(gameGlobalkey, ,rangeSlider); }, child: Container( alignment: Alignment.center, width: 320, padding: EdgeInsets.all(16.0), child: Text('START', style: TextStyle(color: Color.fromARGB(255, 236, 131, 166), decoration: TextDecoration.none),), ), ),]) ], ), ), ); } } class PlayerSlot extends StatelessWidget { int slot_number; bool filled = false; PlayerSlot(this.slot_number, this.filled,Player? p); Player get p => Player('Player $slot_number'); set p(Player newPlayer) => p = newPlayer; void fillSlotWithPlayer(Player player) { filled = true; p = player; } @override Widget build(BuildContext context) { String writeInSlot = filled == true ? p.name : ''; return Container( // ONE PLAYER BOX decoration: BoxDecoration( borderRadius: BorderRadius.circular(10.0), color: Colors.pinkAccent, ), width: 300, height: 40, child: Row( children: [ Container( width: 160, height: 30, child: Text(writeInSlot, style: TextStyle(color: Color.fromARGB(255, 236, 131, 166), decoration: TextDecoration.none, fontSize: 18, fontWeight: FontWeight.bold))), Padding(padding: EdgeInsets.symmetric(horizontal: 40.0)), IconButton( onPressed: () {}, iconSize: 24, icon: Icon(Icons.cancel), color: Color.fromARGB(255, 236, 131, 166), ), ], ), ); } }
Player类代码:
class Player { String name; Player(this.name); set playerName(String newName) => name = newName; void bindPlayerToCharacter(List<Character> c) { } }
问题根源
StackOverflowError的直接原因是PlayerSlot类中对p的setter实现错误:
set p(Player newPlayer) => p = newPlayer;
这里的p = newPlayer会再次调用p的setter,形成无限递归,最终耗尽栈内存抛出异常。
此外还有两个隐性问题:
p的getter每次调用都会新建一个Player实例,没有存储传入的玩家对象,导致无法正确显示创建者信息- PlayerSlot是StatelessWidget,调用
fillSlotWithPlayer修改属性后不会触发UI重建,槽位状态更新无法在界面体现
修复方案
1. 重构PlayerSlot为StatefulWidget并修正属性逻辑
class PlayerSlot extends StatefulWidget { final int slotNumber; final Player? initialPlayer; const PlayerSlot({ Key? key, required this.slotNumber, this.initialPlayer, }) : super(key: key); @override State<PlayerSlot> createState() => _PlayerSlotState(); } class _PlayerSlotState extends State<PlayerSlot> { bool _filled = false; late Player _player; @override void initState() { super.initState(); // 初始化玩家:优先使用传入的初始玩家,否则创建默认玩家 if (widget.initialPlayer != null) { _filled = true; _player = widget.initialPlayer!; } else { _player = Player('Player ${widget.slotNumber}'); } } void fillSlotWithPlayer(Player player) { setState(() { _filled = true; _player = player; }); } @override Widget build(BuildContext context) { String writeInSlot = _filled ? _player.name : ''; return Container( // ONE PLAYER BOX decoration: BoxDecoration( borderRadius: BorderRadius.circular(10.0), color: Colors.pinkAccent, ), width: 300, height: 40, child: Row( children: [ Container( width: 160, height: 30, child: Text( writeInSlot, style: const TextStyle( color: Color.fromARGB(255, 236, 131, 166), decoration: TextDecoration.none, fontSize: 18, fontWeight: FontWeight.bold ) ), ), const Padding(padding: EdgeInsets.symmetric(horizontal: 40.0)), IconButton( onPressed: () {}, iconSize: 24, icon: const Icon(Icons.cancel), color: const Color.fromARGB(255, 236, 131, 166), ), ], ), ); } }
2. 修正WaitingPlayersPage的初始化逻辑
class WaitingPlayersPageState extends State<WaitingPlayersPage> { double rangeSlider = 1; late List<PlayerSlot> playerSlots; @override void initState() { super.initState(); // 批量生成8个槽位,1号槽直接传入创建者 playerSlots = List.generate(8, (index) { final slotNumber = index + 1; return PlayerSlot( slotNumber: slotNumber, initialPlayer: slotNumber == 1 ? widget.creatorOfLobby : null, ); }); } @override Widget build(BuildContext context) { return Scaffold( body: Container( // WHOLE SCREEN alignment: Alignment.center, color: const Color.fromARGB(255, 236, 131, 166), child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ const SizedBox(height: 30,), Container( // BOX FOR PLAYERS decoration: BoxDecoration( borderRadius: BorderRadius.circular(10.0), color: Colors.pink, ), width: 340, height: 500, child: Column( children: [ const Padding(padding: EdgeInsets.symmetric(vertical: 12.0)), // 遍历槽位生成UI,减少重复代码 ...playerSlots.map((slot) { return Column( children: [ slot, const Padding(padding: EdgeInsets.symmetric(vertical: 8.0)), ], ); }), ], ), ), // 以下Slider、按钮部分代码保持不变 const Padding(padding: EdgeInsets.symmetric(vertical: 8.0)), Container( width: 1000, child: Slider( label: 'Number of round(s) : $rangeSlider', value: rangeSlider, onChanged: (double newRangeSlider) { setState(() { rangeSlider = newRangeSlider; }); }, min: 1, max: 10, divisions: 9, ), ), const Padding(padding: EdgeInsets.symmetric(vertical: 8.0)), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: () {}, child: Container( padding: const EdgeInsets.all(16.0), width: 320, alignment: Alignment.center, child: const Text('CANCEL', style: TextStyle(color: Color.fromARGB(255, 236, 131, 166), decoration: TextDecoration.none),), ), ), const Padding(padding: EdgeInsets.symmetric(horizontal: 8.0)), ElevatedButton( onPressed: () { GlobalKey gameGlobalkey = GlobalKey(); //initGame(gameGlobalkey, ,rangeSlider); }, child: Container( alignment: Alignment.center, width: 320, padding: const EdgeInsets.all(16.0), child: const Text('START', style: TextStyle(color: Color.fromARGB(255, 236, 131, 166), decoration: TextDecoration.none),), ), ), ] ) ], ), ), ); } }
内容的提问来源于stack exchange,提问作者Manewow
相关产品推荐
相关产品推荐

