0
votes

I'm currently working on a Flutter mobile app which is supposed to work on Android and IOS. The issue I'm having is about ListView and updating it.

I know I'm doing wrong with a lot of things but I'm learning and I would like to learn properly. So if you have any comments, tips about the code pls give them :)

Basically here is what it does : The main player will choose who will play with him at the game via an AlertDialog which has a CheckboxList inside it and every time he selects a player, it will update a list called choosenPlayers which has all Player objects choosen in it.

Then what I want to do is to display a list of all selected players (a reorderable list to change the order of players) and update it everytime the choosenPlayers list is updated.

I managed to display these players but I have to reload the page by going in the drawer menu and clicking on page link to see added players.

I use a stateful widget for my players reorderable list and I pass to the parent the list of players (This is not the rigth way to do it I know) :

import 'package:flutter/material.dart';
import 'package:mollky/models/player.dart';

class ChoosenPlayers extends StatefulWidget {
  _ChoosenPlayersState _choosenPlayersState = _ChoosenPlayersState();
  List<Player> choosenPlayers = [];
  ChoosenPlayers({Key key, this.choosenPlayers}) : super(key: key);

  @override
  _ChoosenPlayersState createState() => _choosenPlayersState;
}

class _ChoosenPlayersState extends State<ChoosenPlayers> {
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return ReorderableListView(
      onReorder: onReorder,
      children: getListItems(),
    );
  }

  List<ListTile> getListItems() => widget.choosenPlayers
      .asMap()
      .map((i, item) => MapEntry(i, buildTenableListTile(item, i)))
      .values
      .toList();

  ListTile buildTenableListTile(Player item, int index) {
    return ListTile(
      key: ValueKey(item.id),
      title: Text(item.nickname + " " + item.name),
      leading: Text("#${index + 1}"),
    );
  }

  void onReorder(int oldIndex, int newIndex) {
    if (newIndex > oldIndex) {
      newIndex -= 1;
    }

    setState(() {
      Player reOrderedPlayer = widget.choosenPlayers[oldIndex];

      widget.choosenPlayers.removeAt(oldIndex);
      widget.choosenPlayers.insert(newIndex, reOrderedPlayer);
    });
  }
}

Here is the code of the main page where reorderable list is displayed and AlertDialog showed. Sorry, couldn't format with Dart, don't run the code snipped obviously xD

class NewGame extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => NewGameState();
}

class NewGameState extends State<NewGame> {
  List<Player> players = [];

  NewGameState() {
    this.players.add(
        Player(id: 0, name: "Dupont", nickname: "julien", picture: "test"));
    this
        .players
        .add(Player(id: 1, name: "Dpont", nickname: "julien", picture: "test"));
    this
        .players
        .add(Player(id: 2, name: "Dunt", nickname: "juen", picture: "test"));
  }

  static List<Player> _choosenPlayers = [];
  ChoosenPlayers choosenPlayersObject = ChoosenPlayers(
    choosenPlayers: _choosenPlayers,
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        drawer: DrawerWidget(),
        appBar: AppBar(title: Text("Nouvelle partie")),
        body: Column(children: <Widget>[
          Card(
              child: ListTile(
                  leading: Icon(Icons.people),
                  title: Text("Choisissez les joueurs"),
                  onTap: () {
                    showDialog(
                        context: context,
                        builder: (BuildContext context) {
                          return AlertDialog(
                              title: Text("Les joueurs existants"),
                              content:
                                  Stack(overflow: Overflow.visible, children: <
                                      Widget>[
                                Positioned(
                                  right: -40.0,
                                  top: -40.0,
                                  child: InkResponse(
                                    onTap: () {
                                      Navigator.of(context).pop();
                                    },
                                    child: CircleAvatar(
                                      child: Icon(Icons.close),
                                      backgroundColor: Colors.lightBlue,
                                    ),
                                  ),
                                ),
                                Positioned(
                                  child: StatefulBuilder(
                                    builder: (BuildContext context,
                                        StateSetter setState) {
                                      return Container(
                                        width: 350.0,
                                        height: 150.0,
                                        child: ListView.builder(
                                            itemCount: players.length,
                                            itemBuilder:
                                                (context, playerIndex) {
                                              return CheckboxListTile(
                                                title: Text(players[playerIndex]
                                                        .nickname +
                                                    " " +
                                                    players[playerIndex].name),
                                                value: _choosenPlayers.contains(
                                                    players[playerIndex]),
                                                onChanged: (bool value) {
                                                  if (!_choosenPlayers.contains(
                                                      players[playerIndex])) {
                                                    _choosenPlayers.add(
                                                        players[playerIndex]);
                                                    setState(() {});
                                                  } else {
                                                    _choosenPlayers.remove(
                                                        players[playerIndex]);
                                                    setState(() {});
                                                  }
                                                },
                                                secondary: const Icon(
                                                    Icons.hourglass_empty),
                                              );
                                            }),
                                      );
                                    },
                                  ),
                                ),
                              ]));
                        });
                  })),
          Container(
            width: 350.0,
            height: 150.0,
            child: choosenPlayersObject,
          ),
        ]));
  }
}

