Flutter: Changing textstyle of TextSpan with TapGestureRecognizer

2,714

First problem is that you have isPressed inside the _buildTextSpanWithSplittedText method, which will be overwritten on every re-paint. And if you keep that variable at a class level it will be applied to all TextSpans.
So a possible solution can be using a List, here an example:

import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';

class MakeStringClickable extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _MakeStringClickableState();
  }
}

class _MakeStringClickableState extends State<MakeStringClickable> {
  List<TapSection> sections;
  String textToSplit =
      'FirstWord would like to make each word clickable. On click of a particular word it\'s color should change.';
  TapGestureRecognizer r1;
  @override
  void initState() {
    sections = List<TapSection>();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        alignment: Alignment.center,
        child: _buildTextSpanWithSplittedText(textToSplit, context));
  }

  RichText _buildTextSpanWithSplittedText(
      String textToSplit, BuildContext context) {
    final splittedText = textToSplit.split(" ");
    final spans = List<TextSpan>();
    for (int i = 0; i <= splittedText.length - 1; i++) {
      var tapSection = TapSection(callBack: () {
        setState(() {});
      });
      sections.add(tapSection);
      spans.add(TextSpan(
          text: splittedText[i].toString() + " ",
          style: TextStyle(
              color: sections[i].isPressed ? Colors.black : Colors.red),
          recognizer: sections[i].recognizer));
    }
    return RichText(text: TextSpan(children: spans));
  }
}

class TapSection {
  TapGestureRecognizer recognizer;
  bool isPressed = false;
  final Function callBack;

  TapSection({this.callBack}) {
    recognizer = TapGestureRecognizer();
    recognizer.onTap = () {
      this.isPressed = !this.isPressed;
      this.callBack();
    };
  }
}

Please observe that we need to call setState as a callback on this solution.
Hope this help.

Share:
2,714
WeAreThePeople
Author by

WeAreThePeople

Updated on December 10, 2022

Comments

  • WeAreThePeople
    WeAreThePeople over 1 year

    I would like to make each word of a text clickable. Then, when tapping a particular word it's textcolor should change.

    Making each word clickable works just fine. However, somehow the textcolor does not change when I click on a word. This is how far I came:

    import 'package:flutter/material.dart';
    import 'package:flutter/gestures.dart';
    
     class MakeStringClickable extends StatefulWidget{
     @override
     State<StatefulWidget> createState() {
     // TODO: implement createState
     return _MakeStringClickableState();
     }
     }
    
    class _MakeStringClickableState extends State<MakeStringClickable>{
    
    String textToSplit = 'I would like to make each word clickable. On click of a particular word it's color should change.';
    
    @override
    Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
          alignment: Alignment.center,
          child: _buildTextSpanWithSplittedText(textToSplit, context)
      ),
    );
    }
    
    RichText _buildTextSpanWithSplittedText(String textToSplit, BuildContext context) {
    bool isPressed = false;
    final splittedText = textToSplit.split(" ");
    final spans = new List<TextSpan>();
    
      for(int i = 0; i <= splittedText.length - 1; i++ ){
        spans.add(TextSpan(
          text: splittedText[i].toString() + " ",
          style: TextStyle(color: isPressed ? Colors.black : Colors.red),
          recognizer: new TapGestureRecognizer()..onTap = () {
          setState(() {isPressed = !isPressed;});
          }
        ));
      }
      return RichText(text: TextSpan(children: spans));
    }
    }
    

    I expect the color of any word to change to black when I click on it, but somehow changing the style does not work as expected. I hope somebody will be able to help me.