SelectableRegion调用setState后选中状态丢失问题排查与修复咨询
问题说明
实现了一个包含文本组件列表的SelectableRegion,每秒调用一次setState()更新列表。当TextWidget中保留SelectionContainer.disabled时,文本选中状态会在setState()后丢失;移除该组件则选中状态能正常保留。
原因分析
SelectionContainer.disabled的作用是创建一个独立的、禁用文本选择的上下文环境,它会完全阻断父级SelectableRegion的选择上下文传递。每次setState()触发Widget树重建时,带有SelectionContainer.disabled的子组件会重新生成这个独立上下文,导致原来的选中状态关联的元素上下文被重置,无法与新重建的组件匹配,最终选中状态丢失。
而移除SelectionContainer.disabled后,子文本组件共享父级SelectableRegion的选择上下文,重建时组件的选择关联能被正确保留,所以选中状态不会丢失。
修复方案
不需要用SelectionContainer.disabled来实现文本不可选,直接通过TextSpan的样式设置userSelect: TextSelection.none即可。这种方式不会阻断父级选择上下文,同时能让目标文本无法被选中,且setState()后选中状态能正常保留。
修改后的代码示例
import 'dart:async'; import 'package:flutter/material.dart'; import 'dart:math' as math; class SelectionTest extends StatefulWidget { const SelectionTest({Key? key}) : super(key: key); @override State<SelectionTest> createState() => _SelectionTestState(); } class _SelectionTestState extends State<SelectionTest> { final _selectableRegionFocusNode = FocusNode(); late final Timer _timer; var count = 1; @override void initState() { super.initState(); _timer = Timer.periodic(const Duration(milliseconds: 1500), (timer) { count++; setState(() {}); }); } @override dispose() { _selectableRegionFocusNode.dispose(); _timer.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return SelectableRegion( selectionControls: MaterialTextSelectionControls(), // 替换为你项目中的自定义控件即可 focusNode: _selectableRegionFocusNode, child: Container( width: 200, height: 300, color: Color((math.Random().nextDouble() * 0xFFFFFF).toInt()) .withOpacity(1.0), child: ListView.builder( itemBuilder: (context, index) => TextWidget(key: ValueKey(index)), itemCount: count, ), ), ); } } class TextWidget extends StatelessWidget { const TextWidget({Key? key}) : super(key: key); @override Widget build(BuildContext context) { final textSpans = <InlineSpan>[ const TextSpan(text: 'Hey 123 456 789'), TextSpan( text: '-> No issue now <-', style: Theme.of(context) .textTheme .bodyMedium! .copyWith( color: Colors.transparent, userSelect: TextSelection.none, // 通过样式设置文本不可选 ), ), ]; return Text.rich(TextSpan(children: textSpans)); } }
内容的提问来源于stack exchange,提问作者user123
相关产品推荐
相关产品推荐

