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 one\none'),
isExpanded: _isOpen.isNotEmpty ? _isOpen[0] : false,
),
ExpansionPanel(
headerBuilder: (context, isExpanded) => const Text('two'),
body: const Text('two two\ntwo'),
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?
CodePudding user response:
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);
CodePudding user response:
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];