How to make Column scrollable when overflowed but use expanded otherwise

308

Try implementing this solution I've just created without the animation you have. Is a scrollable area at the top and a persistent footer.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: darkBlue,
      ),
      home: SafeArea(
        child: Scaffold(
          appBar: AppBar(
            title: Text("My AppBar"),
          ),
          body: Column(
            children: [
              Expanded(
                child: SingleChildScrollView(
                  child: Column(
                    children: [

                      // Your scrollable widgets here

                      Container(
                        height: 100,
                        color: Colors.green,
                      ),
                      Container(
                        height: 100,
                        color: Colors.blue,
                      ),
                      Container(
                        height: 100,
                        color: Colors.red,
                      ),
                    ],
                  ),
                ),
              ),
              Container(
                child: Text(
                  'Your footer',
                ),
                color: Colors.blueGrey,
                height: 200,
                width: double.infinity,
              )
            ],
          ),
        ),
      ),
    );
  }
}
Share:
308
Kris
Author by

Kris

Developing apps using flutter and firebase to provide the next generation of digital tools to enhance productivity and communication

Updated on January 01, 2023

Comments

  • Kris
    Kris over 1 year

    I am trying to achieve an effect where there is expandable content on the top end of a sidebar, and other links on the bottom of the sidebar. When the content on the top expands to the point it needs to scroll, the bottom links should scroll in the same view.

    Here is an example of what I am trying to do, except that it does not scroll. If I wrap a scrollable view around the column, that won't work with the spacer or expanded that is needed to keep the bottom links on bottom:

    import 'package:flutter/material.dart';
    
    const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          theme: ThemeData.dark().copyWith(
            scaffoldBackgroundColor: darkBlue,
          ),
          debugShowCheckedModeBanner: false,
          home: Scaffold(
            body: Center(
              child: MyWidget(),
            ),
          ),
        );
      }
    }
    
    class MyWidget extends StatefulWidget {
      @override
      State<MyWidget> createState() {
        return MyWidgetState();
      }
    }
    
    class MyWidgetState extends State<MyWidget> {
      List<int> items = [1];
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                IconButton(
                  icon: const Icon(Icons.add),
                  onPressed: () {
                    setState(() {
                      items.add(items.last + 1);
                    });
                  },
                ),
                IconButton(
                  icon: const Icon(Icons.delete),
                  onPressed: () {
                    setState(() {
                      if (items.length != 1) items.removeLast();
                    });
                  },
                ),
              ],
            ),
            for (final item in items)
              MyAnimatedWidget(
                child: SizedBox(
                  height: 200,
                  child: Center(
                    child: Text('Top content item $item'),
                  ),
                ),
              ),
            Spacer(),
            Container(
              alignment: Alignment.center,
              decoration: BoxDecoration(border: Border.all()),
              height: 200,
              child: Text('Bottom content'),
            )
          ],
        );
      }
    }
    
    class MyAnimatedWidget extends StatefulWidget {
      final Widget? child;
    
      const MyAnimatedWidget({this.child, Key? key}) : super(key: key);
    
      @override
      State<MyAnimatedWidget> createState() {
        return MyAnimatedWidgetState();
      }
    }
    
    class MyAnimatedWidgetState extends State<MyAnimatedWidget>
        with SingleTickerProviderStateMixin {
      late AnimationController controller;
    
      @override
      initState() {
        controller = AnimationController(
            value: 0, duration: const Duration(seconds: 1), vsync: this);
        controller.animateTo(1, curve: Curves.linear);
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return AnimatedBuilder(
            animation: controller,
            builder: (context, child) {
              return SizedBox(height: 200 * controller.value, child: widget.child);
            });
      }
    }
    

    I have tried using a global key to get the size of the spacer and detect after rebuilds whether the spacer has been sized to 0, and if so, re-build the entire widget as a list view (without the spacer) instead of a column. You also need to listen in that case for if the size shrinks and it needs to become a column again, it seemed to make the performance noticeably worse, it was tricky to save the state when switching between column/listview, and it seemed not the best way to solve the problem.

    Any ideas?

    • p2kr
      p2kr over 2 years
      You might want to take look at SliverAppBar
    • Kris
      Kris over 2 years
      SliverAppBar is interesting, thank you for the tip - however I was unable to find a combination of Slivers to add to a CustomScrollView that will achieve a similar effect for a widget on the bottom of the view.
  • Kris
    Kris over 2 years
    Hey thanks for the answer, unfortunately I am specifically looking for a way to avoid this scenario because the footer will take up a significant portion of the screen on a mobile device and will look better to scroll with the rest if it needs to.