AI 资讯
Delivering messages with no internet, no servers, and no SIM
Every messenger you use has a hidden dependency: a working network path to a datacenter. Drop into a basement, a packed stadium, a moving train through a tunnel, an exam hall with jammers, or a remote area with no plan, and the app is just a spinner. The people you want to reach are often standing a few meters away, but your message still has to travel to a server on another continent and back. When that path is gone, so is the app. Kabootar is my attempt to remove that dependency entirely. It is a messenger with no backend at all. Your phone forms a peer-to-peer mesh with other phones nearby, and messages hop device to device over Bluetooth and Wi-Fi until they reach the recipient. No internet, no servers, no SIM. It is built in Flutter, and the routing core is plain Dart. The core idea: delay-tolerant networking The insight that makes this work is refusing to assume the recipient is reachable right now . Normal networking is connection-oriented: open a path end to end, then send. If there is no path, there is no delivery. Kabootar instead treats the network as a delay-tolerant network (DTN). A message does not need a live end-to-end path at the moment you hit send. It needs a chain of carriers that will exist over time . You hand your message to whoever is nearby. They hold onto it, carry it as they walk around, and pass it along to the next phone they meet. Eventually a carrier bumps into the recipient and the message lands, even if that is minutes later and both you and the recipient have long since walked away. This is store-and-forward, the same shape as a durable, at-least-once message queue, except the queue is running across a swarm of phones instead of inside a datacenter. How a message actually travels The routing strategy is epidemic routing: flooding. When you send a message, it spreads to everyone in range like a rumor. Each device that receives it re-broadcasts it onward, so the message replicates through the crowd, taking every path at once. That red
AI 资讯
CanvasKit Layout Traps: The Unbounded Constraint Bug That Only Blanks Release Builds
I shipped eight card and casino games to my portfolio in a single commit — solitaire, roulette, video poker, slots, baccarat, keno, war, higher-lower. All client-side Flutter web, all free, all deployed to Firebase Hosting in one push. flutter analyze was clean. I read the diff twice. The build succeeded. I deployed. Then I opened /games/roulette on the live site and got a page with a header, a subtitle, a bankroll readout, a spin button — and a completely blank rectangle where the betting board should have been. No red error screen. No console exception. No 404. Just an empty region the size of the thing that was supposed to be there, on a page where everything else rendered perfectly. The cause was one enum value: CrossAxisAlignment.stretch on a Row that, four widgets up the tree, was sitting inside a scroll view. In debug that combination throws a loud, well-written framework error. In release the assertion that produces that error doesn't exist, so nothing throws at all — the framework computes with infinity and paints nothing. A layout contract violation is not a type error, and no amount of static analysis is going to find it for you. This post is that bug in full, the family of unbounded-constraint traps it belongs to, why debug builds give you a false sense of safety, and the verification discipline I now refuse to skip. Eight games shipped, one board rendered nothing The symptom is worth describing precisely, because it's what makes this class of bug so slow to diagnose. The route loaded. The page scaffold — nav, page header, back link, related-games strip — was all there and correct. Analytics fired the pageview. The bankroll, the chip selector and the spin control rendered. Only the number grid, the largest single widget on the page, drew nothing at all. The space it occupied wasn't even collapsed to zero; it was just empty. The browser console was clean. Not "clean apart from a warning" — genuinely empty. Chrome DevTools' Elements panel showed what it al
AI 资讯
Taming Flutter Infinite Scroll: Why 3 Lines of async* Missed the Point, and How BlocSignal Fixes It
The Ubiquitous Infinite Scroll Pagination Bug Almost every Flutter engineer has encountered the dreaded infinite scroll race condition in production. The user opens a list, flings their thumb down the screen on a spotty cellular connection, and triggers multiple scroll notifications past the bottom threshold within milliseconds. Before the first asynchronous HTTP network request finishes, the scroll listener fires again. Suddenly, your list duplicates items, page counters jump ahead, or the state machine locks up entirely. Recently, mobile developer Ali Wajdan published a widely discussed article titled 3 Lines of Dart async* Code That Fixed My Infinite Scroll Pagination . In his article, Ali accurately diagnoses the root cause of standard pagination headaches: "Most Flutter pagination code I have seen, including my own for years, wraps a mutable state object around a scroll listener. A page counter, a loading boolean, a hasMore flag, and a fetch method the UI calls when it hits the scroll threshold. It works until two scroll events fire close together, or a rebuild triggers a second load before the first future resolves... It is a classic race condition, and it gets worse once the state lives across a page counter, a hasMore flag, and a loading flag that all need to stay in sync." To escape this trap, Ali suggested encapsulating pagination logic inside a Dart async* generator and consuming it with a StreamIterator : // The pattern proposed in Ali Wajdan's article Stream < List < Post >> fetchPostsPaginated ( String query ) async * { var page = 0 ; var hasMore = true ; while ( hasMore ) { final batch = await api . fetchPosts ( query , page: page ); hasMore = batch . isNotEmpty ; page ++ ; yield batch ; } } final iterator = StreamIterator ( fetchPostsPaginated ( query )); Future < List < Post >> loadNextPage () async { if ( ! await iterator . moveNext ()) return const []; return iterator . current ; } On the surface, moving mutable state into local generator variable
AI 资讯
File System Management in Dart
It's a good time to also investigate a bit the dart I/O interfaces, especially the one related to files. The dart:io package must be imported to deal with files and directories in Dart. import 'dart:io' ; A file in Dart is an object instantiated by the File class . This object can be created by simply invoking the default constructor , where its argument will be a String . // default constructor can be used // to create a new object pointing to // a local file. File myFile = File ( "./file1.test" ); fromRawPath() is another constructor, it opens a file based on an List<Uint8> ( Uint8List ), this kind of data is usually generated by utf8.encode or ascii.encode . // fromRawPath constructor can be used // to open a file based on a raw path, // an Uint8list. File myFile2 = File . fromRawPath ( ascii . encode ( "./file2.test" ); ); Finally, the fromUri() constructor will open a file based on an Uri object. // fromUri is another construct that // can be used to open a file based on // an Uri. File myFile3 = File . fromUri ( Uri . file ( "./file3.test" ) ); Now the file object has been created, many attributes and methods are available to control it. Let check first the attributes. In the previous examples, all objects are using a relative path, when the object is returned, the absolute path attribute is set. It is the absolute path representation of the data previously passed. print ( myFile1 . absolute ); print ( myFile2 . absolute ); print ( myFile3 . absolute ); $ dart run File: '/home/user/tmp/cboring/./file1.test' File: '/home/user/tmp/cboring/./file2.test' File: '/home/user/tmp/cboring/file3.test' The original path passed as first argument can be retrieved with the path attribute . print ( myFile1 . path ); print ( myFile2 . path ); print ( myFile3 . path ); $ dart run ./file1.test ./file2.test file3.test The file object can also returns an Uri object with the help of the uri attribute . // this is a closure helper to show // the properties from an Uri object and //
AI 资讯
Production Flutter Networking Without the Boilerplate: Reactive Repositories with BlocSignal
The Networking Architecture Dilemma in Production Flutter If you survey ten seasoned Flutter developers about how they structure networking in production, you will almost certainly see the same multi-tiered pipeline: ┌────────────────────────────────────────────────────────────────────────┐ │ Traditional Flutter Networking Pipeline │ ├────────────────────────────────────────────────────────────────────────┤ │ [Dio / HTTP Client] ─▶ [API Service] ─▶ [Repository Layer] ─▶ │ │ [Cubit / BLoC] ─▶ [UI Builders & Banners] │ └────────────────────────────────────────────────────────────────────────┘ The underlying architectural principles are sound: separation of concerns, testability, and isolating network transport details from UI widgets. However, in practice, this classical layered stack demands an enormous amount of repetitive boilerplate: Async State Union Ceremony : Defining four separate state classes ( Initial , Loading , Success(data) , Failure(error) ) or union types for every single API endpoint. Race Conditions & In-Flight Cancellation : When users type queries or switch tabs rapidly, requests finish out of order. Preventing stale responses requires complex Dio CancelToken plumbing or heavy rxdart switchMap streams. Offline Caching & "Stale-While-Revalidate" : Showing cached data on Frame 1 while fetching fresh updates in the background usually requires database synchronization and stream merging logic. The Repository vs. Controller Divide : Repositories hold data and caching logic, while BLoCs or Cubits hold reactive state. Because Dart only allows single inheritance, developers end up maintaining two separate class hierarchies connected by verbose dependency injection glue. With bloc_signals , we can preserve complete separation of concerns while eliminating 70% of the friction. Let us examine how to architect a modern, clean, production-ready networking layer using CubitSignalMixin , HydratedMixin , and .toAsyncBlocSignal() . ⚡ 1. Symmetrical Async Projection
AI 资讯
Mechanically Eliminating FutureBuilder & StreamBuilder: Universal Signal, Future, and Stream Adapters in BlocSignal
Making the Migration from In-View Asynchrony to Synchronous State Management Truly Mechanical After our recent discussions on why FutureBuilder and StreamBuilder are architectural anti-patterns when placed inside Flutter widget trees, I started thinking: how can we make it even easier—even completely mechanical—to convert from a FutureBuilder or StreamBuilder to a BlocSignalBuilder ? Every Flutter developer knows the history. Years ago, I recorded a video breaking down the hidden traps of placing asynchronous builders in UI views: Why you shouldn't put FutureBuilder in your build method . Even the original official Flutter video on FutureBuilder initially instantiated the network future directly inside the build() method, until I filed an issue to get it corrected (which is why the official Flutter YouTube video still proudly bears "Take 2" on its clapperboard!). The fundamental issue has never been that developers want bad architecture. The issue was friction . FutureBuilder was simply the path of least resistance. To do it "properly" in traditional state management, developers had to create an entire BLoC or Cubit, declare separate Event and State classes (or union types), write boilerplate event handlers, wire asynchronous repository methods, manage subscription lifecycles, and inject everything into the widget tree. With bloc_signals 1.1.0 , that friction disappears completely. We have introduced universal, symmetrical adapter extensions that allow any Dart Future , Stream , ReadonlySignal , or lifted primitive ( value.$ ) to adapt into a synchronous BlocSignalBase container with a single method call. 🧭 The Universal Dual-Track Mental Model When bridging asynchronous sources into synchronous state management, developers typically have one of two distinct intents: Raw Domain Values ( T ): You want raw domain objects (for example int , UserProfile , ThemeMode ) with zero wrapper ceremony, and you have an immediate default or fallback value for frame 0. Rich Asynch
AI 资讯
Pressure-testing Ota on EventCatalog: generated artifact lineage across sibling consumers
The finding EventCatalog exposes a common monorepo failure mode: generated code may exist, its producer may be green, and the real downstream consumer can still fail. Its Langium language server generates AST, grammar, module, and syntax files; a sibling VS Code extension consumes that output alongside the workspace SDK and visualiser. The useful question is therefore not "did generation finish?" It is whether the repository can execute the complete consumer closure from declared dependency hydration through the package that needs the generated result. The contract boundary Ota models the generated output separately from the tasks that establish and consume it: artifacts : language-server-ast : kind : generated_source producer : language-server:generate paths : - packages/language-server/src/generated/ast.ts - packages/language-server/src/generated/grammar.ts - packages/language-server/src/generated/module.ts - packages/language-server/syntaxes/ec.tmLanguage.json - packages/vscode-extension/syntaxes/ec.tmLanguage.json inputs : - packages/language-server/src/ec.langium - packages/language-server/langium-config.json tasks : vscode-extension:build : depends_on : - language-server:generate - language-server:build - sdk:build - visualiser:build requires_artifacts : - language-server-ast The setup task owns typed, frozen-lockfile pnpm hydration with the language-server package filter. That removes bespoke install shell glue without pretending the dependency path is harmless: it reaches the package registry, so the selected closure is intentionally not routine agent-safe execution. Humans and CI can run the declared verification workflow; unattended agents cannot silently acquire that networked setup authority. What Ota had to learn This pressure case made two platform requirements concrete. Generated-source lineage had to remain visible at consumer admission and in execution evidence, rather than surfacing only after a build failure. And pnpm dependency hydration needed a
开发者
I built flutter_auditor — a zero-config CLI tool to audit Flutter apps for permissions, dead assets, security risks, and package hygiene
Shipping a Flutter app without auditing native permissions, release keystores, or asset bloat? To help Flutter developers catch hidden production risks before App Store/Play Store review, I built flutter_auditor — an open-source, zero-config CLI health and security inspector for Flutter & Dart. In just one terminal command (dart run flutter_auditor), it scans your project for: 🔒 17+ Automated Audits: Hardcoded API secrets & exposed .jks keystores Missing iOS Info.plist privacy description strings Unused heavy assets & broken 2.0x/3.0x image variant paths Dangerous manifest flags (android:debuggable="true", allowed cleartext traffic) Unused & transitive package dependencies Give it a try locally on your project and let me know what audits you'd like to see next! 👇 pub.dev: https://pub.dev/packages/flutter_auditor GitHub: https://github.com/thakaredipali/flutter_auditor
AI 资讯
Flutter Streaming UI: How the Typewriter Experience of AI Replies Is Built
The typewriter effect looks simple: characters appear one by one. But behind "skip animation", "no truncation", and "no performance regression" lies a whole set of engineering decisions. The implementation in this article is Flutter/Dart based, but the core semantic decisions — "skip ≠ abort" and "buffer and batch" — are framework-agnostic : Web's EventSource, and native/RN SSE clients, face the same choices. Prologue: a "skip typewriter" button that kept breaking In an AI narrative app (where the user influences an AI-driven interactive story by entering fate instructions), I built a "⏩ skip typewriter" button — users click it to see the full AI reply immediately instead of waiting for the text to appear character by character. The button went through three stages in the dev log: V1 : clicking does nothing — the callback fires, but the user experiences no change V2 : clicking truncates the content — the animation is gone, but the reply is incomplete too Final : clicking reveals the partial text immediately, while the LLM keeps generating the full reply in the background, which appears all at once when done Behind these three versions lie the three most common pitfalls in "streaming UI". This article breaks them down. 1. From SSE to screen: the streaming rendering pipeline Why the LLM "pops" text out The LLM's reply comes back in chunks via HTTP SSE (Server-Sent Events). A typical chunk looks like this: data: { "choices" :[{ "delta" :{ "content" : "Mephistopheles appears" }}]} data: { "choices" :[{ "delta" :{ "content" : "at the study door." }}]} data: [ DONE ] The interval between chunks is determined by the model's generation speed — tens of milliseconds when fast, possibly a full second when slow. That "character-by-character appearance" is what the user perceives as the typewriter animation. Why you can't update the UI on every chunk If you trigger a state update on every chunk, a reply of a few hundred characters can cause dozens or hundreds of UI rebuilds, whi
AI 资讯
One-Shot UI Side Effects in BlocSignal: Snackbars, Dialogs, and Navigation Without State Pollution
Every Flutter developer has run into the Sticky State Dilemma . You build a login screen. When authentication fails, your state container emits an error. You catch it in your UI and show a SnackBar . Everything works—until the user rotates their phone, pulls down the notification shade, or types on the virtual keyboard. Suddenly, the widget tree rebuilds. The state container is still holding AuthErrorState("Invalid password") . The UI listener fires again. And a duplicate snackbar appears out of nowhere. In this article, we’ll explore why domain state machines struggle with transient UI events, how the classic BLoC community worked around this with package:bloc_presentation , and how BlocSignal lets you handle one-shot side effects cleanly with zero additional package dependencies . 1. The Root Problem: Persistent State vs. Ephemeral Actions State management in Flutter is designed to model persistent truth over time: Is the user logged in? AuthState.authenticated(user) Is data loading? TodoState.loading What is the cart total? $49.99 Persistent state answers: "What is the system's current condition?" In contrast, UI presentation actions are ephemeral pulses : Show a brief SnackBar toast. Pop up an alert confirmation dialog. Push a new route on the Navigator stack. Vibrate the haptic motor. These actions answer: "What just happened that requires a one-time reaction?" ┌────────────────────────────────────────────────────────┐ │ State vs. Effects │ ├────────────────────────────┬───────────────────────────┤ │ Persistent State │ Ephemeral Side-Effect │ ├────────────────────────────┼───────────────────────────┤ │ • Survived by UI rebuilds │ • Consumed once & gone │ │ • Represented in signals │ • Triggered by an event │ │ • Backed by equality diffs │ • Zero domain state footprint │ └────────────────────────────┴───────────────────────────┘ 2. The Legacy Workarounds (And Their Hidden Costs) Historically in package:bloc and package:flutter_bloc , developers used one of three
AI 资讯
Dogfooding BlocSignal on the Web: Building a 100K Ops/sec Reactive App with Jaspr and Dart 3.13
Building Pure Dart Web Apps Without Compromise When developers evaluate Dart for the web, they typically face a stark tradeoff: Flutter Web : Exceptional for canvas-driven applications, design systems, and cross-platform desktop/mobile parity—but heavy for content-first landing pages, docs, and fast-loading SEO sites. Jaspr Web : A lightweight, component-driven framework that compiles pure Dart to HTML and CSS with instant first paint and full search engine indexing. When we built the official documentation and showcase site for BlocSignal , we knew Jaspr was the perfect foundation. But like many engineers diving into a new UI paradigm, our initial implementation took a shortcut: we used raw StatefulComponent lifecycles and manual .subscribe() callbacks to wire up our state machines. It worked—but it wasn't idiomatic. In this behind-the-scenes case study, we walk through the process of dogfooding bloc_signals_jaspr across blocsignal.dev , replacing manual subscription glue with declarative consumer components, achieving 100,000 operations/sec in compiled JavaScript , and exploring the sheer developer ergonomics of Dart 3.13 primary constructors . The "Manual Subscription Trap": Why Raw .subscribe() Fails at Scale In classic Flutter or Jaspr development, when you create a state machine without framework-level consumer widgets, you might be tempted to subscribe inside initState() : // ❌ THE ANTI-PATTERN: Manual subscription glue in StatefulComponent class LiveVisualizerState extends State < LiveVisualizer > { late final LiveCounterBloc _bloc ; @override void initState () { super . initState (); _bloc = LiveCounterBloc (); // ⚠️ Flaw 1: Every state change triggers a full component setState _bloc . state . subscribe (( _ ) { if ( mounted ) setState (() {}); }); } @override void dispose () { // ⚠️ Flaw 2: Manual dispose tracking _bloc . close (); super . dispose (); } } While this appears harmless in a simple counter demo, it introduces three severe architectural flaws:
AI 资讯
Unit Testing in BlocSignal: The Practical Handbook
A Practical Guide to Faster, Deterministic Flutter & Dart Unit Testing If you’ve ever written unit tests for classic package:bloc applications using bloc_test , you know the drill: build your BLoC, dispatch an event in act , and assert state emissions in expect . Under the hood, classic BLoC processes state updates asynchronously via Dart microtask-queue Streams . While robust, testing asynchronous streams can introduce microtask timing headaches, race conditions, or the need to drain queues or use fakeAsync when testing complex side-effects. In BlocSignal , state updates propagate synchronously . Calling emit(newState) updates the underlying signal graph in the exact same call stack frame. This handbook is a practical, recipe-based guide to testing BlocSignal and CubitSignal applications using package:bloc_signals_test . Whether you’re coming from classic BLoC or brand new to Signals, this guide shows you how to test every scenario cleanly—and why it’s significantly easier than classic stream-based testing. 🤖 AI Assistant Tip : Working with an AI coding assistant (like Antigravity, Gemini CLI, or Cursor)? The official bloc-signals plugin includes a pre-built testing skill ( plugins/bloc-signals/skills/bloc-signals/ ) that automatically teaches your AI assistant these exact testing conventions, observer scoping rules, and declarative blocSignalTest patterns! 🛠️ Quick Reference: BLoC Streams vs. BlocSignal Testing Testing Task Classic BLoC ( package:bloc_test ) BlocSignal ( package:bloc_signals_test ) Why it’s easier in BlocSignal Execution Environment Often requires flutter test engine Pure dart test execution Blazing Speed : Business logic tests run in pure Dart CLI without booting Flutter UI engine. Simple State Assertions Requires async stream listener or blocTest Direct expect(cubit.state, 1) or blocSignalTest Synchronous : State updates on the next line of code without microtask delay. Failure Diagnostics Legacy Instance of 'CounterCubit' Built-in toString() :
AI 资讯
How I Made Features in a Large Flutter App Actually Removable
"Just delete the features you don't need" is the easiest thing in the world to write in a README, and the hardest to make true. I hit this building a Flutter app with six verticals in one codebase — marketplace, ride-hailing, car rentals, social feed, chat, wallet. Deleting one should have been simple. It wasn't, because every feature had tendrils: a route in the central route table a tab hardcoded in the app shell a button on the home screen a service registered in main() Remove the feature folder and you get a wall of compile errors from files that have nothing to do with it. Here's what actually worked. Make three things data instead of code 1. Routes Each feature exposes its own routes from its own folder: class WalletModule extends AppModule { const WalletModule (); @override String get id = > 'wallet' ; @override List < GetPage > get pages = > [ GetPage ( name: AppRoutes . wallet , page: () = > const WalletScreen ()), ]; } The app's route table becomes a composition: static final routes = < GetPage >[ .. . _centralRoutes , .. . ModuleRegistry . pages , ]; Adding or removing a feature stops being an edit to a shared file. It's one line in a registry. 2. Bottom-navigation tabs This one surprised me. My app shell imported the feed widget directly: // before — the always-present shell depends on an optional feature import '../feed/feed_tab.dart' ; The shell ships in every build. That import meant the social feature could never be removed. So a tab became a small data class that a feature contributes: class ShellTab { final String id ; final int order ; // core tabs use 10/30/40 final IconData icon ; final String labelKey ; final Widget Function () builder ; } Social contributes its tab at order 20, slotting between home and alerts without the shell knowing it exists. The shell merges its own tabs with ModuleRegistry.shellTabs and sorts. 3. Entry points Home screens linked to feature screens with Get.toNamed(...) . Named routes are already decoupled — no import nee
AI 资讯
Porting 16 BLoC & Signal Benchmark Apps to BlocSignal: Elevating Flutter UX & DX
🚀 Ported Example Benchmark Suite Live on blocsignal.dev ! If you’ve been evaluating BlocSignal —the monorepo package bridging the predictable event-driven BLoC architecture with Rody Davis's reactive signals v7 primitives—we’ve got something big to share. We just launched a dedicated Ported Example Suite containing 16 full-featured, runnable benchmark applications adapted directly from the official felangel/bloc (10 apps) and rodydavis/signals.dart (6 apps) example repositories. 🌐 Explore the Live Benchmark Suite : https://blocsignal.dev/ported-examples 💡 Why Port These Benchmark Apps? State management benchmarks are best understood through real-world applications. By porting these established examples 1-to-1, developers can compare BlocSignal side-by-side with original implementations to see concrete architectural benefits: 1. Synchronous Frame Updates (Better Test DX) In classic BLoC, state updates propagate asynchronously over microtask streams. In BlocSignal , emit() updates state synchronously on the exact same frame . This eliminates microtask queue latency, making widget building feel instant and allowing unit tests to assert expect(bloc.stateValue, ...) without pumpAndSettle or stream delays. 2. Reactive computed() Derivations (Zero Event Plumbing) In complex apps like Todos or Dynamic Forms , classic BLoC often requires dispatching intermediate filter events or wiring up CombineLatestStream . With BlocSignal , you declare late final ReadonlySignal<List<Todo>> filteredTodos = computed(...) right inside the constructor. When state changes, downstream signals derive updated values reactively and lazily. 3. Streamless Event Concurrency Transformers (Lower Overhead) Event concurrency transformers ( sequential() , droppable() , restartable() ) in BlocSignal are implemented as streamless higher-order functions using pure Dart Mutex locks. You get full request-cancellation and debouncing capabilities (e.g. in GitHub Search ) with zero RxStream memory allocations .
AI 资讯
Mastering Hive in Flutter: A Step by Step Beginner's Guide to Fast Local Storage
Introduction When building a Flutter application, you'll often need to store data on the user's device. For example: Saving user preferences Storing login information Caching API responses Creating offline applications Building note-taking or to-do apps While there are several local storage solutions available, Hive is one of the fastest and easiest local storage for Flutter developers. In this tutorial, you'll learn Hive from scratch by building a simple example. No prior database knowledge is required. What is Hive? Hive is a lightweight, NoSQL database written entirely in Dart. It stores data directly on the device, making it perfect for Flutter applications. Why use Hive? Extremely fast Works offline No native platform code required Simple API Easy to learn Great for small and medium-sized applications Think of Hive as a collection of boxes where each box stores your application's data. Hive ├── User Box ├── Settings Box ├── Notes Box └── Products Box Each Box is similar to a table in traditional databases. Step 1: Create a Flutter Project Create a new Flutter project. flutter create hive_demo Open the project. cd hive_demo Step 2: Install Hive Open pubspec.yaml and add the following packages. dependencies : flutter : sdk : flutter hive : ^2.2.3 hive_flutter : ^1.1.0 Then install them. flutter pub get Step 3: Initialize Hive Before using Hive, initialize it inside main() . import 'package:flutter/material.dart' ; import 'package:hive_flutter/hive_flutter.dart' ; void main () async { WidgetsFlutterBinding . ensureInitialized (); await Hive . initFlutter (); await Hive . openBox ( 'settings' ); runApp ( const MyApp ()); } Here we open a box called settings . Step 4: Understanding Boxes A Box is where Hive stores data. Imagine this box: Settings Box theme -> dark username -> Alex loggedIn -> true Keys are on the left. Values are on the right. Step 5: Save Data Saving data is incredibly simple. var box = Hive . box ( 'settings' ); box . put ( 'username' , 'John' );
AI 资讯
Displaying async values in Flutter
The build method in Flutter widgets is synchronous. That means it doesn’t like to wait for anything. But sometimes, we need to wait for a value to arrive in order to display it. Let’s think of a simple weather app that displays only the temperature of a city. The app needs to make a request to the backend, get the temperature value, and finally display it. It will have to wait for a response from the backend, but as we discussed, the build method does not like to wait for anything. So how do we solve this issue? Enter: FutureBuilder . FutureBuilder takes a value of type Future and displays widgets until it is resolved. In fact, we can specify which widgets to display not only while loading but also when an error occurs. Let’s see how we can use FutureBuilder in a simple app. First, create an app in a directory of your choice: flutter create future_builder --platforms = macos You can choose whichever platform you want. Open the project in your preferred IDE, and navigate to lib/main.dart . Replace the entire content of the file with the following: import 'package:flutter/material.dart' ; void main () { runApp ( const MyApp ()); } class MyApp extends StatelessWidget { const MyApp ({ super . key }); @override Widget build ( BuildContext context ) { return MaterialApp ( home: const MyHomePage ()); } } class MyHomePage extends StatelessWidget { const MyHomePage ({ super . key }); Future < int > _getTemperature () async { await Future . delayed ( Duration ( seconds: 3 )); // Dummy delay of three seconds. return 25 ; } Future < int > _getTemperatureError () async { await Future . delayed ( Duration ( seconds: 3 )); throw Exception ( 'An error occurred while retrieving the temperature value.' ); } Future < int ? > _getTemperatureEmpty () async { await Future . delayed ( Duration ( seconds: 3 )); return null ; } @override Widget build ( BuildContext context ) { return Scaffold ( body: Center ( child: FutureBuilder ( future: _getTemperature (), builder: ( context , snapshot )
AI 资讯
Switching Tracks in BlocSignal: The Universal State Switchyard for BLoC, Riverpod, and Provider
By Randal L. Schwartz, and a few million TPU cycles Motto: "With the rigor of Bloc and the flex and speed of Signal" Why You Don't Have to Tear Up Your Codebase to Enjoy the Speed of Synchronous Signals If you have followed my talks, articles, or comments in the Flutter community over the years, you know I have been a strong advocate for Riverpod . Riverpod solved many of the fundamental global-state scoping issues inherent in classic InheritedWidget patterns, providing compile-safe dependency injection and clean state isolation. However, as the Flutter ecosystem evolved toward Riverpod 3 , I grew increasingly wary of the direction being pushed: a heavy reliance on mandatory code generation ( @riverpod annotations, build_runner, macros). Code generation introduces build-step friction, bloats compile times, and makes debugging generated syntax opaque. On the other side of the tracks sat BLoC . While I appreciated BLoC's structured, predictable event-to-state machine pattern ( on<Event> ), I was never a big fan of classic BLoC's reliance on underlying Dart Streams . Streams operate asynchronously via microtask queues—introducing subtle frame-rendering latency—and require extensive stream-transformer ceremony for simple state updates. Then came Signals (specifically Rody Davis's signals package). Signals brought raw speed, zero microtask overhead, fine-grained composable reactivity, and pure Dart portability. That realization birthed BlocSignal : combining BLoC's disciplined, enterprise event-state architecture with Signals' synchronous reactivity. And more importantly, it solved the single biggest pain point in Flutter development: the migration trap . 🚂 The Core Metaphor: "Switching Tracks in BlocSignal" In Flutter development, choosing a state management tool often feels like choosing a railroad company. If your team built an application on package:provider or flutter_bloc and wants to adopt Riverpod or Signals, traditional wisdom dictates a nightmare: tearing up al
AI 资讯
I Built a 3D Game in Flutter — With No Game Engine
Everyone says the same thing: Flutter is for apps, not games. So I decided to find out where that's actually true — by building a 3D endless runner in Flutter. From scratch. No Unity, no Unreal, no game engine at all. Just Dart and Flutter's own rendering stack. It runs in your browser right now: ▶️ Play it live (desktop, keyboard controls — A / D to switch lanes, Space to jump). Here's how it works, and what building it taught me about how far Flutter can actually go. The stack: Flutter GPU + flutter_scene The whole thing sits on two pieces most Flutter developers have never touched: Flutter GPU — a low-level rendering API that talks almost directly to the GPU through Impeller (the engine that replaced Skia). This is what makes real-time 3D possible at all. flutter_scene — a higher-level 3D scene API on top of Flutter GPU. It gives you the building blocks a game needs: a scene graph of nodes , a perspective camera , meshes, and glTF model loading. You build a tree of nodes, point a camera at it, and render it every frame inside a normal Flutter widget. That last part still surprises me — the 3D world is just a CustomPaint -style surface living inside an otherwise ordinary Flutter app. Faking an infinite world with a handful of objects An "endless" runner obviously can't build an endless world — you'd run out of memory in seconds. The trick is object pooling : you keep a small pool of track segments and obstacles, and as they scroll past the camera behind the player, you recycle them back to the front with new positions. The player never actually moves forward. The world moves toward the player , and a fixed number of segments cycle forever. Same idea for obstacles and coins. It means the game runs at a constant, tiny memory footprint — which is exactly what keeps it smooth on weaker devices. The parts that were genuinely hard Collision that feels fair. Detecting a collision is easy. Making it feel right is not. Too strict and the player rages at hits that "clearly
AI 资讯
Flutter State Management in 2026: Riverpod vs Bloc vs Provider in Production
Flutter State Management in 2026: Riverpod vs Bloc vs Provider in Production Every Flutter team eventually hits the same wall. The app works, the demo looks great, and then somewhere around screen thirty the setState calls start colliding with each other, a widget rebuilds three times for one tap, and nobody on the team can explain why a loading spinner is stuck on a screen the user already navigated away from. That's the moment state management stops being a "nice to have" architectural decision and becomes the thing standing between you and a shippable product. We've built and maintained Flutter apps across fintech, e-commerce, logistics, and healthcare — different domains, wildly different feature sets, but the same recurring question from every engineering lead we work with: Riverpod, Bloc, or Provider? There's no shortage of opinions online, and most of them are shallow — "Bloc is too much boilerplate," "Riverpod is the future," "Provider is basically deprecated." None of that is useful when you're the one who has to live with the decision for the next two years of feature work. This is a practitioner's comparison, not a popularity contest. We'll walk through what each of these actually does under the hood, where each one falls apart in production, and how we decide which one to reach for on a new project. Why state management is the architecture decision that matters most In most application frameworks, state management is one of several important decisions. In Flutter, it's disproportionately important, because Flutter's entire rendering model is built around widget rebuilds driven by state changes. Get state management wrong and you don't just get messy code — you get a slow app, because unnecessary rebuilds are a real performance cost, not just an aesthetic one. There are three problems every state management approach in Flutter has to solve: Where does state live , and how does a widget deep in the tree access state that was created somewhere else? How do
开源项目
🔥 flutter / flutter - Flutter makes it easy and fast to build beautiful apps for m
GitHub热门项目 | Flutter makes it easy and fast to build beautiful apps for mobile and beyond | Stars: 177,123 | 44 stars today | 语言: Dart