Compare commits

..

No commits in common. "1fb4cfd64c75c893f45bc6d439d803cf3befcef1" and "8568eafba33ddb15de10c6315acd5ecb6efeeaac" have entirely different histories.

6 changed files with 257 additions and 245 deletions

View File

@ -1,13 +1,16 @@
// this file should handle most of the API calls // this file should handle most of the API calls
// it also builds some widgets, but it will be modulated later // chat it did // it also builds some widgets, but it will be modulated later
import 'dart:async'; import 'dart:async';
import 'dart:typed_data'; import 'dart:typed_data';
import 'structs.dart'; import 'structs.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'dart:convert'; import 'dart:convert';
class ApiService { class ApiService {
static String ip = ""; static String ip = "";
static String port = ""; static String port = "";
@ -138,6 +141,7 @@ class ApiService {
} catch (e) { } catch (e) {
print('_getEmailContent caught error: $e'); print('_getEmailContent caught error: $e');
} }
// return content;
return HTMLofThread; return HTMLofThread;
} }
@ -340,41 +344,75 @@ class ApiService {
return AttachmentResponse(name: "error", data: Uint8List(0)); return AttachmentResponse(name: "error", data: Uint8List(0));
} }
Future<List<String>> fetchMarkdownContent( //TODO: MOVE THIS INTO WEB
List<String> IDsString, String emailFolder) async { // Future<List<Map<String, dynamic>>> getMarkerPosition() async {
List<String> MDofThread = []; // //this is so we can put a widget right below each email, but the way how the email content is generated
threadAttachments = []; // //leads to problems as for a) the html is added one right after the other in one iframe, b)
int counter = 0; // // if it was multiple iframes then the scrolling to jump would not work as expected
try { // print("marker called");
//attaches email after email from a thread // // JavaScript code embedded as a string
for (var id in IDsString) { // String jsCode = '''
var url = Uri.http('$ip:$port', 'email_md', {'id': id}); // (async function waitForIframeAndMarkers() {
print(url); // try {
var response = await http.get(url); // return await new Promise((resolve) => {
currThread.add(id); // const interval = setInterval(() => {
if (response.statusCode == 200) { // console.log("⏳ Checking for iframe...");
counter += 1; // var iframe = document.getElementsByTagName('iframe')[0];
Map<String, dynamic> json = jsonDecode(response.body); // if (iframe && iframe.contentDocument) {
// console.log("✅ Iframe found!");
// var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
// var markers = iframeDoc.querySelectorAll('[id^="JuanBedarramarker"]');
// if (markers.length > 0) {
// console.log(` Found markers in the iframe.`);
// var positions = [];
// markers.forEach((marker) => {
// var rect = marker.getBoundingClientRect();
// positions.push({
// id: marker.id,
// x: rect.left + window.scrollX,
// y: rect.top + window.scrollY,
// });
// });
// console.log("📌 Marker positions:", positions);
// clearInterval(interval);
// resolve(JSON.stringify(positions)); // Ensure proper JSON string
// } else {
// console.log("❌ No markers found yet.");
// }
// } else {
// console.log("❌ Iframe not found or not loaded yet.");
// }
// }, 200);
// });
// } catch (error) {
// console.error("JS Error:", error);
// throw error; // Propagate error to Dart
// }
// })();
// ''';
MDofThread.add(json['md'] ?? ''); // try {
try { // // Execute the JavaScript code using eval
List<AttachmentInfo> attachments = // // final result = await js.context.callMethod('eval', [jsCode]);
await getAttachmentsInfo(emailFolder, id);
for (var attachment in attachments) {
//TODO: for each attachment creaate at the bottom a widget for each individual one
threadAttachments
.add(await getAttachment(emailFolder, id, attachment.name));
}
} catch (innerError) {
print('_getAttachment info caught error $innerError');
}
}
}
} catch (e) {
print('_getMDContent caught error: $e');
}
return MDofThread; // if (result != null && result is String) {
} // print("Result received: $result");
// // Parse the JSON string returned by JavaScript into a Dart list of maps
// final List<dynamic> parsedResult = jsonDecode(result);
// var positions = List<Map<String, dynamic>>.from(parsedResult);
// print("positions put on");
// print(positions);
// return positions;
// } else {
// print("result is null or not a string");
// }
// } catch (e, stackTrace) {
// print("Error executing JavaScript: $e");
// print(stackTrace);
// }
// return [];
// }
} }

