当前位置:网站首页>Les différents modèles imbriqués de listview et Pageview avec les conseils de flutter

Les différents modèles imbriqués de listview et Pageview avec les conseils de flutter

2022-07-04 09:10:00 Gsytech

Cette fois Flutter Le truc, c'est que ListView Et PageView Un nid de fantaisie,C'est différent. Scrollable Le problème des conflits imbriqués, je crois que tout le monde n'est pas étranger,Adopté aujourd'hui ListView Et PageView Les trois modèles imbriqués vous permettent de récolter des conseils différents.

Nidification normale

La nidification la plus courante devrait être horizontale PageView Plus longitudinal ListView Combinaison de,En général, cette combinaison ne pose aucun problème,À moins que tu ne sois sur le point de glisser.

Récemment, j'ai rencontré plusieurs personnes en même temps:“Glissement oblique ListView Facile à passer à PageView Glissez” La question de,Comme suit GIF Comme indiqué,Quand l'utilisateur glisse ListView Heure,Après l'inclinaison de la ceinture d'angle de glissement,Ce qui pourrait faire glisser PageView Au lieu de ListView .

Bien que je ne pense pas personnellement que ce soit un problème,Mais si le produit doit être modifié,Dois - je me réécrire PageView Est - ce que le geste de?

Jetons un coup d'oeil.,Peu importe. PageView Toujours ListView Leur effet de glissement provient de Scrollable ,Et Scrollable Réponse interne dans différentes directions,C'est par RawGestureDetector Terminé.:

  • VerticalDragGestureRecognizer Gérer les gestes verticaux
  • HorizontalDragGestureRecognizer Manipuler les gestes horizontaux

Donc, regardez simplement la logique de jugement de leur réponse , Vous pouvez voir une façon très intéressante de computeHitSlopSelon pointer Le type détermine le plus petit pixel nécessaire pour frapper , Touch default est kTouchSlop (18.0).

Tu as vu ça? :Si nous mettons PageView De touchSlop Modifié, Est - il possible d'ajuster la sensibilité de sa réponse ? Juste là. computeHitSlop Méthode,Il peut passer par DeviceGestureSettings Pour configurer,Et DeviceGestureSettings De MediaQuery ,Donc le code suivant:

