Flutter Trending
Flutter 趋势
汇总 GitHub、Dev.to、freeCodeCamp、Hacker News、Medium、Reddit 的近期热门内容。
GitHub
20 条Dart 与 Flutter 相关仓库热度
chen08209/FlClash
更新于 07/22 06:32
localsend/localsend
更新于 07/22 06:32
spotiflacapp/SpotiFLAC-Mobile
更新于 07/22 06:09
Predidit/Kazumi
更新于 07/22 06:00
AppFlowy-IO/AppFlowy
更新于 07/22 06:25
flutter/flutter
更新于 07/22 06:46
bggRGjQaUbCoE/PiliPlus
更新于 07/22 01:32
KaringX/karing
更新于 07/22 02:48
KRTirtho/spotube
更新于 07/22 05:39
echo-loop/Echo-Loop
更新于 07/21 23:05
hiddify/hiddify-app
更新于 07/22 06:11
ente/ente
更新于 07/22 06:45
bagisto/opensource-ecommerce-mobile-app
更新于 07/22 03:36
KaringX/clashmi
更新于 07/22 06:06
ImranR98/Obtainium
更新于 07/22 03:29
Sle2p/AniCh
更新于 07/22 01:45
Chevey339/kelivo
更新于 07/22 01:42
simonoppowa/OpenNutriTracker
更新于 07/22 04:36
venera-app/venera
更新于 07/22 03:02
jiangtian616/JHenTai
更新于 07/21 22:10
Dev.to
12 条开发者文章与项目分享
One Letter Broke Four Toolchains. A Symlink Didn't Fix It.
Onur Kesim
Three Sessions, Four Broken Toolchains When configuring an automated coding assistant or build workflow, one of the most effective safety constraints you can set is simple: "If you see something unexpected, stop and ask." On July 26, 2026, while working on a Flutter + .NET project, that guardrail triggered across three separate build sessions. Three times the automated assistant halted execution, flagging what appeared to be four completely unrelated failures across different tools: dart run build_runner build failed with package_config.json did not contain its own root package. flutter analyze broke down due to a Language Server Protocol (LSP) JSON-RPC framing crash. The Android Gradle Plugin (AGP) threw a path validation check error. A supporting .ps1 script failed to execute due to path literal encoding corruption. Three work sessions, four distinct toolchains, four different error messages, and a separate search rabbit hole for each. Yet none of the error logs pointed to the actual common denominator. While I didn't log the exact SDK patch versions at the moment of failure, the root cause was a single uppercase non-ASCII character—an Ö—in the project's parent directory path. The LSP framing failure in flutter analyze is a prime example of how deep these path leaks go. The official Language Server Protocol specification explicitly defines the header field: "The length of the content part in bytes." When a directory path containing multi-byte UTF-8 characters (like Ö) is injected into protocol payloads, the character count no longer matches the total byte length. The behavior observed—a sudden connection collapse during analysis—is consistent with framing misalignment when character count deviates from raw byte length. The path wasn't just an external string on disk; it was actively leaking into low-level protocol frames. The immediate reaction to path failures on Windows is almost universally: "There must be a space in the directory name." It is a well-worn assumption in software engineering, but an assumption is a hypothesis, not a diagnosis. Removing spaces and non-ASCII characters simultaneously proves nothing about which change fixed the issue. If you alter two variables at once, a passing build won't tell you which one was breaking your toolchain. To determine whether spaces were actually responsible, I ran a single-variable isolation test on the exact same project layout, executing flutter build apk: Pure ASCII path with spaces: EXIT = 0 Identical path with an added Ö: EXIT = 255 One character changed. Everything else stayed identical. C:\Dev\Proje\App Test\ -> EXIT = 0 (Build Passed) C:\Dev\ProjeÖ\App Test\ -> EXIT = 255 (Build Failed) This simple test highlighted a crucial debugging principle: "It's probably spaces" is a guess until you isolate the variables and measure them independently. Once the non-ASCII character was identified as the culprit, the standard workaround on Windows was the obvious next step: leave the physical files where they were, create a directory junction (a Windows reparse point similar to a symlink) from a clean ASCII path like C:\project to the actual directory, and execute the builds through the junction. When tested against the Dart and Flutter toolchains, it worked. Commands executed cleanly through the alias. Then I ran the Android build pipeline, and AGP crashed immediately. The strangest part was the error output itself. AGP did not report C:\project—the junction path passed into the build invocation. Instead, it printed the underlying physical target path containing the Ö. The junction was meant to hide the non-ASCII character, but AGP exposed it anyway. The behavior points directly to how Java handles canonical path resolution. Java's File documentation describes canonical-path resolution as following symbolic links on UNIX platforms, though it doesn't explicitly detail Windows reparse points. I didn't audit AGP's internal source code, but the behavior I measured was unambiguous: AGP printed the physical target path instead of the junction path. To verify whether this behavior was tied to the Java build environment, I ran a counter-test using the exact same junction setup: flutter build web Executing flutter build web through the exact same junction, targeting the exact same physical directory, returned EXIT = 0. The web pipeline doesn't go through Gradle at all, so whatever the JVM does with reparse points never enters the picture — that's the difference I could point to, though I didn't instrument it. [ C:\project (ASCII Junction) ] | +-----------+-----------+ | | (Dart / Web) (JVM / AGP) | | Bypasses Reparse Reads Canonical Path | | v v Passes (EXIT 0) Exposes 'Ö' (EXIT 255) The hypothesis held from both directions: th
Hello, DEV Community
Ali Çimen
I'm a newly graduated developer currently focusing on Flutter and mobile app development. I've been spending a lot of time building projects with Flutter, experimenting with different approaches, and—like many developers today—using AI as part of my development workflow. But I don't want AI to simply write my code for me. I want to understand the code, question the solutions it gives me, fix the mistakes, and learn from the process. So I decided to start sharing that journey here on DEV. I'll be writing about: Flutter projects I'm building Things I'm learning along the way Bugs and problems I encounter How I use AI while developing Architecture and development decisions Lessons I learn from building real projects I'm still at the beginning of my professional journey, but I'm building, learning, and improving every day. Hopefully, some of the things I share will be useful to other developers as well. This is the beginning. Let's build. 🚀 flutter #dart #mobiledevelopment #ai #devjourney
Testing Payment Gateways in Flutter Without Real Money
Gulshan Yadav
So, in this article, I will be showing you how you can test payment gateways in your Flutter app without spending a single rupee — or dollar, or euro. Payment testing is the most anxiety-inducing part of building a checkout, and it should not be: every serious gateway ships a sandbox, and every Flutter project should ship a fake payment client for unit tests. Combine the two and you can test your entire payment flow — button tap, sheet, result handling, error paths — with zero real money involved. In my first payment integration, I tested with a real card in production mode. Never again. What I should have done from day one is this layered approach: sandbox modes for end-to-end flow, a fake client for unit and widget tests, and mocked HTTP for parsing tests. This article is that approach, written down so you do not repeat my mistake. Let's jump into the coding part. Every major gateway has a test environment, and they all work the same way: real API calls, real flows, no real charges. Gateway Sandbox Test card PayPal Sandbox API (api-m.sandbox.paypal.com) + test business/personal accounts N/A — sandbox accounts Stripe Test mode key (sk_test_...) 4242 4242 4242 4242 Razorpay Test mode key 4111 1111 1111 1111 Google Pay Environment.test in google_pay.json Any card in TEST Apple Pay Sandbox card in iOS Wallet settings 4242 4242 4242 4242 The golden rule: the sandbox uses your test API keys, never your live keys. Guard against the live key accidentally leaking into a test build — it is the single most common payment-testing disaster, and it is how people discover they charged a real card in a "test." The sandbox covers end-to-end flow, but it is slow, it is external, and it does not let you script failure. For unit and widget tests, inject a fake payment client behind an interface. First, define the abstraction your UI depends on: abstract class PaymentService { Future<PaymentResult> pay({required String itemId, required String amount}); } class PaymentResult { final bool success; final String? token; final String? error; const PaymentResult.success(this.token) : success = true, error = null; const PaymentResult.failure(this.error) : success = false, token = null; const PaymentResult.cancelled() : success = false, token = null, error = null; } The real implementation calls the gateway SDK (or your backend), while a fake implementation you control entirely: class FakePaymentService implements PaymentService { final bool shouldFail; const FakePaymentService({this.shouldFail = false}); @override Future<PaymentResult> pay({required String itemId, required String amount}) async { if (shouldFail) return const PaymentResult.failure('card_declined'); return PaymentResult.success('tok_fake_12345'); } } Now your widget receives the PaymentService via constructor injection, and your tests swap in the fake: class CheckoutPage extends StatelessWidget { final PaymentService paymentService; const CheckoutPage({super.key, required this.paymentService}); Future<void> _pay(BuildContext context) async { final result = await paymentService.pay(itemId: 'premium', amount: '9.99'); if (!context.mounted) return; if (result.success) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Payment successful')), ); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(result.error ?? 'Payment failed')), ); } } // build() ... } In your widget test: testWidgets('shows success on payment', (tester) async { final service = const FakePaymentService(); await tester.pumpWidget(MaterialApp(home: CheckoutPage(paymentService: service))); await tester.tap(find.text('Pay')); await tester.pump(); expect(find.text('Payment successful'), findsOneWidget); }); testWidgets('shows error when payment fails', (tester) async { final service = const FakePaymentService(shouldFail: true); // ... assert error snackbar appears }); This tests the UI and its error handling — the states that matter to users — without a network call, without a card, and instantly. Here is what the real implementation looks like against a fake, so you can see the interface in action. It calls your backend, which talks to the gateway — this is the production default because the app should never hold the gateway secret: class ApiPaymentService implements PaymentService { final http.Client _client; final String _baseUrl; const ApiPaymentService(this._client, this._baseUrl); @override Future<PaymentResult> pay({required String itemId, required String amount}) async { try { final res = await _client.post( Uri.parse('$_baseUrl/api/create-payment-intent'), body: {'itemId': itemId, 'amount': amount}, ).timeout(const Duration(seconds: 15)); if (res.statusCode == 200) { final token = jsonDecode(res.body)['clientSecret'] as String; return PaymentResu
Building Scalable Mobile Apps in 2026: Lessons from Top App Developers India
Charles Wade
There's a specific moment every mobile team eventually hits: the app works fine in testing, works fine for the first few thousand users, and then something quietly breaks once real traffic and real data volume show up. A screen that loaded instantly during the demo takes four seconds under production load. An API that seemed generously fast starts timing out during peak hours. None of this shows up in a code review it shows up three months after launch, usually on a Friday. Building an app that works and building an app that keeps working as users, data, and feature requests pile up are genuinely different engineering problems. The first is mostly about shipping. The second is about the decisions you made early stack, architecture, data flow that either give you room to grow or quietly box you in. Teams working across a wide range of client bases, including a fair number of app developers India has produced in large volume over the last decade, tend to run into the same scaling walls repeatedly, just with different product names attached. What follows is less a trend report and more a walk through the decisions that actually determine whether an app holds up. A few shifts by 2026 aren't just buzzwords they change how you architect things from day one. AI-assisted features stopped being a differentiator and became closer to a baseline expectation for a lot of product categories search that understands intent instead of exact keyword matches, recommendations that adjust as behavior changes, assistants that can act on structured intent rather than just chatting. On-device intelligence has become a real architectural option too, not a novelty, letting certain tasks run without a network round trip or a per-request cost. Cross-platform tooling has matured to the point where the "native vs cross-platform" debate is less binary than it used to be the honest answer now usually depends on the specific feature, not a blanket policy for the whole app. API-first architecture and cloud-native infrastructure have become close to default assumptions rather than aspirational goals, and users have gotten far less tolerant of slow load times or janky real-time updates than they were even three or four years ago. Security and privacy requirements have tightened too, partly from regulation and partly from users simply expecting better data handling than they used to accept. None of these changes individual features so much as they change what "done" means for a mobile engineering team. A feature that technically works but doesn't hold up under real load, poor connectivity, or scrutiny from a privacy-conscious user isn't really done. Stack debates online tend to be more tribal than useful. The honest version is that Flutter, React Native, and fully native development each solve a different problem well, and the right choice depends on what you're actually optimizing for. Flutter gives you a single codebase with genuinely consistent UI rendering across iOS and Android, since it draws its own widgets rather than relying on native components useful when brand consistency and development speed across both platforms matter more than squeezing out the last bit of native performance. React Native leans more on native components through its bridge (or the newer architecture, which narrows the performance gap further), and tends to suit teams already comfortable in the JavaScript/TypeScript ecosystem who want to share logic without fully committing to Flutter's own rendering model. Native development Swift on iOS, Kotlin on Android still wins when an app needs deep platform integration, heavy real-time processing, complex animation, or first-day access to whatever the platform vendor ships next. It costs more in team size and coordination, since you're maintaining two codebases instead of one, but that cost buys you a ceiling cross-platform frameworks don't fully match. On the backend side, Node.js remains a common choice for API-first mobile backends because of how naturally it handles asynchronous, I/O-heavy workloads though plenty of production systems run just as well on other stacks depending on team expertise. REST APIs are still the default for most mobile-backend communication, simple and well understood. GraphQL earns its place specifically when a mobile client needs to fetch varied, nested data shapes without either over-fetching or making a dozen sequential REST calls it's not a universal upgrade, it's a fit for a specific data-shape problem. None of these choices is right or wrong in isolation. They're right or wrong relative to team expertise, timeline, and what the product actually needs to do a lesson that tends to get relearned the expensive way when a team picks a stack because it was trending rather than because it fit. Architecture decisions matter more than framework decisions for long-term scalability, and they're also where teams tend to under-invest early because the payoff isn't visible until much later. Modula
A Claude Code skill fixed my app's UI — here's what broke and how to use it yourself
Tony Stark
My app worked. It just looked like three different apps stitched together. BrandMeld (Flutter, AI brand generator) had grown screen by screen: some had plain AppBars, some had gradient heroes, settings was a gray ListTile dump, spacing was random, and the "create brand" form asked for 8 fields before you could do anything. Every screen was fine in isolation and inconsistent as a whole. Instead of hand-fixing 14 screens, I installed a Claude Code skill built for mobile UI/UX and pointed it at them. This post is the concrete before/after — the actual problems it fixed — plus how you install and use it. 1. No shared header language. Every screen reinvented its top bar. The skill replaced them with one full-width gradient header pattern (back button + actions merged in), so the app reads as one product. 2. A settings screen that was a wall of gray rows. Became iOS-style grouped cards with tinted icon badges — scannable in a glance. 3. An 8-field create form. Reworked to name-only required, with the rest behind an "Add more details" expander. Then it added a smart default I didn't ask for: pick a preset industry → target audience + keywords auto-fill (without overwriting anything you typed). Cognitive load dropped from "fill a form" to "type a name." 4. A dead-end paywall. "Your trial ended." → a card that lists what you lose access to (names, palettes, logos, brand guides, saved brands) with lock icons. Loss aversion instead of a shrug. 5. Actual rendering bugs — caught from screenshots. This surprised me. I'd send a screenshot and it diagnosed things code review misses: a selected card's border clipped at the top corners (border + Clip.antiAlias on the same container), an avatar hidden behind a floating stats card (header too short, card overlapped it), weird blur blobs behind the header (translucent circles rendering as hard-edged shapes). The screenshot is the debugger. "Redesign this" gives you noise. The skill encodes a rubric the model follows every time: 8-pt spacing grid 60/30/10 color system named patterns (floating stat strips, grouped cards, gradient headers) That's the difference between one-off prettiness and a consistent design system. A skill is just a folder with a SKILL.md. Drop it in: Project: .claude/skills/<name>/SKILL.md Personal (all projects): ~/.claude/skills/<name>/SKILL.md Clone the repo into that folder, restart Claude Code, done. Point it at a screen: "Redesign the settings screen. Apply the design principles." Then iterate with screenshots: "The avatar's hidden — fix it." It keeps your logic/routing and rewrites the layout. You review UI, not re-test features. The real lesson: you can package any discipline into a skill. A SKILL.md is tiny: markdown --- name: my-skill description: "What it does AND when to use it. This is how Claude decides to load it." --- # Instructions The rules/steps Claude should follow when this runs. Encode your conventions — API patterns, test style, commit format — once, and get consistent output forever. Takeaway The win isn't "AI writes UI." It's that a rubric-in-a-file tura cohesive one in an afternoon, and caught rendering bugs fromscreenshots along the way. Skill (clone + drop into .claude/skills/): https://github.com/ceorkm/mobile-app-ui-design If you try it, send me a before/after — those are the fun ones.
Building Enterprise Active Directory, LDAP & Dynamic RBAC in Go & Flutter with Google Antigravity
Mario Ezquerro
Building Enterprise Active Directory, LDAP & Dynamic RBAC in Go & Flutter with Google Antigravity When building a lightweight container orchestrator like Gubernator (gbnt) — designed to strike the perfect balance between the simplicity of Docker Swarm and the flexibility of Nomad under a Roman Empire theme — a critical milestone inevitably emerges: Enterprise Security and Access Control. While a default admin credential works well for local dev environments, moving into enterprise production with multi-disciplinary engineering teams demands: Corporate Single Sign-On (SSO) with Microsoft Active Directory and OpenLDAP. Role-Based Access Control (RBAC) to clearly segregate who can deploy stacks, restart containers, or audit telemetries in read-only mode. Dynamic Group Mapping from corporate security groups (memberOf) to orchestrator roles. Emergency Break-Glass Access (Local Administrator) in case network directory controllers are unreachable. In this article, we explore the complete architecture of the enterprise security engine introduced in Gubernator v2.20.0, and how we leveraged Google Antigravity (AGY) as an autonomous AI pair programmer to design, implement, test, and verify this Full-Stack feature (Go + Flutter Web) across a live 3-node cluster. We designed a decoupled, asymmetric architecture connecting identity providers, REST API middleware, and the Flutter Web UI: ┌────────────────────────────────────────────────────────┐ │ GUBERNATOR WEB UI │ │ - Modern Login Screen with Domain / AD Selector │ │ - Header Role Badge: Admin | Ops | Read-Only. │ └──────────────────────────┬─────────────────────────────┘ │ (REST /api/auth/login) ▼ ┌────────────────────────────────────────────────────────┐ │ GUBERNATOR CORE AUTH ENGINE (Go) │ │ - Local Emergency Admin (admin / admin fallback) │ │ - Multi-Server Active Directory / OpenLDAP Dialers │ │ - LDAPS (Port 636) & StartTLS (Port 389) Handshake │ │ - Dynamic Group DN -> RBAC Role Resolution │ │ - Cryptographic HMAC-SHA256 JWT Token Signing │ └─────────────┬────────────────────────────┬─────────────┘ │ │ ▼ ▼ ┌───────────────────────────┐ ┌──────────────────────────┐ │ Primary Active Directory │ │ Secondary LDAP Server │ │ dc1.corporate.local │ │ dc2.dr-site.local │ └───────────────────────────┘ └──────────────────────────┘ We established three distinct operational tiers: Operational Capability admin operator readonly Overview, Metrics & SRE Telemetry ✅ Full ✅ Full ✅ Full Deploy Stacks (docker-compose.yml) ✅ Full ✅ Full ❌ Restricted Redeploy & Duplicate Stacks ✅ Full ✅ Full ❌ Restricted Delete Stacks ✅ Full ❌ Restricted ❌ Restricted Task Lifecycle (Start / Stop / Restart) ✅ Full ✅ Full ❌ Restricted Container & Node Terminal Shell ✅ Full ✅ Full ❌ Restricted Node Fleet Management (Drain / Activate / Leave) ✅ Full ❌ Restricted ❌ Restricted Caddy TLS Certificates & Ingress Routes ✅ Full ❌ Restricted ❌ Restricted Active Directory & LDAP Directory Settings ✅ Full ❌ Restricted ❌ Restricted Grafana, Jaeger & Weave Scope Dashboards ✅ Full ✅ Full ✅ Full internal/auth/) For LDAP/Active Directory interactions, we used github.com/go-ldap/ldap/v3, and for session management github.com/golang-jwt/jwt/v5. Authentication follows a secure two-phase pattern: Connect and perform a Service Account Bind (BindDN / BindPassword) to query the directory. Search for the user object using a configurable LDAP filter (defaulting to (&(objectClass=user)(sAMAccountName=%s))). Open a secondary connection and perform a Direct User Bind with the user-submitted password against the domain controller. func AuthenticateLDAP(cfg db.LDAPConfig, username, password string) (*AuthResult, error) { conn, err := ConnectLDAP(cfg) if err != nil { return nil, err } defer conn.Close() // 1. Initial service account bind if cfg.BindDN != "" && cfg.BindPassword != "" { if err := conn.Bind(cfg.BindDN, cfg.BindPassword); err != nil { return nil, fmt.Errorf("service account bind failed: %w", err) } } // 2. Search for the user filter := fmt.Sprintf(cfg.UserFilter, ldap.EscapeFilter(username)) searchReq := ldap.NewSearchRequest( cfg.BaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, filter, []string{"dn", "displayName", "mail", "memberOf"}, nil, ) sr, err := conn.Search(searchReq) if err != nil || len(sr.Entries) == 0 { return nil, errors.New("user not found in directory") } userEntry := sr.Entries[0] // 3. Direct user bind to verify password userConn, err := ConnectLDAP(cfg) if err != nil { return nil, err } defer u
Building an AI Assistant: flutter nodejs ai browser automation
Umair Bilal
This article was originally published on BuildZn. Figured out how to tame web automation with an LLM. Everyone talks about AI agents, but getting them to reliably interact with dynamic web pages through a full-stack setup is a whole different beast. Spent weeks wrestling with flaky selectors and race conditions. Here's the blueprint that finally clicked for my personal AI assistant build using Flutter, Node.js, and browser automation. This setup slashed my daily busywork by a solid 60%. My initial goal was simple: stop wasting time on repetitive online tasks. Think filling out expense reports, aggregating data from specific sites, or managing content on platforms without proper APIs. I needed a personal AI assistant build that could understand high-level commands, translate them into browser actions, and then report back. This isn't just about scripting; it's about an LLM making decisions based on current page state and a broader goal. I looked at a few options. Pure Python? Nah, I'm a Flutter guy, wanted a native UI. JavaScript-only? Possible, but I prefer Node.js for backend heavy lifting and orchestration. So, the stack solidified: Flutter: For the cross-platform UI. Desktop support for Windows/macOS was key for a desktop assistant. This gives us flutter desktop automation capabilities on the client side. Node.js: The brain. This is where the AI agent logic lives, handles API calls to LLMs, and orchestrates Playwright. Essentially, our nodejs playwright agent server. Playwright: The hands. Robust, fast, and handles modern web elements way better than Puppeteer for my needs. The core challenge? Bridging the LLM's high-level reasoning with the nitty-gritty of browser interactions. Getting an AI to decide "click this specific button" or "fill this form field" when the page layout changes, or elements appear dynamically, that's where the real work is. Here's the setup, simplified: Flutter UI: User sends a command (e.g., "Summarize unread emails from Project X in Gmail"). Node.js Backend (API): Receives the command. Initial LLM Call (Planner): The backend sends the command to an LLM (e.g., Claude 3.5 Sonnet, or OpenAI's GPT-4o). This "Planner" LLM identifies the initial high-level steps. For Gmail, it might be "1. Navigate to Gmail. 2. Log in. 3. Find unread emails. 4. Filter for Project X. 5. Extract summaries." Action Executor Loop: Node.js initializes Playwright. For each step from the Planner, Node.js tells Playwright to perform an action (e.g., await page.goto('https://gmail.com')). Observation/Reflection (LLM Call - Actuator): After each action, Node.js grabs the current page content (or specific elements). This observation, along with the overall goal and previous steps, is sent back to the LLM. The "Actuator" LLM's job is to decide the next precise browser action (e.g., click on [aria-label="Email address"], fill with myemail@gmail.com, press 'Enter'). This loop continues until the overall goal is met or an error occurs. Result Reporting: Once the task is done, the extracted data or status is sent back to the Flutter UI. Key Components & Their Roles: Flutter (Client): Sends user intents via HTTP requests to Node.js. Displays real-time status updates and final results. Provides a simple UI for configuration and task management. It's truly a cross platform ai assistant client. Node.js (Backend/Agent Orchestrator): Express API: Handles requests from Flutter. LLM Integration: Uses @anthropic-ai/sdk or openai libraries. I used Claude 3.5 Sonnet for its cost-effectiveness and context window. Playwright: @playwright/test for browser control. Task Management: Simple state machine to track ongoing browser sessions and agent steps. Let's get into the code. On the Flutter side, it's pretty standard HTTP stuff. // lib/services/ai_service.dart import 'dart:convert'; import 'package:http/http.dart' as http; class AIService { final String baseUrl = 'http://localhost:3000/api/agent'; // Your Node.js backend Future<String> runBrowserTask(String taskDescription) async { try { final response = await http.post( Uri.parse('$baseUrl/start'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'task': taskDescription}), ); if (response.statusCode == 200) { final data = jsonDecode(response.body); return data['result'] ?? 'Task completed.'; } else { return 'Error: ${response.statusCode} - ${response.body}'; } } catch (e) { return 'Network error: $e'; } } } // In your Flutter widget: // import 'package:your_app/services/ai_service.dart'; // final aiService = AIService(); // String result = await aiService.runBrowserTask("Go to Google, search 'FarahGPT', click first link, tell me the title."); // print(result); This just kicks off the task. The real magic happens on Node.js. First, set up a basic Express server. // server.js const express = require('express'); const { chromiu
Why BlocSignal Doesn't Need Provider (And Why Classic BLoC Always Did)
Randal L. Schwartz
How shedding package:provider eliminates dependency hell, fixes Flutter's lingering ghost rebuild bug, and delivers fine-grained synchronous reactivity in 2026. If you browse r/FlutterDev on any given week, you will find the exact same architectural debate playing out: "Should I use BLoC or Riverpod for my next production app? BLoC has great structure and discipline, but the stream boilerplate is overwhelming. Riverpod is reactive and flexible, but the @riverpod code generation and constant version transitions make it feel heavyweight." And inevitably, someone in the comments will chime in: "I just stick with plain package:provider because it's simple and doesn't require code-gen." This trilemma—BLoC vs. Riverpod vs. Provider—has defined Flutter state management for over six years. But behind this debate lies a little-known architectural secret that explains why Flutter state management felt so fractured in the first place: Classic flutter_bloc was secretly just package:provider in disguise. Let's look at why classic BLoC relied on package:provider, the hidden runtime bugs and dependency deadlocks that came with it, why Riverpod had to break away, and how BlocSignal delivers the ultimate resolution: zero provider, zero streams, and zero code generation. When developers think of Felix Angelov’s classic flutter_bloc, they think of Streams, Sinks, and unidirectional event architectures. But if you open flutter_bloc/pubspec.yaml, you'll find a foundational dependency: dependencies: bloc: ^8.1.4 provider: ^6.0.5 # 👈 The hidden foundation! In classic flutter_bloc: BlocProvider<T> is literally an extension of package:provider's InheritedProvider. MultiBlocProvider is just a thin alias over MultiProvider. RepositoryProvider is literally Provider<T>. Back in 2018–2019, writing custom InheritedWidget plumbing in Flutter was verbose and error-prone. Rémi Rousselet’s package:provider was the newly crowned Google-recommended solution for dependency injection and widget tree scoping. Building flutter_bloc on top of package:provider allowed BLoC to focus on its stream state machine while outsourcing widget tree scoping, lazy instantiation, and disposal to Provider. It seemed like a great shortcut. But over time, coupling BLoC to package:provider introduced two massive architectural headaches. [ Your Application ] ──► [ flutter_bloc ] │ └──► [ package:provider ] ──► [ Transitive Version Lock ] Because package:provider is one of the most widely used packages in the Flutter ecosystem, major version updates (such as migrating from v4 to v5 to v6 for null safety) created widespread dependency deadlocks: Because my_app depends on: - legacy_auth_plugin ^2.1.0 (which depends on provider ^5.0.0) - flutter_bloc ^8.0.0 (which depends on provider ^6.0.5) Version solving failed: Cannot solve dependencies because provider ^5.0.0 is incompatible with provider ^6.0.5! Every Flutter developer has experienced this nightmare: You couldn't upgrade flutter_bloc because an analytics or payment SDK pinned an older provider. Teams were forced to use risky dependency_overrides: in pubspec.yaml and pray that internal breaking changes wouldn't crash production builds. Engineers had to fork third-party repositories just to bump a provider constraint. This is the deepest, most subtle flaw in Flutter's InheritedWidget system—and it was the primary catalyst that drove Rémi Rousselet to abandon Provider and create Riverpod. When an Element calls context.watch<T>() or Provider.of<T>(context), Flutter registers that Element as a dependent of the ancestor InheritedWidget. The fatal catch: Flutter’s engine never unregisters an element from an InheritedWidget on subsequent builds! Dependencies are only cleared when the widget is completely unmounted. Consider this common conditional UI pattern: // 👴 The Classic Provider Ghost Rebuild Trap: Widget build(BuildContext context) { if (isExpanded) { // 🚩 Registers a permanent dependency on DetailsModel final details = Provider.of<DetailsModel>(context); return FullDetailsCard(details); } else { // 👻 GHOST REBUILD: Even when collapsed, this widget STILL rebuilds // on every single change to DetailsModel forever! return const CompactSummaryCard(); } } Once isExpanded is true even once, Flutter permanently binds DetailsModel to that widget. When the card collapses, it continues to rebuild on every DetailsModel emission indefinitely, wasting CPU cycles, battery, and rendering frames on state it isn't even displaying! Rémi recognized that Flutter's InheritedWidget and BuildContext had fundamental limitations that could not be fixed within package:provider: You couldn't easily read state outside the widget tree (for example, in background services or pure Dart logic). The lingering dependency bug caused unavoidable ghost rebuilds on conditional branches. Combining two providers required ugly nested widget hierarch
Offline-First Flutter: Syncing Local and Remote Data Reliably
Bimal Kshetri
Building an offline-first Flutter app is less about caching the network and more about flipping the dependency: the local database becomes your source of truth, and the server becomes a replica you reconcile with later. I've shipped this pattern across several production iOS and Android apps, and the ones that treated the network as optional from day one were dramatically more reliable than the ones that bolted on caching after launch. This is a practical, example-driven guide for intermediate Flutter developers. We'll cover the architecture, picking a Flutter local database, designing a durable sync queue, choosing a conflict resolution strategy, and — the part most tutorials skip — handling the failures that actually happen in the field. The phrase gets thrown around loosely, so let me be precise. An offline-first app has three non-negotiable properties: The UI never blocks on the network. Every read and write hits local storage and returns immediately. Writes are durable before they're sent. A user's edit survives an app kill, a crash, or a dead connection because it's persisted locally first. Sync is a background reconciliation process, not part of the user's interaction loop. The mental model I use: the user only ever talks to the local store. A separate sync engine watches a queue of pending changes and negotiates with the server whenever it can. The two are decoupled. If you remember nothing else from this article, remember that the write path and the sync path must not be the same code path. UI ──► Local DB (source of truth) ──► Sync queue ──► Remote API ▲ │ │ └── streams ─┘ reconcile ◄───────┘ Your local store is the foundation, so pick deliberately. For an offline-first design you want reactive queries (so the UI rebuilds when local data changes) and real transactions (so a write and its queue entry commit atomically). The two I reach for are Drift and Isar. Option Model Reactive queries Transactions Best when Drift Relational (SQLite) Yes (watch()) Strong, SQL Relational data, complex queries, migrations matter Isar NoSQL object store Yes (watch()) Yes Object graphs, very high read throughput, simple schema sqflite Raw SQLite No (manual) Yes You want full control and don't mind wiring reactivity yourself Hive Key-value Limited No Simple settings/blobs, not a sync backbone For anything with relationships and a sync queue, I default to Drift: SQL transactions let me write the row and enqueue the mutation in one atomic step, and watch() gives me a Stream the UI subscribes to. If your data is a big object graph and you care more about raw speed than joins, Isar is excellent. Avoid Hive as a sync backbone — its lack of real transactions will bite you. Here's a minimal Drift schema with the two tables every offline-first app needs — the domain table and an outbox for pending mutations: import 'package:drift/drift.dart'; class Todos extends Table { TextColumn get id => text()(); // client-generated UUID TextColumn get title => text()(); BoolColumn get done => boolean().withDefault(const Constant(false))(); IntColumn get updatedAt => integer()(); // epoch millis, our version clock BoolColumn get deleted => boolean().withDefault(const Constant(false))(); @override Set<Column> get primaryKey => {id}; } class Outbox extends Table { IntColumn get seq => integer().autoIncrement()(); TextColumn get entity => text()(); // e.g. 'todo' TextColumn get entityId => text()(); TextColumn get op => text()(); // 'upsert' | 'delete' TextColumn get payload => text()(); // JSON snapshot IntColumn get attempts => integer().withDefault(const Constant(0))(); } Two details matter here. First, IDs are generated on the client (a UUID), not the server. This lets a user create records offline that already have stable identity. Second, updatedAt doubles as a logical version clock for conflict detection later. The outbox pattern is the heart of a reliable sync engine. Instead of firing an HTTP request when the user edits something, you write the change to local storage and append a record to the outbox table — atomically, in one transaction. A background worker drains the outbox. The atomicity is the whole point. If you write the row but the app dies before you enqueue the sync job, the server never hears about it. A single transaction makes "data changed" and "needs sync" inseparable. Future<void> upsertTodo(Todo todo) async { final next = todo.copyWith(updatedAt: DateTime.now().millisecondsSinceEpoch); await db.transaction(() async { // toCompanion(true) maps nulls to Value.absent() rather than explicit null. await db.into(db.todos).insertOnConflictUpdate(next.toCompanion(true)); await db.into(db.outbox).insert(OutboxCompanion.insert( entity: 'todo', entityId: next.id, op: 'upsert', payload: jsonEncode(next.toJson()), )); }); } The UI ca
Flutter CI/CD with GitHub Actions and Fastlane: A Real Pipeline
Bimal Kshetri
Flutter CI/CD stops being optional the moment you ship to two stores from one codebase and a manual release eats half a day. In this post I walk through the exact pipeline I run on production apps: GitHub Actions runs flutter analyze and tests on every pull request, then — only when I push a version tag — it builds signed iOS and Android artifacts and uploads them to TestFlight and the Google Play internal track. No clicking through Xcode Organizer, no dragging .aab files into a browser. I'll cover the workflow YAML, the Fastlane lanes that do the store uploads, and the part everyone gets wrong the first time: getting signing material — the Android keystore and the iOS distribution certificate — into CI without leaking it. The single most useful decision in a Flutter CI/CD setup is separating two concerns: CI (every PR): fast, cheap, runs on Linux, gates merges. Format check, flutter analyze, unit and widget tests. This should finish in a couple of minutes. CD (on a tag): slow, expensive (macOS minutes for iOS), produces signed builds, talks to the stores. You only want this when you actually intend to release. Tying release to a Git tag like v1.4.0 gives you a clean, auditable trigger. The tag is the release record. Pushing to main shouldn't ship anything — that's how you end up with surprise TestFlight builds at 2am. Trigger Runs on What it does Cost Pull request ubuntu-latest format, analyze, test Low / fast Tag v*.*.* (Android) ubuntu-latest signed .aab to Play internal Low Tag v*.*.* (iOS) macos-latest signed .ipa to TestFlight High (macOS minutes) Keep iOS on its own job. macOS runners bill at roughly ten times the per-minute rate of Linux runners on GitHub-hosted machines, so you don't want every PR burning them. Here's the lint-and-test half of the workflow. It pins the Flutter version (never rely on latest — a Dart SDK bump can break your build on an unrelated PR) and caches pub dependencies so reruns are quick. name: ci on: pull_request: branches: [main] jobs: analyze-and-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: subosito/flutter-action@v2 with: flutter-version: "3.27.1" channel: stable cache: true - name: Install dependencies run: flutter pub get - name: Verify formatting run: dart format --output=none --set-exit-if-changed . - name: Analyze run: flutter analyze --fatal-infos - name: Run tests run: flutter test --coverage A few things I insist on: dart format --set-exit-if-changed fails the build on unformatted code. It ends the "whitespace-only diff" wars permanently. --fatal-infos treats analyzer infos as failures. If your analysis_options.yaml flags something, the PR should be red. Half-honored lint rules are worse than none. cache: true on flutter-action caches the SDK and the pub cache between runs, which shaves a minute or more off most runs. If you run integration tests or golden tests, gate the expensive ones behind a separate job or a label so day-to-day PRs stay fast. flutter build produces the artifact; Fastlane handles delivery — uploading that artifact to the right track with the right credentials. You keep two fastlane folders, one under android/ and one under ios/, each with its own Fastfile. For Android you authenticate with a Google Cloud service account JSON that has been granted access in the Play Console. The upload_to_play_store action (the supply integration) does the upload. # android/fastlane/Fastfile default_platform(:android) platform :android do desc "Upload a signed AAB to the Play Store internal track" lane :internal do upload_to_play_store( track: "internal", aab: "../build/app/outputs/bundle/release/app-release.aab", json_key_data: ENV["PLAY_STORE_SERVICE_ACCOUNT_JSON"], release_status: "draft", skip_upload_apk: true, skip_upload_metadata: true, skip_upload_images: true, skip_upload_screenshots: true ) end end I pass the service-account JSON as raw data via json_key_data (read from an env var) rather than a file on disk, so nothing sensitive is ever written to the runner's filesystem. release_status: "draft" leaves a human to do the final promote — automate the upload, keep the publish deliberate. For iOS, upload_to_testflight (Pilot) talks to App Store Connect. Use an App Store Connect API key, not your Apple ID and an app-specific password — API keys don't trip two-factor auth and don't break the moment Apple decides your session looks suspicious. # ios/fastlane/Fastfile default_platform(:ios) platform :ios do desc "Upload the signed IPA to TestFlight" lane :beta do app_store_connect_api_key( key_id: ENV["ASC_KEY_ID"], issuer_id: ENV["ASC_ISSUER_ID"], key_content: ENV["ASC_KEY_CONTENT"], # base64-encoded .p8 is_key_content_base64: true ) upload_to_testflight( ipa: "../build/ios/ipa/R
Clean Architecture in Flutter with BLoC: A Practical Guide
Bimal Kshetri
Clean architecture in Flutter is the single biggest reason the production apps I ship stay maintainable after a year of feature churn. Over 4+ years building iOS and Android apps, I've watched "just put the logic in the widget" turn setState spaghetti into a codebase nobody wants to touch. This guide walks through how I actually split a Flutter app into domain, data, and presentation layers with BLoC — using one concrete feature so you can copy the structure into your own project today. I'll build a small "Todos" feature end to end: an entity, a use case, a repository with a DTO mapper, and a Cubit that drives the UI. The point isn't the todo list — it's the boundaries between layers and why each one earns its keep. The core idea is the dependency rule: source-code dependencies point inward. The UI knows about the domain; the domain knows about nothing. Your business rules never import Flutter, Firebase, Dio, or Supabase. That inversion buys three things I care about on every project: Testability. Domain logic runs in plain Dart unit tests — no widget pump, no emulator, no network. Swappable infrastructure. Move from REST to GraphQL, or Firestore to a local SQLite cache, by rewriting one data-layer class. The domain and UI don't change. Parallel work. Once the domain contract exists, one person builds the API client while another builds the screen against a fake. Here's the layer breakdown I use, and what's allowed to live in each: Layer Knows about Contains Depends on Domain Nothing external Entities, repository interfaces, use cases Pure Dart only Data Domain + the outside world DTOs, mappers, repository implementations, data sources Domain Presentation Domain Blocs/Cubits, states, widgets Domain Notice the data and presentation layers both depend on domain, and domain depends on neither. That's the whole game. I organise by feature first, then by layer. A flat models/, services/, screens/ split looks tidy on day one and becomes a scavenger hunt by feature five. Feature-first keeps everything you touch for one change in one place. lib/ features/ todos/ domain/ entities/todo.dart repositories/todo_repository.dart # abstract usecases/get_todos.dart data/ models/todo_dto.dart # JSON -> DTO + mapper datasources/todo_remote_data_source.dart repositories/todo_repository_impl.dart presentation/ cubit/todos_cubit.dart cubit/todos_state.dart pages/todos_page.dart core/ error/failures.dart usecases/usecase.dart The core/ directory holds cross-feature primitives — failure types, a base UseCase contract, shared extensions. Everything feature-specific stays inside its feature folder. The domain is pure Dart. No package:flutter, no JSON, no annotations. Start with the entity — the shape your app reasons about, not the shape the API returns. // domain/entities/todo.dart import 'package:equatable/equatable.dart'; class Todo extends Equatable { const Todo({ required this.id, required this.title, required this.isCompleted, }); final String id; final String title; final bool isCompleted; @override List<Object?> get props => [id, title, isCompleted]; } I extend Equatable so two todos with the same fields compare equal — that makes Cubit state comparisons and tests painless. Next, the repository interface. This is the contract the domain demands from the outside world. It lives in domain, but it's implemented in data. That's the dependency inversion that keeps Firebase or Dio out of your business rules. // domain/repositories/todo_repository.dart import 'package:dartz/dartz.dart'; import '../../../../core/error/failures.dart'; import '../entities/todo.dart'; abstract interface class TodoRepository { Future<Either<Failure, List<Todo>>> getTodos(); Future<Either<Failure, Todo>> toggle(String id); } I return Either<Failure, T> from dartz instead of throwing across layers. Failures become values you must handle, not exceptions that silently bubble into the UI. Failure is a sealed type in core/: // core/error/failures.dart sealed class Failure { const Failure(this.message); final String message; } class ServerFailure extends Failure { const ServerFailure([super.message = 'Something went wrong']); } class NetworkFailure extends Failure { const NetworkFailure([super.message = 'No internet connection']); } Now the use case. A use case is one application action with a single public method. It reads almost like a sentence: get todos. This is where orchestration lives — call a repository, maybe combine two, apply a business rule — without the Cubit ever knowing how data is fetched. // core/usecases/usecase.dart import 'package:dartz/dartz.dart'; import '../error/failures.dart'; abstract interface class UseCase<Type, Params> { Future<Either<Failure, Type>> call(Params params); } class NoParams { const NoParams(); } // domain/usecases/get_todos.dart
Apple Pay in Flutter: The Easiest Implementation
Gulshan Yadav
So, in this article, I will be showing you how you can integrate Apple Pay into your Flutter app — and yes, this one is genuinely the easiest of the mobile wallets, because Apple has wrapped the entire flow into a native payment sheet. No card form, no bank list, no OTP. The user double-clicks the side button, confirms with Face ID, and the payment token is out. The reason Apple Pay integration is easy is that Apple does almost everything for you: the card vault, the biometrics, the tokenization, the UI. What is not easy — and what stops most people for a full day — is the Apple Developer setup on the way in. This article covers both: the configuration you have to get right before Flutter, and the minimal Dart you need after. Let's jump into the coding part. For this purpose, we need to add this dependency in your pubspec.yaml file: dependencies: flutter: sdk: flutter pay_ios: ^1.0.0 pay_ios is Apple's official Flutter plugin for Apple Pay (part of the flutter-pay-plugins). It wraps PassKit's PKPaymentAuthorizationViewController, so you never touch Swift. That single package is the whole dependency story — one line, no extra UI packages, no separate button package. Before you write a single line of Dart, three things must exist on the Apple side, or your payment sheet will silently refuse to show: A Merchant ID. In the Apple Developer portal, go to Certificates, Identifiers & Profiles → Identifiers, create a Merchant ID like merchant.com.yourcompany.yourname. This is the ID your code references, and it must be enabled for your App ID. The Apple Pay capability. In Xcode, open the Runner target → Signing & Capabilities → add "Apple Pay" and select your merchant ID. If you use flutter build, verify this in the generated Xcode project before building the app. A Payment Processing Certificate. In the Merchant ID settings, create a merchant identity certificate. Apple uses this to encrypt the payment token. If you use a payment provider (Stripe, Adyen, Braintree), they generate this certificate for you; otherwise you create a CSR from Apple. The classic failure: everything works in code, and the payment sheet says "Apple Pay is not available." Nine times out of ten it is the entitlement or the merchant ID mismatch, not your Dart. Here is the entire Flutter side. Create a checkout page and present the Apple Pay sheet: import 'package:flutter/material.dart'; import 'package:pay_ios/pay_ios.dart'; class CheckoutPage extends StatefulWidget { const CheckoutPage({super.key}); @override State<CheckoutPage> createState() => _CheckoutPageState(); } class _CheckoutPageState extends State<CheckoutPage> { late final ApplePayClient _client; @override void initState() { super.initState(); _client = ApplePayClient( paymentConfiguration: PaymentConfiguration.fromJsonString( ''' { "merchantId": "merchant.com.yourcompany.yourname", "merchantName": "Your App Name", "countryCode": "US", "currencyCode": "USD" } ''', ), ); } Future<void> _payWithApplePay() async { try { final result = await _client.presentApplePay( displayItems: const [ ApplePayItem( label: 'Premium Plan', amount: '9.99', type: ApplePayItemType.final_, ), ], merchantCapabilities: const [ MerchantCapability.threeDSecure, ], supportedNetworks: const [ ApplePayCardNetwork.visa, ApplePayCardNetwork.mastercard, ApplePayCardNetwork.amex, ApplePayCardNetwork.discover, ], requiredBillingContactFields: const [ ApplePayContactField.postalAddress, ], requiredShippingContactFields: const [ ApplePayContactField.email, ApplePayContactField.phone, ], ); if (result is ApplePayResult.success) { // Send result.token to YOUR backend for verification. await _verifyOnServer(result.token); } else if (result is ApplePayResult.canceled) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Payment cancelled')), ); } } catch (e) { debugPrint('Apple Pay error: $e'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Checkout')), body: Center( child: ElevatedButton( onPressed: _payWithApplePay, child: const Text('Pay with Apple Pay'), ), ), ); } } That is the whole implementation. presentApplePay launches the native sheet, the user confirms with Face ID or Touch ID, and you receive an ApplePayResult with the token. Same rule as every wallet integration, and it is non-negotiable: the device gives you a token, not money. Send it to your backend, and let your backend decrypt and charge it through your payment provider. On the backe
freeCodeCamp
15 条教程、指南与实践文章
How to Automate Flutter Releases with Fastlane and GitHub Actions for Firebase App Distribution, Google Play, TestFlight, and App Store Connect
Atuoha Anthony
Picture this: it's 4pm on a Friday, and your team has just merged the last feature for the sprint. But your product manager asks for a new build on TestFlight by the end of the day so the client can r
Flutter Frontend Systems Design: How to Think Like a Senior Engineer in the AI Age
Jesutoni Aderibigbe
Systems design has always been treated as a backend problem. Ask a group of Flutter engineers what systems design means, and most will describe server architecture: load balancers, databases, and micr
How to Test AI Features in Flutter [Full Handbook]
Atuoha Anthony
You've spent two weeks building an AI assistant. The streaming chat looks beautiful, the system prompt is tight, and safety filters are configured. You demoed it to the team, and everyone was impresse
A Deep Dive into Behavioral Patterns: The Visitor Design Pattern and its Clean Operations Across Complex Object Structures
Oluwaseyi Fatunmole
There's a problem that shows up in almost every growing software system, and most developers don't even realize they're hitting it until the damage is already done. You have a set of objects: differen
Bluetooth Low Energy in Flutter: A Handbook for Devs
Nikheel Vishwas Savant
Most Flutter tutorials stop at network calls and REST APIs. The moment you need to talk to a physical device, a heart rate monitor, a smart bulb, a fitness tracker, an industrial sensor, or your own c
From RPC to gRPC: Understanding Remote Procedure Calls, Protocol Buffers, and Modern Distributed Systems Communication
Oluwaseyi Fatunmole
Every application, at some point, needs to talk to another system. A mobile app talks to a backend. A backend service talks to a payment gateway. An authentication service talks to a user service. A d
The Observer Design Pattern Handbook: Event-Driven Architecture & Domain-Driven Design in Dart
Oluwaseyi Fatunmole
Every application, at some point, has to deal with a fundamental challenge: something happens, and several other things need to react to it. A user logs in, and the app needs to save a token, cache th
How to Fix App Jank: A Practical Guide to Profiling Flutter Apps with DevTools
Gidudu Nicholas
Flutter makes it fast to build beautiful UIs. That speed is one of the framework's greatest strengths, but it also creates a subtle problem: performance issues are easy to introduce and difficult to f
How to Use Claude Code to Build Flutter Apps Faster — Best Practices for 2026
Jesutoni Aderibigbe
In early 2023, I was interning at a US-based company, long before agentic AI became part of everyday development. We had tools like ChatGPT, Gemini, and Copilot, but they were mostly chat interfaces:
Advanced Dart: Learn Asynchronous Programming with Streams, Isolates, and the Event Loop
Gidudu Nicholas
I had been writing Flutter apps for over a year before I actually understood how Dart handles concurrency. I knew how to use await. I knew FutureBuilder and StreamBuilder well enough to get things wor
How to Use Dart Dot Shorthands: A Handbook for Devs
Atuoha Anthony
If you've written Flutter code for more than a month, you've likely written this line hundreds of times: mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, main
How to Structure Large Flutter Applications for Scalable and Maintainable Growth
Ethiel ADIASSA
Flutter makes it extremely fast to build UIs. That speed is one of the framework’s greatest strengths, but it also creates a subtle problem: applications often grow much faster than their architecture
How Flutter Renders Under the Hood: BuildContext and Element Tree Explained
Gidudu Nicholas
The first time I saw "Looking up a deactivated widget's ancestor is unsafe" in a stack trace, I genuinely didn't know what it meant. I copied the error into Google, found three different Stack Overflo
How to Handle Errors the Right Way in Flutter: A Practical Guide to Sealed Classes, Records, and Result Types
Gidudu Nicholas
I used to think I was handling errors well in my Flutter apps. I had try/catch blocks everywhere. I was catching exceptions, logging them, and showing error messages to users. It felt solid. Then I st
How to Use DartExceptor: A Lighter Way to Handle Errors in Dart 3
Oluwaseyi Fatunmole
If you've worked with Flutter for any meaningful length of time, you've likely written this: try { final user = await repo.getUser(); print(user.name); } catch (e) { print('Something went wrong:
Hacker News
20 条技术社区讨论与项目链接
Flutter_scene 0.21.0 runs 3D apps with Flutter GPU and Impeller
chem83
Article URL: https://xcancel.com/antibot/captcha Comments URL: https://news.ycombinator.com/item?id=49307343 Points: 1 # Comments: 0
Flet: Build cross-platform apps in Python, on top of Flutter
theanonymousone
Article URL: https://flet.dev/ Comments URL: https://news.ycombinator.com/item?id=49283154 Points: 1 # Comments: 0
Flutter 3.47
gumby271
Article URL: https://flutter.dev/blog/whats-new-in-flutter-3-47 Comments URL: https://news.ycombinator.com/item?id=49280061 Points: 206 # Comments: 215
I built a local 3D marketplace with pizza-style tracking in Flutter
Nearnook_dev
Article URL: https://play.google.com/store/apps/details?id=com.lahoucintiyar.nearnook&hl=en_US Comments URL: https://news.ycombinator.com/item?id=49277267 Points: 1 # Comments: 0
Openhare: AI-powered desktop SQL client. Cross-platform. Built with Flutter
thunderbong
Article URL: https://github.com/sjjian/openhare Comments URL: https://news.ycombinator.com/item?id=49276189 Points: 4 # Comments: 0
FieldFleet – self-hostable field operations built with Flutter and Supabase
iguardo
Article URL: https://taskfleetai.github.io/fieldfleet/ Comments URL: https://news.ycombinator.com/item?id=49262555 Points: 3 # Comments: 0
Flutter desktop apps can draw to multiple windows now
matthewkosarek
Article URL: https://www.youtube.com/watch?v=yXj6HGTgKX0 Comments URL: https://news.ycombinator.com/item?id=49243250 Points: 2 # Comments: 0
Denial WM: New Wayland Compositor with Flutter Directly Embedded
mikece
Article URL: https://www.phoronix.com/news/Denial-WM-Compositor Comments URL: https://news.ycombinator.com/item?id=49184965 Points: 4 # Comments: 0
Rejourney Flutter Analytics in Beta: Session Replay for Flutter GPU and Impeller
mrr7337
Article URL: https://rejourney.co/engineering/2026-08-01/flutter-sdk-open-beta Comments URL: https://news.ycombinator.com/item?id=49162055 Points: 1 # Comments: 0
I made Squirrel game with Flutter and Flame
dmvvilela
Article URL: https://danvilela.com/squirrel-up Comments URL: https://news.ycombinator.com/item?id=49137413 Points: 5 # Comments: 0
Kaisel – Routes as Values. Dart 3 Native Router for Flutter
TheWiggles
Article URL: https://kaisel.dev/ Comments URL: https://news.ycombinator.com/item?id=49135985 Points: 58 # Comments: 11
A LangGraph pipeline that generates compiling Flutter apps (with a repair loop)
m2magents
Article URL: https://github.com/carlosge492/app-generation-microservice Comments URL: https://news.ycombinator.com/item?id=49130994 Points: 1 # Comments: 0
Built a household organizer in Flutter – what would you improve?
mladenConcept
Article URL: https://apps.apple.com/us/app/roomli-shared-home-organizer/id6761788107 Comments URL: https://news.ycombinator.com/item?id=49102118 Points: 1 # Comments: 0
Show HN: Cross-Platform Flutter and React-Native Plugin – FFmpeg-Kit-Extended
akashskypatel
Full featured React-Native and Flutter plugin that lets you execute FFmpeg, FFprobe and FFplay commands without needing to deploy those executables. Get the full power of FFmpeg at native performance level. Includes support for 100+ external libraries with pre-bundled preset distributions for 24 different binaries distributed per platform with various included features plus the ability to deploy custom builds. https://github.com/akashskypatel/ffmpeg-kit-extended https://www.npmjs.com/package/ffmpeg-kit-extended https://pub.dev/packages/ffmpegkitextended_flutter Core features: - Cross-Platform Support: Works on Android, iOS, macOS, tvOS, Linux and Windows. -- Android: Full video playback support with native surface rendering. --- x86: x86 architecture is not supported due to its legacy status. -- iOS & macOS: High-performance video playback with CVPixelBuffer and Metal integration. -- iOS: Supports both physical devices and simulators. x86_64 architecture is not supported due to its legacy status. - FFmpeg, FFprobe & FFplay: Latest 8.1.2 API support for media manipulation, information retrieval, and audio/video playback. - Video Playback: Complete cross-platform video playback with unified surface API. - Real-time Streaming: Position and video dimension streams for live playback monitoring. - Asynchronous Execution: Run long-running tasks without blocking the UI thread. - Parallel Execution: Run multiple tasks in parallel. - Callback Support: detailed hooks for logs, statistics, and session completion. - Session Management: Full control over execution lifecycle (start, cancel, list). - Extensible: Designed to allow custom native library loading and configuration. - Full package Introspection API: Get detailed information about the package, including version, build date, and available muxers, demuxers, encoders, decoders, filters, etc. - Deploy Custom Builds: You can deploy custom builds of ffmpeg-kit-extended. See: https://github.com/akashskypatel/ffmpeg-kit-builders - Prebuilt Distributions: Supported pre-built bundle types are debug, base, full, audio, video, and video_hw. - Supported Licenses: supports both GPL and LGPL licenses Platform Support: Android (and Android TV) - Flutter & React-Native iOS (and Simulator) - Flutter & React-Native tvOS (and Simulator) - React-Native macOS - Flutter & React-Native Linux - Flutter Windows - Flutter & React-Native Flutter currently does not natively support tvOS, so tvOS support is not available for Flutter. React-native currently does not natively support Linux, so Linux support is not available for React-native. Demo : https://raw.githubusercontent.com/akashskypatel/ffmpeg-kit-e... Comments URL: https://news.ycombinator.com/item?id=49052579 Points: 2 # Comments: 0
The wall every Flutter builder hits, and the ways around it
raeddev
Article URL: https://nowa.dev/blog/why-we-built-our-own-flutter-runtime/ Comments URL: https://news.ycombinator.com/item?id=49004951 Points: 1 # Comments: 0
Show HN: Ranking 19 LLMs on Flutter code by compile pass & hidden-test pass-at-1
GeorgiKadrev
Article URL: https://nativevibe.dev/flutter-benchmark Comments URL: https://news.ycombinator.com/item?id=48920784 Points: 2 # Comments: 0
Layer_shell.dart – Build Wayland layer shell surfaces in Flutter
matthewkosarek
Article URL: https://github.com/mattkae/layer_shell.dart Comments URL: https://news.ycombinator.com/item?id=48897454 Points: 2 # Comments: 0
IDE with agentic support built using Flutter
geordee
Article URL: https://lumide.dev Comments URL: https://news.ycombinator.com/item?id=48816087 Points: 8 # Comments: 3
Show HN: FluxDown – Free, open-source IDM alternative in Rust and Flutter
zero-lab
Article URL: https://github.com/zerx-lab/FluxDown Comments URL: https://news.ycombinator.com/item?id=48784543 Points: 2 # Comments: 0
Show HN: Dart_agent_core – Run AI agents in Flutter apps with lifecycle hooks
sparkleMing
Article URL: https://github.com/memex-lab/dart_agent_core Comments URL: https://news.ycombinator.com/item?id=48758624 Points: 1 # Comments: 0
Medium
10 条Flutter 相关文章精选
Flutter in Connected Apps and Automotive Use Cases
Shishir
The automotive industry is undergoing a digital transformation, with connected apps and smart vehicle solutions playing a pivotal role in… Continue reading on Medium »
Beginner’s Guide: Using Vertex AI with Flutter to Extract Text from Complex Images
Shishir
Build a mobile app that accurately reads distorted, noisy, or handwritten text using Google’s AI and Flutter — no AI experience required! Continue reading on Medium »
Concurrency in Dart (Part — 1)
Gaurav Swarankar
Running/handling multiple tasks within a program, where tasks can make progress without unnecessarily waiting for each other. Dart… Continue reading on Medium »
Making Coding Agents More Efficient for Flutter and React Native Development
Asiri Piyajanaka
Coding agents have become a regular part of how I work across different stacks. From Flutter and React Native to web development, Python… Continue reading on Medium »
Flutter 3.47 Just Rewrote the Rules of UI Design — Here’s Why Every Developer Should Care
Nicolas
Material and Cupertino are no longer baked into the SDK. Impeller now runs your desktop apps by default. And Apple’s next OS cycle just… Continue reading on Medium »
Compose Multiplatform vs Flutter
AndroidLab by Andre
Kotlin 2.2, shared ViewModels, and native performance are changing the game — here’s what actually matters when picking your stack this… Continue reading on Medium »
Flutter 3.47 Migration Guide: 10 Things to Check Before Upgrading
Ravi Savaliya
Upgrading to Flutter 3.47? Here are 10 important changes to check first, from Material and Cupertino packages to Impeller, iOS… Continue reading on Medium »
AI-Powered Cleaning Recommendations: Turning Solar Data Into One Clear Instruction
Yagiz Ugurlu
Article 5 of the “Building Sunchronize” series, documenting the technical and product decisions behind a Flutter + Firebase solar… Continue reading on Medium »
Flutter 3.47: A Practical Guide to the Latest Changes, Features, and Migration
Roshni Savaliya
Everything Flutter developers need to know about the latest release, with real-world examples and migration best practices. Continue reading on Medium »
Stop Debugging BLoC with Print Statements — Use BlocObserver Instead
Pankaj Ram
When working with BLoC or Cubit in Flutter, debugging state management can become difficult as the application grows. Continue reading on Medium »
社区日榜讨论与资源
We shipped a terminal on Flutter’s engine and it beats the hand-written native ones — the C++ engine is better than it gets credit for
/u/starling-dev
We just released a terminal emulator that renders through Flutter’s engine — and at steady state it runs our ten-workload benchmark suite in 0.73x of ghostty’s wall time on Linux and 0.71x of Windows Terminal’s on Windows, at about half the CPU each. ghostty’s renderer is purpose-built for terminals in Zig; Windows Terminal’s is purpose-built in C++. Ours is the same general-purpose engine that draws your Flutter widgets. https://github.com/starling-build/starling/releases/tag/terminal-v0.1.0 — Apache-2.0, charts and side-by-side videos at https://starling.build/terminal.html The reason I think this belongs here: it is an unusually clean measurement of what the engine contributes, because we swapped out everything above it. There is no Dart VM in this process — we ported Flutter’s framework layer to Swift and drive the engine’s C/C++ core directly. So the rasteriser, the compositor, the text stack and the GPU path are stock Flutter; the language and the widget layer are not. When the numbers come out ahead of two purpose-built native renderers, that is the engine’s win, not ours. A terminal is a nastier rendering target than it sounds. Every frame can invalidate the entire screen — 47x201 cells, ~9,400 glyphs, each with its own foreground colour, background, bold/italic/underline, and any script on earth. No dirty-region shortcuts when someone cats a file. DOOM-Fire, which repaints every cell every frame, runs at ~1,600 fps on Linux through this engine. That is the engine’s raster path doing the work. What actually made it fast: drawRawAtlas. The naive approach — and our first one — was one Paragraph per row, letting the text engine shape and lay out a line of styled runs. It works and it looks right, but you pay shaping for content that never changes shape: a terminal cell is a fixed box with one grapheme in it. So we rasterise each glyph once into a texture atlas and emit the whole grid as a single call: canvas.drawRawAtlas(atlasImage, transforms, srcRects, colors, BlendMode.dstIn, cullRect, paint) One textured quad per cell, tinted per quad, no shaping in the frame at all. Measured against the paragraph path at the same frame rate, it costs 44% less CPU. If you are drawing a large grid of repeating glyphs in Dart — a spreadsheet, a hex viewer, a chart with data labels, a code editor — Canvas.drawRawAtlas is available to you and is dramatically cheaper than a Paragraph per row. It is the most useful thing I learned from this project. submitted by /u/starling-dev [link] [comments]
I am looking for Flutter open-source projects to contribute to
/u/GNNK71
I am looking for Flutter open-source projects to contribute to on Github. submitted by /u/GNNK71 [link] [comments]
Try Flutter Web with WebAssembly Week
/u/kevmoo
Join us for Try Flutter Web with WebAssembly Week! Unlock up to 2x–5x faster web performance with Wasm compilation in Flutter 3.47 Run `flutter build web --wasm`, test your app, and share your wins using #FlutterWasmWeek! Details: https://flutter.dev/blog/try-flutter-web-with-webassembly-week submitted by /u/kevmoo [link] [comments]
Built a Chrome extension using Flutter Web compiled to WASM — everything runs buttery smooth.
/u/jhhuij78
submitted by /u/jhhuij78 [link] [comments]
TTS/STT can't tell "wind" from "wind" — how do you handle heteronyms in a pronunciation-teaching app?
/u/Fair_Expression_3291
I'm building a vocabulary-learning app in Flutter where hearing and saying the word correctly is the product, not a nice-to-have. I've hit a problem I can't design around and I'd rather ask than keep patching. The stack Flutter, ~1,600 words live across EN/ES/PT/IT/FR TTS: ElevenLabs (eleven_multilingual_v2) called through a Supabase Edge Function so the key never ships in the client Every clip cached server-side once per (text, language), shared across all users — so a given string is synthesized exactly once, ever Cached again on-device (150MB LRU) so replays are instant and offline flutter_tts as fallback behind a 2.5s timeout so playback never goes silent STT: speech_to_text for a pronunciation-practice screen — hear the word, say it, get graded The problem: heteronyms, in both directions Output. "Wind" (moving air) and "wind" (to coil) are the same string and different sounds. TTS picks one reading and commits. My word library actually knows which sense is on screen — every entry carries a part of speech — but there's no API surface to hand that over. ElevenLabs pronunciation dictionaries are exact-string, case-sensitive, and have no POS or context scoping, so one spelling gets one entry and the second sense is unreachable. Phoneme tags do exist, but per the docs only on eleven_flash_v2 and v3 — not the multilingual model I'm on, and switching models means re-synthesizing the whole cache and losing voice identity across five languages. Input. This is the part that actually bothers me. The practice screen normalizes the transcript and Levenshtein-scores it against the target. But STT returns orthography — say either reading of "wind" and the transcript is "wind" either way. A learner who mispronounces it scores full marks. The feature is structurally incapable of catching the error it exists to catch. What I've tried Respelling the audio-only string before it reaches the engine — the screen text is never touched. wind(noun) → winned, wind(verb) → wined, read(past) → red, and so on. This is basically ElevenLabs' own recommended "alias" workaround and it works for the ~8 vowel-shift pairs I've mapped. Side benefit: since my cache key is a hash of (lang + text), two senses naturally get two cache entries. It fails in three ways: Stress-shift pairs. REcord/reCORD, PREsent/preSENT, CONtent/conTENT. Respelling can't encode stress, and I haven't found a trick spelling that does. Monolingual. It's an English orthography hack. Nothing about it transfers to ES/PT/IT/FR, all of which have their own homographs. Manual. Hand-curated table. Doesn't scale to a few thousand words. What I'm actually asking Is there a TTS API that accepts a sense/POS hint, or per-request phonemes, on a multilingual model? Or does everyone route heteronyms to a separate English-only model and eat the voice mismatch? If IPA is the only real answer — has anyone found v3-class IPA reliable enough in production? The docs quote 80–90% consistency, which for a teaching app means the wrong pronunciation ships to a learner one time in eight. For stress-shift specifically: any orthographic trick that works, or is phoneme-level control genuinely the only path? On the STT side — is there a mobile-viable way to get phonemes rather than words? I've looked at wav2vec2 phoneme-CTC or a forced aligner with GOP scoring via ONNX on-device, but I don't know if that's realistic on a mid-range phone or if I'm about to spend a month learning that it isn't. Whisper doesn't help; it also returns orthography. The unglamorous option: detect heteronyms and simply disable pronunciation scoring for them, with an honest note to the user. Is that what shipped apps actually do? If you've built pronunciation feedback into anything real, I'd love to know where you drew the line between "graded properly" and "good enough." Happy to share code for any of the above. submitted by /u/Fair_Expression_3291 [link] [comments]
I built a real-time messaging + multiplayer games app in Flutter. Here's what surprised me
/u/Ordinary-Pen-8374
I've been building a social/messaging app called Riv with Flutter, and the biggest surprise wasn't actually building the UI. It was getting all the different real-time pieces to behave like one system. The app has real-time messaging, presence indicators, group chats, notifications, and multiplayer games. That meant I had to deal with things like state synchronization, reconnects, message delivery, game state, background behavior and keeping the UI responsive while everything was changing underneath it. The interesting part was that problems that look completely unrelated at first often turned out to be the same underlying problem: what should the client consider authoritative, and what should happen when the client is temporarily out of sync? I'm still refining the architecture, but the app is now live. I'm posting this mainly because I'd be interested in hearing how other Flutter developers would approach the architecture differently. If anyone is interested, I can also break down how I handled the real-time messaging/game state separation. submitted by /u/Ordinary-Pen-8374 [link] [comments]
[UPDATE] Flutter Desktop: Opening Custom URI Schemes on Windows
/u/HumboldtBudo
submitted by /u/HumboldtBudo [link] [comments]
Game Effects
/u/GNNK71
I've developed games in Flutter, including using the Flame engine, but I'm having trouble creating eye-catching effects. Can you recommend any frameworks or resources I can draw from? submitted by /u/GNNK71 [link] [comments]
[Package Update] flutter_easy_seo: Support for hidden widgets (e.g. TabBarView)
/u/Plus-Area840
Following up on my previous post (see link at the bottom) I released an update to flutter_easy_seo, which is now open source under Apache 2.0!. What's New: Capturing Hidden Widgets Content inside inactive TabBarView tabs, PageView pages, or off-screen list items typically isn't part of Flutter's active widget tree, preventing flutter_easy_seo from extracting it for SEO HTML. With this update, widgets that become visible are automatically captured and persisted in the internal HTML structure, even after Flutter unmounts them (e.g., when switching tabs). How to use it: Interactive Mode: Simply click through your app views manually. Automated Mode: Trigger widget visits in your test scripts: `await tester.tap(find.text('Tab Name'))` Just like dynamic route collection, this ensures non-visible widget content is fully indexed without altering your app's structure. pub.dev: flutter_easy_seo Live Showcase: https://fluttereasyseo.lxandr.at Original Discussion: Previous Post submitted by /u/Plus-Area840 [link] [comments]
I built an Agent Skill to reduce unnecessary work in Flutter & React Native coding agents
/u/asiriscol
The idea is simple: coding agents often do way more than necessary after a small change. Reread lots of files, rerun analysis/tests, build apps, dump huge logs into context, etc. This skill tries to make the workflow smarter: read only the context actually needed reuse existing project patterns load only relevant rules classify changes by risk run the minimum sufficient verification avoid unnecessary builds/tests/retries keep verbose command output out of the model context It uses a V0–V5 verification model, from “no verification needed” up to focused runtime/device testing. Currently focused on Flutter + React Native. https://github.com/asiriPiyajanaka/mobile-development-skills Would love feedback from people using coding agents regularly, especially around cases where your agent wastes context or runs unnecessary checks. submitted by /u/asiriscol [link] [comments]
building a drawing app in flutter with chatgpt
/u/Accurate_Fig_1854
submitted by /u/Accurate_Fig_1854 [link] [comments]
Reddit Challenge: Give Me Something Hard to Build in Flutter 🚀
/u/Virtual-Match4871
I want to start a challenge with the Flutter community. Give me something to build. It can be anything you think would be a good challenge for a Flutter developer: 📱 A difficult UI/interaction — ideally something that can be built in 1–2 screens ⚡ A challenging animation or custom widget 🧠 A complex Flutter implementation 🔄 An interesting state-management challenge 🎨 A UI from an app that would be difficult to recreate 📦 Something that could become a useful pub.dev package 🛠️ A Flutter developer-tool idea you wish existed 🔥 Or just something you’ve always wanted to see implemented in Flutter The rules are simple: You give me the challenge → I build it → I share the result back with the community. I’m not looking for easy CRUD screens. Give me something that actually makes me think, learn something new, or push Flutter to its limits. If your challenge is selected, I’ll share the implementation, what I learned, and the final result. So, what would you challenge me to build? Drop your hardest/most interesting Flutter challenge below. 👇 Let’s see what the community can come up with. submitted by /u/Virtual-Match4871 [link] [comments]