You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Windows桌面应用列表项高度不一致时滚动卡顿抖动问题求助

Flutter Windows桌面应用列表项高度不一致时滚动卡顿抖动问题求助

各位好,我最近在开发Flutter Windows桌面应用时碰到了一个头疼的滚动问题:当列表里的项高度不一致时,滚动操作会出现明显的卡顿和抖动情况:

  • 拖动滚动条的时候,滚动条会明显跟不上鼠标光标的移动,还时不时出现晃动、抖动的情况
  • 用鼠标滚轮滚动时(我只是向上滚动),整个滚动动作也是一卡一卡的,非常不流畅

更奇怪的是,这个问题只有当列表项高度不固定的时候才会出现,如果所有项高度一致,滚动就完全正常。我录了两个动图分别展示这两种场景下的问题:

  • 拖动滚动条时的抖动现象
  • 仅向上滚动时鼠标滚轮引发的抖动表现

下面是能复现问题的核心代码,大家可以帮忙看看哪里出问题了吗?

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:infinite_scrolling_example_flutter/app/state.dart';

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

  @override
  State<InfiniteScrollList> createState() => _InfiniteScrollListState();
}

class _InfiniteScrollListState extends State<InfiniteScrollList> {
  final ScrollController _controller = ScrollController();

  @override
  void initState() {
    super.initState();
    _controller.addListener(scrollListener);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void scrollListener() {
    final appState = Provider.of<AppState>(context, listen: false);
    final maxVerticalScrollBound = _controller.position.maxScrollExtent;
    final triggerPoint = maxVerticalScrollBound * 0.7;
    final currentScrollOffset = _controller.offset;

    if (currentScrollOffset > 0.0 && !appState.showFab) {
      appState.showFabButton(true);
    }
    if (currentScrollOffset == 0.0 && appState.showFab) {
      appState.showFabButton(false);
    }
    if (currentScrollOffset > triggerPoint && !appState.isLoading) {
      appState.appendList();
    }
  }

  void scrollToTop() {
    _controller.animateTo(
      0.0,
      duration: const Duration(milliseconds: 300),
      curve: Curves.bounceInOut,
    );
  }

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: false,
      child: Consumer<AppState>(
        builder: (context, value, child) {
          return Scaffold(
            appBar: AppBar(
              automaticallyImplyLeading: false,
              title: const Text('Infinite Scrolling List'),
              centerTitle: true,
            ),
            floatingActionButton: value.showFab
                ? FloatingActionButton(
                    onPressed: scrollToTop,
                    child: const Icon(Icons.arrow_circle_up_rounded),
                  )
                : null,
            body: Scrollbar(
              controller: _controller,
              interactive: true,
              child: ListView.custom(
                controller: _controller,
                physics: const ClampingScrollPhysics(),
                childrenDelegate: SliverChildBuilderDelegate(
                  (context, index) {
                    if (index == value.intList.length) {
                      return const Padding(
                        padding: EdgeInsets.symmetric(vertical: 20),
                        child: Center(child: CircularProgressIndicator()),
                      );
                    }
                    final element = value.intList[index];
                    final randomSymbols = generateRandomSymbols();
                    return Padding(
                      padding: const EdgeInsets.symmetric(
                          vertical: 10, horizontal: 20),
                      child: Container(
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.circular(12),
                          boxShadow: [
                            BoxShadow(
                              color: Colors.black.withOpacity(0.1),
                              blurRadius: 3.0,
                              spreadRadius: 3.0,
                            ),
                          ],
                        ),
                        child: ListTile(
                          title: Text("Element No $element $randomSymbols"),
                          subtitle: Text("Is Even => ${element.isEven}"),
                        ),
                      ),
                    );
                  },
                  childCount: value.intList.length + (value.isLoading ? 1 : 0),
                  addAutomaticKeepAlives: false,
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}

/// Generates a string of random symbols
///
/// [count] - Number of symbols to generate (optional, defaults to random between 100-10000)
/// [includeLineBreaks] - Whether to add line breaks every 80 characters (default: false)
String generateRandomSymbols({int? count, bool includeLineBreaks = false}) {
  final random = Random();
  final symbolCount = count ?? (random.nextInt(9901) + 100);
  final symbols = [
    '!', '@', '#', '%', '^', '&', '*', '(', ')', '-', '_', '=', '+', '[', ']', '{', '}', '|', '\\', ':', ';', '"', "'", '<', '>', ',', '.', '?', '/', '~', '`',
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
    '☺', '☻', '♠', '♣', '♥', '♦', '♪', '♫', '☼', '►', '◄', '↕', '‼', '¶', '§', '▬', '↨', '↑', '↓', '→', '←', '∟', '↔', '▲', '▼'
  ];
  final buffer = StringBuffer();
  for (int i = 0; i < symbolCount; i++) {
    buffer.write(symbols[random.nextInt(symbols.length)]);
    if (includeLineBreaks && (i + 1) % 80 == 0) {
      buffer.write('\n');
    }
  }
  return buffer.toString();
}

这段代码里,我通过generateRandomSymbols生成长度随机的字符串,让每个列表项的高度不一样,从而触发这个滚动问题。

有没有大佬遇到过类似的问题?求指点一下怎么解决,万分感谢!


内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 11:23:07