【问题标题】:Keep position in list with new items Flutter ListView使用新项目在列表中保持位置 Flutter ListView
【发布时间】:2019-07-14 20:37:00
【问题描述】:

我正在构建一个 Flutter 应用,但遇到了 ListView.builder 的问题。

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

我的问题是,当添加新项目并调用 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.
    );

【问题讨论】:

    标签: listview flutter


    【解决方案1】:

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

    我之前没有广泛使用过 StreamBuilder,所以你必须在代码中弄清楚。有两种方法可以解决此问题。

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

    2. 将您的 Stream 构建器移动到 ListView 中,仅编译并返回子项。如果这是不可能的,那么考虑将 StreamBuilder 放在 ScrollView 中并返回一个 Column ,其中包含所有子项作为小部件。这将产生相同的效果。如果您需要触摸输入,只需在列表项周围使用 GestureDetector 并实现 onTap。

    【讨论】:

    • 似乎我需要添加代码,因为我实际上正在使用有状态小部件
    • 是的,拥有代码将帮助我们找出问题所在。
    • @NickMowen 改变了答案。
    • 我添加了我的第二次尝试,但仍然没有成功,并在添加新项目时移至顶部。有趣的是,无论是使用流构建器还是这个新尝试,点赞一条推文(更新数据库和流/列表)都不会移动列表,但添加新项目似乎会将其推到顶部。
    • 似乎不一定滚动到顶部,只是列表根据添加的新项目的高度跳跃。我正在尝试禁用此跳转
    猜你喜欢
    • 2020-04-06
    • 2019-01-21
    • 1970-01-01
    • 2011-11-08
    • 1970-01-01
    • 2020-09-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多