Why Flexible widget filling all the available space despite its content being smaller?

Issue

I designed layout divided in two parts:-

1.Horizontal Lisview fixed at top

2.Vertical scrollview for rest of layout

I want Horizontal Lisview to occupy space only that much what is needed to cover its content.That’s why i put it inside Flexible widget.
I want Vertical Scrollview to fill all the leftover available space.That’s why i put it inside Expandable widget.

My layout looks like this:-

Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Flexible(
      
      child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Flexible(
              child: NotificationListener<ScrollEndNotification>(
                  child: ListView(
                    scrollDirection: Axis.horizontal,
                    controller: _scrollController,
                    children: moduleList
                        .map((module) => moduleTemplate(module))
                        .toList(),
                  ),
                  onNotification: (t) {
                    if (_scrollController.position.pixels != 0) {
                      setState(() {
                        isScrolledToEnd = true;
                      });
                    } else {
                      setState(() {
                        isScrolledToEnd = false;
                      });
                    }
                    // allow the notification to continue to be dispatched to further ancestors.
                    return true;
                  }),
            ),
         
          ]),
    ),
    Expanded(
      
      child: SingleChildScrollView(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  ...
                ]),
            Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  ...
                ]),
                    ],
                  ),
                ),
              ),
            )
          ],
        ),
      ),
    )
  ],
));

// List item

Widget moduleTemplate(module) {
    return Container(
        margin: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
           ...
          ],
        
      ),
    );
  }

When i run ths code I see both my Flexible and Expandable widget occupies half-half part of screen due to which i see lot of empty space in first part of layout which contains only Horizontal Listview.
My expectation was that my first part of layout will only occupy space it requires to display its content since it’s placed inside Flexible widget whose FIT value is Loose by default.
To resolve this i have to specify flex value which fixes the issue but depending on screen size it might overflow the top part of layout.Hence i want to fix this without using flex.Why my top layout taking half of screen and is there any better way of fixing it?

In short,My Output without flex value looks like screen divided equally in two part..First part has horizontal listview at top and rest space is empty..second part has SingleChildScrollView..I want to align SingleChildScrollView immediately bottom to listview removing that unoccupied empty space

Solution

I’m not sure why your code doesn’t work as expected but this should accomplish your desired outcome:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);
  Widget moduleTemplate(module) {
    return Container(
      margin: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Container(
            width: 100,
            height: 100,
            color: Colors.pink,
          )
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Demo"),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SingleChildScrollView(
            scrollDirection: Axis.horizontal,
            child: Row(
              children: List.generate(5, (index) => null)
                  .map((module) => moduleTemplate(module))
                  .toList(),
            ),
          ),
          Expanded(
            child: SingleChildScrollView(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Container(
                        width: 100,
                        height: 100,
                        color: Colors.blue,
                      )
                    ],
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Container(
                        width: 100,
                        height: 100,
                        color: Colors.red,
                      ),
                      Container(
                        width: 100,
                        height: 100,
                        color: Colors.red,
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

enter image description here

The black container is the horizontal scrollview with 5 pink boxes and the rest is the vertical scrollview. I’ve had to implement something similar to this a couple of times and each time combining SingleChildScrollView with Row had far better results and just worked. I’m sure there is a way to make ListView work as well, but in this case it seems ListView can be replaced with SingleChildScrollView without any problem.

Answered By – Ramin

Answer Checked By – Katrina (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published.