Listview 使用新项目保持列表中的位置颤振列表视图

Listview 使用新项目保持列表中的位置颤振列表视图,listview,flutter,Listview,Flutter,我正在构建一个颤振应用程序,但ListView.builder出现了问题 我当前的设置是一个StreamBuilder,它有一个对象列表。每个对象都绑定到ListView.builder中的一个项目 我的问题是,当添加新项目时,我调用StreamController$add()时,列表会自动滚动到列表的顶部。我正在寻找要维护的列表位置,由用户(或按钮)滚动到顶部 我已经在列表项中添加了键,这有助于解决其他问题,但这次没有。我还向listview添加了一个PageStorageKey,但这没有帮助

我正在构建一个颤振应用程序,但ListView.builder出现了问题

我当前的设置是一个StreamBuilder,它有一个对象列表。每个对象都绑定到ListView.builder中的一个项目

我的问题是,当添加新项目时,我调用StreamController$add()时,列表会自动滚动到列表的顶部。我正在寻找要维护的列表位置,由用户(或按钮)滚动到顶部

我已经在列表项中添加了键,这有助于解决其他问题,但这次没有。我还向listview添加了一个PageStorageKey,但这没有帮助。我也有一个滚动控制器,但还没有弄清楚如何工作这种行为

如果需要,我可以提供代码示例。提前谢谢

相关代码:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: albatrossDarkTheme,
      home: TimelinePage(),
    );
  }
}

class TimelinePage extends StatefulWidget {
  TimelinePage({Key key}) : super(key: key);

  @override
  _TimelinePageState createState() => _TimelinePageState();
}

class _TimelinePageState extends State<TimelinePage> {
  AlbatrossClient _client;
  GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey();
  PageStorageKey _pageKey = PageStorageKey(8);
  TimelineDatabase _database = TimelineDatabase();
  StreamController<List<Tweet>> _controller;
  ScrollController _scrollController;

  Stream<List<Tweet>> _getStream() {
    return _controller.stream;
  }

  Future<void> _refreshTimeline() async {
    _client.refreshTimeline().then((result) {
      if (result == 0)
        _database.getTimeline(false).then((update) {
          _controller.add(update);
        });
    });
  }

  _updateTimeline() async {
    _database.getTimeline(false).then((update) => _controller.add(update));
  }

  @override
  void initState() {
    _client = AlbatrossClient();
    _controller = StreamController<List<Tweet>>();
    _scrollController = ScrollController(keepScrollOffset: true);
    _database.getTimeline(false).then((saved) => _controller.add(saved));
      _client.refreshTimeline().then((result) {
        if (result == 0)
          _database.getTimeline(false).then((update) {
            _controller.add(update);
          });
      });

    super.initState();
  }

  @override
  void dispose() {
    _controller.close();
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: SearchAppbar(() {
        _scaffoldKey.currentState.openDrawer();
      }),
      drawer: Drawer(
        child: DrawerMain.getDrawer(context, _client.getProfile()),
      ),
      body: Container(
          child: RefreshIndicator(
              onRefresh: _refreshTimeline,
              child: StreamBuilder<List<Tweet>>(
                stream: _getStream(),
                builder: (context, snapshot) {
                  if (snapshot.hasData) {
                    return ListView.builder(
                      key: _pageKey,
                      controller: _scrollController,
                      itemCount:
                          snapshot.data != null ? snapshot.data.length : 0,
                      itemBuilder: (context, index) {
                        return RowTweet(
                            tweet: snapshot.data[index],
                            update: _updateTimeline,
                            animate: true);
                      },
                    );
                  } else
                    return Container(
                      alignment: Alignment(0.0, 0.0),
                      child: CircularProgressIndicator(),
                    );
                },
              ))),
      floatingActionButton: FloatingActionButton(
        tooltip: 'Compose Tweet',
        onPressed: _composeTweet,
        foregroundColor: Colors.white,
        child: Image.asset(
          "icons/ic_tweet_web.webp",
          width: 38,
          height: 38,
        ),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

因此,每次通过流发出新值时,都会重新生成整个列表视图。解决此问题的方法是将列表视图移出流生成器,并仅在流生成器中修改列表视图的子级

我以前没有广泛使用过StreamBuilder,所以您必须在代码中找到它。有两种方法可以解决这个问题

  • 在成员变量中保留小部件列表,如果列表为空,则返回指示符,否则返回ListView(_yourMemberListOfWidgets)。在不使用流生成器的情况下连接到流。当返回一个新值时,将其设置为作为成员变量保留的小部件列表,并调用setState更新您的状态

  • 将流生成器移动到ListView中,编译并仅返回子级。如果这是不可能的,那么考虑把StreamBuilder放在一个ScLoVIEW中,并返回一个列,其中所有的孩子都是小部件。这将产生同样的效果。如果您需要触摸输入,只需在列表项周围使用手势检测器并实现onTap


  • 似乎我需要添加代码,因为事实上我正在使用一个有状态的widgetYep,拥有代码将帮助我们解决问题。@NickMowen更改了答案。我添加了第二次尝试,但仍然不起作用,并在添加新项目时移到顶部。有趣的是,无论是流生成器还是这个新尝试,比如推特(更新数据库和流/列表)都不会移动列表,但添加新项目似乎会将其推到顶部。它似乎不一定会滚动到顶部,只是列表会随着添加的新项目的高度而跳跃。我正在尝试禁用此跳转
    return Scaffold(
          key: _scaffoldKey,
          appBar: SearchAppbar(() {
            _scaffoldKey.currentState.openDrawer();
          }),
          drawer: Drawer(
            child: DrawerMain.getDrawer(context, _client.getProfile()),
          ),
          body: Container(
              child: RefreshIndicator(
                  onRefresh: _refreshTimeline,
                  child: _timeline.isNotEmpty
                      ? ListView.builder(
                          key: _pageKey,
                          controller: _scrollController,
                          itemCount: _timeline.length,
                          itemBuilder: (context, index) {
                            return RowTweet(
                                tweet: _timeline[index],
                                update: _updateTimeline,
                                animate: true);
                          })
                      : Container(
                          alignment: Alignment(0, 0),
                          child: CircularProgressIndicator(),
                        ))),
          floatingActionButton: FloatingActionButton(
            tooltip: 'Compose Tweet',
            onPressed: _composeTweet,
            foregroundColor: Colors.white,
            child: Image.asset(
              "icons/ic_tweet_web.webp",
              width: 38,
              height: 38,
            ),
          ), // This trailing comma makes auto-formatting nicer for build methods.
        );