6
votes

Flutter PageView: désactiver le défilement vers la gauche ou vers la droite

J'ai un PageView, comment puis-je désactiver le défilement gauche ou droit. Je sais qu'en utilisant NeverScrollableScrollPhysics, nous pouvons désactiver le défilement mais comment désactiver le défilement dans une direction.


0 commentaires

3 Réponses :


5
votes

Vous pouvez créer votre propre ScrollPhysics pour autoriser uniquement à aller vers la droite:

            @override
              Widget build(BuildContext context) {
                return Scaffold(
                    appBar: AppBar(),
                    body: PageView.builder(
                      itemCount: 4,
                      physics: CustomScrollPhysics(),
                      itemBuilder: (context, index) => Center(
                            child: Text("Item $index"),
                          ),
                    ));
              }

Usage:

            class CustomScrollPhysics extends ScrollPhysics {
              CustomScrollPhysics({ScrollPhysics parent}) : super(parent: parent);

              bool isGoingLeft = false;

              @override
              CustomScrollPhysics applyTo(ScrollPhysics ancestor) {
                return CustomScrollPhysics(parent: buildParent(ancestor));
              }

              @override
              double applyPhysicsToUserOffset(ScrollMetrics position, double offset) {
                isGoingLeft = offset.sign < 0;
                return offset;
              }

              @override
              double applyBoundaryConditions(ScrollMetrics position, double value) {
                //print("applyBoundaryConditions");
                assert(() {
                  if (value == position.pixels) {
                    throw FlutterError(
                        '$runtimeType.applyBoundaryConditions() was called redundantly.\n'
                        'The proposed new position, $value, is exactly equal to the current position of the '
                        'given ${position.runtimeType}, ${position.pixels}.\n'
                        'The applyBoundaryConditions method should only be called when the value is '
                        'going to actually change the pixels, otherwise it is redundant.\n'
                        'The physics object in question was:\n'
                        '  $this\n'
                        'The position object in question was:\n'
                        '  $position\n');
                  }
                  return true;
                }());
                if (value < position.pixels && position.pixels <= position.minScrollExtent)
                  return value - position.pixels;
                if (position.maxScrollExtent <= position.pixels && position.pixels < value)
                  // overscroll
                  return value - position.pixels;
                if (value < position.minScrollExtent &&
                    position.minScrollExtent < position.pixels) // hit top edge

                  return value - position.minScrollExtent;

                if (position.pixels < position.maxScrollExtent &&
                    position.maxScrollExtent < value) // hit bottom edge
                  return value - position.maxScrollExtent;

                if (!isGoingLeft) {
                  return value - position.pixels;
                }
                return 0.0;
              }
            }


2 commentaires

Obtenir This class (or a class which this class inherits from) is marked as '@immutable', but one or more of its instance fields are not final: CustomScrollPhysics.isGoingLeft Des idées? Je suppose que je ne peux pas rendre isGoingLeft final.


Bonjour, comment ajuster le code pour gérer un défilement vertical dans une direction (vers le bas)? Merci



3
votes

Vous pouvez également utiliser la bibliothèque horizontale_blocked_scroll_physics que j'ai récemment écrite qui vous permettra de bloquer les mouvements gauche et droit.


0 commentaires

1
votes

Est venu avec une approche légèrement meilleure pour cela.

Cette approche ne nécessite pas d'instanciation de nouvelles instances physiques et peut être mise à jour dynamiquement.

Utilisez le paramètre de fonction onAttemptDrag pour autoriser ou refuser les demandes de balayage. Votre code dans cette fonction devrait être efficace car il sera appelé plusieurs fois par seconde (lors du défilement). En outre, vous souhaiterez peut-être ajouter un indicateur dans cette fonction qui autorise les demandes d'origine programmatique à passer. Par exemple, dans le cas d'un "bouton suivant", cette implémentation physique empêcherait également les fonctions jumpTo(..) et animateTo de fonctionner, vous auriez donc besoin d'un indicateur qui renverra temporairement une valeur par défaut true pour un transition de page si le bouton suivant a été enfoncé. Faites-moi savoir si vous avez des questions ou comment améliorer cela.

