J'ai un écran qui se construit à l'aide de MaterialApp
, DefaultTabController
, Scaffold
et TabBarView
.
dans cet écran, j'ai un contenu de corps qui récupère une liste d'éléments de sqllite en utilisant StreamBuilder
. j'obtiens exactement 100 éléments ("liste finie") à montrer en utilisant ListView
.
ma question, en utilisant ListView.builder
, comment pouvons-nous sauter à certains index lorsque cet écran s'est ouvert?
mon écran principal:
import 'package:draggable_scrollbar/draggable_scrollbar.dart'; import 'package:flutter/material.dart'; class MyDraggableScrollBar { static Widget create({ @required BuildContext context, @required ScrollController scrollController, @required double heightScrollThumb, @required Widget child, }) { return DraggableScrollbar( alwaysVisibleScrollThumb: true, scrollbarTimeToFade: Duration(seconds: 3), controller: scrollController, heightScrollThumb: heightScrollThumb, backgroundColor: Colors.green, scrollThumbBuilder: ( Color backgroundColor, Animation<double> thumbAnimation, Animation<double> labelAnimation, double height, { Text labelText, BoxConstraints labelConstraints, }) { return InkWell( onTap: () {}, child: Container( height: height, width: 7, color: backgroundColor, ), ); }, child: child, ); } }
class MyDraggableScrollBar.dart :
... ScrollController controller = ScrollController(); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner : false, home: DefaultTabController( length: 3, child: Scaffold( appBar: AppBar( backgroundColor: Pigment.fromString(UIData.primaryColor), elevation: 0, centerTitle: true, title: Text(translations.text("quran").toUpperCase()), bottom: TabBar( tabs: [ Text("Tab1"), Text("Tab2"), Text("Tab3") ], ), leading: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ Expanded( child: InkWell( child: SizedBox(child: Image.asset("assets/images/home.png"), height: 10, width: 1,), onTap: () => Navigator.of(context).pop(), ) ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _scrollToIndex, tooltip: 'Testing Index Jump', child: Text("GO"), ), body: TabBarView( children: [ Stack( children: <Widget>[ MyDraggableScrollBar.create( scrollController: controller, context: context, heightScrollThumb: 25, child: ListView( controller: controller, children: <Widget>[ Padding( padding: EdgeInsets.fromLTRB(30, 15, 30, 8), child: Container( alignment: Alignment.center, height: 30, child: ClipRRect( borderRadius: BorderRadius.circular(8), child: TextField( style: TextStyle(color: Colors.green), decoration: new InputDecoration( contentPadding: EdgeInsets.all(5), border: InputBorder.none, filled: true, hintStyle: new TextStyle(color: Colors.green, fontSize: 14), prefixIcon: Icon(FontAwesomeIcons.search,color: Colors.green,size: 17,), hintText: translations.text("search-quran"), fillColor: Colors.grey[300], prefixStyle: TextStyle(color: Colors.green) ), onChanged: (val) => quranBloc.searchSurah(val), ), ) ) ), //surah list streamBuilderQuranSurah(context) ], ) ) // MyDraggableScrollBar ], ), Icon(Icons.directions_transit), Icon(Icons.directions_bike), ], ) ))); } Widget streamBuilderQuranSurah(BuildContext ctx){ return StreamBuilder( stream: quranBloc.chapterStream , builder: (BuildContext context, AsyncSnapshot<ChaptersModel> snapshot){ if(snapshot.hasData){ return ListView.builder( controller: controller, shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemCount:(snapshot.data.chapters?.length ?? 0), itemBuilder: (BuildContext context, int index) { var chapter = snapshot.data.chapters?.elementAt(index); return chapterDataCell(chapter); }, ); } else{ return SurahItemShimmer(); } }, ); } ...
J'ai essayé de trouver d'autres solutions mais ne semble pas fonctionner, par exemple indexed_list_view qui ne prend en charge que la liste infinie
et il semble que le flutter n'ait toujours pas de fonctionnalité pour cela, voir ce problème
Une idée ?
4 Réponses :
Solution générale:
Pour stocker tout ce qui peut être représenté sous forme de nombre / chaîne / liste de chaînes, Flutter fournit un plugin puissant et facile à utiliser qui stocke les valeurs nécessaires à stocker avec une clé. Ainsi, la prochaine fois que vous en aurez besoin, vous devrez récupérer ou même mettre à jour cette valeur, tout ce dont vous aurez besoin est cette clé.
Pour commencer, ajoutez le plugin shared_preferences au fichier pubspec.yaml,
class MyWidget extends StatefulWidget { @override _MyWidgetState createState() => _MyWidgetState(); } class _MyWidgetState extends State<MyWidget> with WidgetsBindingObserver{ //[...] ScrollController _scrollController; SharedPreferences _sharedPreferences; Future<void> resumeController() async{ _sharedPreferences = await SharedPreferences.getInstance().then((_sharedPreferences){ if(_sharedPreferences.getKeys().contains("scroll-offset-0")) _scrollController= ScrollController(initialScrollOffset:_sharedPreferences.getDouble("scroll-offset-0")); else _sharedPreferences.setDouble("scroll-offset-0", 0); setState((){}); return _sharedPreferences; }); } @override void initState() { resumeController(); WidgetsBinding.instance.addObserver(this); super.initState(); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); _scrollController.dispose(); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { if(state==AppLifecycleState.paused || state==AppLifecycleState.inactive || state==AppLifecycleState.suspending) _sharedPreferences.setDouble("scroll-offset-0", _scrollController.offset); super.didChangeAppLifecycleState(state); } @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( appBar: AppBar( title: Text("Smart Scroll View"), ), body: ListView.builder( itemCount: 50, controller: _scrollController, itemBuilder: (c,i)=> Padding( padding: EdgeInsets.symmetric(horizontal: 24,vertical: 16), child: Text((i+1).toString()), ), ), ), ); } }
Exécutez flutter pub get
partir du terminal ou si vous utilisez IntelliJ, cliquez simplement sur Packages get
(vous le trouverez quelque part dans le coin supérieur droit de votre écran lors de la visualisation du fichier pubspec.yaml
)
Une fois la commande ci-dessus exécutée avec succès, importez le fichier ci-dessous dans votre main.dart
ou le fichier concerné.
@override void dispose() { WidgetsBinding.instance.removeObserver(this); _scrollController.dispose(); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { if(state==AppLifecycleState.paused || state==AppLifecycleState.inactive || state==AppLifecycleState.suspending) _sharedPreferences.setDouble("scroll-offset-0", _scrollController.offset); super.didChangeAppLifecycleState(state); }
Maintenant, attachez simplement un ScrollController à votre widget ListView.builder()
et assurez-vous que le décalage final / dernier est stocké avec une clé spécifique en utilisant shared_preferences chaque fois que l'utilisateur quitte l'application de quelque manière que ce soit et est défini lorsque l'initState de votre widget concerné est appelé.
Afin de savoir détecter les changements dans l'état de notre application et agir en conséquence, nous WidgetsBindingObserver
de WidgetsBindingObserver
de notre classe.
Étapes à suivre:
Étendez la classe WidgetsBindingObserver avec la classe State de votre StatefulWidget.
Définissez une fonction asynchrone resumeController()
tant que fonction membre de la classe ci-dessus.
resumeController(); WidgetsBinding.instance.addObserver(this);
ScrollController _scrollController; SharedPreferences _sharedPreferences;
resumeController()
et transmettez votre classe à la méthode addObserver de l'objet d'instance dans la classe WidgetsBinding.Future<void> resumeController() async{ _sharedPreferences = await SharedPreferences.getInstance().then((_sharedPreferences){ if(_sharedPreferences.getKeys().contains("scroll-offset-0")) _scrollController= ScrollController(initialScrollOffset:_sharedPreferences.getDouble("scroll-offset-0")); else _sharedPreferences.setDouble("scroll-offset-0", 0); setState((){}); return _sharedPreferences; });
import 'package:shared_preferences/shared_preferences.dart';
ScrollController()
au Scrollable concerné.Exemple de travail:
dependencies: flutter: sdk: flutter shared_preferences: "<newest version>"
Pensez-vous que AppLifecycleState.paused
, AppLifecycleState.inactive
, AppLifecycleState.suspending
couvrent tous les cas et que le décalage de défilement sera toujours enregistré, quelle que soit la façon dont l'application est «fermée»?
De plus, la suspending
n'est plus (plus une) valeur de AppLifecycleState
la solution que j'ai trouvée sans connaître la taille de votre widget affiche une «sous-liste» inversée de l'index à la fin, puis faites défiler vers le haut de votre «sous-liste» et réinitialisez la liste entière. Comme il s'agit d'une liste inversée, l'élément sera ajouté en haut de la liste et vous resterez à votre position (l'index).
le problème est que vous ne pouvez pas utiliser un listView.builder car vous devrez changer la taille de la liste
class _ListViewIndexState extends State<ListViewIndex> { ScrollController _scrollController; List<Widget> _displayedList; @override void initState() { super.initState(); _scrollController = ScrollController(); _displayedList = widget.items.sublist(0, widget.items.length - widget.index); if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) { SchedulerBinding.instance.addPostFrameCallback((_) { //here the sublist is already build completeList(); }); } } completeList() { //to go to the last item(in first position) _scrollController.jumpTo(_scrollController.position.maxScrollExtent); //reset the list to the full list setState(() { _displayedList = widget.items; }); } @override Widget build(BuildContext context) { return Stack( children: <Widget>[ ListView( controller: _scrollController, reverse: true, children: _displayedList, ), ] ); } }
Le package https://pub.dev/packages/indexed_list_view pourrait peut-être vous aider pour cela. Utilisez quelque chose comme ceci:
IndexedListView.builder( controller: indexScrollController, itemBuilder: itemBuilder ); indexScrollController.jumpToIndex(10000);
Vous pouvez utiliser https://pub.dev/packages/scrollable_positioned_list . Vous pouvez transmettre l'index initial au widget.
ScrollablePositionedList.builder( initialScrollIndex: 12, //you can pass the desired index here// itemCount: 500, itemBuilder: (context, index) => Text('Item $index'), itemScrollController: itemScrollController, itemPositionsListener: itemPositionsListener, );
Connaissez-vous déjà le numéro d'index ou l'élément que vous souhaitez ignorer?
Salut @AjilO. oui parce que c'est une liste finie et que ce sera un paramètre d'un autre écran.
vous recherchez probablement ce stackoverflow.com/a/58809961/6668797
ScrollController(initialScrollOffset: _)