Home > OS >  Flutter / Dart Regex - Highlight Words matching search query
Flutter / Dart Regex - Highlight Words matching search query

Time:03-30

I currently have built a search bar with a table that lists out data. I am trying to match each word or letter in the search query and highlight the words accordingly. Here is an example:

Example

Currently I understand how to achieve this with the UI but I am having issues with the logic and am a bit stuck.

Right now I am just trying to loop through each entry in the table, and simple match that customer field with the text in the Search Bar.

So far, as I am looping through each entry, I am seeing if a word in that entry is a match by doing this:

bool match = this.wordsToHighlight.any((ele) => entry.value.contains(ele));

if it's a match then I am looping through each word in the search query and matching them via regex like so:

for(final entry in item.entries) {
   // for each entry return words to highlight
   // does this word have any of the query words in it?
   bool match = this.wordsToHighlight.any((ele) => entry.value.contains(ele));
   
   if(match) {
      // highlight the words that match and return that string
      var textSpans = [];

      for(final word in wordsToHighlight) {
         var result = entry.value.toString().replaceAllMapped(RegExp('[\<t\>]$word', caseSensitive: false), (match) => '<t>${match.group(0)}</t>');
         print('Result: $result');
      }
}

I am surrounding each match in a sort of like HTML. I am just trying to visually see the text that I want to add highlights to be wrapped correctly for each matching word from the search query. (This isn't my solution, just trying to see visually in debug terminal)

I made a mini POC and it looks like this:

enter image description here

If I type in 280 So uff it gives me these results:

<t>280</t>083 Some Stuff
28083 <t>So</t> Stuff
28083 Some St<t>uff</t>

It's sort of working but as you can see I'm looping each query word and doing a regex on the entry column. Ideally I'd like to have this as my result:

<t>280</t>83 <t>So</t>me St<t>uff</t>

Just to be clear, the reason I'm adding the tags is to just see it working in the console for now. Eventually I want to use a RichText / TextSpan widget to add background color to the text, but I just need to get the matches right before I do this.

How is it possible to match the search bar query to get the correct results I am looking for? Hopefully someone can help please! Thank you!

Full Source Code for my simple POC:

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  
  final _controller = TextEditingController();

  List<String> wordsToHighlight = [];

  void search(String query) {
    setState(() {
      wordsToHighlight = query.split(" ");
    });
  }

  @override
  Widget build(BuildContext context) {
    print(wordsToHighlight);
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: [
            
            SearchWidget(
              textEditingController: _controller, 
              onChangeCallback: (val) => search(val), 
              clearTextCallback: () {
                _controller.clear();
                setState(() {
                  wordsToHighlight.clear();
                });
              },
            ),
            
            const SizedBox(height: 50),
            
            Expanded(
              child: ListView.builder(
                itemCount: mockData.length,
                itemBuilder: (context, index) {

                  final item = mockData[index];

                  for(final entry in item.entries) {
                    // for each entry return words to highlight
                    // does this word have any of the query words in it?
                    bool match = this.wordsToHighlight.any((ele) => entry.value.contains(ele));
                    
                    if(match) {
                      // highlight the words that match and return that string
                      var textSpans = [];

                      for(final word in wordsToHighlight) {
                        var result = entry.value.toString().replaceAllMapped(RegExp(word, caseSensitive: false), (match) => '<t>${match.group(0)}</t>');
                        print('Result: $result');
                      }
                    }

                  }

                  return Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 5),
                    child: Row(
                      children: [
                        for(final item in item.entries)
                          Expanded(
                            child: Text(item.value),
                          )
                      ],
                    ),
                  );
                },
              ),
            )
          ],
        ),
      ),
    );
  }
}

CodePudding user response:

You can use dynamic_text_highlighting package instead of default Text widget to highlight the words.

Widget buildDTH(String text, List<String> highlights) {
  return DynamicTextHighlighting(
    text: text,
    highlights: highlights,
    color: Colors.yellow,
    style: TextStyle(
      fontSize: 18.0,
      fontStyle: FontStyle.italic,
    ),
    caseSensitive: false,
  );
}

`

void applyChanges(List<String> newHighlights) {
  setState(() {
    highlights = newHighlights;
  });
}
  • Related