body: MediaQuery(  ///Élévation touchSlop À 50 ,Voilà. pageview  Le glissement peut avoir un peu d'effet ,  /// Mais le taux approximatif a résolu le problème du déclenchement par glissement oblique   data: MediaQuery.of(context).copyWith(      gestureSettings: DeviceGestureSettings(    touchSlop: 50,  )),  child: PageView(    scrollDirection: Axis.horizontal,    pageSnapping: true,    children: [      HandlerListView(),      HandlerListView(),    ],  ),),

Petit conseil 1: En imbriquant un MediaQuery ,Et ajuster gestureSettings De touchSlop Pour modifier PageView La spiritualité de ,Et n'oubliez pas,Il faut aussi ListView De touchSlop Basculer par défaut De kTouchSlop

class HandlerListView extends StatefulWidget {  @override  _MyListViewState createState() => _MyListViewState();}class _MyListViewState extends State<HandlerListView> {  @override  Widget build(BuildContext context) {    return MediaQuery(      ///Ici. touchSlop   Besoin de revenir à la valeur par défaut       data: MediaQuery.of(context).copyWith(          gestureSettings: DeviceGestureSettings(        touchSlop: kTouchSlop,      )),      child: ListView.separated(        itemCount: 15,        itemBuilder: (context, index) {          return ListTile(            title: Text('Item $index'),          );        },        separatorBuilder: (context, index) {          return const Divider(            thickness: 3,          );        },      ),    );  }}

Enfin, regardons l'effet,Comme suit GIF Comme indiqué, Maintenant, même si tu glisses en diagonale , Ça déclenche aussi PageView Glissement horizontal de , Déclenché uniquement en mouvement latéral PageView Un geste.,Bien sûr., S'il y a un problème avec cette écriture grossière , Ça a dû baisser PageView Sensibilité de la réponse .

Dans la même direction PageView Nidification ListView

Après l'introduction de l'utilisation régulière , Et puis quelque chose de différent , Dans le passage vertical PageView Imbriqué de rouleaux verticaux ListView , Avez - vous d'abord l'impression de ne pas être fiable ,Pourquoi y a - t - il une telle scène??

Pour les produits, Ils ne se pencheront pas sur la façon dont vous , Ils se tapaient la tête et disaient que Taobao pouvait , Pourquoi tu ne peux pas , Donc si c'était toi ,Que feriez - vous??

Et à propos de ce besoin , Les résultats des discussions en cours dans la communauté sont les suivants: :Prends ça. PageView Et ListView Diapositive désactivée pour ,Et à travers RawGestureDetector Autogestion.

Si vous n'êtes pas intéressé à réaliser une analyse logique , Vous pouvez regarder directement à la fin de cette section Lien source .

Ne paniquez pas avant de vous voir gérer. , Bien que vous deviez le faire vous - même PageView Et ListView La distribution des gestes , Mais il n'y a pas de réécriture nécessaire. PageView Et ListView , On peut les réutiliser. Darg Logique de réponse,Le code ci - dessous:

  • Adoption NeverScrollableScrollPhysics C'est interdit. PageView Et ListView L'effet de défilement de
  • En haut RawGestureDetectorDe VerticalDragGestureRecognizer Gérer les événements gestuels par vous - même
  • Configuration PageController Et ScrollController Pour obtenir l'état
body: RawGestureDetector(  gestures: <Type, GestureRecognizerFactory>{    VerticalDragGestureRecognizer: GestureRecognizerFactoryWithHandlers<            VerticalDragGestureRecognizer>(        () => VerticalDragGestureRecognizer(),        (VerticalDragGestureRecognizer instance) {      instance        ..onStart = _handleDragStart        ..onUpdate = _handleDragUpdate        ..onEnd = _handleDragEnd        ..onCancel = _handleDragCancel;    })  },  behavior: HitTestBehavior.opaque,  child: PageView(    controller: _pageController,    scrollDirection: Axis.vertical,    /// Masque la réponse de glissement par défaut     physics: const NeverScrollableScrollPhysics(),    children: [      ListView.builder(        controller: _listScrollController,        /// Masque la réponse de glissement par défaut         physics: const NeverScrollableScrollPhysics(),        itemBuilder: (context, index) {          return ListTile(title: Text('List Item $index'));        },        itemCount: 30,      ),      Container(        color: Colors.green,        child: Center(          child: Text(            'Page View',            style: TextStyle(fontSize: 50),          ),        ),      )    ],  ),),

Alors regardons _handleDragStart Réalisation,Le code ci - dessous, En générant des gestes details Heure, Notre jugement principal :

  • Adoption ScrollController Jugement ListView Visible ou non
  • Déterminer si la position tactile est ListIView Dans le champ d'application
  • Déterminer par statut Controller Pour produire Drag Objet, Utilisé en réponse à des événements de glissement ultérieurs
​  void _handleDragStart(DragStartDetails details) {    ///Juge d'abord Listview  Est visible ou peut être appelé     /// Généralement invisible  hasClients false ,Parce que PageView Pas du tout. keepAlive    if (_listScrollController?.hasClients == true &&        _listScrollController?.position.context.storageContext != null) {      ///Accès ListView De  renderBox      final RenderBox? renderBox = _listScrollController          ?.position.context.storageContext          .findRenderObject() as RenderBox;​      /// Déterminer si la position du toucher est  ListView Intérieur      /// Ce n'est généralement pas le cas parce que  ListView  Ça a glissé , Les coordonnées ne correspondent pas à la position tactile       if (renderBox?.paintBounds              .shift(renderBox.localToGlobal(Offset.zero))              .contains(details.globalPosition) ==          true) {        _activeScrollController = _listScrollController;        _drag = _activeScrollController?.position.drag(details, _disposeDrag);        return;      }    }​    /// C'est le moment de penser  PageView  Besoin de glisser     _activeScrollController = _pageController;    _drag = _pageController?.position.drag(details, _disposeDrag);  }

Devant nous, on commence surtout par toucher , Lors de la détermination de l'objet à répondre ListView Toujours PageView ,Et à travers _activeScrollController Enregistrer l'objet de réponse bien sûr ,Et à travers Controller Générer un Drag Objet.

En termes simples: Au moment de l'événement de glissement , Par défaut, un Drag Utilisé pour gérer les événements de glissement ultérieurs ,Drag L'événement original sera traité avant d'être donné à ScrollPosition Pour déclencher un effet de glissement ultérieur .

Et puis... _handleDragUpdate Méthode, Il s'agit principalement de déterminer si la réponse doit passer à PageView:

  • Si ce n'est pas nécessaire, continuez avec ce que vous avez _drag?.update(details)Réponse ListView Rouler
  • Passez si nécessaire _pageController Changer de _drag Objet utilisé pour répondre
void _handleDragUpdate(DragUpdateDetails details) {  if (_activeScrollController == _listScrollController &&​      ///Les doigts se déplacent vers le Haut, C'est - à - dire qu'il va bientôt montrer le bas  PageView      details.primaryDelta! < 0 &&​      /// En bas ,Passer à PageView      _activeScrollController?.position.pixels ==          _activeScrollController?.position.maxScrollExtent) {    /// Basculer le Contrôleur approprié     _activeScrollController = _pageController;    _drag?.cancel();​    ///RÉFÉRENCES  Scrollable - Oui.    /// Parce que c'est le Contrôleur de commutation , C'est - à - dire mettre à jour  Drag    /// Le processus de glisser - déposer passe à  PageView - Oui.,Il faut donc  DragStartDetails    ///Il faut donc DragUpdateDetails Devenir DragStartDetails    ///Extrait PageView À l'intérieur. Drag Correspondant details    _drag = _pageController?.position.drag(        DragStartDetails(            globalPosition: details.globalPosition,            localPosition: details.localPosition),        _disposeDrag);  }  _drag?.update(details);}

Voici un petit point de connaissance:Comme indiqué dans le code ci - dessus, On peut passer par details.primaryDelta Déterminer si la direction de glissement et le mouvement sont la broche

Enfin: GIF Comme indiqué,Je vois. PageView Nidification ListView Le glissement dans la même direction peut fonctionner normalement , Mais il y a encore deux petits problèmes ,Comme le montre l'illustration:

  • Après le basculement ListView L'emplacement n'a pas été sauvegardé
  • Le produit doit être retiré ListView Effet de débordement de bord de

C'est pourquoi nous devons avoir raison. ListView Fais - en un. KeepAlive , Et l'enlever d'une manière simple Android Les bords glissent Material Effets:

  • Adoption with AutomaticKeepAliveClientMixin Jean ListView Maintenir également la position de glissement après la commutation
  • Adoption ScrollConfiguration.of(context).copyWith(overscroll: false) Enlèvement rapide Scrollable Le bord de Material Effets
child: PageView(  controller: _pageController,  scrollDirection: Axis.vertical,  ///Enlevez Android  Effet de traînée de bord par défaut sur   scrollBehavior:      ScrollConfiguration.of(context).copyWith(overscroll: false),​​///C'est exact. PageView À l'intérieur. ListView Fais - le. KeepAlive  Rappelez - vous la position class KeepAliveListView extends StatefulWidget {  final ScrollController? listScrollController;  final int itemCount;​  KeepAliveListView({    required this.listScrollController,    required this.itemCount,  });​  @override  KeepAliveListViewState createState() => KeepAliveListViewState();}​class KeepAliveListViewState extends State<KeepAliveListView>    with AutomaticKeepAliveClientMixin {  @override  Widget build(BuildContext context) {    super.build(context);    return ListView.builder(      controller: widget.listScrollController,​      /// Masque la réponse de glissement par défaut       physics: const NeverScrollableScrollPhysics(),      itemBuilder: (context, index) {        return ListTile(title: Text('List Item $index'));      },      itemCount: widget.itemCount,    );  }​  @override  bool get wantKeepAlive => true;}

Donc voici un autre petit truc pour déverrouiller :Adoption ScrollConfiguration.of(context).copyWith(overscroll: false) Enlèvement rapide Android Glissez sur le bord Material 2Effets,Pourquoi dire Material2, Parce que Material3 Ça a changé. ,Les détails sont visibles: Flutter 3 En bas ThemeExtensions Et Material3 .

Le code source de cette sous - section est visible : https://github.com/CarGuo/gsy_flutter_demo/blob/7838971cefbf19bb53a71041cd100c4c15eb6443/lib/widget/vp_list_demo_page.dart#L75

Dans la même direction ListView Nidification PageView

Y a - t - il quelque chose de plus inhabituel? ?La réponse est oui., Après tout, la petite tête du produit , Comment peut - on imaginer En glissant verticalement ListView Imbriqué verticalement commuté PageView Ce besoin.

Avec les idées qui précèdent , En fait, la mise en oeuvre de cette logique est aussi différente :Prends ça. PageView Et ListView Diapositive désactivée pour ,Et à travers RawGestureDetector Autogestion, La différence est la différence dans la distribution des méthodes gestuelles .

RawGestureDetector(          gestures: <Type, GestureRecognizerFactory>{            VerticalDragGestureRecognizer: GestureRecognizerFactoryWithHandlers<                    VerticalDragGestureRecognizer>(                () => VerticalDragGestureRecognizer(),                (VerticalDragGestureRecognizer instance) {              instance                ..onStart = _handleDragStart                ..onUpdate = _handleDragUpdate                ..onEnd = _handleDragEnd                ..onCancel = _handleDragCancel;            })          },          behavior: HitTestBehavior.opaque,          child: ListView.builder(                /// Masque la réponse de glissement par défaut                 physics: NeverScrollableScrollPhysics(),                controller: _listScrollController,                itemCount: 5,                itemBuilder: (context, index) {                  if (index == 0) {                    return Container(                      height: 300,                      child: KeepAlivePageView(                        pageController: _pageController,                        itemCount: itemCount,                      ),                    );                  }                  return Container(                      height: 300,                      color: Colors.greenAccent,                      child: Center(                        child: Text(                          "Item $index",                          style: TextStyle(fontSize: 40, color: Colors.blue),                        ),                      ));                }),        )

Encore une fois, _handleDragStart Méthode, Il faut d'abord juger :

  • ListView Si elle a glissé , Ne répondez pas en haut PageView Les événements de
  • Si ListView Pas de glissement en haut , Déterminer si la position du geste est PageView - Oui.,Si c'est une réponse PageView Les événements de
  void _handleDragStart(DragStartDetails details) {    /// Tant que ce n'est pas le haut ,Pas de réponse PageView Glissement    /// Donc ce jugement ne supporte que la verticale  PageView In  ListView En haut    if (_listScrollController.offset > 0) {      _activeScrollController = _listScrollController;      _drag = _listScrollController.position.drag(details, _disposeDrag);      return;    }​    ///En ce moment  ListView En haut    if (_pageController.hasClients) {      ///Accès PageView      final RenderBox renderBox =          _pageController.position.context.storageContext.findRenderObject()              as RenderBox;​      /// Déterminer si la plage de toucher est  PageView      final isDragPageView = renderBox.paintBounds          .shift(renderBox.localToGlobal(Offset.zero))          .contains(details.globalPosition);​      ///Si dans PageView  Ça passe à  PageView      if (isDragPageView) {        _activeScrollController = _pageController;        _drag = _activeScrollController.position.drag(details, _disposeDrag);        return;      }    }​    ///Non. PageView  Li continue à répondre  ListView    _activeScrollController = _listScrollController;    _drag = _listScrollController.position.drag(details, _disposeDrag);  }

Et puis... _handleDragUpdate Méthode,Juge si PageView A glissé jusqu'à la dernière page , Basculer également l'événement de glissement vers ListView

void _handleDragUpdate(DragUpdateDetails details) {  var scrollDirection = _activeScrollController.position.userScrollDirection;​  /// Déterminer si la réponse est toujours  _pageController, C'est la dernière page?   if (_activeScrollController == _pageController &&      scrollDirection == ScrollDirection.reverse &&​      /// C'est la dernière page? , Retour à la dernière page  pageController      (_pageController.page != null &&          _pageController.page! >= (itemCount - 1))) {    ///Revenir en arrière ListView    _activeScrollController = _listScrollController;    _drag?.cancel();    _drag = _listScrollController.position.drag(        DragStartDetails(            globalPosition: details.globalPosition,            localPosition: details.localPosition),        _disposeDrag);  }  _drag?.update(details);}

Bien sûr.,Encore une fois. KeepAlive Et supprimer la liste Material Effets de bord ,L'effet final est le suivant GIF Comme indiqué.

Le code source de cette sous - section est visible :https://github.com/CarGuo/gsy_flutter_demo/blob/7838971cefbf19bb53a71041cd100c4c15eb6443/lib/widget/vp_list_demo_page.dart#L262

Un dernier petit conseil. :Si vous avez besoin de Flutter Processus d'impression des jeux de gestes ,Configurable debugPrintGestureArenaDiagnostics = true;Allez. Flutter Processus de traitement des jeux de gestes de sortie .

import 'package:flutter/gestures.dart';void main() {  debugPrintGestureArenaDiagnostics = true;  runApp(MyApp());}

Enfin

Pour conclure, Ce chapitre décrit comment passer Darg Résoudre toutes sortes de conflits de gestes causés par la nidification , Je crois que tout le monde sait comment utiliser Controller Et Darg Pour personnaliser rapidement certaines exigences de glissement ,Par exemple ListView Liaison ListView L'effet de glissement différentiel de :

///listView Liaison listViewclass ListViewLinkListView extends StatefulWidget {  @override  _ListViewLinkListViewState createState() => _ListViewLinkListViewState();}​class _ListViewLinkListViewState extends State<ListViewLinkListView> {  ScrollController _primaryScrollController = ScrollController();  ScrollController _subScrollController = ScrollController();​  Drag? _primaryDrag;  Drag? _subDrag;​  @override  void initState() {    super.initState();  }​  @override  void dispose() {    _primaryScrollController.dispose();    _subScrollController.dispose();    super.dispose();  }​  void _handleDragStart(DragStartDetails details) {    _primaryDrag =        _primaryScrollController.position.drag(details, _disposePrimaryDrag);    _subDrag = _subScrollController.position.drag(details, _disposeSubDrag);  }​  void _handleDragUpdate(DragUpdateDetails details) {    _primaryDrag?.update(details);​    ///Divisé par10 Pour obtenir un effet différentiel     _subDrag?.update(DragUpdateDetails(        sourceTimeStamp: details.sourceTimeStamp,        delta: details.delta / 30,        primaryDelta: (details.primaryDelta ?? 0) / 30,        globalPosition: details.globalPosition,        localPosition: details.localPosition));  }​  void _handleDragEnd(DragEndDetails details) {    _primaryDrag?.end(details);    _subDrag?.end(details);  }​  void _handleDragCancel() {    _primaryDrag?.cancel();    _subDrag?.cancel();  }​  void _disposePrimaryDrag() {    _primaryDrag = null;  }​  void _disposeSubDrag() {    _subDrag = null;  }​  @override  Widget build(BuildContext context) {    return Scaffold(        appBar: AppBar(          title: Text("ListViewLinkListView"),        ),        body: RawGestureDetector(          gestures: <Type, GestureRecognizerFactory>{            VerticalDragGestureRecognizer: GestureRecognizerFactoryWithHandlers<                    VerticalDragGestureRecognizer>(                () => VerticalDragGestureRecognizer(),                (VerticalDragGestureRecognizer instance) {              instance                ..onStart = _handleDragStart                ..onUpdate = _handleDragUpdate                ..onEnd = _handleDragEnd                ..onCancel = _handleDragCancel;            })          },          behavior: HitTestBehavior.opaque,          child: ScrollConfiguration(            ///Enlevez Android  Effet de traînée de bord par défaut sur             behavior:                ScrollConfiguration.of(context).copyWith(overscroll: false),            child: Row(              children: [                new Expanded(                    child: ListView.builder(​                        /// Masque la réponse de glissement par défaut                         physics: NeverScrollableScrollPhysics(),                        controller: _primaryScrollController,                        itemCount: 55,                        itemBuilder: (context, index) {                          return Container(                              height: 300,                              color: Colors.greenAccent,                              child: Center(                                child: Text(                                  "Item $index",                                  style: TextStyle(                                      fontSize: 40, color: Colors.blue),                                ),                              ));                        })),                new SizedBox(                  width: 5,                ),                new Expanded(                  child: ListView.builder(​                      /// Masque la réponse de glissement par défaut                       physics: NeverScrollableScrollPhysics(),                      controller: _subScrollController,                      itemCount: 55,                      itemBuilder: (context, index) {                        return Container(                          height: 300,                          color: Colors.deepOrange,                          child: Center(                            child: Text(                              "Item $index",                              style:                                  TextStyle(fontSize: 40, color: Colors.white),                            ),                          ),                        );                      }),                ),              ],            ),          ),        ));  }}
原网站

版权声明
本文为[Gsytech]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/185/202207040855380945.html