2017-09-12 3 views
3

J'ai créé une page contenant plusieurs champs de texte et boutons dans une colonne contenue dans un conteneur contenant une image d'arrière-plan. Et ce conteneur est lui-même l'enfant d'un widget scrollview. Par conséquent, lorsqu'une personne clique sur l'un des champs, son clavier apparaîtra (en prenant une partie de l'écran), ce qui signifie que certains boutons/champs sont hors écran, ce qui correspond à l'objectif du widget scrollview.Comment limiter la distance de défilement dans une vue déroulante dans Flutter?

Le problème ici est que je veux limiter à quel point la vue de défilement permet à un utilisateur de faire défiler.

Il y a un espace vide sous le bouton le plus bas, et je ne veux pas que l'utilisateur puisse faire défiler tout le chemin. Cela permet également de simplifier l'expérience et de ne pas dépasser les champs qu'il doit saisir.

Mais comme l'image d'arrière-plan fait partie de la vue déroulante, la vue permet à l'utilisateur de faire défiler aussi loin que possible. comme le bas de l'image. Je veux limiter cela.

En guise de suivi, j'essaie de comprendre comment définir une position de défilement initiale. (Ainsi, lorsque vous cliquez sur un champ, la vue déroulante défile jusqu'au premier champ de texte, de sorte que tous les champs sont visibles, sans que l'utilisateur doive les faire défiler, mais je ne souhaite pas que cette position de défilement soit appliquée à nouveau. chaque fois que l'utilisateur clique sur un champ, bien sûr.)

Voici le pertinent (si l'un de mes codes semble vraiment mauvais, veuillez le dire, je suis nouveau à la programmation en général et accepter tous les conseils pour améliorer):

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(), 
       ], 
      )) 
      ]) 
     ], 
    ), 
    )); 
    } 
} 

Répondre

4

Pour le défilement automatique des champs en vue, il semble que vous êtes aux prises avec issue 10826. J'ai posté un workaround sur ce problème. J'ai adapté la solution de contournement à votre exemple de code; voir ci-dessous. (Vous pouvez modifier un peu.)

Si vous voulez empêcher les utilisateurs de défilement, vous pouvez simplement faire en sorte que tous les champs sont visibles en utilisant les mêmes techniques ci-dessous, puis utiliser un NeverScrollableScrollPhysics comme physics de le ListView. Ou si vous vous sentez ambitieux, vous pouvez implémenter une physique de défilement personnalisée, comme illustré dans le Gallery example. Si j'étais toi je tiendrais le # 10826 pour être réparé, cependant.

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'), 
       ), 
       ], 
      ), 
      ), 
      ], 
     ), 
    ), 
    ); 
    } 
} 
+0

Merci beaucoup. Je fais ça depuis si longtemps ... –