Scrollable Container inside a Column
Asked Answered
M

4

76

I tried a few different approaches but I can't get this to work. The layout I want to achieve is really simple and it's a breeze to implement in native Android:

  • Fixed Container on top (blue)
  • Scrollable Container below (red). A ListView won't work in my case.

I tried to use SingleChildScrollView, but it doesn't seem to work inside a Column. Maybe I'm doing something wrong or I'm not using the right widgets...

My result:

enter image description here

Scaffold(
  body: Column(
    children: <Widget>[
      Container(
        height: 100.0,
        color: Colors.blue,
      ),
      SingleChildScrollView(
        child: Container(
          color: Colors.red,
          padding: EdgeInsets.all(20.0),
          child: Column(
            children: <Widget>[
              Text('Red container should be scrollable'),
              Container(
                width: double.infinity,
                height: 700.0,
                padding: EdgeInsets.all(10.0),
                color: Colors.white.withOpacity(0.7),
                child: Text('I will have a column here'),
              )
            ],
          ),
        ),
      ),
    ],
  ),
)
Melodize answered 19/10, 2018 at 8:51 Comment(0)
O
3

The problem is that Column widget does not support scrolling. In order to make it work you may switch to ListView, but current implementation lack of some sort of header for sections. In order to get them you may use sticky_headers package like that:

Widget build(BuildContext context) => Scaffold(
      body: new ListView.builder(
          itemCount: 1,
          padding: EdgeInsets.zero,
          itemBuilder: (context, index) {
            return new StickyHeader(
                header: Container(
                  height: 100.0,
                  color: Colors.blue,
                ),
                content: Container(
                  color: Colors.red,
                  padding: EdgeInsets.all(20.0),
                  child: Column(
                    children: <Widget>[
                      Text('Red container should be scrollable'),
                      Container(
                        width: double.infinity,
                        height: 700.0,
                        padding: EdgeInsets.all(10.0),
                        color: Colors.white.withOpacity(0.7),
                        child: Text('I will have a column here'),
                      )
                    ],
                  ),
                ));
          }));
On answered 19/10, 2018 at 13:46 Comment(1)
Not the answer I was hoping for, but thank you, at least I know I have to find another way of implementing what I need.Melodize
S
198

I think that maybe after a year you have managed to do that.

But for others searching for the same problem, the easiest way is to wrap the SingleChildScrollView inside an Expanded widget.

Widget build(BuildContext context) =>
Scaffold(
  body: Column(
    children: <Widget>[
      Container(
        height: 100.0,
        color: Colors.blue,
      ),
      Expanded(
        child: SingleChildScrollView(
          child: Container(
            color: Colors.red,
            padding: EdgeInsets.all(20.0),
            child: Column(
              children: <Widget>[
                Text('Red container should be scrollable'),
                Container(
                  width: double.infinity,
                  height: 700.0,
                  padding: EdgeInsets.all(10.0),
                  color: Colors.white.withOpacity(0.7),
                  child: Text('I will have a column here'),
                )
              ],
            ),
          ),
        ),
      ),
    ],
  ),
);
Strongroom answered 26/10, 2019 at 2:38 Comment(7)
Yes! Worked for my.Vulgarian
I love Flutter but sometimes, I feel like .... I am having so many child-z just to achieve a very simple stuffDeflect
That's brilliant! Should change this answer to be the best one ;DNorvun
This should be the accepted solutionSomewhat
Can you please explain the reason why it works with Expanded, i couldn't find it in docsShriner
Because SingleChildScrollView do not have a bounded height it assumes its height based on the children (in this case 700), and overflows the available space, so you need to use the Expanded widget, it will calculate the remaining available height for the column and tell SingleChildScrollView to use it. docs.flutter.dev/development/ui/layout/constraintsStrongroom
In some cases you can use "Flexible" instead "Expanded"Correspondence
O
3

The problem is that Column widget does not support scrolling. In order to make it work you may switch to ListView, but current implementation lack of some sort of header for sections. In order to get them you may use sticky_headers package like that:

Widget build(BuildContext context) => Scaffold(
      body: new ListView.builder(
          itemCount: 1,
          padding: EdgeInsets.zero,
          itemBuilder: (context, index) {
            return new StickyHeader(
                header: Container(
                  height: 100.0,
                  color: Colors.blue,
                ),
                content: Container(
                  color: Colors.red,
                  padding: EdgeInsets.all(20.0),
                  child: Column(
                    children: <Widget>[
                      Text('Red container should be scrollable'),
                      Container(
                        width: double.infinity,
                        height: 700.0,
                        padding: EdgeInsets.all(10.0),
                        color: Colors.white.withOpacity(0.7),
                        child: Text('I will have a column here'),
                      )
                    ],
                  ),
                ));
          }));
On answered 19/10, 2018 at 13:46 Comment(1)
Not the answer I was hoping for, but thank you, at least I know I have to find another way of implementing what I need.Melodize
M
1

I managed to implement a working layout using a Stack, the only down-side being that if I have a TextField and I scroll down, the cursor 'bubble' shows up above my top container... which is kind of ugly. The order of my widgets in the Stack doesn't affect this.

See screenshot

  Widget build(BuildContext context) =>
  Scaffold(
    body: Stack(
      children: <Widget>[
        Container(
          height: 100.0,
          color: Colors.blue,
        ),
        Container(
          margin: EdgeInsets.only(top: 100.0),
          child: SingleChildScrollView(
            child: Container(
              color: Colors.red,
              padding: EdgeInsets.all(20.0),
              child: Column(
                children: <Widget>[
                  Container(
                    width: double.infinity,
                    height: 700.0,
                    padding: EdgeInsets.all(10.0),
                    color: Colors.white.withOpacity(0.7),
                    child: TextField(),
                  )
                ],
              ),
            ),
          ),
        ),
      ],
    ),
  );
Melodize answered 19/10, 2018 at 14:32 Comment(0)
P
1

If you don't want to expand your column to maximal size, wrap your SingleChildScrollView into Flexible:

return Column(
  mainAxisSize: MainAxisSize.min,
  children: [
    //header
    Container(
      color: Colors.red,
      height: 50,
      child: const Text("Header"),
    ),
    Flexible(
        child: SingleChildScrollView(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          //top
          Container(
            color: Colors.blue,
            height: 100,
            child: const Text("Top"),
          ),
          Container(
            color: Colors.yellow,
            height: 5000,
          ),
          //bottom
          Container(
            color: Colors.green,
            height: 100,
            child: const Text("Bottom"),
          ),
        ],
      ),
    )),
    //footer
    Container(color: Colors.red, height: 50, child: const Text("Footer")),
  ],
);
Pinnatifid answered 14/2 at 12:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.