用新项目在列表中保持位置Flutter ListView

时间:2019-02-21 02:47:22

标签: listview flutter

我正在构建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.
    );

1 个答案:

答案 0 :(得分:0)

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

我之前没有广泛使用StreamBuilder,因此您必须在代码中加以了解。有两种解决方法。

  1. 保留成员变量中的窗口小部件列表,如果列表为空则返回指示器,否则返回ListView(_yourMemberListOfWidgets)。无需流构建器即可连接到流。返回新值时,将其设置为保留为成员变量的小部件列表,然后调用setState更新状态。

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