Ayren King
Ayren King

Reputation: 427

How should I go about updating a Stateless Widget when it's reloaded?

I'm using a JSON file to store a list that I want to display and update. I have one StatefulWidget(called "HOME") that shows the list using a ListView.builder. I also have a StatefulWidget(called "ADD") that adds an object to the list and updates the JSON file. When I finish adding the list a Navigate back to "HOME", I receive this error: Error Upon Returning to HOME

enter image description here
HOME:

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {

  PlayerList playerList;



  @override
  void initState() {
    super.initState();
    readPlayerData().then((PlayerList list) {
      playerList = list;
    });

    
  }

  @override
  Widget build(BuildContext context) {

    // add Player to the PlayerList
    void addPlayer() async {
      await Navigator.pushReplacement(
        context,
        MaterialPageRoute(builder: (context) => AddPlayer()),
      );

      initState();
    }

    void removePlayer(int index) async {

      playerList.players.removeAt(index);
      await writePlayerData(playerList);
    }

    playerList = ModalRoute.of(context).settings.arguments;

    return Scaffold(
      backgroundColor: Colors.grey[900],
      appBar: AppBar(
        title: Text('Smash Tracker'),
        centerTitle: true,
        backgroundColor: Colors.grey[850],
        elevation: 0,
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {addPlayer();},
        child: Icon(Icons.add),
        backgroundColor: Colors.grey[600],
      ),
      body: Padding(
        padding: EdgeInsets.all(15.0),
        child: Container(
          child: Card(
            color: Colors.grey[900],
            shadowColor: Colors.black87,
            child: ListView.builder(
              itemCount: playerList.players.length,
              itemBuilder: (context, index) {
                return ListTile(
                  //leading: characterIcons(playerList.players[index]),
                  title: Text(
                    playerList.players[index].playerId,
                    style: TextStyle(
                      color: Colors.amberAccent,
                    ),
                  ),
                  subtitle: Text(
                    playerList.players[index].playerSetCount,
                    style: TextStyle(
                      color: Colors.amberAccent,
                    ),
                  ),
                  onTap: () {
                    Navigator.pushNamed(context, '/playerCard', arguments: {
                      'player': playerList.players[index],
                      'playerList': playerList,
                      'index': index
                    });
                  },
                  trailing: IconButton(
                    onPressed: () async {
                      removePlayer(index);
                      PlayerList newPlayerList = await readPlayerData();
                      setState(()  {
                        playerList = newPlayerList;
                      });
                      },
                    icon: Icon(Icons.delete_outline),
                    color: Colors.white,
                  ),
                );
              },
            ),
          ),
        ),
      ),
    );
  }

}

The code used to save a new Object when the 'Save' button is pressed
ADD:

  void _saveForm() async {
      
      PlayerList playerList = await readPlayerData();

      Player newPlayer = new Player("${playerName.trim()}", "0 - 0",
          new Characters(_myChar1Result, _myChar2Result), "");
      playerList.players.add(newPlayer);

      await writePlayerData(playerList);

      Navigator.pushReplacement(
        context,
        MaterialPageRoute(builder: (context) => Home()),
      );
  }

I think the error occurs when the ListView.builder attemps to call on the playerList object. I'm not sure. I'll be happy to provide any additional information.
'Edit:'Here is the readPlayerData() including the methods it relies on:

Future<PlayerList> readPlayerData () async {
  try {
    final file = await _localFile;
    String contents = await file.readAsString();
    final jsonResponse = (jsonDecode(contents)); //wrap in encode
    PlayerList playerList = PlayerList.fromJson(jsonResponse);
    return playerList;
  } catch (e) {
    print(e);
    return getPlayerList(); //just loads a placeholder json in the assets folder;
  }

Future<String> get _localPath async {
  final directory = await getApplicationDocumentsDirectory();
  return directory.path;
}

Future<File> get _localFile async {
  final path = await _localPath;
  print(path);
  return File('$path/playerData.json');
}

The json file saves the way I want it to.

Upvotes: 2

Views: 336

Answers (1)

Alok
Alok

Reputation: 8978

There were multiple problems in your code, and this is how I fixed it.

SOLUTION I will give you the solution file wise, so follow along

1. HOME

  • Moving out the methods => addPlayer() and removePlayer()
  • Adding setState((){}) for playerList in initState()
  • Removing this line completely, playerList = ModalRoute.of(context).settings.arguments;. Too buggy to use to this line
  • Right usage of Navigator.pushAndRemoveUntil() in addPlayer()
class _HomeState extends State<Home> {
  PlayerList playerList;

  @override
  void initState() {
    super.initState();
    readPlayerData().then((PlayerList list) {
      setState(() => playerList = list);
    });
  }

  // add Player to the PlayerList
  void addPlayer() async {
    Navigator.pushAndRemoveUntil(
        context,
        MaterialPageRoute(builder: (context) => AddPlayer()),
        (_) => false);

    //initState();
  }

  void removePlayer(int index) async {
    playerList.players.removeAt(index);
    await writePlayerData(playerList);
  }

  @override
  Widget build(BuildContext context) {
    // copy paste your Scaffold content here
    // rest everything will be same
    // NO layerList = ModalRoute.of(context).settings.arguments;
    return Scaffold();
  }
}

2. ADD

  • Importing of the Characters Modal was wrong, and conflicting. It should be imported like import 'package:smash_tracker/models/character_model.dart' as Characters;
  • Then while passing the Object, it should be passed like this new Characters.Characters(_myChar1Result, _myChar2Result)
  • Correct usage of Navigator.pushAndReplaceUntil()
  • Removing this line inside Widget{} PlayerList playerList = ModalRoute.of(context).settings.arguments;
  • Rest will be same
import 'package:smash_tracker/models/character_model.dart' as Characters;

void _saveForm() async {
    setState(() {
      _myChar1Result = _myChar1;
      _myChar2Result = _myChar2;
    });

    PlayerList playerList = await readPlayerData();

    Player newPlayer = new Player("${playerName.trim()}", "0 - 0",
        new Characters.Characters(_myChar1Result, _myChar2Result), "");
    playerList.players.add(newPlayer);

    print('added new Player');
    await writePlayerData(playerList);
    print('updated JSON');

    Navigator.pushAndRemoveUntil(
        context,
        MaterialPageRoute(builder: (context) => Home())
        (_) => false);
  }

  @override
  Widget build(BuildContext context) {
    // no more PlayerList playerList = ModalRoute.of(context).settings.arguments;
    return Scaffold();
  }

After testing the app, it works like the below.

Result GIF

Upvotes: 2

Related Questions