Flutter - Partition column into a variable size limited to half, and fill what's left with expanded

107 Views Asked by At

What I want to do:

I am trying to partition a column into two parts: upper (for settings) and lower (for data).

The settings part is relatively small, but might grow bigger depending on what the user adds (filters, etc...). As such, its size is unknown when building.

When the settings part grow bigger than half the column, they should be limited to half the column. The user should be able to scroll through the rest. If it is smaller than half the column, the settings should only take the necessary space.

The data part should always fill the rest of the column: meaning, at least half of it, and if the settings container is smaller than half, then the data takes up this leftover space too.

My current "solution" is as follows:

import 'dart:io';
import 'package:flutter/material.dart';
import 'dart:math';


class TestWidget extends StatefulWidget {
  final File file;
  final FileInterpreter currentInterpreter;
  final void Function({File? newFile, FileInterpreter<dynamic>? newInterpreter}) changeViewCallback;

  const TestWidget({super.key, required this.file, required this.currentInterpreter, required this.changeViewCallback});

  @override
  State<TestWidget> createState() => TestWidgetState();
}

class TestWidgetState extends State<TestWidget> {

  var rng = Random();
  Widget? extensionPanel;

  
  @override
  Widget build(BuildContext context) => Container(
    color: Colors.amber,
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        // just a button to test different settings sizes
        Container(
          color: Colors.lightBlue.shade100,
          child: SizedBox(height: 25, child: Row(children: [const Text("quick settings go here"), IconButton(onPressed: () => setState(() {extensionPanel = Text("SETTINGS START${"\n - :D"*rng.nextInt(40)}\nSETTINGS END");}), icon: const Icon(Icons.keyboard_arrow_down_sharp))],)),
        ),
        
  
        // extension of the quick settings
        if (extensionPanel!=null) Flexible(
          fit: FlexFit.loose, 
          child: Container(
            color: Colors.blue,
            child: SingleChildScrollView(controller: ScrollController(), child: extensionPanel,)
          )
        ),
        if (extensionPanel!=null) Container(
          color: Colors.red,
          child: Row(children: [const Expanded(child: Divider()), IconButton(onPressed: (){setState(() {extensionPanel=null;});}, icon: const Icon(Icons.keyboard_arrow_up)), const Expanded(child: Divider())],)
        ),
  
        // Important info
        Expanded(
          child: Container(
            color: Colors.green,
            child: const Center(child: Text("I am centered, very big, and fill ALL the empty vertical space\nEven the space left behind by the settings container\n:D"),)
          ),
        )
      ],
      ),
    );
}

The widget contains sugar to collapse the settings panel and change the settings size randomly. It gives me the following result:

This solution contains multiple containers: the full column is orange, and contains:

  • Light blue: fixed size, allows to refresh the settings size.
  • Blue: the flexible, scroll-able settings. Should only take the necessary space, and AT MOST half the column.
  • Red: fixed size, to collapse the settings panel.
  • Green: The expanded data. Should fill ALL the remaining vertical orange space.

Analysis of the result, and issues

It is almost perfect, except:

✅ The good:

  • when there are a lot of settings: -> they are limited to half the screen (blue container). -> the green container uses the rest of the space (half the column). -> I can scroll through the settings with no issues.
  • when there's not many settings: -> the settings use up only what they need.
  • when I collapse the settings: -> the green container takes all the space.

⛔ The bad:

  • when there's not many settings: -> the green section uses only half the column (there's leftover orange space)

What should happen:

  • any leftover orange space, should be given to the green container.

Other attempts

I tested multiple variations:

  • giving the green container more flex: ⛔ fills more space, but not all. Also, makes the settings section too small when there are a lot of settings.
  • giving the settings flexible container flex: 0: ⛔ if there are many settings, takes up more than half the space. Worst case, causes overflow. Also removes the scrollable property of the settings.
  • Using a SizedBox instead of Flexible: ⛔ I don't know how much space the settings bit will take... Different settings might need different sizes, that's why I want it flexible. Surely it cannot be this difficult?
  • Using two Expanded and forgetting about it: ⛔ Sure, but the green container displays a ton of very important information. Wasting almost half the column in empty space is very sad...

I also had a look at the solutions given in :

Sadly, to no avail.

1

There are 1 best solutions below

2
On

If you are using Flexible and Expanded in single Column or Row, then space will be allocated equally with flex value defaulted as 1.

You may have to remove Flexible widget for blue area, but surely you will face the other issue if there are many items.