I want the users of my app to be able to select text (words, but also whole sentences) in a WebViewWidget
, and have an audio of that text played to them (via tts). I only want this to work with u̲n̲d̲e̲r̲l̲i̲n̲e̲d̲ text (or text visually highlighted and set apart from the rest in any other way).
This is part of my Widget tree, where I have tested a few gestureRecognizers (please read the comments):
child: GestureDetector(
//gestureRecognizers of WebViewWidget don't do anything without this GestureDetector,
//see: https://stackoverflow.com/questions/58811375/tapgesturerecognizer-not-working-in-flutter-webview#answer-59298134
onTap: () => print('this line doesnt get printed'),
child: WebViewWidget(
controller: _controller,
gestureRecognizers: {
Factory<LongPressGestureRecognizer>(() => LongPressGestureRecognizer()
..onLongPress = () {
print('onLongPress');
}
..onLongPressStart = (LongPressStartDetails details) {
print('onLongPressStart, ${details.globalPosition}, ${details.localPosition}');
}),
Factory<TapGestureRecognizer>(() => TapGestureRecognizer()
..onTap = () {
print('onTap'); //this doesn't get printed at all
}
..onTapDown = (TapDownDetails details) {
//comment: this doesn't get printed unless I tap for a little long time (not as long as longPress)
print('onTapDown, details: $details');
/*TODO: I want to get a text selection and play audio (with tts)
String selectedText = ...?
TTSHelper.speak(text: selectedText);
*/
}),
},
),
),
Another approach to a possible solution for what I want
I have an initialization function, where I take html (without the html, body & head tags...) from firestore and wrap it in the html, body & head tags along with some custom CSS.
If there is no way to achieve my goal with Dart code, maybe there is a way for me to add JavaScript to the html, so that underlined words/sentences can be tapped to play audio via tts, or at least send that text over to my Dart code somehow?
String _prepareHTML(String content) {
return """<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {background-color: ${getCSSColor(context, CustomColors.mainAppBG)};}
h1, h2, h3, h4, h5, h6 {color: ${getCSSColor(context, CustomColors.blueWhiteText)};}
p {color: ${getCSSColor(context, CustomColors.text)};}
table {
width: 100%;
table-layout: fixed;
word-wrap: break-word;
}
table, tr, th, td {
color: ${getCSSColor(context, CustomColors.text)};
border: 1px solid ${getCSSColor(context, CustomColors.text)};
border-collapse: collapse;
}
th, td {
padding: 6px 12px;
font-weight: bold;
font-size: 1.25em;
}
</style>
</head>
<body>
$content
</body>
</html>""";
}
I think you might be able to achieve what you want by using some javascript and a javascriptChannel
on the WebViewWidget
. General idea below:
var controller = WebViewController();
controller.addJavascriptChannel(
JavascriptChannel(
name: 'underlinedTextSelectChannel',
onMessageReceived: (message) {
var data = json.decode(message.message);
var text = data.text as String;
TTSHelper.speak(text: selectedText);
}
)
)
...
var html = """
<html>
...
<body>
$content
# Within content:
This is some text <p class="underlined-text">Say this!</p> Don't say this.
<script>
var underlinedTexts = document.querySelectorAll('.underlined-text');
for(var t of underlinedTexts) {
t.addEventListener('onClick', (e) => {
underlinedTextSelectChannel.postMessage(JSON.stringify({
text: e.target.innerText
}))
})
}
</script>
</body>
</html>
"""