class LockingPageScrollPhysics extends ScrollPhysics {
  /// Requests whether a drag may occur from the page at index "from"
  /// to the page at index "to". Return true to allow, false to deny.
  final Function(int from, int to) onAttemptDrag;

  /// Creates physics for a [PageView].
  const LockingPageScrollPhysics(
      {ScrollPhysics parent, @required this.onAttemptDrag})
      : super(parent: parent);

  @override
  LockingPageScrollPhysics applyTo(ScrollPhysics ancestor) {
    return LockingPageScrollPhysics(
        parent: buildParent(ancestor), onAttemptDrag: onAttemptDrag);
  }

  double _getPage(ScrollMetrics position) {
    if (position is PagePosition) return position.page;
    return position.pixels / position.viewportDimension;
  }

  double _getPixels(ScrollMetrics position, double page) {
    if (position is PagePosition) return position.getPixelsFromPage(page);
    return page * position.viewportDimension;
  }

  double _getTargetPixels(
      ScrollMetrics position, Tolerance tolerance, double velocity) {
    double page = _getPage(position);
    if (velocity < -tolerance.velocity)
      page -= 0.5;
    else if (velocity > tolerance.velocity) page += 0.5;
    return _getPixels(position, page.roundToDouble());
  }

  @override
  double applyBoundaryConditions(ScrollMetrics position, double value) {
    assert(() {
      if (value == position.pixels) {
        throw FlutterError('$runtimeType.applyBoundaryConditions() was called redundantly.\n'
            'The proposed new position, $value, is exactly equal to the current position of the '
            'given ${position.runtimeType}, ${position.pixels}.\n'
            'The applyBoundaryConditions method should only be called when the value is '
            'going to actually change the pixels, otherwise it is redundant.\n'
            'The physics object in question was:\n'
            '  $this\n'
            'The position object in question was:\n'
            '  $position\n');
      }
      return true;
    }());

    /*
     * Handle the hard boundaries (min and max extents)
     * (identical to ClampingScrollPhysics)
     */
    if (value < position.pixels && position.pixels <= position.minScrollExtent) // under-scroll
      return value - position.pixels;
    if (position.maxScrollExtent <= position.pixels && position.pixels < value) // over-scroll
      return value - position.pixels;
    if (value < position.minScrollExtent &&
        position.minScrollExtent < position.pixels) // hit top edge
      return value - position.minScrollExtent;
    if (position.pixels < position.maxScrollExtent &&
        position.maxScrollExtent < value) // hit bottom edge
      return value - position.maxScrollExtent;

    bool left = value < position.pixels;

    int fromPage, toPage;
    double overScroll = 0;

    if (left) {
      fromPage = position.pixels.ceil() ~/ position.viewportDimension;
      toPage = value ~/ position.viewportDimension;

      overScroll = value - fromPage * position.viewportDimension;
      overScroll = overScroll.clamp(value - position.pixels, 0.0);
    } else {
      fromPage =
          (position.pixels + position.viewportDimension).floor() ~/ position.viewportDimension;
      toPage = (value + position.viewportDimension) ~/ position.viewportDimension;

      overScroll = value - fromPage * position.viewportDimension;
      overScroll = overScroll.clamp(0.0, value - position.pixels);
    }

    if (fromPage != toPage && !onAttemptDrag(fromPage, toPage)) {
      return overScroll;
    } else {
      return super.applyBoundaryConditions(position, value);
    }
  }

Voici une bibliothèque sur laquelle j'ai travaillé qui utilise ce contrôleur modifié: https://github.com/RobluScouting/FlutterBoardView .


0 commentaires