How to limit scroll distance in a scroll view in Flutter?

后端 未结 1 1172
旧巷少年郎
旧巷少年郎 2021-02-10 08:35

I\'ve made a page that contains several textfields and buttons in a column which is contained in a container that has a background image. And this container is itself the child

相关标签:
1条回答
  • 2021-02-10 09:12

    For auto-scrolling the fields into view, it sounds like you are wrestling with issue 10826. I posted a workaround on that issue. I adapted the workaround to your sample code; see below. (You may want to tweak it a little.)

    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.

    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'),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    0 讨论(0)
提交回复
热议问题