Search code examples
flutterscrollview

In a modal bottom sheet, how to keep the title fixed whilst the list scrolls?


I am showing a simple modal bottom sheet where I'd like it to have a pinned title at the top (aka non scrollable), followed by a scrolling list of (expandable) items.

My current implementation shows the bottom overflows and no scrolling possible.

My code as follows:

/// Returns the list picker with folding super category
  static Widget foldingSuperCategoryListPicker(
      {required Function(ClassifiedAdCategory) onTap}) {
    return Column(
        children: ClassifiedAdSuperCategory.values.map((s) {
      final children = ClassifiedAdCategory.values
          .where((c) => c.superCategory == s)
          .map((c) => ListTile(title: Text("$c"), onTap: () => onTap(c)))
          .toList();

      return ExpansionTile(title: Text("$s"), children: children);
    }).toList());
  }

  /// Open the list picker in a modal bottom sheet with super category being unfoldable by the user
  static showModalBottomSheetListPicker(BuildContext context,
      {required Function(ClassifiedAdCategory) onTap}) {
    showModalBottomSheet(
        context: context,
        builder: (context) => Padding(
            padding: const EdgeInsets.all(20),
            child: Column(children: [
              Text(i18n_Select_a_category.i18n),
              const Text(""),
              SingleChildScrollView(
                child: foldingSuperCategoryListPicker(onTap: onTap),
              )
            ])));
  }


Solution

  • So, without your types, it's hard for me to find a solution that solves your problem exactly, but I can give you general help.

    The main thing you're missing here is an Expanded widget inside the column. Here's an example that can work for you:

    class MyWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return ElevatedButton(
            child: const Text("Open the Sheet"),
            onPressed: () => showModalBottomSheet(
                context: context,
                builder: (BuildContext context) => BottomSheetContents()));
      }
    }
    
    class BottomSheetContents extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Column(children: [
          const Padding(
              child: Text("This is a title"), padding: EdgeInsets.all(10)),
          Expanded(
              child: ListView(children: List<Widget>.filled(20, ListItemCard(), growable: true)))
        ]);
      }
    }
    
    class ListItemCard extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return const ListTile(title: Text("Text"));
      }
    }
    

    The ListView can be replaced by a SingleChildScrollView in your example, but it's less performant, as the SingleChildScrollView is less memory-efficient. You could turn your foldingSuperCategoryListPicker into a builder function which you could feed into a ListView.builder widget.