skip to Main Content

I have a Flutter app where I am trying to display a set of four ExpansionTile widgets in a fixed height column, where only one of the ExpansionTile widgets is expanded/active at one time.

The active ExpansionTile is set within an ‘Expanded’ widget to take the maximum vertical space. This leaves a block of vertical space for the active ExpansionTile children widgets to be displayed. The child widgets are a list of CheckboxListTile widgets.

In this case, the list of child widgets requires more vertical space than is available, and I have been trying to set this to scroll but I can’t a way to do this. Currently I get the dreaded "RenderFlex overflowed error"

enter image description here

The code below is for one of the ExpansionTile’s

class _CategoryList extends ConsumerWidget {
  
  const _CategoryList();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final aCategories = ref.watch(userPreferencesProvider.select((value) => value.categories));
    return Column(
      children: [
        ListView.builder(
          shrinkWrap: true,
            itemCount: aCategories.length,
            itemBuilder: (context, index) {
              return CheckboxListTile(
                controlAffinity: ListTileControlAffinity.leading,
                  title: Text(aCategories[index].title),
                  value: false,
                  onChanged: (value) {
                    if (value != null) {
                      if (value) {
                        ref.watch(taskFilterProvider.notifier).addCategoryFilter(aCategories[index].id);
                      } else {
                        ref.watch(taskFilterProvider.notifier).removeCategoryFilter(aCategories[index].id);
                      }
                    }
                  });
            }),
      ],
    );
  }
}

// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

class _CategoriesPanel extends ConsumerWidget with PanelEventHandler {

  const _CategoriesPanel();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final isExpanded = ref.watch(_activePanelProvider) == TaskFilterPanel.category;
    return Card(
        elevation: 9.0,
        child: ExpansionTile(
          initiallyExpanded: isExpanded,
          title: Text('Categories'),
          onExpansionChanged: ((value) {
            handleChange(ref, TaskFilterPanel.category, value);
          }),
          children: [_CategoryList()], // _buildContent(ref),
        ));
  }
}

// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Any suggestions?

3

Answers


  1. Chosen as BEST ANSWER

    There must be some intrinsic limitation with 'ExpansionTitle' that blocks the idea of scrolling children.

    To get around this I've had to build moy own version of an ExpansionTile, see the code below, and this now works fine.

    class _CategoryList extends ConsumerWidget {
      
      const _CategoryList();
    
      Map<String,String> _toMap(SearchFieldFilter? aFilter) {
        Map<String,String> aMap = {};
        if (aFilter != null) {
          if (aFilter.operand == SearchOperand.equals) {
            aMap[aFilter.value.valueString!] = aFilter.value.valueString!;
          } else {
            if (aFilter.operand == SearchOperand.inList) {
              for (FilterValue aValue in aFilter.values) {
                aMap[aValue.valueString!] = aValue.valueString!;
              }
            }
    
          }
        }
        return aMap;
      }
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final aMap = _toMap(ref.watch(taskFilterProvider.select((value) => value.search.findFilterByFieldName(GdTaskDefUtil.attributeCategoryId))));
        final aCategories = ref.watch(userPreferencesProvider.select((value) => value.categories));
        return ListView.builder(
          shrinkWrap: true,
            itemCount: aCategories.length,
            itemBuilder: (context, index) {
              return CheckboxListTile(
                controlAffinity: ListTileControlAffinity.leading,
                  title: Text(aCategories[index].title),
                  value: aMap[aCategories[index].id] != null,
                  onChanged: (value) {
                    if (value != null) {
                      if (value) {
                        ref.watch(taskFilterProvider.notifier).addCategoryFilter(aCategories[index].id);
                      } else {
                        ref.watch(taskFilterProvider.notifier).removeCategoryFilter(aCategories[index].id);
                      }
                    }
                  });
            });
      }
    }
    
    // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    
    class _PanelHeading extends ConsumerWidget {
    
      final TaskFilterPanel _panel;
      final String _title;
      final bool _expanded;
    
      const _PanelHeading(this._panel, this._title, this._expanded);
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        return GestureDetector(
            onTap: () {
              ref.read(_activePanelProvider.notifier).state = _panel;
            },
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text(_title, style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)),
                Icon(_expanded ? Icons.expand_less : Icons.expand_more)
              ],
            )
        );
      }
    }
    
    
    // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    
    class _CategoriesPanel extends ConsumerWidget with PanelEventHandler {
    
      const _CategoriesPanel();
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final isExpanded = ref.watch(_activePanelProvider) == TaskFilterPanel.category;
        return Card(
          elevation: 9.0,
          child: Padding(
            padding: const EdgeInsets.all(10.0),
            child: Column(children: [
              _PanelHeading(TaskFilterPanel.category, 'Categories', isExpanded),
              isExpanded ? Expanded(flex: 1, child: _CategoryList(),) : SizedBox(height: 0.0, width: 0.0,),
            ],),
          ),
        );
      }
    }
    

  2. Wrap your Column in a SingleChildScrollView, to make it scrollable.

    class _CategoryList extends ConsumerWidget {
      const _CategoryList();
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final aCategories = ref.watch(userPreferencesProvider.select((value) => value.categories));
        return SingleChildScrollView(
          child: Column(
            children: [
              ListView.builder(
                shrinkWrap: true,
                itemCount: aCategories.length,
                itemBuilder: (BuildContext context, int index) {
                  return CheckboxListTile(
                    controlAffinity: ListTileControlAffinity.leading,
                    title: Text(aCategories[index].title),
                    value: false,
                    onChanged: (bool? value) {
                      if (value != null) {
                        if (value) {
                          ref.watch(taskFilterProvider.notifier).addCategoryFilter(aCategories[index].id);
                        } else {
                          ref.watch(taskFilterProvider.notifier).removeCategoryFilter(aCategories[index].id);
                        }
                      }
                    },
                  );
                },
              ),
            ],
          ),
        );
      }
    }
    
    Login or Signup to reply.
  3. Can you try this

    return ListView.builder(
              shrinkWrap: true,
                itemCount: aCategories.length,
                itemBuilder: (context, index) {
                  return CheckboxListTile(
                    controlAffinity: ListTileControlAffinity.leading,
                      title: Text(aCategories[index].title),
                      value: false,
                      onChanged: (value) {
                        if (value != null) {
                          if (value) {
                            ref.watch(taskFilterProvider.notifier).addCategoryFilter(aCategories[index].id);
                          } else {
                            ref.watch(taskFilterProvider.notifier).removeCategoryFilter(aCategories[index].id);
                          }
                        }
                      });
                });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search