web-dev-qa-db-ja.com

Flutter ListView.Builder - プログラムで特定のインデックスへジャンプする方法

MaterialAppDefaultTabControllerScaffoldTabBarViewを使ってビルドする画面があります。

この画面では、StreamBuilderを使用してSQLITEから要素のリストを再現するボディコンテンツがあります。 ListViewを使用して、正確な100個の要素(「有限リスト」)が表示されます。

私の質問、_ListView.builder_、この画面が開かれたときに特定のインデックスにジャンプする方法は?

マイスクリーン:

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

classmydraggableScrollBar.dart

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

私は他の解決策を見つけましたが、たとえば機能していないようです indexed_list_view それは無限リストのみをサポートしています

そしてそれはまだこれに特徴を持っていないのは、 この問題

何か案が ?

9
zukijuki

使用できます https://pub.dev/packages/scrollable_positioned_list 。初期インデックスをウィジェットに渡すことができます。

ScrollablePositionedList.builder(
 initialScrollIndex: 12, //you can pass the desired index here//
 itemCount: 500,
 itemBuilder: (context, index) => Text('Item $index'),
 itemScrollController: itemScrollController,
 itemPositionsListener: itemPositionsListener,
);
 _
1
yuktatn

一般的な解決策:

文字列の数/文字列/リストとして表すことができるものを格納するには、Flutterは強力な使いやすいプラグインを提供しています。次に必要なときに必要なときに、その値が必要なのはそのキーを取得または更新する必要があります。

開始するには、pubspec.yamlファイルに shared_preferences プラグインを追加します。

_dependencies:
  flutter:
    sdk: flutter
  shared_preferences: "<newest version>"
_

_flutter pub get_をターミナルから実行するか、Intellijを使用している場合は_Packages get_をクリックするだけで、_pubspec.yaml_ファイルを表示している間に画面の右上隅のそばにあることがわかります。

上記のコマンドが正常に実行されたら、_main.Dart_または関係するファイルの下のファイルをインポートします。

_  import 'package:shared_preferences/shared_preferences.Dart';
_

これで、 ScrollControllerListView.builder()ウィジェットに添付して、最終/最後のオフセットがすべての方法でアプリを離したときにshared_preferencesを使用して特定のキーと一緒に格納されていることを確認してください。あなたの関心のあるウィジェットのinitstateが呼び出されたときに設定されます。

私たちのアプリの状態の変化を検出し、それに従って行動することを知るために、私たちは私たちのクラスにWidgetsBindingObserverを継承します。

以下の手順:

  1. WidgetSbindingObServerクラスをに並びステートフルウェイの状態クラスを延長します。

  2. 上記のクラスの関数メンバーとして、非同期関数resumeController()を定義します。

_  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;
    });
_
  1. ScrollControllerを保存して保存して、SharedPreferencesのインスタンスを保存して使用するための2つの変数を宣言します。
_  ScrollController _scrollController;
  SharedPreferences _sharedPreferences;
_
  1. resumeController()を呼び出して、WidgetSbindingクラスのインスタンスオブジェクトのaddobServerメソッドにクラスを渡します。
_  resumeController();
  WidgetsBinding.instance.addObserver(this);
_
  1. このコードをクラス定義(他のメンバー関数の外部)に貼り付けます。
_ @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);
  }
_
  1. ScrollController()を心配するスクロール可能に渡します。

実装例:

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

https://pub.dev/packages/indexed_list_view パッケージはあなたをこれに役立てることができます。このようなものを使う:

IndexedListView.builder(
    controller: indexScrollController, 
    itemBuilder: itemBuilder
);


indexScrollController.jumpToIndex(10000);
 _
0
Benjamin Corben

ウィジェットのサイズを知らずに解決策

ウィジェットのサイズを知らずに見つけられない解決策は、インデックスから最後まで逆の「サブリスト」を表示してから、「サブリスト」の上部までスクロールしてリスト全体をリセットします。リバースリストのように、アイテムはリストの上部に追加され、あなたはあなたの位置(インデックス)に滞在します。

問題は、リストのサイズを変更する必要があるため、ListView.Builderを使用できないことです。

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,
        ),
      ]
    );
  }
}
 _
0
Shinbly