Flutter:将项目添加到列表并滚动到末尾后,Listview 获取滚动控制器的完整大小

问题描述 投票:0回答:3

这篇帖子描述了一个非常相似的问题,但那里的答案并不能解决所有问题:

我有一个可能很长的列表,用户可以在其中添加新项目(一次添加)。添加后/添加时,列表应滚动到末尾。

(顺便说一句,不,

reverse: true
不是一个选项)

读完另一篇文章后,我明白使用

SchedulerBinding.instance.addPostFrameCallback((_) => scrollToEnd());
应该达到技巧 b/c 新列表
maxScrollExtent
将是正确的。

但它工作不可靠: 当已经滚动到列表末尾或接近末尾时,一切正常。 但是,当添加新项目时列表滚动到其开头(或从结尾的某种方式)时,列表会滚动,但滚动位置恰好偏离了一项 - 最新的一项。

我认为这可能与

ListView.builder
不能让所有孩子都活着有关 - 但如何解决它?

哦还有额外的问题:刚刚发现了另一个非常奇怪的行为:添加两个项目后,最后一个项目有点看不见,但列表不可滚动 - 这很奇怪。但更奇怪的是,在下一个添加项目单击时,列表会滚动一点点 - 但从未创建新项目!?

这是一个完整的例子:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

void main() {
  runApp(MyList());
}

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

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

var items = List<String>.generate(8, (i) => "Item $i");

class _MyListState extends State<MyList> {
  static ScrollController _scrollController = ScrollController();

  void add() {
    setState(() {
      items.add("new Item ${items.length}");
      print(items.length);
    });
    SchedulerBinding.instance.addPostFrameCallback((_) => scrollToEnd());
  }

  void scrollToEnd() {
    _scrollController.animateTo(_scrollController.position.maxScrollExtent,
        duration: const Duration(milliseconds: 350), curve: Curves.easeOut);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "List",
      home: Scaffold(
          appBar: AppBar(
            title: Text("List"),
          ),
          body: ListView.builder(
            controller: _scrollController,
            itemCount: items.length,
            shrinkWrap: true,
            itemBuilder: (context, index) {
              return ListTile(
                title: Text('${items[index]}'),
              );
            },
          ),
          bottomSheet: Container(
              decoration: BoxDecoration(
                  border:
                      Border(top: BorderSide(color: Colors.black, width: 1))),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.end,
                children: [
                  FloatingActionButton(
                    onPressed: () {
                      add();
                    },
                    child: Icon(Icons.add),
                  )
                ],
              ))),
    );
  }
}
listview flutter
3个回答
9
投票

我将滚动到

maxScrollExtent
Scrollable.ensureVisible
结合在一起,并且它们都修复了另一个的缺陷。

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

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

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

class _MyListState extends State<MyList> {
  final ScrollController _scrollController = ScrollController();
  final lastKey = GlobalKey();
  List<String> items;

  @override
  void initState() {
    super.initState();
    items = List<String>.generate(8, (i) => "Item $i");
  }

  void add() {
    setState(() {
      items.add("new Item ${items.length}");
    });
    SchedulerBinding.instance.addPostFrameCallback((_) => scrollToEnd());
  }

  void scrollToEnd() async {
    await _scrollController.animateTo(
        _scrollController.position.maxScrollExtent,
        duration: const Duration(milliseconds: 350),
        curve: Curves.easeOut);
    Scrollable.ensureVisible(lastKey.currentContext);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: "List",
        home: Scaffold(
          body: ListView.builder(
            controller: _scrollController,
            itemCount: items.length,
            shrinkWrap: true,
            itemBuilder: (context, index) {
              return ListTile(
                title: Text('${items[index]}'),
                key: index == items.length - 1 ? lastKey : null,
              );
            },
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              add();
            },
            child: Icon(Icons.add),
          ),
        ));
  }
}
如果项目尚未创建,

Scrollable.ensureVisible
本身无法提供可见性,但当项目非常接近时可以处理它们。


2
投票

指定太大的scrollPosition不会出错,ScrollController会自动滚动到最终的最大值。我定义了一个 _scrollController 并执行以下命令:

_scrollController.animateTo(
    _scrollController.position.maxScrollExtent + 200,
    duration: const Duration(milliseconds: 350),
    curve: Curves.easeOut);

_scrollController.jumpTo(_scrollController.position.maxScrollExtent + 200);

0
投票

这是另一个黑客,而且是一个愚蠢的黑客,但它对我有用:)看到被投票的答案也是一个黑客(除了尊重@Spatz),我认为添加我的答案是有价值的。

我只是重复滚动:D

  await _scrollController.animateTo(
    _scrollController.position.maxScrollExtent,
    duration: const Duration(milliseconds: 400),
    curve: Curves.easeOut,
  );

  await _scrollController.animateTo(
    _scrollController.position.maxScrollExtent,
    duration: const Duration(milliseconds: 400),
    curve: Curves.easeOut,
  );
© www.soinside.com 2019 - 2024. All rights reserved.