<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ayush Raina</title>
    <description>The latest articles on DEV Community by Ayush Raina (@ayush_raina_50d571bb314d9).</description>
    <link>https://dev.to/ayush_raina_50d571bb314d9</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4207085%2F4da6b29a-118a-4439-8f31-29058c491d0d.jpg</url>
      <title>DEV Community: Ayush Raina</title>
      <link>https://dev.to/ayush_raina_50d571bb314d9</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ayush_raina_50d571bb314d9"/>
    <language>en</language>
    <item>
      <title>Why Cross-Platform Flutter Apps Still Need Native Kotlin MethodChannels for Low-Latency Hardware</title>
      <dc:creator>Ayush Raina</dc:creator>
      <pubDate>Sun, 25 Oct 2026 18:10:02 +0000</pubDate>
      <link>https://dev.to/ayush_raina_50d571bb314d9/why-cross-platform-flutter-apps-still-need-native-kotlin-methodchannels-for-low-latency-hardware-4agn</link>
      <guid>https://dev.to/ayush_raina_50d571bb314d9/why-cross-platform-flutter-apps-still-need-native-kotlin-methodchannels-for-low-latency-hardware-4agn</guid>
      <description>&lt;p&gt;The "100% Dart" Myth in Production Mobile Engineering&lt;br&gt;
One of the most common architectural mistakes teams make when adopting Flutter is assuming that every feature should be written purely in Dart. While Flutter's rendering engine and pub.dev ecosystem handle 90% of standard CRUD and UI workflows effortlessly, forcing high-frequency hardware interactions—such as raw stylus ink prediction, custom WebRTC media tracks, or USB serial communication—through pure Dart abstractions often introduces unacceptable latency.&lt;/p&gt;

&lt;p&gt;Where Pure Dart Hits a Bottleneck&lt;br&gt;
In a recent interactive whiteboard project deployed on large Android touch panels, relying purely on standard Flutter gesture arenas resulted in a noticeable 70.7ms stylus stroke lag. Why? Because touch events had to travel from the Android OS input pipeline, across the Flutter engine boundary, through Dart's gesture disambiguation arena, and back into the render tree before a single pixel appeared on screen.&lt;/p&gt;

&lt;p&gt;The Hybrid Solution: Native Kotlin + Flutter UI&lt;br&gt;
Instead of fighting the framework, the cleanest architectural decision is knowing when to drop down into native Kotlin via MethodChannel and EventChannel:&lt;/p&gt;

&lt;p&gt;Let Native Handle Raw Hardware Interrupts: By capturing motion events and rendering predictive hardware-accelerated overlays directly on the Android SurfaceView layer in Kotlin, we reduced stroke latency from 70.7ms down to 8.7ms (an 88% reduction).&lt;/p&gt;

&lt;p&gt;Use Flutter for State Orchestration and Cross-Platform Sync: Once a stroke is completed, the coordinate array is passed over a binary messenger channel to Flutter, which manages the Undo/Redo stack, BLoC state, and WebSocket broadcasting to 300+ participants.&lt;/p&gt;

&lt;p&gt;Example: Clean Binary Messenger Bridge in Kotlin&lt;/p&gt;

&lt;p&gt;class StylusLatencyPlugin : FlutterPlugin, MethodCallHandler {&lt;br&gt;
    private lateinit var channel: MethodChannel&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
    channel = MethodChannel(binding.binaryMessenger, "com.inboard/stylus_accel")
    channel.setMethodCallHandler(this)
}

override fun onMethodCall(call: MethodCall, result: Result) {
    when (call.method) {
        "enablePredictiveOverlay" -&amp;gt; {
            val strokeWidth = call.argument&amp;lt;Double&amp;gt;("width") ?: 3.0
            // Trigger native Android front-buffered rendering
            result.success(true)
        }
        else -&amp;gt; result.notImplemented()
    }
}

override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
    channel.setMethodCallHandler(null)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;Final Verdict&lt;br&gt;
Choosing Flutter shouldn't mean abandoning native Android engineering. The strongest mobile architectures treat Flutter as a supercharged UI and business-logic layer while unapologetically leveraging native Kotlin whenever sub-10ms hardware performance is on the line.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>kotlin</category>
      <category>android</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Implementing Streaming RAG Responses in Flutter with Server-Sent Events (SSE)</title>
      <dc:creator>Ayush Raina</dc:creator>
      <pubDate>Sun, 25 Oct 2026 18:08:10 +0000</pubDate>
      <link>https://dev.to/ayush_raina_50d571bb314d9/implementing-streaming-rag-responses-in-flutter-with-server-sent-events-sse-562f</link>
      <guid>https://dev.to/ayush_raina_50d571bb314d9/implementing-streaming-rag-responses-in-flutter-with-server-sent-events-sse-562f</guid>
      <description>&lt;p&gt;Why Standard REST Calls Fail for Mobile AI UX&lt;br&gt;
