Flutter无障碍开发:加载新题目后让屏幕阅读器自动聚焦题目
Flutter测验项目无障碍优化:切换题目时自动聚焦到题干
要实现选择答案后加载下一题时,屏幕阅读器自动聚焦到题干,需要结合FocusNode和UI更新后的回调来完成,以下是修改后的完整代码及关键说明:
import 'package:flutter/material.dart'; class QuizPage extends StatefulWidget { const QuizPage({super.key}); @override _QuizPageState createState() => _QuizPageState(); } class _QuizPageState extends State<QuizPage> { int _currentQuestionIndex = 0; late FocusNode _questionFocusNode; final List<Map<String, Object>> _questions = [ { 'question': '天空是什么颜色?', 'answers': ['蓝色', '绿色', '黄色'], 'correctAnswer': 0, }, { 'question': '太阳系有多少颗行星?', 'answers': ['7', '8', '9'], 'correctAnswer': 1, }, { 'question': '世界上最大的海洋是哪个?', 'answers': ['大西洋', '印度洋', '太平洋'], 'correctAnswer': 2, }, { 'question': '法国的首都是哪里?', 'answers': ['柏林', '马德里', '巴黎'], 'correctAnswer': 2, }, ]; @override void initState() { super.initState(); // 初始化聚焦节点 _questionFocusNode = FocusNode(); } @override void dispose() { // 销毁聚焦节点,避免内存泄漏 _questionFocusNode.dispose(); super.dispose(); } void _answerQuestion(int selectedAnswerIndex) { if (selectedAnswerIndex == _questions[_currentQuestionIndex]['correctAnswer']) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('回答正确!')), ); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('回答错误!')), ); } setState(() { if (_currentQuestionIndex < _questions.length - 1) { _currentQuestionIndex++; // UI更新完成后,自动聚焦到题干 WidgetBinding.instance.addPostFrameCallback((_) { _questionFocusNode.requestFocus(); }); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('测验已完成!')), ); } }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('测验'), ), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // 将题干包装在Focus组件中,关联聚焦节点 Focus( focusNode: _questionFocusNode, child: Semantics( label: '第${_currentQuestionIndex + 1}题', child: Text( _questions[_currentQuestionIndex]['question'] as String, style: const TextStyle(fontSize: 24), ), ), ), const SizedBox(height: 20), ...(_questions[_currentQuestionIndex]['answers'] as List<String>) .asMap() .entries .map((entry) { int index = entry.key; String answer = entry.value; return Semantics( label: '选项${entry.key + 1}:$answer', child: ElevatedButton( onPressed: () => _answerQuestion(index), child: Text(answer), ), ); }).toList(), ], ), ), ); } }
关键修改说明
- 初始化与销毁FocusNode:在
initState中创建_questionFocusNode,dispose中销毁,防止内存泄漏。 - UI更新后触发聚焦:在
setState更新题目索引后,通过WidgetBinding.instance.addPostFrameCallback确保UI完全更新后,再调用_questionFocusNode.requestFocus()发起聚焦,避免因UI未更新导致聚焦失效。 - 关联聚焦节点到题干:用
Focus组件包裹题干的Semantics和Text,将focusNode绑定到该组件,让屏幕阅读器能定位并读取题干内容。 - 语义化优化:更新了
Semantics的中文标签,确保屏幕阅读器能清晰告知用户当前是第几题、选项内容等信息。
内容的提问来源于stack exchange,提问作者Jameel
相关产品推荐
相关产品推荐

