Garrison
Garrison

Reputation: 99

Flutter - How can I set the state of a Checkbox widget in a dynamic list of Checkbox

Sorry if this isn't perfectly clear. I welcome any suggestions for clarification as I'm new to Flutter and also to asking questions on StackOverflow.

I'll post what is and isn't working. Basically I want to have a Perk widget with a varying number of Checkbox widgets in it. When I create the widgets within the build method I can set the state of them (checked or unchecked) but dynamically creating Checkbox widgets using a for loop in a list and passing that list to a Row makes the Checkboxes unresponsive.

DOESN'T WORK (but I don't know why and it's what I'm going for)

import 'package:flutter/material.dart';
import 'package:gloomhaven_enhancement_calc/data/constants.dart';

class Perk extends StatefulWidget {
  final int numOfChecks;
  final String details;
  final List<String> icons;

  Perk(this.numOfChecks, this.details, this.icons);

  @override
  State<StatefulWidget> createState() => PerkState();
}

class PerkState extends State<Perk> {
  int _numOfChecks;
  String _details;
  List<String> _icons;
  bool _isChecked = false;

  List<Checkbox> checkList = [];

  @override
  void initState() {
    super.initState();
    _numOfChecks = widget.numOfChecks;
    _details = widget.details;
    _icons = widget.icons;

    setState(() {
      void _itemChange(bool val) {
//    setState(() {
        _isChecked = val;
//    });
        print('clliccked' + val.toString());
      }
      for (int x = 0; x < _numOfChecks; x++) {
        checkList.add(Checkbox(
          value: _isChecked,
          onChanged: (bool value) => _itemChange(value),
        ),);
      }
    });
  }



  Widget build(BuildContext context) {
    return Container(
        color: Colors.grey.withOpacity(0.75),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            // here I pass the list of checkboxes created above, but they
            // don't respond to user clicks
            Row(children: checkList),
            Expanded(
              child: Text(
                _details,
                style: TextStyle(fontFamily: secondaryFontFamily),
              ),
            ),
          ],
        ));
  }
}

WORKS (but I have to hard-code in the number of checkboxes - I need to be able to create them dynamically based on numOfChecks parameter)

import 'package:flutter/material.dart';
import 'package:gloomhaven_enhancement_calc/data/constants.dart';

class Perk extends StatefulWidget {
  final int numOfChecks;
  final String details;
  final List<String> icons;

  Perk(this.numOfChecks, this.details, this.icons);

  @override
  State<StatefulWidget> createState() => PerkState();
}

class PerkState extends State<Perk> {
  int _numOfChecks;
  String _details;
  List<String> _icons;
  bool _isChecked = false;

  List<Checkbox> checkList = [];

  @override
  void initState() {
    super.initState();
    _numOfChecks = widget.numOfChecks;
    _details = widget.details;
    _icons = widget.icons;
  }

  void checkboxChecked(bool val) {
//    setState(() {
    _isChecked = val;
//    });
    print('clliccked' + val.toString());
  }

  Widget build(BuildContext context) {
    return Container(
        color: Colors.grey.withOpacity(0.75),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            Row(children:
            <Widget>[
              // here I create checkboxes based on the number but it's 
              // obviously a hack just to make it work
              Checkbox(
                value: _isChecked,
                onChanged: (bool value) => checkboxChecked(value),
              ),
              _numOfChecks > 1 ? Checkbox(
                value: _isChecked,
                onChanged: (bool value) => checkboxChecked(value),
              ) : Container(),
              _numOfChecks > 2 ? Checkbox(
                value: _isChecked,
                onChanged: (bool value) => checkboxChecked(value),
              ) : Container(),
              _numOfChecks > 3 ? Checkbox(
                value: _isChecked,
                onChanged: (bool value) => checkboxChecked(value),
              ) : Container(),
              _numOfChecks > 4 ? Checkbox(
                value: _isChecked,
                onChanged: (bool value) => checkboxChecked(value),
              ) : Container(),
            ]
            ),
            Expanded(
              child: Text(
                _details,
                style: TextStyle(fontFamily: secondaryFontFamily),
              ),
            ),
          ],
        ));
  }
}

BONUS

I need to also save each checkbox state in sharepreferences. I was going to just create a separate entry for each one but I'm sure there's a more efficient way. They don't need to do anything, just need to remember if they're checked or unchecked and persist that through restarts.

Upvotes: 2

Views: 6792

Answers (1)

dreambit.io dreambitio
dreambit.io dreambitio

Reputation: 1902

DOESN'T WORK section has a lot of mistakes.

You have to store details of checkboxes (titles, icons, count) inside of Perk class. You need only titles and icons. Then you can retrieve checkboxes count by calling titles.length. State of checked items has to be inside of State class (List checkList).

Change type of _checkList to List and initialize it in initState.

@override
void initState() {
    super.initState();
    ...
    _checkList = List(widget.titles.length);// or List.generate(widget.numOfChecks, (i) => false);
}

To build a number of widgets (or other items) you can use this approach:

Row(
  children: List.generate(
    _numOfChecks,
    (i) => Checkbox(
      value: _checkList[i],
      onChanged: (bool value) => setState(() { _checkList[i] = value; }),
    )
  ).toList();
)

I hope my answer will help you to find right solution.

Upvotes: 3

Related Questions