Want to limit scroll distance in a scroll view in Flutter? If you want to prevent users from scrolling, you might want to just ensure that all the fields are visible using the same techniques below and then use a NeverScrollableScrollPhysics as the physics of the ListView. Or if you’re feeling ambitious you could implement a custom scroll physics as shown in the Gallery example. If I were you I’d hold out for #10826 to be fixed, though.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | 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'), ), ], ), ), ], ), ), ); } } |
If you like this question & answer and want to contribute, then write your question & answer and email to freewebmentor[@]gmail.com. Your question and answer will appear on FreeWebMentor.com and help other developers.