'Flutter SwitchListTile won't switch despite stateful widget

I'm trying to create a sort of modal to create a chat room that can be either private or public, and I'm trying to add a toggle that will unveil a text input when activated. I'm running into an unknown issue where the toggle is stuck and won't switch state. Here's my code, any help is much appreciated!

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    List<GameRoom> roomList = [
      GameRoom("Room test 1", "guy 1", false),
      GameRoom("Private Room 1", "guy 2", true, "password")
    ];
    final _inputController = TextEditingController();
    bool isRoomProtected = false;

    return Scaffold(
      appBar: AppBar(
        title: Text("Welcome, ${globals.username}"),
      ),
      floatingActionButton: FloatingActionButton.extended(
          label: const Text("Create room"),
          icon: const Icon(Icons.add),
          onPressed: () {
            showModalBottomSheet(
                context: context,
                builder: (BuildContext context) {
                  return Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      const Text("Create de salle"),
                      TextField(
                        controller: _inputController,
                        maxLines: 1,
                        decoration: const InputDecoration(
                          labelText: 'Room name',
                        ),
                      ),
                      SwitchListTile(
                        title: const Text("Make room private"),
                        secondary: const Icon(Icons.lock),
                        value: isRoomProtected,
                        onChanged: (bool value) => setState(() {
                          print("test");
                          isRoomProtected = value;
                        }),
                      ),
                      TextButton(onPressed: () {}, child: const Text("Ok !")),
                    ],
                  );
                });
          }),
      body: Column(
        children: [
          TextButton(onPressed: () {}, child: Text("put searchbar here")),
          Column(
            children: [
              for (GameRoom room in roomList)
                Card(
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: <Widget>[
                      ListTile(
                        leading: Icon(room.hasPassword ? Icons.lock : Icons.lock_open),
                        title: Text(room.name),
                        subtitle: Text("owner : ${room.owner}"),
                      ),
                      Row(
                        mainAxisAlignment: MainAxisAlignment.end,
                        children: <Widget>[
                          Padding(
                            padding: const EdgeInsets.only(bottom: 10.0, right: 10.0),
                            child: TextButton(
                              child: const Text('Join room'),
                              onPressed: () {/* ... */},
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                )
            ],
          ),
        ],
      ),
    );
  }
}


Solution 1:[1]

Answer: I needed to add StatefulBuilder. I also wrapped the switch in a column, added a textField wrapped in a visibility and a box of the same sized wrapped in a visibility configured to appear when the textField isn't visible. This makes it so there's no jitter when toggling on and off.

StatefulBuilder(
                          builder: (BuildContext context, StateSetter stateSetter) {
                            return Column(
                              children: [
                                SwitchListTile(
                                  title: const Text("Lock room with password"),
                                  value: lockedRoom,
                                  onChanged: (val) {
                                    stateSetter(() => lockedRoom = val);
                                  },
                                  secondary: Icon(lockedRoom ? Icons.lock : Icons.lock_open),
                                ),
                                Padding(
                                  padding: const EdgeInsets.symmetric(horizontal: 15.0),
                                  child: Visibility(
                                    visible: lockedRoom,
                                    child: TextField(
                                      controller: _roomPasswordController,
                                      maxLines: 1,
                                      decoration: const InputDecoration(
                                        labelText: 'Room password',
                                      ),
                                    ),
                                  ),
                                ),
                                Visibility(
                                  visible: !lockedRoom,
                                  child: Container(
                                    height: 52,
                                  ),
                                ),
                              ],
                            );
                          },
                        ),

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Tom