保持列表中的位置与新项目Flutter ListView

vhmi4jdf  于 2023-06-24  发布在  Flutter
关注(0)|答案(2)|浏览(215)

我正在构建一个Flutter应用程序,遇到了ListView. builder的问题。
我目前的设置是一个StreamBuilder,它有一个对象列表。每个对象都绑定到ListView. builder中的1个项。
我的问题是,当添加新项目时,我调用StreamController$add(),列表会自动滚动到列表的顶部。我正在寻找列表的位置要维护,它是由用户(或按钮)滚动到顶部。
我已经添加了关键的列表项目,这有助于其他问题,但不是这一个。我还在列表视图中添加了一个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.
    );
  }
}

以下是我的新尝试:

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.
    );
goucqfw6

goucqfw61#

因此,每次通过流发出一个新值时,整个列表视图都会被重新构建。解决这个问题的方法是将列表视图移出流构建器,只修改流构建器中列表视图的子视图。
我以前没有广泛使用过StreamBuilder,所以你必须在代码中弄清楚它。有两种方法可以解决。
1.在成员变量中保留一个小部件列表,如果列表为空则返回指示器,否则返回ListView(_yourMemberListOfWidgets)。在没有流构建器的情况下连接到流。当返回一个新值时,将其设置为作为成员变量保存的小部件列表,并调用setState来更新状态。
1.将您的Stream构建器移动到ListView中,编译并仅返回子对象。如果这不可能,那么考虑将StreamBuilder放置在ScrollView中,并返回一个Column,其中所有子控件都作为小部件。这将给予相同的效果。如果你需要触摸输入,只需要在你的列表项目周围使用GestureDetector并实现onTap。

xfb7svmp

xfb7svmp2#

在ListView.builder中,您使用了key属性。你可以跳过这一步

相关问题