flutter - 更改选项卡时如何获取Flutter ScrollController以保存ListView.builder()的位置?

标签 flutter dart flutter-layout flutter-web

我用两个选项卡制作了一个简单的示例,每个选项卡都包含一个ListView构建器。我的目标是能够在第一个 ListView 中滚动,切换到第二个选项卡,然后切换回第一个选项卡,并从之前看到相同的滚动位置。

我曾尝试将Keys添加到每个 ListView 中,但这只是一个猜测,因为我不完全了解key。那没有帮助。

为什么ScrollControllers不保存滚动位置?

这是main.dart示例:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  ScrollController controllerA = ScrollController(keepScrollOffset: true);
  ScrollController controllerB = ScrollController(keepScrollOffset: true);
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 2,
      child: Scaffold(
        appBar: AppBar(
          bottom: TabBar(
            tabs: <Widget>[
              Text('controllerA'),
              Text('controllerB'),
            ],
          ),
        ),
        body: TabBarView(
          children: <Widget>[
            ListView.builder(
                controller: controllerA,
                itemCount: 2000,
                itemBuilder: (context, i) {
                  return ListTile(
                      title: Text(
                    i.toString(),
                    textScaleFactor: 1.5,
                    style: TextStyle(color: Colors.blue),
                  ));
                }),
            ListView.builder(
                controller: controllerB,
                itemCount: 2000,
                itemBuilder: (context, i) {
                  return Card(
                    child: ListTile(
                      title: Text(i.toString()),
                    ),
                  );
                }),
          ],
        ),
      ),
    );
  }
}

这是我想要的骇人但实用的示例。但是,这感觉不像是正确的方法,因为它每帧都重建了两个 Controller 。
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  double offsetA = 0.0;
  double offsetB = 0.0;

  @override
  Widget build(BuildContext context) {
    ScrollController statelessControllerA =
        ScrollController(initialScrollOffset: offsetA);
    statelessControllerA.addListener(() {
      setState(() {
        offsetA = statelessControllerA.offset;
      });
    });

    ScrollController statelessControllerB =
        ScrollController(initialScrollOffset: offsetB);
    statelessControllerB.addListener(() {
      setState(() {
        offsetB = statelessControllerB.offset;
      });
    });

    return DefaultTabController(
      length: 2,
      child: Scaffold(
        appBar: AppBar(
          bottom: TabBar(
            tabs: <Widget>[
              Text('controllerA'),
              Text('controllerB'),
            ],
          ),
        ),
        body: TabBarView(
          children: <Widget>[
            ListView.builder(
                controller: statelessControllerA,
                itemCount: 2000,
                itemBuilder: (context, i) {
                  return ListTile(
                      title: Text(
                    i.toString(),
                    textScaleFactor: 1.5,
                    style: TextStyle(color: Colors.blue),
                  ));
                }),
            ListView.builder(
                controller: statelessControllerB,
                itemCount: 2000,
                itemBuilder: (context, i) {
                  return Card(
                    child: ListTile(
                      title: Text(i.toString()),
                    ),
                  );
                }),
          ],
        ),
      ),
    );
  }
}

最佳答案

您可以使用AutomaticKeepAliveClientMixin将状态保留在“选项卡 View ”中。

例如

class GetListView extends StatefulWidget{
  @override
  State<StatefulWidget> createState() =>_GetListViewState();

}

class _GetListViewState extends State<GetListView> with AutomaticKeepAliveClientMixin<GetListView>{

  @override
  Widget build(BuildContext context){
    return ListView.builder(

                itemCount: 2000,
                itemBuilder: (context, i) {
                  return ListTile(
                      title: Text(
                    i.toString(),
                    textScaleFactor: 1.5,
                    style: TextStyle(color: Colors.blue),
                  ));
                });
  }

  @override
  bool get wantKeepAlive => true;

} 

不要在ListView.builder的子级中使用TabBarView,而要使用GetListView

例如
TabBarView(
          children: <Widget>[
            GetListView(),
            ListView.builder(
                controller: controllerB,
                itemCount: 2000,
                itemBuilder: (context, i) {
                  return Card(
                    child: ListTile(
                      title: Text(i.toString()),
                    ),
                  );
                }),
          ],
        ),
      )

实现此目的的第二种方法是使用PageStorageKey。 Scrollable使用PageStorageKey保存滚动偏移量。每次滚动完成时,可滚动页面的存储都会更新。

例如
 ListView.builder(
                key: PageStorageKey<String>('controllerA'),
                controller: statelessControllerA,
                itemCount: 2000,
                itemBuilder: (context, i) {
                  print("Rebuilded 1");
                  return ListTile(
                      title: Text(
                    i.toString(),
                    textScaleFactor: 1.5,
                    style: TextStyle(color: Colors.blue),
                  ));
                }),

注意:在第二个示例中,小部件将每次使用特定的滚动偏移量进行重建。建议使用第一个解决方案。

关于flutter - 更改选项卡时如何获取Flutter ScrollController以保存ListView.builder()的位置?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60292911/

相关文章:

android - 仅使用 Android 模块在 Android Studio 中打开 Flutter 项目

dart - 有没有类似 Redux for Flutter 的东西?

android - 我们需要每天早上7点运行重复通知,内容是动态的

flutter - Flutter如何在特定屏幕上强制使用暗模式

flutter - 对于TextField小部件中的长文本,如何在未聚焦后从头开始显示文本?

flutter - 如何修复工具栏中的 CircularProgressIndicator() 大小

android - 如何在 Flutter 中删除 Firebase 云消息传递 token

bit-manipulation - 如何在 Dart 中进行按位无符号(零填充)右移?

dart - <link rel =“import” href =“packages/polymer/polymer.html”>与Dart 1.4.3,聚合物0.11.0-dev.6

Flutter 在气泡聊天消息上对齐时间戳文本