Search code examples
flutterflutter-layoutsinglechildscrollviewflutter-texttextspan

How to use focus on RichText in flutter?


I have a list of text span like this

var spans = [
TextSpan(text: content, style: style),
TextSpan(text: content, style: style) 
//and 100 more
]   

I am loading this inside SingleChildScrollView like this.

        SingleChildScrollView(
        controller: scrollController,
        child: Text.rich(
                  TextSpan(children: spans),
                       ),
                     )        

Now My question is how can I shift focus between the TextSpan list?. Say I want to load TextSpan at position 10 from spans list at top of screen without manually scrolling.


Solution

  • You could add a WidgetSpan next to each of your TextSpan, so you can assign it a key property which would be a GlobalKey so you can access its BuildContext to call the method Scrollable.ensureVisible().

    Here's a code sample inspired by an answer to a similar question:

    class ScrollToTextSpanPage extends StatefulWidget {
      const ScrollToTextSpanPage({Key? key}) : super(key: key);
    
      @override
      State<ScrollToTextSpanPage> createState() => _ScrollToTextSpanPageState();
    }
    
    class _ScrollToTextSpanPageState extends State<ScrollToTextSpanPage> {
      final _keys = List<GlobalKey>.generate(
        _kTextBlocks.length,
        (_) => GlobalKey(),
      );
    
      void _goToSpan(int spanIndex) {
        Scrollable.ensureVisible(
          _keys[spanIndex].currentContext!,
          alignment: 0.2,
          duration: const Duration(milliseconds: 300),
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Scroll to TextSpan'),
          ),
          floatingActionButton: FloatingActionButton.extended(
            onPressed: () => _goToSpan(8),
            label: const Text('Go to span 8'),
            icon: const Icon(Icons.navigate_next),
          ),
          body: SingleChildScrollView(
            padding: const EdgeInsets.all(16),
            child: Text.rich(
              TextSpan(
                children: [
                  for (int i = 0; i < _kTextBlocks.length; i++) ...[
                    WidgetSpan(child: SizedBox(key: _keys[i])),
                    TextSpan(
                      text: 'Span $i\n',
                      style: const TextStyle(fontWeight: FontWeight.bold),
                      children: [
                        TextSpan(
                          text: '${_kTextBlocks[i]}\n\n',
                          style: const TextStyle(fontWeight: FontWeight.normal),
                        ),
                      ],
                    ),
                  ],
                ],
              ),
            ),
          ),
        );
      }
    }
    

    Try the sample on DartPad