View File

@ -1,18 +1,16 @@
import 'package:crab_ui/api_service.dart'; import 'package:crab_ui/api_service.dart';
import 'package:crab_ui/attachmentDownload.dart'; import 'package:crab_ui/attachmentDownload.dart';
import 'package:crab_ui/collapsableEmails.dart';
import 'package:crab_ui/structs.dart'; import 'package:crab_ui/structs.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'attachmentWidget.dart'; import 'attachmentWidget.dart';
class EmailToolbar extends StatefulWidget { class EmailToolbar extends StatefulWidget {
final Function(String) onJumpToNumbering; final Function(String) onJumpToSpan;
final Function(String) onViewspecs;
final VoidCallback onButtonPressed; final VoidCallback onButtonPressed;
EmailToolbar( EmailToolbar(
{Key? key, required this.onButtonPressed, required this.onJumpToNumbering, required this.onViewspecs}) {Key? key, required this.onButtonPressed, required this.onJumpToSpan})
: super(key: key); : super(key: key);
@override @override
@ -21,8 +19,7 @@ class EmailToolbar extends StatefulWidget {
class _DynamicClassesAugment extends State<EmailToolbar> { class _DynamicClassesAugment extends State<EmailToolbar> {
String selectedClass = 'Class 1'; String selectedClass = 'Class 1';
TextEditingController _jumpController = TextEditingController(); // TextEditingController _jumpController = TextEditingController();
TextEditingController _viewspecsController = TextEditingController();
// late final FocusNode _JumpItemfocusNode; // late final FocusNode _JumpItemfocusNode;
// late final FocusNode _viewSpecsfocusNode; // late final FocusNode _viewSpecsfocusNode;
@ -49,7 +46,7 @@ class _DynamicClassesAugment extends State<EmailToolbar> {
void dispose() { void dispose() {
// _JumpItemfocusNode.dispose(); // _JumpItemfocusNode.dispose();
// _viewSpecsfocusNode.dispose(); // _viewSpecsfocusNode.dispose();
_jumpController.dispose(); // _jumpController.dispose();
super.dispose(); super.dispose();
} }
@ -74,20 +71,20 @@ class _DynamicClassesAugment extends State<EmailToolbar> {
child: Text('Attachments'), child: Text('Attachments'),
), ),
SizedBox(width: 8), SizedBox(width: 8),
// ElevatedButton( ElevatedButton(
// onPressed: AugmentClasses.handleOpen, onPressed: AugmentClasses.handleOpen,
// child: Text('Open'), child: Text('Open'),
// ), ),
// SizedBox(width: 8), // SizedBox(width: 8),
ElevatedButton( ElevatedButton(
onPressed: AugmentClasses.handleFind, onPressed: AugmentClasses.handleFind,
child: Text('Find'), child: Text('Find'),
), ),
// SizedBox(width: 8), // SizedBox(width: 8),
// ElevatedButton( ElevatedButton(
// onPressed: AugmentClasses.handleStop, onPressed: AugmentClasses.handleStop,
// child: Text('Stop'), child: Text('Stop'),
// ), ),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
AugmentClasses.handleMove(context); AugmentClasses.handleMove(context);
@ -134,10 +131,10 @@ class _DynamicClassesAugment extends State<EmailToolbar> {
// width: 8, // width: 8,
// ), // ),
Container( Container(
width: 100, width: 50,
height: 30, height: 30,
child: TextField( child: TextField(
controller: _jumpController, // controller: _jumpController,
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: OutlineInputBorder(),
// suffixIcon: Icon(Icons.search) // suffixIcon: Icon(Icons.search)
@ -145,7 +142,7 @@ class _DynamicClassesAugment extends State<EmailToolbar> {
onSubmitted: (value) { onSubmitted: (value) {
print("onSubmitted"); print("onSubmitted");
if (value.isNotEmpty) { if (value.isNotEmpty) {
widget.onJumpToNumbering(value); widget.onJumpToSpan(value);
} }
}, },
), ),
@ -182,18 +179,14 @@ class _DynamicClassesAugment extends State<EmailToolbar> {
onPressed: () => AugmentClasses.ViewSpecsButton(context), onPressed: () => AugmentClasses.ViewSpecsButton(context),
child: Text('ViewSpecs:')), child: Text('ViewSpecs:')),
Container( Container(
width: 100, width: 50,
height: 30, height: 30,
child: TextField( child: TextField(
controller: _viewspecsController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: '', labelText: '',
border: OutlineInputBorder(), border: OutlineInputBorder(),
// suffixIcon: Icon(Icons.style_rounded) // suffixIcon: Icon(Icons.style_rounded)
), ),
onSubmitted: (value) {
widget.onViewspecs(value);
},
), ),
), ),
ElevatedButton( ElevatedButton(
@ -491,8 +484,30 @@ class AugmentClasses {
print("Stop button pressed"); print("Stop button pressed");
} }
static void handleJump(String value) { static void handleJump(String spanId) {
print(value); String js_code = '''
var iframe = document.getElementsByTagName('iframe')[0]; // 0 for the first iframe, 1 for the second, etc.
// Check if the iframe is loaded and has content
if (iframe && iframe.contentDocument) {
// Access the document inside the iframe
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
// Find the element with the specific id inside the iframe
var targetElement = iframeDoc.getElementById("$spanId"); // Replace '36 ' with the actual id of the target element
// If the element exists, scroll to it
if (targetElement) {
targetElement.scrollIntoView();
console.log('Scrolled to element with id "$spanId" inside the iframe.');
} else {
console.log('Element with id "$spanId" not found inside the iframe.');
}
} else {
console.log('Iframe not found or not loaded.');
}
''';
// js.context.callMethod('eval', [js_code]);
} }
static void invisibility(String htmlClass) {} static void invisibility(String htmlClass) {}

View File

@ -3,13 +3,13 @@ import 'package:flutter/material.dart';
class CollapsableEmails extends StatefulWidget { class CollapsableEmails extends StatefulWidget {
final List<String> thread; // email id's in the form xyz@gmail.com final List<String> thread; // email id's in the form xyz@gmail.com
final List<String> threadMarkdown; final List<String> threadHTML;
final String threadIDs; final String threadIDs;
CollapsableEmails( CollapsableEmails(
{required this.thread, {required this.thread,
required this.threadMarkdown, required this.threadHTML,
required this.threadIDs, String? targetJumpNumbering, String? targetViewspecs}); required this.threadIDs});
@override @override
State<CollapsableEmails> createState() => _CollapsableEmailsState(); State<CollapsableEmails> createState() => _CollapsableEmailsState();

View File

@ -1,4 +1,3 @@
import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'api_service.dart'; import 'api_service.dart';
import 'structs.dart'; import 'structs.dart';
@ -8,20 +7,13 @@ import 'package:markdown/markdown.dart' as md;
class CollapsableEmails extends StatefulWidget { class CollapsableEmails extends StatefulWidget {
final List<String> thread; // email id's in the form xyz@gmail.com final List<String> thread; // email id's in the form xyz@gmail.com
// final List<String> threadHTML; to be replaced with the MD final List<String> threadHTML;
final List<String> threadMarkdown;
final String threadIDs; final String threadIDs;
final String? targetJumpNumbering;
final String? targetViewspecs;
const CollapsableEmails({ CollapsableEmails(
required this.thread, {required this.thread,
// required this.threadHTML, required this.threadHTML,
required this.threadMarkdown, required this.threadIDs});
required this.threadIDs,
this.targetJumpNumbering,
this.targetViewspecs,
});
@override @override
State<CollapsableEmails> createState() => _CollapsableEmailsState(); State<CollapsableEmails> createState() => _CollapsableEmailsState();
@ -57,32 +49,15 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
bool zoomOut = false; bool zoomOut = false;
bool zoomIn = true; bool zoomIn = true;
late List<AugmentTree> threadNodes = []; late List<AugmentTree> threadNodes = [];
static bool leftNumbering = true;
static bool rightNumbering = true;
bool showWhole = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
threadNodes = []; threadNodes = [];
currentZoomTree = []; currentZoomTree = [];
// _markdownConverter(); _markdownConverter();
_serializableData(widget.threadIDs); // this _serializableData(widget.threadIDs); // this
_markdown2Tree(widget.threadMarkdown); _markdown2Tree(allMarkdown);
}
@override
void didUpdateWidget(covariant CollapsableEmails oldWidget) {
// TODO: implement didUpdateWidget
super.didUpdateWidget(oldWidget);
if (widget.targetJumpNumbering != null &&
widget.targetJumpNumbering != oldWidget.targetJumpNumbering) {
_handleJump(widget.targetJumpNumbering!);
}
if (widget.targetViewspecs != null &&
widget.targetViewspecs != oldWidget.targetViewspecs) {
_handleViewspecs(widget.targetViewspecs!);
}
} }
@override @override
@ -90,16 +65,12 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
super.dispose(); super.dispose();
} }
// void _markdownConverter() async { void _markdownConverter() async {
// // to list of markdown for (int email = 0; email < widget.threadHTML.length; email++) {
// // for (int email = 0; email < widget.threadHTML.length; email++) { String markdown = html2md.convert(widget.threadHTML[email]);
// // String markdown = html2md.convert(widget.threadHTML[email]); allMarkdown.add(markdown);
// // allMarkdown.add(markdown); }
// // } }
// for (int email = 0; email < widget.threadMarkdown.length; email++) {
// allMarkdown.add(email);
// }
// }
void _add2Tree(AugmentTree tree, md.Element node2add) { void _add2Tree(AugmentTree tree, md.Element node2add) {
// adds node to its corresponding place // adds node to its corresponding place
@ -273,15 +244,6 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
], ],
), ),
SizedBox(width: 12.0), SizedBox(width: 12.0),
if (leftNumbering)
Padding(
padding: const EdgeInsets.fromLTRB(0, 10, 5, 0),
child: Text(
childNode.numbering,
style:
TextStyle(color: Color(Colors.purple[400]!.value)),
),
),
Expanded( Expanded(
child: MarkdownBlock( child: MarkdownBlock(
data: childNode.data, data: childNode.data,
@ -291,15 +253,10 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
.darkConfig, // or lightConfig depending on theme .darkConfig, // or lightConfig depending on theme
), ),
), ),
if (rightNumbering) Text(
Padding( childNode.numbering,
padding: const EdgeInsets.fromLTRB(5, 10, 5, 0), style: TextStyle(color: Color(Colors.purple[400]!.value)),
child: Text( )
childNode.numbering,
style:
TextStyle(color: Color(Colors.purple[400]!.value)),
),
),
], ],
), ),
), ),
@ -309,101 +266,6 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
); );
} }
void _handleJump(String queryNumbering) {
print(queryNumbering);
if (queryNumbering.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Please enter a numbering to jump to.')),
);
return;
}
final int targetEmailIndex = _expandedEmails.first;
if (targetEmailIndex >= threadNodes.length) {
// Error handling
return;
}
final AugmentTree rootOfCurrentEmail = threadNodes[targetEmailIndex];
final AugmentTree? foundNode =
_findNodeByNumbering(rootOfCurrentEmail, queryNumbering);
if (foundNode != null) {
setState(() {
currentZoomTree[targetEmailIndex] = foundNode; // Update the state
});
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Numbering "$queryNumbering" not found.')),
);
}
}
void _handleViewspecs(String viewspecsQuery) {
print(viewspecsQuery);
if (viewspecsQuery.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Please enter the viewspecs.')),
);
return;
}
final int targetEmailIndex = _expandedEmails.first;
if (targetEmailIndex >= threadNodes.length) {
// Error handling
return;
}
if (viewspecsQuery.contains('n')) {
setState(() {
leftNumbering = false; // Update the state
rightNumbering = false;
});
}
if (viewspecsQuery.contains('m')) {
setState(() {
rightNumbering = true;
leftNumbering = true;
});
}
if (viewspecsQuery.contains('H')) {
setState(() {
leftNumbering = !leftNumbering;
});
}
if (viewspecsQuery.contains('G')) {
setState(() {
rightNumbering = !rightNumbering;
});
}
if (viewspecsQuery.contains('w')) {
setState(() {
showWhole = true;
});
}
// else {
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(content: Text('Numbering "$viewspecsQuery" not found.')),
// );
// }
}
AugmentTree? _findNodeByNumbering(AugmentTree root, String numbering) {
//recursively finds the node you mentioned
// to find the AugmentTree node corresponding to the `numbering`.
if (root.numbering == numbering) {
return root;
}
for (var child in root.children) {
final found = _findNodeByNumbering(child, numbering);
if (found != null) {
return found;
}
}
return null;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return _isLoaded return _isLoaded

View File

@ -140,8 +140,7 @@ class EmailPageState extends State<EmailPage> {
return Scaffold( return Scaffold(
body: EmailListScreen( body: EmailListScreen(
emails: emails, emails: emails,
// getEmailContent: apiService.fetchEmailContent, getEmailContent: apiService.fetchEmailContent,
getEmailContent: apiService.fetchMarkdownContent,
folder: widget.selectedFolder, //try to grab from it directly folder: widget.selectedFolder, //try to grab from it directly
), ),
); );

View File

@ -2,9 +2,11 @@ import 'package:flutter/material.dart';
import 'dart:ui_web' as ui; import 'dart:ui_web' as ui;
import 'augment.dart'; import 'augment.dart';
// import 'dart:js_interop' as js; //eventually for manipulating css // import 'dart:js_interop' as js; //eventually for manipulating css
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'collapsableEmails.dart'; import 'collapsableEmails.dart';
import 'api_service.dart'; import 'api_service.dart';
class EmailView extends StatefulWidget { class EmailView extends StatefulWidget {
final List<String> emailContent; final List<String> emailContent;
final String from; final String from;
@ -42,8 +44,6 @@ class _EmailViewState extends State<EmailView> {
{'id': 'marker2', 'x': 150, 'y': 200}, {'id': 'marker2', 'x': 150, 'y': 200},
{'id': 'marker3', 'x': 250, 'y': 300}, {'id': 'marker3', 'x': 250, 'y': 300},
]; ];
String? _targetJumpNumbering;
String? _targetViewspecs;
@override @override
void initState() { void initState() {
@ -55,26 +55,39 @@ class _EmailViewState extends State<EmailView> {
// _registerViewFactory(currentContent); // _registerViewFactory(currentContent);
} }
// void _registerViewFactory(List<String> currentContent) { // i think this doesnt work anymore
// setState(() { //update to do item per item
// // each item to have itsviewtype ID
// // is this necessarey here??
// //could just move to collapsable
// viewTypeId = 'iframe-${DateTime.now().millisecondsSinceEpoch}';
// final emailHTML = web.document.createElement('div') as web.HTMLDivElement
// ..id = viewTypeId
// ..innerHTML = currentContent[0].toJS; // temporarily index because it has to do all of them
// emailHTML.style
// ..width = '100%'
// ..height = '100%'
// ..overflow = 'auto'
// ..scrollBehavior = 'smooth';
// ui.platformViewRegistry.registerViewFactory(
// viewTypeId,
// (int viewId) => emailHTML,
// );
// });
// }
void _scrollToNumber(String spanId) { void _scrollToNumber(String spanId) {
AugmentClasses.handleJump(spanId); AugmentClasses.handleJump(spanId);
} }
void _handleJumpRequest(String numbering) {
setState(() {
_targetJumpNumbering = numbering;
});
}
void _handleViewspecsRequest(String viewspecsCommand) {
setState(() {
_targetViewspecs = viewspecsCommand;
});
}
// TODO: void _invisibility(String ) //to make purple numbers not visible // TODO: void _invisibility(String ) //to make purple numbers not visible
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// print("thread id ${widget.id}");
ApiService.currThreadID = widget.id; ApiService.currThreadID = widget.id;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
@ -85,9 +98,28 @@ class _EmailViewState extends State<EmailView> {
Column( Column(
children: [ children: [
EmailToolbar( EmailToolbar(
onJumpToNumbering: _handleJumpRequest, onJumpToSpan: _scrollToNumber,
onViewspecs: _handleViewspecsRequest, onButtonPressed: () => {},
onButtonPressed: () => {print("email tool bar pressed")}, // AugmentClasses.handleJump(viewTypeId, '1');
// print("button got pressed?");
// _registerViewFactory(r"""
// <h1>Welcome to My Website</h1>
// <p>This is a simple HTML page.</p>
// <h2>What is HTML?</h2>
// <p>HTML (HyperText Markup Language) is the most basic building~ block of the Web. It defines the meaning and structure of web content. Other technologies besides HTML are generally used to describe a web page's appearance/presentation (CSS) or functionality/behavior (JavaScript).</p>
// <h3>Here's a simple list:</h3>
// <ul>
// <li>HTML elements are the building blocks of HTML pages</li>
// <li>HTML uses tags like <code>&lt;tag&gt;</code> to organize and format content</li>
// <li>CSS is used with HTML to style pages</li>
// </ul>
// <p>Copyright © 2023</p>
// """);
// print("change");
// widget.emailContent = r"
//
), ),
Row( Row(
// title of email // title of email
@ -127,16 +159,82 @@ class _EmailViewState extends State<EmailView> {
Expanded( Expanded(
child: CollapsableEmails( child: CollapsableEmails(
//change here //change here
thread: widget.messages, //this wont work in serializable thread: widget.messages, //this wont work in serializable
// threadHTML: widget.emailContent, // old html threadHTML: widget.emailContent,
threadMarkdown: widget.emailContent,
threadIDs: widget.id, threadIDs: widget.id,
targetJumpNumbering: _targetJumpNumbering,
targetViewspecs: _targetViewspecs,
), ),
), ),
// Expanded(
// child: HtmlElementView(
// key: UniqueKey(),
// viewType: viewTypeId,
// ),
// ),
], ],
), ),
// Overlay widgets dynamically based on marker positions
// FutureBuilder<List<Map<String, dynamic>>>(
// future: _markerPositionsFuture,
// builder: (context, snapshot) {
// print("FutureBuilder state: ${snapshot.connectionState}");
// if (snapshot.connectionState == ConnectionState.waiting) {
// return Center(child: CircularProgressIndicator());
// }
// if (snapshot.hasError) {
// print("Error in FutureBuilder: ${snapshot.error}");
// return Center(child: Text('error loading markers'));
// }
// if (snapshot.hasData && snapshot.data != null) {
// final markers = snapshot.data!;
// return Stack(
// children: markers.map((marker) {
// return Positioned(
// left: marker['x'].toDouble(),
// top: marker['y'].toDouble(),
// child: GestureDetector(
// onTap: () {
// print('Tapped on ${marker['id']}');
// },
// child: Container(
// width: 50,
// height: 50,
// color: Colors.red,
// child: Center(
// child: Text(
// marker['id'],
// style: TextStyle(color: Colors.white),
// ),
// ),
// ),
// ),
// );
// }).toList(),
// );
// }
// return SizedBox.shrink(); // No markers found
// },
// ),
// Red widget overlay
// Positioned(
// left: 8, // Adjust based on your desired position
// top: 100 + 44 + 5, // Adjust based on your desired position
// child: IgnorePointer(
// ignoring: true, // Ensures the iframe remains interactive
// child: Container(
// color: Colors.red,
// width: 100,
// height: 50,
// child: Center(
// child: Text(
// 'Overlay',
// style: TextStyle(color: Colors.white),
// ),
// ),
// ),
// ),
// ),
], ],
)); ));
} }