dart – 如何在Flutter的滚动视图中限制滚动距离?
我创建了一个页面,其中包含一个列中的几个文本字段和按钮,该列包含在具有背景图像的容器中.而这个容器本身就是一个scrollview小部件的子代.
因此,当一个人点击其中一个字段时,他们的键盘会弹出(占据屏幕的一部分),这意味着一些按钮/字段在屏幕外,这是scrollview小部件用于其目的的地方. 这里的问题是我想限制滚动视图允许用户滚动的距离. 最低按钮下面有一些空白区域,我不希望用户能够一直滚动到那里.这也是让体验变得简单,并且不会让用户“过度滚动”超过他应该输入的字段. 但由于背景图像是滚动视图的一部分,因此视图将允许用户向下滚动到图像底部.我想限制这个. 作为后续工作,我试图弄清楚如何设置初始滚动位置. (因此,当单击某个字段时,滚动视图会向下滚动到第一个文本字段,因此所有字段都在视图中.用户无需向下滚动它们.但我不希望重新应用此滚动位置每当用户点击某个字段时,当然.) 这是相关的(如果我的任何代码看起来非常糟糕请说出来,我是一般的编程新手并接受任何建议改进): 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 SignUpButton(),],)) ]) ],)); } } 解决方法
对于自动滚动字段进入视图,听起来你正在和
issue 10826搏斗.我在这个问题上发布了
workaround.我将解决方法改为您的示例代码;见下文. (你可能想稍微调整一下.)
如果您想阻止用户滚动,您可能只想确保使用下面相同的技术可见所有字段,然后使用NeverScrollableScrollPhysics作为ListView的物理.或者,如果你有野心,你可以实现自定义滚动物理,如Gallery example所示.如果我是你,我会坚持#10826被修复. 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'),child: new Text('Sign up'),); } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |