0

I'm passing data from a stateful widget, and I access it like in this example (widget.variable)

https://stackoverflow.com/a/50818870/806009

However, occasionally it throws an error (about 1 in 20) for a line in this comparison

════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The method '>' was called on null.
Receiver: null
Tried calling: >(10)

if (widget.x > 10){...}

It seems that widget.x is not known at this time. Should I use initState to ensure that value is "ready"?

Such as

  @override
  void initState() {
    super.initState();
    this.x = widget.x
  }

Full Code

class PlayerSelection extends StatefulWidget {
  final int teamId;
  final int leagueId;
  final League.League league;
  final String className;
  List<PlayerObj> playersListOfClass;
  final int index;
  final int remaining;

  PlayerSelection({Key key, @required this.teamId, this.leagueId, this.league, this.className, this.playersListOfClass, this.index, this.remaining}) : super(key: key);

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

class _PlayerSelection extends State<PlayerSelection> {
  var playerWidgets = <Widget>[];
  List<PlayerObj> selectedPlayers = [];
  List<PlayerObj> playerList = [];
  PlayerObj draftedPlayer;
  List<PlayerClass> playerClassList = [];
  bool _isFavorited = false;
  Modal modal = new Modal();
  int lineupWeek = 0;
  int lineupSize = 0;
  String intervalLabel = '';
  bool _is_full = false;
  bool _is_locked = false;
  int remaining = 0;
  var currentColor = Colors.black;
  var rejectedPlayerId;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      resizeToAvoidBottomPadding: false,
      appBar: globals.MyAppBar(
          leading: IconButton(icon: Icon(Icons.close),
              onPressed: (){
                Navigator.pop(context, {'player': null,'index': this.widget.index});
          }), // go to league route,),
          title: Text(widget.className)
      ),
      body: Container(
        child: Column(children: [Expanded(child: ListView(children: _getLineup(this.widget.playersListOfClass))),]),
      ),
    );
  }


  List<Widget>_getLineup(playerList) {
    List<Widget> widgets = [];
    var index = 0;
    playerList.forEach((player) =>
        widgets.add(
            Padding(
              padding: const EdgeInsets.symmetric(horizontal:16.0),
              child: Container(
                decoration: BoxDecoration(
                  color: Colors.white,
                  border: Border(
                    bottom: BorderSide(width: 1.0, color: Colors.grey.withOpacity(0.3)),
                  ),
                ),
                child: new ListTile(
                  leading: GestureDetector(
                    onTap: (){
                      if(!_is_full) {
                        Navigator.push(
                          context,
                          MaterialPageRoute(builder: (context) =>
                              Players.PlayerDetail(playerId: player.playerId,
                                  leagueId: widget.leagueId,
                                  playerName: player.playerName,
                                  playerBio: player.playerBio)),
                        );
                      }
                    },
                    child: ClipOval(
                      child: _playerPicWidget(player)
                    ),
                  ),
                  title: GestureDetector(
                      onTap: (){
                        Navigator.push(
                          context,
                          MaterialPageRoute(builder: (context) =>
                              Players.PlayerDetail(playerId: player.playerId,
                                  leagueId: widget.leagueId,
                                  playerName: player.playerName,
                                  playerBio: player.playerBio)),
                        );

                      },
                      child: _playerNameWidget(player)
                  ),
                  trailing: _trailingWidget(player),
                  onTap: () => player.playerPrice <= widget.remaining ? Navigator.pop(context, {'player': player,'index': this.widget.index}) : null,

                   // return player back
                ),
              ),
            )
        )
    );
    return widgets;
  }
  Widget _playerNameWidget(player){
    if(this._is_full && !this.selectedPlayers.contains(player)){ //show disabled selection
      return Opacity(opacity:.25, child:Text("${player.playerName}"));
    }
    else {
      if(this.widget.league.hasBudget){ // can afford player, not full
        if(player.playerPrice <= widget.remaining || this.selectedPlayers.contains(player)){
          return Text("${player.playerName}");
        }
        else { // can't afford player, not full
          return Opacity(opacity:.25, child:Text("${player.playerName}"));
        }
      }
      else { // slot still open
        return Text("${player.playerName}");
      }
    }
  }
  Widget _playerPicWidget(player){
    if(player == this.draftedPlayer){
      return Opacity(opacity: .25, child: Image.network('${player.playerImageUrl}',
      fit: BoxFit.scaleDown,
      height: 45,
      width: 45,
      ));
    }
    else {
        if(player.playerPrice <= widget.remaining || this.selectedPlayers.contains(player)){
          return Image.network('${player.playerImageUrl}',
            fit: BoxFit.scaleDown,
            height: 45,
            width: 45,
          );
        }
    }
  }
  Widget _trailingWidget(player){
    List<Widget> tWidgets;
    double playerOpacity = 1;
    if(player.playerPrice > widget.remaining){
      playerOpacity = .25;
    }
    tWidgets = [
      Padding(padding: const EdgeInsets.symmetric(horizontal:10.0),
      child: Opacity(opacity:playerOpacity, child:Text("\$${globals.commaFormat.format(player.playerPrice)}")),
    ), Opacity(opacity: playerOpacity, child: Icon(Icons.add))];
    return Row(mainAxisSize: MainAxisSize.min, children: tWidgets);
  }
}
user1961
  • 1,270
  • 2
  • 17
  • 27
  • Can you please provide your own code? It should'nt be necessary to initialize it because the widget is part of context which is available before initState – Chris Pi Jan 30 '21 at 21:01
  • That's what I thought too - and it only fails for this comparison about 1/10 chance of throwing this error. – user1961 Jan 30 '21 at 21:06
  • `PlayerSelection({Key key, @required this.teamId, this.leagueId, this.league, this.className, this.playersListOfClass, this.index, this.remaining}) : super(key: key);` This Line gets me. If really only your teamId is required, you should everytime nullcheck all the other fields if not, it is possible to get a null through here what maybe causes your problem when you are not sure if the data could be null which gets passed. Also if you use named parameter w/o required annotation, they are optional to fill and are type of null – Chris Pi Jan 30 '21 at 21:28
  • @chrispi they are all required and I'll add it as such. The only way to test it truly is to click it 20x and hope it doesn't fail. – user1961 Jan 30 '21 at 21:33
  • No they are not :) If you just make `@required this.teamId` it ist just `teamId` that is required. If you want to have them all required, you have to annotate every single field like `PlayerSelection({Key key, @required this.teamId, @required this.leagueId, @required this.league, @required this.className, @required this.playersListOfClass, @required this.index, @required this.remaining}) : super(key: key);` – Chris Pi Jan 30 '21 at 21:36
  • 1
    If this happens one time out of x times, then maybe your data source is corrupted. Have you tried to log the data which gets passed in the `PlayerSelection` widget? – Chris Pi Jan 30 '21 at 21:40
  • @ChrisPi good suggestion, that "remaining" variable is calculated in another widget before it's passed to maybe that's where the issue lies – user1961 Jan 30 '21 at 21:43
  • You could try to make a static value instead of "remaining" and try to hit it a few times to check if the error happens again. – Chris Pi Jan 30 '21 at 21:45
  • 1
    @ChrisPi you were right; it has to do with this value not being passed under a certain pathway to this widget. So my problem was not actually my problem, and my tap path was just inconsistent enough to lead me away from the true issue. Thanks all. – user1961 Jan 30 '21 at 22:34
  • You're welcome! :) As I said, nullchecks are essential (there is a reason why the dart team goes non nullable in a few versions). And dont forget to mark your issue as solved! Happy developing! – Chris Pi Jan 30 '21 at 22:47

1 Answers1

0

The issue was a data issue unrelated to the passing of data.

user1961
  • 1,270
  • 2
  • 17
  • 27