Flutter ListView.builder - 如何以编程方式跳转到某个索引

anu*_*der 11 user-interface scroll dart flutter

我有一个构建使用屏幕MaterialAppDefaultTabControllerScaffoldTabBarView

在这个屏幕中,我有正文内容,它使用StreamBuilder. 我得到精确的 100 个元素(“有限列表”)使用ListView.

我的问题是,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();
        }
      },
    );
  }
...
Run Code Online (Sandbox Code Playgroud)

MyDraggableScrollBar.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,
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

我试过找到其他解决方案,但似乎不起作用,例如仅支持无限列表的indexed_list_view

并且似乎颤振仍然没有此功能,请参阅此问题

任何的想法 ?

小智 5

您可以使用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,
);
Run Code Online (Sandbox Code Playgroud)


Nep*_*low 3

一般解决方案:

为了存储任何可以表示为数字/字符串/字符串列表的内容,Flutter 提供了一个强大且易于使用的插件,它存储需要与键一起存储的值。因此,下次您需要时,您将需要检索甚至更新该值,您所需要的只是该键。

首先,将shared_preferences插件添加到pubspec.yaml文件中,

dependencies:
  flutter:
    sdk: flutter
  shared_preferences: "<newest version>"
Run Code Online (Sandbox Code Playgroud)

flutter pub get从终端运行,或者如果您使用 IntelliJ,只需单击Packages get(查看文件时您会在屏幕右上角的某个位置找到它pubspec.yaml

成功执行上述命令后,将以下文件导入您main.dart或相关文件中。

  import 'package:shared_preferences/shared_preferences.dart';
Run Code Online (Sandbox Code Playgroud)

现在只需将ScrollController附加到您的ListView.builder()小部件,并确保每当用户以任何方式离开应用程序时,最终/最后一个偏移量都与特定键一起存储,并在调用相关小部件的 initState 时设置。

为了知道检测应用程序状态的变化并根据它采取行动,我们将继承WidgetsBindingObserver我们的类。

应遵循的步骤:

  1. 扩展 WidgetsBindingObserver 类以及StatefulWidget的 State 类。

  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;
    });
Run Code Online (Sandbox Code Playgroud)
  1. 声明两个变量,一个用于存储和传递滚动控制器,另一个用于存储和使用 SharedPreferences 的实例。
  ScrollController _scrollController;
  SharedPreferences _sharedPreferences;
Run Code Online (Sandbox Code Playgroud)
  1. 调用resumeController()您的类并将其传递给 WidgetsBinding 类中实例对象的 addObserver 方法。
  resumeController();
  WidgetsBinding.instance.addObserver(this);
Run Code Online (Sandbox Code Playgroud)
  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);
  }
Run Code Online (Sandbox Code Playgroud)
  1. 将 传递ScrollController()给相关的 Scrollable。

工作示例:

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()),
                ),
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)