How to limit scroll to scroll in Flutter?

I created a page containing several text fields and buttons in a column that is contained in a container with a background image. And this container itself is a child of the scrollview widget.

So, when a person clicks on one of the fields, their keyboard will pop up (taking part of the screen), which means that some buttons / fields are displayed behind the scenes, and it is there that the scrollview widget performs its task.

The problem here is that I want to limit how far the scroll allows the user to scroll.

There are a few spaces under the bottom button, and I don't want the user to be able to scroll all the way there. This simplifies the process too much and does not allow the user to "skip" past the fields that he must enter.

But since the background image is part of the scroll, the view allows the user to scroll as far as the bottom of the image. I want to limit this.

As a continuation, I am trying to figure out how to set the start position of the scroll. (So ​​when you click on a field, the scroll view scrolls to the very first text field, so all fields are in view, without having to scroll down to them. However, I don’t want this scroll position to be reapplied every time the user clicks on the field, of course.)

Here is the corresponding one (if any of my codes looks very bad, say so, I'm new to programming in general and accept any tips for improvement):

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(),
              ],
            ))
          ])
        ],
      ),
    ));
  }
}
+4
source share
1

, issue 10826. . ; . (, .)

, , , NeverScrollableScrollPhysics physics ListView. , , , . , # 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'),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}
+6

Source: https://habr.com/ru/post/1685514/


All Articles