When integrating Large Language Models (LLMs) and Retrieval-Augmented Generation (RAG) into mobile apps, waiting 5 to 10 seconds for a complete JSON payload blocks the user interface and makes the application feel unresponsive. By consuming Server-Sent Events (SSE) over a Dart Stream, we can render tokens in real time as soon as the vector retrieval step finishes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating the Dart SSE Stream Client
Using the standard http package, we can send a http.Request and listen directly to the chunked byte stream instead of awaiting http.post:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;import 'dart:convert';&lt;br&gt;
import 'package:http/http.dart' as http;&lt;/p&gt;

&lt;p&gt;class RagStreamService {&lt;br&gt;
  final http.Client _client = http.Client();&lt;/p&gt;

&lt;p&gt;Stream streamQuery({&lt;br&gt;
    required String prompt,&lt;br&gt;
    required List retrievedContextIds,&lt;br&gt;
  }) async* {&lt;br&gt;
    final request = http.Request(&lt;br&gt;
      'POST',&lt;br&gt;
      Uri.parse('&lt;a href="https://api.example.com/v1/rag/stream'" rel="noopener noreferrer"&gt;https://api.example.com/v1/rag/stream'&lt;/a&gt;),&lt;br&gt;
    );&lt;br&gt;
    request.headers['Content-Type'] = 'application/json';&lt;br&gt;
    request.headers['Accept'] = 'text/event-stream';&lt;br&gt;
    request.body = jsonEncode({&lt;br&gt;
      'prompt': prompt,&lt;br&gt;
      'context_ids': retrievedContextIds,&lt;br&gt;
      'stream': true,&lt;br&gt;
    });&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final response = await _client.send(request);

if (response.statusCode != 200) {
  throw Exception('Failed to connect to RAG stream: ${response.statusCode}');
}

await for (final chunk in response.stream
    .transform(utf8.decoder)
    .transform(const LineSplitter())) {
  if (chunk.startsWith('data: ')) {
    final data = chunk.substring(6).trim();
    if (data == '[DONE]') break;

    final decoded = jsonDecode(data) as Map&amp;lt;String, dynamic&amp;gt;;
    final token = decoded['token'] as String?;
    if (token != null) {
      yield token;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
}&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Consuming the Token Stream Efficiently in the UI
To avoid rebuilding the entire screen on every token arrival, isolate the streaming text area inside a dedicated StreamBuilder or a scoped ValueNotifier:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;class StreamingAnswerCard extends StatefulWidget {&lt;br&gt;
  final Stream tokenStream;&lt;br&gt;
  const StreamingAnswerCard({super.key, required this.tokenStream});&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/override"&gt;@override&lt;/a&gt;&lt;br&gt;
  State createState() =&amp;gt; _StreamingAnswerCardState();&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;class _StreamingAnswerCardState extends State {&lt;br&gt;
  final StringBuffer _buffer = StringBuffer();&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/override"&gt;@override&lt;/a&gt;&lt;br&gt;
  Widget build(BuildContext context) {&lt;br&gt;
    return StreamBuilder(&lt;br&gt;
      stream: widget.tokenStream,&lt;br&gt;
      builder: (context, snapshot) {&lt;br&gt;
        if (snapshot.hasData) {&lt;br&gt;
          _buffer.write(snapshot.data);&lt;br&gt;
        }&lt;br&gt;
        return Padding(&lt;br&gt;
          padding: const EdgeInsets.all(16.0),&lt;br&gt;
          child: SelectableText(&lt;br&gt;
            _buffer.toString(),&lt;br&gt;
            style: const TextStyle(fontSize: 16, height: 1.5),&lt;br&gt;
          ),&lt;br&gt;
        );&lt;br&gt;
      },&lt;br&gt;
    );&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Key Production Takeaway&lt;br&gt;
Always decouple your vector search retrieval latency from token generation by sending an initial status event over the stream, and ensure your http.Client is properly closed on widget disposal to prevent memory and socket leaks when users navigate away mid-generation.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>ai</category>
      <category>dart</category>
      <category>openai</category>
    </item>
    <item>
      <title>Optimizing High-Frequency Stylus Input and Real-Time Canvas Rendering in Flutter</title>
      <dc:creator>Ayush Raina</dc:creator>
      <pubDate>Sun, 25 Oct 2026 17:49:40 +0000</pubDate>
      <link>https://dev.to/ayush_raina_50d571bb314d9/optimizing-high-frequency-stylus-input-and-real-time-canvas-rendering-in-flutter-15a7</link>
      <guid>https://dev.to/ayush_raina_50d571bb314d9/optimizing-high-frequency-stylus-input-and-real-time-canvas-rendering-in-flutter-15a7</guid>
      <description>&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The Problem with Default Canvas Rendering&lt;br&gt;
When building interactive whiteboard applications in Flutter for large touch panels and tablets, standard widget rebuilds and naive CustomPainter implementations often introduce noticeable input latency (50–70ms+). When hundreds of stylus points are captured per second alongside real-time WebSocket synchronization, calling setState() on every pointer move triggers unnecessary widget tree rebuilds and frame drops.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Decoupling Input Capture from Widget Tree Rebuilds&lt;br&gt;
Instead of calling setState() on every onPanUpdate or onPointerMove event, high-frequency stylus points should be buffered and passed directly to the canvas repaint listener using a ChangeNotifier:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;import 'package:flutter/material.dart';&lt;/p&gt;

&lt;p&gt;class StrokeController extends ChangeNotifier {&lt;br&gt;
  final List _points = [];&lt;br&gt;
  List get points =&amp;gt; List.unmodifiable(_points);&lt;/p&gt;

&lt;p&gt;void addPoint(Offset point) {&lt;br&gt;
    _points.add(point);&lt;br&gt;
    notifyListeners(); // Marks only RenderCustomPaint as dirty&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;void clear() {&lt;br&gt;
    _points.clear();&lt;br&gt;
    notifyListeners();&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Passing the Repaint Listenable to CustomPainter
By passing the StrokeController directly to super(repaint: controller), Flutter bypasses the widget build and layout phases completely during active drawing and jumps straight to the paint phase:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;class WhiteboardPainter extends CustomPainter {&lt;br&gt;
  final StrokeController controller;&lt;/p&gt;

&lt;p&gt;WhiteboardPainter(this.controller) : super(repaint: controller);&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/override"&gt;@override&lt;/a&gt;&lt;br&gt;
  void paint(Canvas canvas, Size size) {&lt;br&gt;
    final paint = Paint()&lt;br&gt;
      ..color = Colors.black87&lt;br&gt;
      ..strokeWidth = 3.5&lt;br&gt;
      ..strokeCap = StrokeCap.round&lt;br&gt;
      ..style = PaintingStyle.stroke;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final points = controller.points;
for (int i = 0; i &amp;lt; points.length - 1; i++) {
  canvas.drawLine(points[i], points[i + 1], paint);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/override"&gt;@override&lt;/a&gt;&lt;br&gt;
  bool shouldRepaint(covariant WhiteboardPainter oldDelegate) =&amp;gt; false;&lt;br&gt;
}&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Capturing Raw Pointer Events Without Gesture Arena Delay
Standard GestureDetector widgets wait briefly to disambiguate taps from drags. For zero-latency stylus input, wrapping the CustomPaint in a raw Listener widget captures hardware pointer events at the exact millisecond they touch the glass:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;class WhiteboardCanvas extends StatefulWidget {&lt;br&gt;
  const WhiteboardCanvas({super.key});&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/override"&gt;@override&lt;/a&gt;&lt;br&gt;
  State createState() =&amp;gt; _WhiteboardCanvasState();&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;class _WhiteboardCanvasState extends State {&lt;br&gt;
  final StrokeController _controller = StrokeController();&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/override"&gt;@override&lt;/a&gt;&lt;br&gt;
  void dispose() {&lt;br&gt;
    _controller.dispose();&lt;br&gt;
    super.dispose();&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/override"&gt;@override&lt;/a&gt;&lt;br&gt;
  Widget build(BuildContext context) {&lt;br&gt;
    return Listener(&lt;br&gt;
      onPointerMove: (PointerMoveEvent event) {&lt;br&gt;
        _controller.addPoint(event.localPosition);&lt;br&gt;
      },&lt;br&gt;
      child: CustomPaint(&lt;br&gt;
        painter: WhiteboardPainter(_controller),&lt;br&gt;
        size: Size.infinite,&lt;br&gt;
      ),&lt;br&gt;
    );&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Batching WebSocket Synchronization
Rather than awaiting network acknowledgment before rendering ink on the screen, render strokes locally immediately (optimistic rendering) and stream batched coordinate deltas over WebSockets every 10–16ms (matching a 60–120Hz refresh cycle). Combined with direct hardware-accelerated overlay rendering on Android interactive panels, this architecture reduced our production stylus latency by 88% (from 70.7ms down to 8.7ms) while keeping up to 300 connected participants in real-time sync.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>android</category>
      <category>performance</category>
      <category>ai</category>
      <category>flutter</category>
    </item>
  </channel>
</rss>
