Flutter 当屏幕首次构建时,如何判断SingleChildScrollView中是否不需要滚动?
我有一个Flutter 当屏幕首次构建时,如何判断SingleChildScrollView中是否不需要滚动?,flutter,dart,Flutter,Dart,我有一个SingleChildScrollView。有时它的child比屏幕长,在这种情况下,SingleChildScrollView允许您滚动。但有时它的子项比屏幕短,在这种情况下不需要滚动 我试图在屏幕底部添加一个箭头,提示用户可以/应该向下滚动查看其余内容。我成功地实现了这一点,除非SingleChildScrollView的子视图比屏幕短。在这种情况下,不需要滚动,所以我想根本不显示箭头 我已经尝试制作一个侦听器来执行此操作,但是侦听器在您开始滚动之前不会被激活,在这种情况下,您无法滚
SingleChildScrollView
。有时它的child
比屏幕长,在这种情况下,SingleChildScrollView
允许您滚动。但有时它的子项比屏幕短,在这种情况下不需要滚动
我试图在屏幕底部添加一个箭头,提示用户可以/应该向下滚动查看其余内容。我成功地实现了这一点,除非SingleChildScrollView
的子视图比屏幕短。在这种情况下,不需要滚动,所以我想根本不显示箭头
我已经尝试制作一个侦听器来执行此操作,但是侦听器在您开始滚动之前不会被激活,在这种情况下,您无法滚动
我还尝试在显示箭头的三元运算符中访问\u scrollController
的属性,但引发了一个异常:scrollController未附加到任何滚动视图。
这里有一个完整的示例应用程序,展示了我正在做的事情,所以如果你想看到它运行,你可以复制并粘贴它。为了简单起见,我用文本
小部件的列
替换了所有内容:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) =>
MaterialApp(home: Scaffold(body: MyScreen()));
}
class MyScreen extends StatefulWidget {
@override
_MyScreenState createState() => _MyScreenState();
}
class _MyScreenState extends State<MyScreen> {
ScrollController _scrollController = ScrollController();
bool atBottom = false;
@override
void initState() {
super.initState();
// Activated when you get to the bottom:
_scrollController.addListener(() {
if (_scrollController.position.extentAfter == 0) {
setState(() {
atBottom = true;
});
}
});
// Activated as soon as you start scrolling back up after getting to the bottom:
_scrollController.addListener(() {
if (_scrollController.position.extentAfter > 0 && atBottom) {
setState(() {
atBottom = false;
});
}
});
// I want this to activate if you are at the top of the screen and there is
// no scrolling to do, i.e. the widget being displayed fits in the screen:
_scrollController.addListener(() {
if (_scrollController.offset == 0 &&
_scrollController.position.extentAfter == 0) {
setState(() {
atBottom = false;
});
}
});
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
SingleChildScrollView(
controller: _scrollController,
scrollDirection: Axis.vertical,
child: Container(
width: MediaQuery.of(context).size.width,
child: Column(
children: [
for (int i = 0; i < 100; i++)
Text(
i.toString(),
),
],
),
),
),
atBottom
? Container()
: Positioned(
bottom: 10,
right: 10,
child: Container(
child: Icon(
Icons.arrow_circle_down,
),
),
),
],
);
}
}
导入“包装:颤振/材料.省道”;
void main()=>runApp(MyApp());
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文)=>
MaterialApp(主页:脚手架(主体:MyScreen());
}
类MyScreen扩展StatefulWidget{
@凌驾
_MyScreenState createState()=>\u MyScreenState();
}
类_MyScreenState扩展状态{
ScrollController_ScrollController=ScrollController();
bool-atBottom=false;
@凌驾
void initState(){
super.initState();
//在到达底部时激活:
_scrollController.addListener((){
如果(_scrollController.position.extentAfter==0){
设置状态(){
atBottom=true;
});
}
});
//在到达底部后开始向上滚动时激活:
_scrollController.addListener((){
if(_scrollController.position.extentAfter>0&&atBottom){
设置状态(){
atBottom=false;
});
}
});
//我想这是激活,如果你在屏幕的顶部,有
//无需滚动,即显示的小部件适合屏幕:
_scrollController.addListener((){
如果(_scrollController.offset==0&&
_scrollController.position.extentAfter==0){
设置状态(){
atBottom=false;
});
}
});
}
@凌驾
无效处置(){
_scrollController.dispose();
super.dispose();
}
@凌驾
小部件构建(构建上下文){
返回堆栈(
儿童:[
SingleChildScrollView(
控制器:\ u滚动控制器,
滚动方向:轴垂直,
子:容器(
宽度:MediaQuery.of(context).size.width,
子:列(
儿童:[
对于(int i=0;i<100;i++)
正文(
i、 toString(),
),
],
),
),
),
底层
?容器()
:定位(
底部:10,
右:10,,
子:容器(
子:图标(
图标。箭头\圆圈\向下,
),
),
),
],
);
}
}
导入计划程序颤振库:
在state对象内部但在build
方法外部创建一个布尔标志,以跟踪是否已调用build
:
在build
方法的开头添加以下内容:
(布尔标志防止此代码导致反复调用build
。)
以下是实现此解决方案的示例应用程序的完整代码:
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) =>
MaterialApp(home: Scaffold(body: MyScreen()));
}
class MyScreen extends StatefulWidget {
@override
_MyScreenState createState() => _MyScreenState();
}
class _MyScreenState extends State<MyScreen> {
ScrollController _scrollController = ScrollController();
bool atBottom = false;
// ======= new code =======
bool buildCalledYet = false;
// ========================
@override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.extentAfter == 0) {
setState(() {
atBottom = true;
});
}
});
_scrollController.addListener(() {
if (_scrollController.position.extentAfter > 0 && atBottom) {
setState(() {
atBottom = false;
});
}
});
// ======= The third listener is not needed. =======
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// =========================== new code ===========================
if (!buildCalledYet) {
buildCalledYet = true;
SchedulerBinding.instance.addPostFrameCallback((_) {
setState(() {
atBottom = !(_scrollController.position.maxScrollExtent > 0);
});
});
}
// ================================================================
return Stack(
children: [
SingleChildScrollView(
controller: _scrollController,
scrollDirection: Axis.vertical,
child: Container(
width: MediaQuery.of(context).size.width,
child: Column(
children: [
for (int i = 0; i < 100; i++)
Text(
i.toString(),
),
],
),
),
),
atBottom
? Container()
: Positioned(
bottom: 10,
right: 10,
child: Container(
child: Icon(
Icons.arrow_circle_down,
),
),
),
],
);
}
}
导入“包装:颤振/材料.省道”;
导入“package:flatter/scheduler.dart”;
void main()=>runApp(MyApp());
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文)=>
MaterialApp(主页:脚手架(主体:MyScreen());
}
类MyScreen扩展StatefulWidget{
@凌驾
_MyScreenState createState()=>\u MyScreenState();
}
类_MyScreenState扩展状态{
ScrollController_ScrollController=ScrollController();
bool-atBottom=false;
//=======新代码=======
bool buildCalledYet=false;
// ========================
@凌驾
void initState(){
super.initState();
_scrollController.addListener((){
如果(_scrollController.position.extentAfter==0){
设置状态(){
atBottom=true;
});
}
});
_scrollController.addListener((){
if(_scrollController.position.extentAfter>0&&atBottom){
设置状态(){
atBottom=false;
});
}
});
//=======不需要第三个侦听器=======
}
@凌驾
无效处置(){
_scrollController.dispose();
super.dispose();
}
@凌驾
小部件构建(构建上下文){
//========================================新代码===========================
如果(!buildCalledYet){
buildCalledYet=true;
SchedulerBinding.instance.addPostFrameCallback((){
设置状态(){
atBottom=!(_scrollController.position.maxScrollExtent>0);
});
});
}
// ================================================================
返回堆栈(
儿童:[
SingleChildScrollView(
控制器:\ u滚动控制器,
滚动方向:轴垂直,
子:容器(
宽度:MediaQuery.of(context).size.width,
儿童:公司
bool buildCalledYet = false;
if (!firstBuild) {
firstBuild = true;
SchedulerBinding.instance.addPostFrameCallback((_) {
setState(() {
atBottom = !(_scrollController.position.maxScrollExtent > 0);
});
});
}
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) =>
MaterialApp(home: Scaffold(body: MyScreen()));
}
class MyScreen extends StatefulWidget {
@override
_MyScreenState createState() => _MyScreenState();
}
class _MyScreenState extends State<MyScreen> {
ScrollController _scrollController = ScrollController();
bool atBottom = false;
// ======= new code =======
bool buildCalledYet = false;
// ========================
@override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.extentAfter == 0) {
setState(() {
atBottom = true;
});
}
});
_scrollController.addListener(() {
if (_scrollController.position.extentAfter > 0 && atBottom) {
setState(() {
atBottom = false;
});
}
});
// ======= The third listener is not needed. =======
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// =========================== new code ===========================
if (!buildCalledYet) {
buildCalledYet = true;
SchedulerBinding.instance.addPostFrameCallback((_) {
setState(() {
atBottom = !(_scrollController.position.maxScrollExtent > 0);
});
});
}
// ================================================================
return Stack(
children: [
SingleChildScrollView(
controller: _scrollController,
scrollDirection: Axis.vertical,
child: Container(
width: MediaQuery.of(context).size.width,
child: Column(
children: [
for (int i = 0; i < 100; i++)
Text(
i.toString(),
),
],
),
),
),
atBottom
? Container()
: Positioned(
bottom: 10,
right: 10,
child: Container(
child: Icon(
Icons.arrow_circle_down,
),
),
),
],
);
}
}