Flutter - 如何制作一个自定义小部件,将页脚添加到接收到的可滚动小部件中?
Flutter - How to make a custom widget that adds a footer to a received scrollable widget?
我正在创建一个自定义小部件,它将页脚作为最后一项添加到收到的任何可滚动小部件中。
理想的用法是这样的:
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
。我尝试用 Wrap
或 IntrinsicHeight
包装 widget.child
但它不起作用。如果 widget.child
没有 shrinkWrap: true
,我唯一能做的就是制作一个 assert
。
widget.child
不必滚动,否则自定义小部件不会滚动,因为 widget.child
被 ListView
包裹。我尝试用 IgnorePointer
或 GestureDetector
包装 widget.child
但我无法点击这些项目。如果 widget.child
phyisics
不是 NeverScrollableScrollPhysics
. ,我唯一能做的就是制作一个 assert
- 如果
widget.child
有一个 controller
,因为 widget.child
不应该是可滚动的,我必须将那个 controller
传递给父 ListView
但是我会得到:ScrollController attached to multiple scroll views
。我唯一能做的就是允许自定义小部件接收 controller
并在 widget.child
具有 controller
时生成 assert
。
问题
- 如何解决这些问题?
- 这是一个好方法吗?
- 我还可以采用哪些其他方法?
查看 ScrollView
class 我找到了一个解决方案:
在自定义小部件中,我必须创建一个新的 Scrollable
小部件,而不是使用收到的 widget.child
,这样做:
Scrollable
会有NeverScrollableScrollPhysics
Scrollable
不会有 controller
-
Scrollable
将 return 与 ShrinkWrappingViewport
axisDirection
和 slivers
来自 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')),
)
如果有人认为这不好并且有更好的方法,请告诉我。
我正在创建一个自定义小部件,它将页脚作为最后一项添加到收到的任何可滚动小部件中。
理想的用法是这样的:
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
。我尝试用Wrap
或IntrinsicHeight
包装widget.child
但它不起作用。如果widget.child
没有shrinkWrap: true
,我唯一能做的就是制作一个assert
。 widget.child
不必滚动,否则自定义小部件不会滚动,因为widget.child
被ListView
包裹。我尝试用IgnorePointer
或GestureDetector
包装widget.child
但我无法点击这些项目。如果widget.child
phyisics
不是NeverScrollableScrollPhysics
. ,我唯一能做的就是制作一个 - 如果
widget.child
有一个controller
,因为widget.child
不应该是可滚动的,我必须将那个controller
传递给父ListView
但是我会得到:ScrollController attached to multiple scroll views
。我唯一能做的就是允许自定义小部件接收controller
并在widget.child
具有controller
时生成assert
。
assert
问题
- 如何解决这些问题?
- 这是一个好方法吗?
- 我还可以采用哪些其他方法?
查看 ScrollView
class 我找到了一个解决方案:
在自定义小部件中,我必须创建一个新的 Scrollable
小部件,而不是使用收到的 widget.child
,这样做:
Scrollable
会有NeverScrollableScrollPhysics
Scrollable
不会有controller
-
Scrollable
将 return 与ShrinkWrappingViewport
axisDirection
和slivers
来自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')),
)
如果有人认为这不好并且有更好的方法,请告诉我。