1

我正在创建一个自定义小部件,该小部件将页脚作为最后一项添加到收到的任何可滚动小部件中。

理想的用途是这样的:

ScrollableWithFooter(
    child: ListView.builder(...),
    footer: Text('Footer'),
)

ScrollableWithFooter(
    child: GridView.builder(...),
    footer: Text('Footer'),
)

ScrollableWithFooter(
    child: CustomListView.builder(...),
    footer: Text('Footer'),
)

ScrollableWithFooter(
    child: SingleChildScrollView.builder(...),
    footer: Text('Footer'),
)

ScrollableWithFooter(
    child: StaggeredGridView.builder(...),
    footer: Text('Footer'),
)

这是我正在采取的方法:

class ScrollableWithFooter extends StatefulWidget {
  final ScrollView child;
  final Widget footer;

  ScrollableWithFooter({Key key, this.child, this.footer}) : super(key: key);

  @override
  State<StatefulWidget> createState() => ScrollableWithFooterState();
}

class ScrollableWithFooterState extends State<ScrollableWithFooter> {
  final _scrollController = ScrollController();

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      controller: _scrollController,
      scrollDirection: widget.child.scrollDirection,
      itemCount: 2,
      itemBuilder: (context, index) {
        if (index == 0) {
          return widget.child;
        }
        return widget.footer;
      },
    );
  }
}

请注意,它有一个ScrollController, 因为我需要知道页脚对于其他功能是否可见。

问题

  • widget.child必须包装其内容,否则 :Vertical viewport was given unbounded height. 我尝试widget.childWrapor包裹,IntrinsicHeight但它不起作用。我唯一能做的就是做一个assertif widget.childdoesn't have shrinkWrap: true
  • widget.child不必滚动,否则自定义小部件不会滚动,因为widget.childListView. 我尝试widget.childIgnorePointeror包裹,GestureDetector但我无法点击这些项目。我唯一能做的就是做一个assertif the widget.child phyisicsare not NeverScrollableScrollPhysics
  • 如果widget.childcontroller, 因为widget.child不应该是可滚动的,我必须将它传递controller给父级ListView,但我会得到:ScrollController attached to multiple scroll views. 我唯一能做的就是允许自定义小部件接收 acontroller并使assertifwidget.child具有controller.

问题

  • 我该如何解决这些问题?
  • 这是一个好方法吗?
  • 我还可以采取哪些其他方法?
4

1 回答 1

0

查看ScrollView课程,我找到了解决方案:

在自定义小部件中,widget.child我必须创建一个新的小部件,而不是使用收到的Scrollable,这样做:

  • ScrollableNeverScrollableScrollPhysics
  • Scrollable不会有controller
  • Scrollable返回aShrinkWrappingViewportaxisDirectionfromsliverswidget.child

这是自定义小部件的实现:

var _scrollController = ScrollController();

@override
void initState() {
  super.initState();
  _scrollController = widget.child.controller ?? _scrollController;
}

@override
Widget build(BuildContext context) {
  return ListView.builder(
    controller: widget.child.controller,
    scrollDirection: widget.child.scrollDirection,
    itemCount: 2,
    itemBuilder: (context, index) {
      if (index == 0) {
        return Scrollable(
          physics: NeverScrollableScrollPhysics(),
          axisDirection: widget.child.getDirection(context),
          viewportBuilder: (context, offset) {
            return ShrinkWrappingViewport(
              axisDirection: widget.child.getDirection(context),
              offset: offset,
              slivers: widget.child.buildSlivers(context),
            );
          },
        );
      }
      return widget.footer;
    },
  );
}

编辑:这是一个简化的替代方案:

@override
Widget build(BuildContext context) {
  return Scrollable(
    controller: widget.child.controller,
    axisDirection: widget.child.getDirection(context),
    viewportBuilder: (context, offset) {
      return ShrinkWrappingViewport(
        axisDirection: widget.child.getDirection(context),
        offset: offset,
        slivers: widget.child.buildSlivers(context)
          ..add(SliverList(delegate: SliverChildListDelegate([widget.footer]))),
      );
    },
  );
}

以下是一些如何使用它的示例:

final _scrollController = ScrollController();

// ListView
ScrollableWithFooter(
  child: ListView.builder(
    controller: _scrollController,
    itemCount: 50,
    itemBuilder: (context, index) => Text('Item $index'),
  ),
  footer: Center(child: Text('Footer')),
)

// GridView
ScrollableWithFooter(
  child: GridView.builder(
    controller: _scrollController,
    itemCount: 50,
    itemBuilder: (context, index) => Text('Item $index'),
    gridDelegate:
        SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
  ),
  footer: Center(child: Text('Footer')),
)

如果有人认为这不好并且有更好的方法,请告诉我。

于 2019-10-21T14:46:51.137 回答