skip to Main Content

I’m currently using flutter web to build a simple website, i’m trying to make a footer at the bottom of the screen.

My main problem is : i don’t want a fixed footer i want it to be at the end of the ScrollView but with a sticky header.

You can check attached screenshots to see what is my problem and what i want 🙂

Please don’t answer only code i really want to understand how to layout work in flutter and why it’s not working with spacer inside my widget 🙂

Regards, RaYmMiE

This is my code, i sometime use Spacer(), but it’s not working here and i’m trying to figure out why :/

import 'package:flutter/material.dart';

class Test extends StatefulWidget {
  static const String route = '/test';
  const Test({super.key});
  @override
  State<Test> createState() => _TestState();
}

class _TestState extends State<Test> {

  @override
  Widget build(BuildContext context) {
    return
      Scaffold(
          backgroundColor: Colors.white,
          body: Column(
            mainAxisSize: MainAxisSize.max,
            children: [
              SingleChildScrollView(
                child: Column(children: [
                  Container(height: 200, width: double.infinity, color: Colors.blueAccent,),
                  Container(height: 200, width: double.infinity, color: Colors.redAccent,),
                  //Spacer(),
                  Container(height: 200, width: double.infinity, color: Colors.yellowAccent,),
                  Align(alignment: Alignment.bottomCenter, child: Container(height: 200, width: double.infinity, color: Colors.greenAccent,),)
                ]),
              )]
          ),
      );
            
  }
}

2

Answers


  1. Your Spacer is not working because Columns, or scroll viewports always give unbound constraints in the scroll direction (or infinite space).

    Per default a column will size itself to the maximum of the incoming constraints in the main axis (or scroll diration), except if it has MainaxisSize.min, or receives unbound constraints iutself. Then it will size itself to the sum of the children.

    Now the problem is a Spacer (like any Flex widget) tries to size itself to fill a portion of the available/free space which works fine if the column itself can expand. But if the column has to shrink in size to fit the children, then how could the spacer fill a portion of what available space?

    Also if we look at scrollable viewports, they available space for the children is always infinite (or the unbound constraints) in the scroll direction (or main axis), because well, they can scroll. So you can’t really use flexible widgets inside of a scrollable viewport.

    So if we want to combine scrolling if the widgets are too big for the screen, but also have something that fills a portion of the available space if the widgets are smaller than the screen size, then you can use the following:

    
    class Test extends StatefulWidget {
      static const String route = '/test';
    
      const Test({super.key});
    
      @override
      State<Test> createState() => _TestState();
    }
    
    class _TestState extends State<Test> {
      double currentSize = 50;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.white,
          body: Column(
            children: <Widget>[
              ElevatedButton(
                onPressed: () => setState(() {
                  currentSize = currentSize * 2;
                  if (currentSize > 1500) {
                    currentSize = 50;
                  }
                }),
                child: const Text("make size biiiiig"),
              ),
              Expanded(
                child: LayoutBuilder(
                  builder: (BuildContext context, BoxConstraints viewportConstraints) {
                    return SingleChildScrollView(
                      child: ConstrainedBox(
                        constraints: BoxConstraints(minHeight: viewportConstraints.maxHeight),
                        child: IntrinsicHeight(
                          child: Column(
                            children: <Widget>[
                              Container(height: currentSize, color: Colors.redAccent),
                              Container(height: currentSize, color: Colors.blueAccent),
                              const Spacer(),
                              Container(height: currentSize, color: Colors.yellowAccent),
                            ],
                          ),
                        ),
                      ),
                    );
                  },
                ),
              ),
            ],
          ),
        );
      }
    }
    
    

    Here you have no scrolling but flexible space at first, but then when the buttons get too big, you will have scrolling and no flexible space.

    But the layout algorithm will be twice as expensive (performance wise) for this example!

    Coming back to your initial code snippet, if you want to use a Spacer, you have to remove the scroll view and also "expand" the inner column, so that it takes up the remaining space of the outer column instead of receiving unbound constraints:

      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.white,
          body: Column(
            mainAxisSize: MainAxisSize.max,
            children: <Widget>[
              Expanded(
                child: Column(
                  children: <Widget>[
                    Container(height: 200, color: Colors.redAccent),
                    Container(height: 200, color: Colors.blueAccent),
                    const Spacer(),
                    Container(height: 200, color: Colors.yellowAccent),
                  ],
                ),
              ),
            ],
          ),
        );
      }
    
    Login or Signup to reply.
  2. The issue in your current code is that you wrapped your Column inside a SingleChildScrollView. This means that the height of your Column is not limited by the screen height but can be as large as necessary to display all the children of the column. So even if you add a Spacer() at the end of the column, it won’t push the last child down because the column can continue to expand.

    So, if we want to combine scrolling if the widgets are too large for the screen but also have something that fills some of the available space if the widgets are smaller than the screen size, then you can use the following approach:

    Widget build(BuildContext context) {
        return Scaffold(
          body: CustomScrollView(
            slivers: [
              SliverToBoxAdapter(
                  child: ElevatedButton(
                  onPressed: () => setState(() {
                    currentSize = currentSize * 2;
                    if (currentSize > 1500) {
                      currentSize = 50;
                    }
                  }),
                  child: const Text("make size biiiiig"),
                )
              ),
               SliverList(
                
                delegate: SliverChildListDelegate(
                  [
                    Container(height: currentSize, color: Colors.redAccent),
                    Container(height: currentSize, color: Colors.blueAccent),
                    Container(height: currentSize, color: Colors.yellowAccent),
                  ]
                )
              ),
              SliverFillRemaining(
                hasScrollBody: false, //on désactive le corps déroulant
                child: Align(
                  alignment: Alignment.bottomCenter,
                  child: Container(
                    width: double.infinity,
                    height: 50,
                    color: Colors.blueAccent,
                    child: const Center(
                      child: Text(
                        'Footer',
                        style: TextStyle(color: Colors.white, letterSpacing: 4),
                      ),
                    ),
                  ),
                ),
              )
            ],
          ),
        );
      }
    

    SliverFillRemaining is a sliver that contains a single box child that fills the remaining space in the viewport.

    I discuss this in one of my blog posts.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search