skip to Main Content

I saw the Widget of the Week at https://www.youtube.com/watch?v=2aJZzRMziJc and copied the code to a State class that I cannot get to work.

class SearchResultsPageState extends State<SearchResultsPage> {
  final List<bool> _isOpen = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("test"),
      ),
      body: SingleChildScrollView(
        child: ExpansionPanelList(
          children: [
            ExpansionPanel(
              headerBuilder: (context, isExpanded) => const Text('one'),
              body: const Text('one onenone'),
              isExpanded: _isOpen.isNotEmpty ? _isOpen[0] : false,
            ),
            ExpansionPanel(
              headerBuilder: (context, isExpanded) => const Text('two'),
              body: const Text('two twontwo'),
              isExpanded: _isOpen.isNotEmpty ? _isOpen[1] : false,
            ),
          ],
          expansionCallback: (i, isOpen) => setState(() {
            _isOpen[i] = !isOpen;
          }),
        ),
      ),
    );
  }
}

I have breakpoints at:

isExpanded: _isOpen.isNotEmpty ? _isOpen[0] : false,

isExpanded: _isOpen.isNotEmpty ? _isOpen[1] : false,

expansionCallback: (i, isOpen) => setState(() {
  _isOpen[i] = !isOpen;
}),

The expansionCallback gets called on every click of the button but an Error is thrown and the the isExpanded property is not re-evaluated. The console shows:

The following RangeError was thrown while handling a gesture:
RangeError (index): Invalid value: Valid value range is empty: 0

And it points to:

_isOpen[i] = !isOpen;

What am I missing or doing wrong?

2

Answers


  1. Because your _isOpen list is empty. Initialise list with default values and it will work for you

     final List<bool> _isOpen = [false, false];
    

    For multiple data you can declare it like:

    List<bool> _isOpen = List<bool>.filled(10, false);
    
    Login or Signup to reply.
  2. When working with a Widget that highly depends on boundaries like ListView, ExpansionPanel etc. You should be more careful with null/empty values for the boundaries to avoid errors like "RangeError".

    REASON FOR ERROR

    In this case, your _isOpen is an empty bool list (final List<bool> _isOpen = [];). And you are trying to get _isOpen[0] or _isOpen[1] if ‘isNotEmpty’.

    You might be thinking all looks good right? Well NO!!

    Dart handles List[index] with valid length. While You are trying to get an index that didn’t yet exist after you think you already checked for not being empty.

    SOLUTION

    Use

    final List<bool> _isOpen = [false, false];
    

    Same reason we use

    final bool _isLoading = false;
    

    And not longer

    final bool _isLoading;
    

    Another way is to use late and initialize the _isOpen later before using it with the ExpansionPanel

    late final List<bool> isOpen;
      
    isOpen = [false, false];
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search