最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

flutter - The issue with scrolling component - Stack Overflow

programmeradmin3浏览0评论

The issue where the position of the displayed content on the page changes when the Flutter scrolling component slides down or up to load and add data

When I swipe the page to load data, the content displayed on the page changes, which is too bad for me. The effect I want is that when I swipe to the top or bottom of the page to load data, the content displayed on the page remains unchanged, rather than displaying newly loaded data content

Here is my code

class DemoChatListPage extends StatefulWidget {
  const DemoChatListPage({super.key});

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

class _DemoChatListPageState extends State<DemoChatListPage> {
  List<String> messages = List.generate(20, (index) => 'Message $index');
  final ScrollController _scrollController = ScrollController();
  bool isLoadingMoreTop = false;
  bool isLoadingMoreBottom = false;
  double currentScrollOffset = 0;

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(_scrollListener);
  }

  @override
  void dispose() {
    _scrollController.removeListener(_scrollListener);
    _scrollController.dispose();
    super.dispose();
  }

  void _scrollListener() {
    if (_scrollController.position.atEdge) {
      if (_scrollController.position.pixels == 0) {
        // to top
        _loadMoreTop();
      } else {
        // to bottom
        _loadMoreBottom();
      }
    }
  }

  Future<void> _loadMoreTop() async {
    if (isLoadingMoreTop) return;
    currentScrollOffset = _scrollController.position.maxScrollExtent;
    setState(() {
      isLoadingMoreTop = true;
    });

    await Future.delayed(const Duration(seconds: 1));
    List<String> newMessages = List.generate(10, (index) => 'New Message Top $index');

    setState(() {
      messages.insertAll(0, newMessages);
      isLoadingMoreTop = false;
    });

    WidgetsBinding.instance.addPostFrameCallback((_) {
      _scrollController.jumpTo(_scrollController.position.maxScrollExtent - currentScrollOffset);
    });
  }

  Future<void> _loadMoreBottom() async {
    if (isLoadingMoreBottom) return;
    setState(() {
      isLoadingMoreBottom = true;
    });
    await Future.delayed(const Duration(seconds: 1));
    setState(() {
      List<String> newMessages = List.generate(10, (index) => 'New Message Bottom $index');
      messages.addAll(newMessages);
      isLoadingMoreBottom = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Chat'),
      ),
      body: Stack(
        children: [
          ListView.builder(
            controller: _scrollController,
            itemCount: messages.length,
            itemBuilder: (context, index) {
              return Container(
                height: Random().nextInt(51) + 50,
                color: Colors.blue[index % 2 == 0 ? 200 : 300],
                child: ListTile(
                  title: Text(messages[index]),
                ),
              );
            },
          ),
          if (isLoadingMoreTop)
            const Positioned(
              top: 0,
              left: 0,
              right: 0,
              child: LinearProgressIndicator(),
            ),
          if (isLoadingMoreBottom)
            const Positioned(
              bottom: 0,
              left: 0,
              right: 0,
              child: LinearProgressIndicator(),
            ),
        ],
      ),
    );
  }
}

The content displayed during data loading The content displayed after data loading is completed

The issue where the position of the displayed content on the page changes when the Flutter scrolling component slides down or up to load and add data

When I swipe the page to load data, the content displayed on the page changes, which is too bad for me. The effect I want is that when I swipe to the top or bottom of the page to load data, the content displayed on the page remains unchanged, rather than displaying newly loaded data content

Here is my code

class DemoChatListPage extends StatefulWidget {
  const DemoChatListPage({super.key});

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

class _DemoChatListPageState extends State<DemoChatListPage> {
  List<String> messages = List.generate(20, (index) => 'Message $index');
  final ScrollController _scrollController = ScrollController();
  bool isLoadingMoreTop = false;
  bool isLoadingMoreBottom = false;
  double currentScrollOffset = 0;

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(_scrollListener);
  }

  @override
  void dispose() {
    _scrollController.removeListener(_scrollListener);
    _scrollController.dispose();
    super.dispose();
  }

  void _scrollListener() {
    if (_scrollController.position.atEdge) {
      if (_scrollController.position.pixels == 0) {
        // to top
        _loadMoreTop();
      } else {
        // to bottom
        _loadMoreBottom();
      }
    }
  }

  Future<void> _loadMoreTop() async {
    if (isLoadingMoreTop) return;
    currentScrollOffset = _scrollController.position.maxScrollExtent;
    setState(() {
      isLoadingMoreTop = true;
    });

    await Future.delayed(const Duration(seconds: 1));
    List<String> newMessages = List.generate(10, (index) => 'New Message Top $index');

    setState(() {
      messages.insertAll(0, newMessages);
      isLoadingMoreTop = false;
    });

    WidgetsBinding.instance.addPostFrameCallback((_) {
      _scrollController.jumpTo(_scrollController.position.maxScrollExtent - currentScrollOffset);
    });
  }

  Future<void> _loadMoreBottom() async {
    if (isLoadingMoreBottom) return;
    setState(() {
      isLoadingMoreBottom = true;
    });
    await Future.delayed(const Duration(seconds: 1));
    setState(() {
      List<String> newMessages = List.generate(10, (index) => 'New Message Bottom $index');
      messages.addAll(newMessages);
      isLoadingMoreBottom = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Chat'),
      ),
      body: Stack(
        children: [
          ListView.builder(
            controller: _scrollController,
            itemCount: messages.length,
            itemBuilder: (context, index) {
              return Container(
                height: Random().nextInt(51) + 50,
                color: Colors.blue[index % 2 == 0 ? 200 : 300],
                child: ListTile(
                  title: Text(messages[index]),
                ),
              );
            },
          ),
          if (isLoadingMoreTop)
            const Positioned(
              top: 0,
              left: 0,
              right: 0,
              child: LinearProgressIndicator(),
            ),
          if (isLoadingMoreBottom)
            const Positioned(
              bottom: 0,
              left: 0,
              right: 0,
              child: LinearProgressIndicator(),
            ),
        ],
      ),
    );
  }
}

The content displayed during data loading The content displayed after data loading is completed

Share Improve this question edited Feb 17 at 6:27 DarkBee 15.6k8 gold badges72 silver badges116 bronze badges asked Feb 17 at 4:17 Gasong PonGasong Pon 112 bronze badges New contributor Gasong Pon is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
Add a comment  | 

1 Answer 1

Reset to default 0

The problem in your case is the variable height of each Container which is causing a small bump in the current scroll position.

I removed your line height: Random().nextInt(51) + 50 and it works like a charm.

Otherwise you have to precompute all your heights and jumpTo according to the updated direction + the sum of all new heights.

Best of luck.

发布评论

评论列表(0)

  1. 暂无评论