AI 资讯
Automating an app with no DOM: driving Flutter/canvas editors with coordinates only
In my last post I said that for normal HTML pages, element-based automation ( find / read_page ) beats coordinates every time. This post is about the apps where that advice is useless. Flutter Web apps. Canvas-rendered editors. Every button and panel you can see on screen doesn't exist in the DOM — it's all pixels painted onto a single canvas. find returns nothing. read_page 's accessibility tree is effectively empty. I got Claude to drive the Rive editor (an animation tool built with Flutter) all the way through selecting assets and exporting them. Here's the procedure that survived contact with reality. Step zero: confirm you're actually in this situation Coordinate automation is fragile, so you should only accept it after ruling out the alternative. The test is quick: run read_page . If the visible UI has almost no corresponding nodes, you're looking at a canvas-rendered app, and coordinates are the only interface you have. The four rules 1. Wait for the window size to settle before anything else Same failure mode as my previous post: right after load, the viewport hasn't reached its final width (I measured 1664→1920 over 2–3 seconds), and clicks based on an early screenshot land to the right of the target. Read innerWidth via javascript_tool twice; only proceed when two consecutive reads match. But matching innerWidth alone isn't enough — also confirm devicePixelRatio hasn't changed since the screenshot you're about to act on (a follow-up to my previous post surfaced this: when DPI or scaling changes, the whole coordinate space rescales the same way, but the new values stabilize immediately, so an innerWidth -only check can't catch it). Canvas apps deserve extra paranoia here, because there is no element-based fallback when a click misses. 2. Read text by zooming, not by extracting Text painted on canvas can't be pulled out of the DOM. To read a menu item or panel label, zoom into that region and read the enlarged screenshot as an image. Full-page screenshots ma
AI 资讯
I built on-device workout rep counting in Flutter — here's what actually worked
I'm building TrainWiz , a Flutter app that turns real exercise into a pet-raising game: you do squats or push-ups, your phone counts the reps, and a little creature levels up and evolves. The core technical problem sounds trivial and absolutely is not: count reps from the camera, on-device, without uploading a single frame. Here's what broke along the way, and what finally worked. Why on-device Two reasons: privacy and latency. A fitness camera that streams your body to a server is a non-starter for most people, and rep feedback has to feel instant or the whole "game" loop dies. So everything runs locally with tflite_flutter + an on-device pose model — no footage ever leaves the phone. Naive attempt #1: joint-angle thresholds The obvious approach: track the knee angle, count a rep when it dips below X° and comes back up. // looks fine in a demo, dies in the real world final kneeAngle = angleBetween ( hip , knee , ankle ); if ( ! _down && kneeAngle < 100 ) _down = true ; if ( _down && kneeAngle > 160 ) { reps ++ ; _down = false ; } It demos beautifully. Then real users prop the phone on the floor, stand at an angle, and it falls apart. The trap: a phone camera gives you 2D pose. A "120° knee angle" flattens completely depending on where the camera sits — the same squat reads as 90° or 150° purely from perspective. Lifting to 3D via the model's z doesn't save you either; monocular z is noisy enough that the angle jitters across your threshold and double-counts. Naive attempt #2: a "body-line" gate Next idea: figure out which exercise you're doing so I can pick the right signal. Standing (squat) vs. horizontal (push-up) should be easy — just check if shoulder, hip and heel form a straight line, right? Wrong, again for the 2D reason. In a real push-up shot from the front-corner, shoulder–hip–heel are not collinear on the image plane — perspective bends them. I gated push-up counting on "body is a straight line" and it would just... stop counting mid-set. Nothing is more
AI 资讯
Why Serverpod? One Language for Your Entire Stack
Why this series I love writing clean architecture . Not because it looks nice in a diagram, but because it survives change — new requirements, new team members, and now, AI-assisted development , where you want boundaries an AI can respect and tests that catch it when it wanders. The problem in most Flutter stacks is the seam between app and backend. You write Dart on the client, then switch to a different language, a hand-written REST layer, DTOs that drift out of sync, and serialization bugs nobody notices until production. Serverpod removes that seam. You write Dart on the server too, and the client-server communication code is generated for you — type-safe, end to end. What is Serverpod? Serverpod is an open-source backend framework that lets you build the entire stack in Dart. Instead of context-switching between languages, your models, your API, and your database logic all live in one language. What you get out of the box: Endpoints — server methods your Flutter client calls directly. The communication code is generated, so there's no hand-written REST/JSON glue. An ORM — type-safe, statically analyzed database access with migrations and relationships. No raw SQL required. Code generation — define a model once; get serialization and client bindings on both sides automatically. Real-time data — streaming over WebSockets, managed for you. Auth — integrations for Google, Apple, and Firebase. The extras enterprises actually need — file uploads, task scheduling, caching, logging, and error monitoring. And on the "is this serious enough for production?" question: Serverpod says it's battle-tested in real-world apps and secured by over 5,000 automated tests, scaling from hobby projects to millions of users without code changes. That's exactly the property you want in an enterprise foundation. The architecture at a glance Here's how the pieces fit. A Serverpod project is generated as three packages: myapp_server → your backend: endpoints, models, business logic, DB my
AI 资讯
Can FlutterFlow Build a Better Dev.to App?
We have all been riding the massive vibe coding wave lately. It feels like pure magic to sit back, tell an AI assistant what to build, and watch a full application appear out of thin air. But if you have ever tried to take that exact same web workflow and deploy a smooth, native app onto an iPhone or Android, you know exactly where the frustration sets in. Are you a vibecoder who loves to build applications and you have built many websites? You have built and deployed many websites. Now you really want to make a mobile application that could disrupt the market and go really viral. Have you heard of FlutterFlow ? Have you tried using it? If the answer is no, then I will tell you about FlutterFlow and then you can decide whether you want to check it out and vibe code mobile applications. I will share the app that I created as well. What is FlutterFlow anyway? Have you ever tried building mobile applications and heard of Flutter and Dart? If you haven't, you should definitely check them out. When I was in college looking for a path to choose whether to pursue app development or web development. I explored both options. While exploring app development, I used and built applications using Flutter, an open-source framework created by Google, which uses a programming language called Dart. While Flutter itself is built by Google, FlutterFlow is an independent, visual low-code platform founded by ex-Google engineers. Today, many of us are familiar with AI vibe-coding tools like Cursor and Claude, which allow us to generate code for websites using conversational prompts. FlutterFlow, however, operates differently than vibe-coding: instead of writing code through chat prompts, it provides a visual, drag-and-drop canvas where you can build and design native mobile applications visually while it automatically generates clean Flutter code in the background. I recently had the opportunity to attend a workshop held by the FlutterFlow team and there, I was blown away by the magic of
AI 资讯
How to Implement Biometric Authentication in a Flutter App (The Right Way)
In today's world, security is no longer optional - it's expected. Whether it's a fintech app, a fitness tracker, or an internal company tool, users want fast and secure access without the hassle of remembering passwords. That's exactly where biometric authentication comes in. In this guide, we'll walk through how we implement biometric authentication in a Flutter app , the practical approach we follow in production, and the common mistakes developers often make (and how to avoid them). Why Biometric Authentication? Before jumping into implementation, let's quickly understand why it matters: Faster login experience (no typing passwords) More secure than traditional authentication Native support across Android & iOS Better user trust and retention What We Use in Flutter To implement biometric authentication, we rely on: local_auth package (official Flutter plugin) Native biometric APIs under the hood (Face ID, Touch ID, Fingerprint) Step 1: Add Dependency dependencies : local_auth : ^3.0.1 Then run: flutter pub get Step 2: Platform Setup ✅ Android Setup Inside android/app/src/main/AndroidManifest.xml : <uses-permission android:name= "android.permission.USE_BIOMETRIC" /> Also ensure: <uses-feature android:name= "android.hardware.fingerprint" android:required= "false" /> ✅ iOS Setup Inside ios/Runner/Info.plist : <key> NSFaceIDUsageDescription </key> <string> We use Face ID to authenticate you securely </string> ⚠️ Without this, Face ID will NOT work and your app may crash. Step 3: Implement Biometric Logic Here's how we structure it in production: import 'package:flutter/foundation.dart' ; import 'package:local_auth/local_auth.dart' ; class BiometricService { final LocalAuthentication _auth = LocalAuthentication (); /// Check if device supports biometrics Future < bool > isBiometricAvailable () async { try { final bool canCheckBiometrics = await _auth . canCheckBiometrics ; final bool isDeviceSupported = await _auth . isDeviceSupported (); return canCheckBiometrics &&
AI 资讯
Shipping one Flutter codebase to 6 platforms: what I learned building Tuneline
I spent the last several months solo-building Tuneline , a cross-platform media player, from a single Flutter codebase that ships native apps to macOS, Windows, Linux, Android, Google TV, and iOS . No Electron. Here is the stack and a few things that bit me. The stack Flutter 3.38 / Dart 3.10 — one codebase, six targets. media_kit for playback — libmpv on desktop, ExoPlayer on Android. Avoiding per-platform video plugins was the single biggest sanity win. Riverpod for state, Hive for local storage, Dio for HTTP. Node.js + Prisma backend for the cloud-sync layer, so your library, favorites, and settings replicate across devices. GoRouter with a single-route, tab-driven shell so the same layout reflows from a phone to a 10-foot TV UI. Things that bit me TV is its own design language. A 10-foot, focus-based UI is not a big phone. D-pad focus traversal, larger hit targets, and a separate Google TV store listing were all non-trivial. Per-platform video quirks. Desktop (libmpv) and mobile (ExoPlayer) disagree on enough edge cases that a shared abstraction over media_kit earned its keep. Sync is a distributed-systems problem in disguise. "Set up once, never rebuild it" sounds simple until two devices edit the same data offline. Keeping one canonical decoder for both the socket sync-down and the REST pull saved me from a whole class of drift bugs. One codebase is not one design. Window management on desktop, picture-in-picture per platform, and safe-area handling on mobile each needed platform-specific care even with a shared core. The product Tuneline is a bring-your-own-content player, like VLC — you supply your own playlists and it does not host anything. Every viewing feature is free on one device, and the only paid tier is cloud sync plus multi-device. No subscriptions. Site: https://tuneline.app — happy to answer any Flutter or cross-platform questions in the comments.
AI 资讯
Shipping an Android podcast player with on-device Whisper — 6 lessons from a Czech indie launch
After a year of nights and weekends, I shipped Lucidcast — an Android podcast player I built because every mainstream app I tried was missing the same three things. The app went live on Google Play this week. This post is for indie devs thinking about shipping their own Android project — six lessons I learned the expensive way. What Lucidcast does (90-second context) A podcast player with on-device AI: Whisper transcribes downloaded episodes locally (audio never leaves the phone) AI episode summaries via Gemini, with a live progress ring "Podcast" wake-word voice commands that work on the lock screen and in Android Auto Smart Pause auto-pauses on loud noises or when someone talks to you Plus the usual: chapters, transcripts, value tags (Podcasting 2.0), 22 languages, Android Auto, live radio, no accounts, no ads. Free includes the full podcast player. Pro one-time unlocks the audio intelligence engine. AI Pack subscription enables Whisper + summaries. Made in EU by a one-person Czech indie team (Prismatic s.r.o.). Play Store | Landing OK, lessons. 1. On-device Whisper is harder than it looks (NDK 29 patch needed) I wanted local transcription so audio doesn't leave the device. whisper_ggml is the only Flutter binding I found that works end-to-end. Catch: it needs Android NDK 29.0.13113456 while most other plugins are still on 27. Setting ndkVersion = "29..." in android/app/build.gradle.kts works for new builds, but the plugin's auto-detected version was sometimes off — needed a small patch script ( flutter/tool/patch_whisper_ggml.sh ) to enforce it during CI. Battery cost is real: I gate transcription to charging-only mode by default with a configurable battery threshold (10-80 %, default 50 %). When the user toggles "transcribe downloaded episodes," I queue the work but only execute when the phone is plugged in. Users on r/podcasts would otherwise notice a 5-10 % overnight battery drain. 2. Sharing the microphone is a contract nobody documents Smart Pause uses noise
AI 资讯
Flutter Agent Skills: How to Make Your AI Agent Actually Good at Flutter
TL;DR: Your AI coding assistant is a generalist. It writes Flutter that looks right but quietly reaches for 2022 patterns. Agent Skills are a new, official way (from the Dart and Flutter teams) to hand your agent task-specific, battle-tested workflows it loads on demand. Two repos, flutter/skills and dart-lang/skills , ship ready-to-use skills for responsive layouts, routing, testing, localization, static analysis, and more. Install in one command: npx skills add flutter/skills --skill '*' --agent universal npx skills add dart-lang/skills --skill '*' --agent universal This post breaks down what they are, how they differ from rules files and MCP, the full catalog, what a real skill looks like under the hood, and whether they actually move the needle. (Spoiler: mostly yes, with one honest caveat.) Let me tell you about a fight I have almost every day. I ask my AI agent to make a screen adapt to tablets. It confidently hands me code that switches layout based on MediaQuery.orientationOf(context) . It looks clean. It compiles. It even runs . And it's wrong, because device orientation has nothing to do with how much window space your app actually has on a foldable, in split-screen, or in a resizable desktop window. The model isn't dumb. It's a generalist trained on a giant pile of Flutter code, much of it old. And here's the uncomfortable truth the Flutter team said out loud when they launched this feature: Flutter and Dart ship new features faster than LLMs can update their training data. That lag has a name, the knowledge gap , and it's why your agent keeps writing rookie Flutter with a straight face. Agent Skills are the Flutter team's answer to that gap. I've been running them on real projects, and they're one of the few "AI workflow" things in 2026 that earned the hype instead of borrowing it. Let's get into it. Table of Contents The real problem: your AI is a generalist What are Agent Skills, exactly? Skills vs Rules vs MCP: who does what The full catalog: every of
AI 资讯
Why I built a native libmpv IPTV player for Windows — an HDR tone-mapping deep-dive
Up front, so there's no confusion: the app I'm describing (Nightmare TV) is a player only . You bring your own M3U / Xtream Codes playlist — it ships with no channels and no content. This post is about the playback engineering, not about where streams come from. Think "VLC for IPTV," not a content service. The problem that started it I watch a lot of live content on my PC — sports, mostly. And every IPTV player I tried on Windows fell into one of two buckets: An Android app running in an emulator. TiviMate and the good mobile players are Android-only, so on a desktop you end up in an emulator or a VM. Input lag, no real HDR path, fans spinning. A thin ExoPlayer / libVLC wrapper. These run natively, but most of them treat HDR as "pass the HDR10 metadata to the display and hope." On an SDR panel — or even a lot of HDR panels — bright skies in a football match blow out to a flat white blob, and 4K HEVC with a DTS track stutters because the decode path isn't doing what you think it is. I wanted the thing that didn't exist: a native Windows player with a reference-grade video path. So I built it on libmpv — the same playback core mpv uses — with a Flutter desktop shell on top for the UI. This post is the part I actually find interesting: the HDR tone-mapping pipeline. Why HDR "just passing through" isn't enough HDR10 content is mastered in the PQ (ST.2084) transfer function against a mastering display — often 1000 nits, sometimes 4000. Your screen is whatever it is: a 350-nit SDR laptop, a 600-nit "HDR400" monitor, an 800-nit OLED. If you map PQ straight to the panel, everything above the panel's peak just clips — all the highlight detail collapses to maximum white. Tone-mapping is the process of intelligently compressing the mastering range into the display range so you keep highlight detail instead of clipping it. The naive version (a fixed curve, or clipping) is what most wrapper players ship. The good version adapts to both the content and the display. The pipeline H
AI 资讯
Genesis AI SDK — A Universal Flutter SDK for AI Agents
One unified API for Gemini, OpenAI, Anthropic, HuggingFace, Ollama, on-device Gemma, and GGUF models — with tool calling, memory, and safety guardrails built in. The Problem Building AI agents in Flutter is fragmented. Every provider has a different API shape. There's no standard way to switch between cloud and on-device inference. Tool calling, persistent memory, and safety guardrails are always custom implementations. The result: developers rebuild the same plumbing for every project. What It Is genesis_ai_sdk is a universal Flutter SDK for building AI agents that run locally and in the cloud. One clean API. Seven providers. Zero vendor lock-in. Supports: Gemini (Google) OpenAI (GPT-4o) Anthropic (Claude) HuggingFace (any public model, no download needed) Ollama (local server, no API key) On-device Gemma (fully offline) On-device GGUF via llama.cpp (fully offline) Switch providers by changing one line. Your agent code stays the same. Quick Start — 10 Lines of Code import 'package:genesis_ai_sdk/genesis_ai_sdk.dart' ; final agent = GenesisAgent ( provider: GeminiProvider ( apiKey: 'YOUR_KEY' ), systemPrompt: 'You are a helpful assistant.' , tools: [ GenesisTools . calculator , GenesisTools . dateTime ], ); final response = await agent . chat ( 'What is 1337 * 42, and what day is it?' ); print ( response ); The agent figures out which tool to call, executes it, and returns the answer. No prompt engineering needed. The Features That Actually Matter Real Tool Calling — Not Just Text The ReAct loop is fully implemented. The agent reasons → calls tools → observes results → repeats until it has a complete answer. An onStep callback fires for every intermediate step — perfect for building a "thinking…" UI. Custom tools are five lines: final weatherTool = GenesisTool . define ( name: 'get_weather' , description: 'Returns weather for a city.' , params: { 'city' : ToolParam . string ( description: 'City name' )}, execute: ( args ) async = > fetchWeather ( args [ 'city' ]), )
AI 资讯
I kept forgetting what subscriptions I was paying for, so I built something about it
I was looking at my bank statement one day and realised I was paying for 4 things I completely forgot about. Combined it was around 40 euros a month just silently leaving my account. I'm a 17 year old developer from Cyprus and I spent the last few weeks building Capsule, a simple subscription tracker that shows you everything you pay for, alerts you before renewals, and tracks how much you save by cancelling things. No bank connection required. You just add your subscriptions manually. Privacy first. It's not on the Play Store yet but the waitlist is live at capsule.crickdevs.com if anyone wants early access. Would genuinely love feedback from real people before I launch.