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

Flutter如何实现ListView.builder动态跟随RichText指定TextSpan显示

解决方案

实现思路

  • 用WidgetSpan包裹需要对齐的目标文本,绑定GlobalKey用于获取组件坐标
  • 页面渲染完成后通过GlobalKey读取目标文本的屏幕位置,动态更新列表的定位参数
  • 文本内容变化重绘时会自动重新计算位置,适配动态RichText的使用场景

完整可运行代码

import 'package:flutter/material.dart';

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

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

class _ListViewBuilderOverRichTextState
    extends State<ListViewBuilderOverRichText> {
  final FocusNode _focusNode = FocusNode();
  // 用于定位目标文本的GlobalKey
  final GlobalKey _targetTextKey = GlobalKey();
  // 列表的定位偏移
  double _listTop = 0;
  double _listLeft = 0;
  // 列表固定宽高,可根据需求调整
  final double _listWidth = 200;
  final double _listHeight = 200;

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

  // 更新列表位置
  void _updateListPosition() {
    final RenderBox? renderBox = _targetTextKey.currentContext?.findRenderObject() as RenderBox?;
    if (renderBox == null) return;
    // 获取目标文本在屏幕上的坐标
    final targetOffset = renderBox.localToGlobal(Offset.zero);
    setState(() {
      _listLeft = targetOffset.dx;
      // 若要显示在文本上方,改为 targetOffset.dy - _listHeight 即可
      _listTop = targetOffset.dy + renderBox.size.height;
    });
  }

  @override
  Widget build(BuildContext context) {
    // 每帧渲染完成后更新列表位置,适配文本动态变化的场景
    WidgetsBinding.instance.addPostFrameCallback((_) => _updateListPosition());

    final List<String> entries = <String>['A', 'B', 'C'];
    final List<int> colorCodes = <int>[100, 500, 600];
    return Scaffold(
      appBar: AppBar(
        title: const Text('ListView.builder over RichText'),
      ),
      body: Stack(
        clipBehavior: Clip.none,
        children: [
          Column(
            children: [
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 5.0),
                child: RichText(
                  textAlign: TextAlign.left,
                  text: TextSpan(
                    text: 'dynamicaly changing text dynamicaly changing text dynamicaly changing text dynamicaly changing text dynamicaly changing text dynamicaly changing text dynamicaly changing text dynamicaly changing text dynamicaly changing text dynamicaly changing text dynamicaly changing text dynamicaly changing text',
                    style: const TextStyle(color: Colors.black, fontSize: 18.0),
                    children: <InlineSpan>[
                      // 目标文本用WidgetSpan包裹,绑定GlobalKey
                      WidgetSpan(
                        child: Text(
                          '  ListView.builder',
                          key: _targetTextKey,
                          style: const TextStyle(color: Colors.red, fontSize: 18.0),
                        ),
                      )
                    ],
                  ),
                ),
              ),
            ],
          ),
          Positioned(
            top: _listTop,
            left: _listLeft,
            width: _listWidth,
            height: _listHeight,
            child: Container(
              color: Colors.white,
              child: ListView.builder(
                padding: const EdgeInsets.all(8),
                itemCount: entries.length,
                itemBuilder: (BuildContext context, int index) {
                  return Container(
                    height: 50,
                    color: Colors.amber[colorCodes[index]],
                    child: Center(child: Text('Entry ${entries[index]}')),
                  );
                }
              ),
            ),
          )
        ],
      ),
    );
  }
}

核心说明

  • WidgetSpan支持在RichText的文本树中插入普通Widget,是实现文本内特定片段定位的常用方案
  • addPostFrameCallback会在当前帧渲染完成后触发,此时才能获取到组件的真实渲染坐标
  • 文本内容更新触发重绘时,会自动重新计算列表位置,无需额外手动处理适配逻辑

内容的提问来源于stack exchange,提问作者Jan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 01:45:03