【问题标题】:Flutter - How to make a custom widget that adds a footer to a received scrollable widget?Flutter - 如何制作一个自定义小部件,为接收到的可滚动小部件添加页脚?
【发布时间】:2020-02-17 12:49:43
【问题描述】:

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

理想的用法是这样的:

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。我尝试用WrapIntrinsicHeight 包装widget.child,但它不起作用。如果widget.child 没有shrinkWrap: true,我唯一能做的就是创建一个assert
  • widget.child 不必滚动,否则自定义小部件将不会滚动,因为 widget.childListView 包裹。我尝试用IgnorePointerGestureDetector 包裹widget.child,但我无法点击这些项目。如果widget.child phyisics 不是NeverScrollableScrollPhysics,我唯一能做的就是创建一个assert
  • 如果widget.child 有一个controller,因为widget.child 不应该是可滚动的,我必须将controller 传递给父ListView,但我会得到:ScrollController attached to multiple scroll views。我唯一能做的就是允许自定义小部件接收controller 并在widget.childcontroller 时生成assert

问题

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

【问题讨论】:

  • 您要添加页脚,使其看起来像列表视图的最后一项,还是应该是容器底部的真正页脚?
  • 应该是最后一项
  • 看看这个github.com/showang/flutter_easy_listview,我想你可以在GitHub上挖掘他的代码,这里的代码不多
  • 谢谢,但这仅适用于 ListView,我需要制作一个适用于任何类型 ScrollView 的自定义小部件,例如 GridView、StaggeredGridView 等。如果我采用类似于该链接的方法,我需要为每种类型的 ScrollView 制作许多自定义小部件,并传递每种类型的所有属性。

标签: flutter dart flutter-widget


【解决方案1】:

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

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

  • Scrollable 将有 NeverScrollableScrollPhysics
  • Scrollable 不会有controller
  • Scrollable 将返回一个 ShrinkWrappingViewport 并带有 axisDirectionslivers 来自 widget.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')),
)

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-09
    • 2021-09-11
    • 1970-01-01
    • 2020-04-11
    • 1970-01-01
    • 2019-04-01
    相关资源
    最近更新 更多