【发布时间】:2018-10-16 03:23:49
【问题描述】:
我知道ListView 或SingleChildScrollView RefreshIndicator 只能在本机工作,但我想在不滚动的页面上使用RefreshIndicator。这甚至可能吗?如果可以,怎么做?
【问题讨论】:
标签: flutter
我知道ListView 或SingleChildScrollView RefreshIndicator 只能在本机工作,但我想在不滚动的页面上使用RefreshIndicator。这甚至可能吗?如果可以,怎么做?
【问题讨论】:
标签: flutter
您必须执行以下操作:
SingleChildScrollView 并将属性physics 设置为AlwaysScrollableScrollPhysics()。MediaQuery.of(context).size.height 获得。完整的例子:
classs MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: () {},
child: SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Container(
child: Center(
child: Text('Hello World'),
),
height: MediaQuery.of(context).size.height,
),
),
);
}
}
【讨论】:
所以在我的例子中,我想在一个空列表中显示一个占位符:
RefreshIndicator(
child: Stack(
children: <Widget>[
Center(
child: Text("The list is empty"),
),
ListView()
],
),
onRefresh: () {},
)
【讨论】:
大多数其他答案都可以,但它们都有一些缺点:
SingleChildScrollView而不调整高度会导致“滚动发光效果”不在页面底部。MediaQuery 或LayoutBuilder)将解决此问题,但是当您的内容高度大于可用高度时会出现渲染溢出空间。最后,以下解决方案非常适合我:
LayoutBuilder(
builder: (context, constraints) => RefreshIndicator(
onRefresh: _refresh,
child: SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight
),
child: Text("Your Widget")
)
)
),
)
【讨论】:
建议将AlwaysScrollableScrollPhysics() 与RefreshIndicator 一起使用,因为如果Scrollable 可能没有足够的内容进行过度滚动,请考虑将其physics 属性设置为AlwaysScrollableScrollPhysics。
无需创建自己的子类,parent 可用于组合不同类型的 ScrollPhysics 对象以获得所需的滚动物理效果。例如:
ListView(
physics: const AlwaysScrollableScrollPhysics(),
children: ...
)
注意:RefreshIndicator 只能用于垂直滚动视图。
【讨论】:
更灵活的解决方案是将可滚动的空/错误状态小部件放入 LayoutBuilder
LayoutBuilder(
builder: (context, constraints) => SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: SizedBox(
height: constraints.maxHeight,
child: ErrorState(
subtitle: (snapshot.data as ErrorStateful).errorMessage),
),
),
);
【讨论】:
是否可以没有不滚动的页面?
-没有。
请阅读 RefreshIndicator 中参数 child 的文档:
树中此小部件下方的小部件。
刷新指示器将堆叠在这个孩子的顶部。当孩子的 Scrollable 后代过度滚动时,该指示器将出现。
通常是 [ListView] 或 [CustomScrollView]。
有解决办法吗?
-是的
您只能在 Listview 的子列表中放置一个小部件:
new RefreshIndicator(
child: new ListView(
children: <Widget>[
new Text('This is child with refresh indicator'),
],
),
onRefresh: () {},
)
【讨论】:
在 ListView.builder 中使用 physics: AlwaysScrollableScrollPhysics()
例子:
RefreshIndicator(
onRefresh: () => _onRefresh(),
child: Center(
child: ListView.builder(
physics: AlwaysScrollableScrollPhysics(),
controller: _scrollController,
itemCount: 4,
addAutomaticKeepAlives: true,
itemBuilder: (BuildContext context, int position) {
return Text("Hello $position");
}
),
),
)
【讨论】:
其他答案中的SingleChildScrollView 解决方案适用于大多数情况。
我有一个用例*,它不起作用,因为 SingleChildScrollView 中的内容想要扩展以填充剩余的高度。所以SingleChildScrollView 和Expanded 小部件相互矛盾。
最终,拯救了这一天的是使用 CustomScrollView 和 SliverFillRemaining 小部件。
无需计算自定义小部件尺寸。
代码:
class DemoApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: RefreshIndicator(
onRefresh: () async {
await Future.delayed(const Duration(seconds: 1));
},
child: CustomScrollView(
slivers: <Widget>[
SliverFillRemaining(
child: Container(
color: Colors.blue,
child: Center(
child: Text("No results found."),
),
),
),
],
),
),
),
);
}
}
您也可以在 DartPad 上进行测试。
*我的用例:
【讨论】:
补充 Gustavo 答案,您可以使用约束以这种方式定义 SingleChildScrollView 的最小大小
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: () {},
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height,
),
child: SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Center(
child: Text('Hello World'),
),
),
),
);
}
}
【讨论】:
这是你需要的完整代码(包括解决了使用appBar时的高度问题):
import 'package:flutter/material.dart';
class RefreshFullScreen extends StatefulWidget {
const RefreshFullScreen({Key? key}) : super(key: key);
@override
_RefreshFullScreenState createState() => _RefreshFullScreenState();
}
class _RefreshFullScreenState extends State<RefreshFullScreen> {
@override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height; // Full screen width and height
EdgeInsets padding = MediaQuery.of(context).padding; // Height (without SafeArea)
double netHeight = height - padding.top - kToolbarHeight; // Height (without status and toolbar)
return Scaffold(
appBar: AppBar(),
body: RefreshIndicator(
onRefresh: () {
return Future.delayed(
Duration(seconds: 1),
() {
},
);
}, child: SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Container(
color: Colors.red,
height: netHeight,
)
),
),
);
}
}
结果如下:
【讨论】:
您可以使用CustomScrollView 和只有一个孩子SliverFillRemaining 作为我不显示数据小部件的情况:
CustomScrollView(
slivers: [
SliverFillRemaining(
child: Center(
child: Text(
AppLocalizations.of(context).noData,
),
),
)
],
)
【讨论】: