Listview with scrolling Footer at the bottom

前端 未结 3 1650
自闭症患者
自闭症患者 2020-12-05 16:07

I\'m trying to create a scrolling list widgets which takes displays a few items and has the ability to add a footer at the bottom which scrolls along.

If the scrolli

相关标签:
3条回答
  • 2020-12-05 16:27

    You will need to create a custom RenderBox for this. As there's no widgets which supports this out of the box.

    SliverFillRemaining comes pretty close. But it's sizing/scrolling behavior is different then what you'd expect. As, if present, will almost always make the Scrollable... scrollable.

    Instead, we can copy paste the sources of SliverFillRemaining. And make some edits

    class SliverFooter extends SingleChildRenderObjectWidget {
      /// Creates a sliver that fills the remaining space in the viewport.
      const SliverFooter({
        Key key,
        Widget child,
      }) : super(key: key, child: child);
    
      @override
      RenderSliverFooter createRenderObject(BuildContext context) => new RenderSliverFooter();
    }
    
    class RenderSliverFooter extends RenderSliverSingleBoxAdapter {
      /// Creates a [RenderSliver] that wraps a [RenderBox] which is sized to fit
      /// the remaining space in the viewport.
      RenderSliverFooter({
        RenderBox child,
      }) : super(child: child);
    
      @override
      void performLayout() {
        final extent = constraints.remainingPaintExtent - math.min(constraints.overlap, 0.0);
        var childGrowthSize = .0; // added
        if (child != null) {
           // changed maxExtent from 'extent' to double.infinity
          child.layout(constraints.asBoxConstraints(minExtent: extent, maxExtent: double.infinity), parentUsesSize: true);
          childGrowthSize = constraints.axis == Axis.vertical ? child.size.height : child.size.width; // added
        }
        final paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: extent);
        assert(paintedChildSize.isFinite);
        assert(paintedChildSize >= 0.0);
        geometry = new SliverGeometry(
          // used to be this : scrollExtent: constraints.viewportMainAxisExtent,
          scrollExtent: math.max(extent, childGrowthSize),
          paintExtent: paintedChildSize,
          maxPaintExtent: paintedChildSize,
          hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0,
        );
        if (child != null) {
          setChildParentData(child, constraints, geometry);
        }
      }
    }
    

    Here I changed one 3 things

    • Unconstrained the child maxExtent. Because if there's no more screen-space available, that would enforce a height of 0 to the footer.
    • changed SliverGeometry scrollExtent from "full screen height" to "actual available size". So that it actually only fill the remaining visible space. Not fill the screen.
    • added a minimum value to that same scrollExtent, equal to the actual footer height. So that if there's no more space remaining in the viewport, the children is simply added without any spacing around it.

    We can now use it inside our CustomScrollView as usual.

    End result :

    new CustomScrollView(
      slivers: <Widget>[
        new SliverFixedExtentList(
          itemExtent: 42.0,
          delegate: new SliverChildBuilderDelegate((context, index) {
            return new SizedBox.expand(
              child: new Card(),
            );
          }, childCount: 42),
        ),
        new SliverFooter(
          child: new Align(
            alignment: Alignment.bottomCenter,
            child: new Container(
              height: 42.0,
              color: Colors.red,
            ),
          ),
        ),
      ],
    ),
    
    0 讨论(0)
  • 2020-12-05 16:28

    Something like https://flutter.io/web-analogs/#setting-absolute-position? That appears to cause a widget to stay in a fixed place on the screen.

    0 讨论(0)
  • 2020-12-05 16:29

    This can be achieved by using a SingleChildScrollView with special constraints, as explained here.

    Take a look at the example below:

    @override
    Widget build(BuildContext context) {
      return LayoutBuilder(
        builder: (BuildContext context, BoxConstraints constraints) {
          return SingleChildScrollView(
            child: ConstrainedBox(
              constraints: constraints.copyWith(
                minHeight: constraints.maxHeight,
                maxHeight: double.infinity,
              ),
              child: IntrinsicHeight(
                child: Column(
                  children: <Widget>[
                    Container(height: 200, color: Colors.blue),
                    Container(height: 200, color: Colors.orange),
                    Container(height: 200, color: Colors.green),
                    Container(height: 50, color: Colors.pink),
                    Expanded(
                      child: Align(
                        alignment: Alignment.bottomCenter,
                        child: Container(
                          width: double.infinity,
                          color: Colors.red,
                          padding: EdgeInsets.all(12.0),
                          child: Text('FOOTER', textAlign: TextAlign.center,),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),
          );
        }
      );
    }
    
    

    This builds the following layout:

    If you change the height of the pink container to something larger, say 500, you will see the footer also scrolls with the entire list.

    Thanks to Simon Lightfoot for his help pointing me in the right direction!

    0 讨论(0)
提交回复
热议问题