Home > Back-end >  When I change an object's attribute it does not persist in the object somehow, knowing that obj
When I change an object's attribute it does not persist in the object somehow, knowing that obj

Time:07-17

My project is a quiz game. I have GameController class that extends ChangeNotifier and accessed throughout multiple widgets. in GameController I have a Game type attribute which has a list of teams List\<Player>.

class Player {
  String name;
  int score;
  dynamic historique;

  void addScore(int x) {
    this.score  = x;
  }
}

class Game {
  Map<String, dynamic> gameOptions = {};
  String mode = "";
  List<Player> teamsList;
  int nbRounds;
  List<Question> questionsList; // number of questions should be a multiple of nbRounds
}

class GameController extends ChangeNotifier{
  late Game game;
  late int nbTeams;
  late int _questionInd = 0;
  late int _teamInd = 0;
  int _timerState = 0;
  int currentRound = 1;
  bool responded = false;
  bool gameOver = false;


  set timerState(int value) {
    _timerState = value;
    notifyListeners();
  }

  setGame(Game game) {
    this.game = game;
    nbTeams = game.teamsList.length;
  }

  void nextQuestion() {
    if (_questionInd%nbTeams >= nbTeams - 1) {
      if (currentRound == game.nbRounds) {
        gameOver = true;
        return;
      }
      currentRound  ;
    }
    _questionInd  ;
    _teamInd = (_teamInd   1) % nbTeams;
    timerState = 0;
    notifyListeners();
  }

  addResponse(int score) {
    print("from controll");
    print(getCurrentTeam().toString());
    getCurrentTeam().addScore(score);
    print(getCurrentTeam().toString());
    responded = true;
    notifyListeners();
  }

  testPlayer() { // outputs player1 30 player2 30
    var player1 = getCurrentTeam();
    var player2 = getCurrentTeam();
    player1.addScore(10);
    player2.addScore(20);

    print("test player1");
    print(player1.toString());
    print("test player2");
    print(player2.toString());

  }

  subResponse(int score) {
    var team = getCurrentTeam();
    team.score -= score;
    responded = false;
    notifyListeners();
  }

  int get questionInd => _questionInd;
  int get timerState => _timerState;

  Question getCurrentQuestion() {
    return game.questionsList[_questionInd];
  }

  Player getCurrentTeam() {
    return game.teamsList[_teamInd];
  }
}

The problem is when I update a player's score, weirdly it is not persisted in the object and the score returns to default value zero when I go to next question in the questions list or next page which has results.

i have an answerCard widget that calls on addResponse/sub with GestureDetecture widget to add the score to the current team.

class AnswerCard extends StatefulWidget {
  AnswerCard({Key? key, required this.answer, required this.score})
      : super(key: key);
  String answer;
  int score;

  @override
  _AnswerCardState createState() => _AnswerCardState();
}

class _AnswerCardState extends State<AnswerCard> {
  bool hiddenScore = true;
  bool selected = false;
  Color bgColor = CustomColor.yellow;
  showScore() {
    setState(() {
      hiddenScore = !hiddenScore;
    });
  }
  changeBg() {
    setState(() {
      bgColor = selected ? CustomColor.yellow : CustomColor.yellowDark;
    });
  }

  @override
  Widget build(BuildContext context) {
    final GameController game = Provider.of<GameController>(context);

    return GestureDetector(
      onTap: () {
        game.testPlayer();
        if (game.timerState != 0){
          showScore();
          if (!selected) {
            game.addResponse(widget.score);
          } else {
            game.subResponse(widget.score);
          }
          changeBg();
          selected = !selected;
        }
      },
      child: Container(
        height: 8 * SizeConfig.defaultSize,
        width: 8 * SizeConfig.defaultSize,
        decoration: new BoxDecoration(
          borderRadius: BorderRadius.circular(1.7 * SizeConfig.defaultSize),
          color: bgColor,
        ),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(widget.answer),
            SizedBox(
              height: SizeConfig.defaultSize,
            ),
            !hiddenScore
                ? Text(widget.score.toString())
                : Image.asset(
                    'assets/icons/question_mark.webp',
                    height: 20,
                    width: 20,
                  ),
          ],
        ),
      ),
    );
  }
}

this is home page widget simplified

class _HomeState extends State<Home> {
  @override
  Widget build(BuildContext context) {
    var game = new Game(
      teamsList: [
        Player(name: "Aerobotix", score: 0),
        Player(name: "IEEE", score: 0),
        Player(name: "Theatro", score: 0),
      ],
      nbRounds: 2,
      questionsList: [
        new Question(answersList: [
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
        ], question: "question1", timer: 30),
        new Question(answersList: [
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
        ], question: "question2", timer: 30),
        new Question(answersList: [
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
        ], question: "question3", timer: 30),
        new Question(answersList: [
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
        ], question: "question4", timer: 30),
        new Question(answersList: [
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
        ], question: "question5", timer: 30),
        new Question(answersList: [
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
          {"answer": "ijeba", "score": 10},
        ], question: "question6", timer: 30),
      ],
    );
    print("call build in home screen");
    final GameController gameController = Provider.of<GameController>(context);
    gameController.setGame(game);
    return Scaffold(
        body: ElevatedButton.icon(
          icon: Icon(),
          onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) {
                      return StartGame();
                    },
                  ),
                );
              },
     );
  }
}

CodePudding user response:

The problem resides in the _HomeState, take a look at this snippet:

class _HomeState extends State<Home> {
  @override
  Widget build(BuildContext context) {
    var game = new Game(
      teamsList: [
        Player(name: "Aerobotix", score: 0),
        Player(name: "IEEE", score: 0),
        Player(name: "Theatro", score: 0),
      ],
      ...

Every time Home is redrawn/rebuilt the players are created again with score of 0. To fix that the players should be created in the _HomeState.initState(). The trick to do that is to use a late variable like so:

class _HomeState extends State<Home> {
  late Game game;

  @override
  void initState() {
    super.initState();
    game = new Game(
      teamsList: [
        Player(name: "Aerobotix", score: 0),
        Player(name: "IEEE", score: 0),
        Player(name: "Theatro", score: 0),
      ],
      ...
    );
  }

  @override
  Widget build(BuildContext context) {
    ...
  }
}

CodePudding user response:

i solved this after debugging. the problem was Game object was recreating in each notifyListeners() call in GameController. so i just added listen: false since i only set the game and dont need to listen.

  • Related