gpt4 book ai didi

dart - 如何在 Flutter 的 ScrollView 中限制滚动距离?

转载 作者:IT老高 更新时间:2023-10-28 12:40:08 25 4
gpt4 key购买 nike

我创建了一个页面,其中包含多个文本字段和列中的按钮,该列包含在具有背景图像的容器中。而这个容器本身就是一个 ScrollView 小部件的子级。

所以当一个人点击其中一个字段时,他们的键盘会弹出(占据屏幕的一部分),这意味着一些按钮/字段在屏幕外,这就是 ScrollView 小部件发挥作用的地方。

这里的问题是我想限制 ScrollView 允许用户滚动的距离。

最下方的按钮下方有一些空白区域,我不希望用户能够一直滚动到那里。这也让体验保持简单,并且不会让用户“过度滚动”他应该输入的字段。

但由于背景图像是 ScrollView 的一部分,因此该 View 将允许用户向下滚动到图像的底部。我想限制这个。

作为后续,我试图弄清楚如何设置初始滚动位置。 (因此,当单击一个字段时, ScrollView 会向下滚动到第一个文本字段,因此所有字段都在 View 中。用户无需向下滚动到它们。但是我不希望重新应用此滚动位置当然,每次用户点击一个字段时。)

这里是相关的(如果我的任何代码看起来真的很糟糕,请说出来,我一般是编程新手并接受任何改进建议):

class LoginPageConstructor extends StatelessWidget {
@override
Widget build(BuildContext context) {
AssetImage loginBackgroundAsset =
new AssetImage("assets/loginscreen/backgroundrock.png");
// var _scrollController = new ScrollController(
// initialScrollOffset: 200.0,
// keepScrollOffset: true);
return new Scaffold(
body: new Container(
child: new ListView(key: new PageStorageKey("Divider 1"),
// controller: _scrollController,
children: <Widget>[
new Stack(children: <Widget>[
new Container(
constraints: new BoxConstraints.expand(height: 640.0),
decoration: new BoxDecoration(
image: new DecorationImage(
image: loginBackgroundAsset, fit: BoxFit.cover)),
child: new Column(
children: <Widget>[
new Divider(height: 300.0,),
new Center(child: new UsernameText(),),
new Divider(height: 8.0,),
new Center(child: new PasswordText(),),
new Divider(),
new LoginButton(),
new Divider(),
new SignUpButton(),
],
))
])
],
),
));
}
}

最佳答案

为了将字段自动滚动到 View 中,听起来您正在与 issue 10826 搏斗。 .我发布了 workaround在那个问题上。我根据您的示例代码调整了解决方法;见下文。 (您可能需要稍微调整一下。)

如果您想阻止用户滚动,您可能只想使用以下相同的技术确保所有字段都可见,然后使用 NeverScrollableScrollPhysics 作为 physics ListView。或者,如果您有野心,您可以实现自定义滚动物理,如 Gallery example 所示。 .不过,如果我是你,我会坚持修复 #10826。

video

import 'package:meta/meta.dart';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() {
runApp(new MaterialApp(home: new LoginPage()));
}

/// A widget that ensures it is always visible when focused.
class EnsureVisibleWhenFocused extends StatefulWidget {
const EnsureVisibleWhenFocused({
Key key,
@required this.child,
@required this.focusNode,
this.curve: Curves.ease,
this.duration: const Duration(milliseconds: 100),
}) : super(key: key);

/// The node we will monitor to determine if the child is focused
final FocusNode focusNode;

/// The child widget that we are wrapping
final Widget child;

/// The curve we will use to scroll ourselves into view.
///
/// Defaults to Curves.ease.
final Curve curve;

/// The duration we will use to scroll ourselves into view
///
/// Defaults to 100 milliseconds.
final Duration duration;

EnsureVisibleWhenFocusedState createState() => new EnsureVisibleWhenFocusedState();
}

class EnsureVisibleWhenFocusedState extends State<EnsureVisibleWhenFocused> {
@override
void initState() {
super.initState();
widget.focusNode.addListener(_ensureVisible);
}

@override
void dispose() {
super.dispose();
widget.focusNode.removeListener(_ensureVisible);
}

Future<Null> _ensureVisible() async {
// Wait for the keyboard to come into view
// TODO: position doesn't seem to notify listeners when metrics change,
// perhaps a NotificationListener around the scrollable could avoid
// the need insert a delay here.
await new Future.delayed(const Duration(milliseconds: 600));

if (!widget.focusNode.hasFocus)
return;

final RenderObject object = context.findRenderObject();
final RenderAbstractViewport viewport = RenderAbstractViewport.of(object);
assert(viewport != null);

ScrollableState scrollableState = Scrollable.of(context);
assert(scrollableState != null);

ScrollPosition position = scrollableState.position;
double alignment;
if (position.pixels > viewport.getOffsetToReveal(object, 0.0)) {
// Move down to the top of the viewport
alignment = 0.0;
} else if (position.pixels < viewport.getOffsetToReveal(object, 1.0)) {
// Move up to the bottom of the viewport
alignment = 1.0;
} else {
// No scrolling is necessary to reveal the child
return;
}
position.ensureVisible(
object,
alignment: alignment,
duration: widget.duration,
curve: widget.curve,
);
}

Widget build(BuildContext context) => widget.child;
}

class LoginPage extends StatefulWidget {
LoginPageState createState() => new LoginPageState();
}

class LoginPageState extends State<LoginPage> {
FocusNode _usernameFocusNode = new FocusNode();
FocusNode _passwordFocusNode = new FocusNode();

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Example App'),
),
body: new Container(
child: new ListView(
physics: new NeverScrollableScrollPhysics(),
key: new PageStorageKey("Divider 1"),
children: <Widget>[
new Container(
constraints: new BoxConstraints.expand(height: 640.0),
decoration: new BoxDecoration(
image: new DecorationImage(
image: new NetworkImage(
'https://flutter.io/images/flutter-mark-square-100.png',
),
fit: BoxFit.cover,
),
),
child: new Column(
children: <Widget>[
new Container(
height: 300.0,
),
new Center(
child: new EnsureVisibleWhenFocused(
focusNode: _usernameFocusNode,
child: new TextFormField(
focusNode: _usernameFocusNode,
decoration: new InputDecoration(
labelText: 'Username',
),
),
),
),
new Container(height: 8.0),
new Center(
child: new EnsureVisibleWhenFocused(
focusNode: _passwordFocusNode,
child: new TextFormField(
focusNode: _passwordFocusNode,
obscureText: true,
decoration: new InputDecoration(
labelText: 'Password',
),
),
),
),
new Container(),
new RaisedButton(
onPressed: () {},
child: new Text('Log in'),
),
new Divider(),
new RaisedButton(
onPressed: () {},
child: new Text('Sign up'),
),
],
),
),
],
),
),
);
}
}

关于dart - 如何在 Flutter 的 ScrollView 中限制滚动距离?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46172962/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com