I've seen nothing on forums about updating list without triggering a callback like onRefresh which is not what I want.

It is a real nightmare xD. Sorry for french words btw I can translate if needed but they are not important, simple text.

Here are two screenshots of the list and alert dialog :

enter image description here

enter image description here

Thank you in advance :)

1

1 Answers

0
votes

The state of parent widget is not updated. That's why, even though the payer is added to the list. But not shown to in parent widget. The setState you called only update the state of StatefulBuilder not of the NewGame. Check out the below code.

demo

  import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: NewGameScreen(),
    );
  }
}

class NewGameScreen extends StatefulWidget {
  @override
  _NewGameScreenState createState() => _NewGameScreenState();
}

class _NewGameScreenState extends State<NewGameScreen> {
  List<Player> _availablePlayers = [];
  List<Player> _selectedPlayers = [];

  @override
  void initState() {
    super.initState();
    _availablePlayers = [
      Player(id: 0, name: "Ross", nickname: "Geller", picture: "test"),
      Player(id: 1, name: "Rachel", nickname: "Green", picture: "test"),
      Player(id: 2, name: "Chandler", nickname: "Bing", picture: "test"),
    ];
  }

  _selectPlayer() {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text("Existing players"),
          content: Stack(
            overflow: Overflow.visible,
            children: <Widget>[
              Positioned(
                right: -40.0,
                top: -40.0,
                child: InkResponse(
                  onTap: () {
                    Navigator.of(context).pop();
                  },
                  child: CircleAvatar(
                    child: Icon(Icons.close),
                    backgroundColor: Colors.lightBlue,
                  ),
                ),
              ),
              StatefulBuilder(
                builder: (BuildContext context, StateSetter alertState) {
                  return Container(
                    width: 350.0,
                    height: 150.0,
                    child: ListView.builder(
                      itemCount: _availablePlayers.length,
                      itemBuilder: (context, playerIndex) {
                        return CheckboxListTile(
                          title:
                              Text(_availablePlayers[playerIndex].nickname + " " + _availablePlayers[playerIndex].name),
                          value: _selectedPlayers.contains(_availablePlayers[playerIndex]),
                          onChanged: (bool value) {
                            if (_selectedPlayers.contains(_availablePlayers[playerIndex])) {
                              _selectedPlayers.remove(_availablePlayers[playerIndex]);
                            } else {
                              _selectedPlayers.add(_availablePlayers[playerIndex]);
                            }
                            setState(() {});//ALSO UPDATE THE PARENT STATE
                            alertState(() {});
                          },
                          secondary: const Icon(Icons.hourglass_empty),
                        );
                      },
                    ),
                  );
                },
              ),
            ],
          ),
        );
      },
    );
  }

  _onReorder(int oldIndex, int newIndex) {
    if (newIndex > oldIndex) {
      newIndex -= 1;
    }
    print('oldIndex:$oldIndex');
    print('newIndex:$newIndex');
    setState(() {
      Player player = _selectedPlayers[newIndex];
      _selectedPlayers[newIndex] = _selectedPlayers[oldIndex];
      _selectedPlayers[oldIndex] = player;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("New Game")),
      body: Column(
        children: <Widget>[
          Card(
            child: ListTile(
              leading: Icon(Icons.people),
              title: Text("Choose players"),
              onTap: _selectPlayer,
            ),
          ),
          Flexible(
            child: ReorderableListView(
              onReorder: _onReorder,
              children: _selectedPlayers.map((player) {
                return ListTile(
                  key: ValueKey(player.id),
                  title: Text(player.nickname + " " + player.name),
                  leading: Text("#${_selectedPlayers.indexOf(player) + 1}"),
                );
              }).toList(),
            ),
          ),
        ],
      ),
    );
  }
}

class Player {
  int id;
  String name;
  String nickname;
  String picture;

  Player({this.id, this.name, this.nickname, this.picture});
}

Hope it helps :)