[{"data":1,"prerenderedAt":3389},["ShallowReactive",2],{"navigation_docs_en":3,"-en-examples-complete-todo-app":342,"-en-examples-complete-todo-app-surround":3384},[4,45,91,137,195,236,257,291,327],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Getting Started","i-lucide-rocket","/en/getting-started","en/01.getting-started",[10,15,20,24,29,34,39],{"title":11,"path":12,"stem":13,"icon":14},"What is Qora?","/en/getting-started/what-is-qora","en/01.getting-started/2.what-is-qora","i-lucide-info",{"title":16,"path":17,"stem":18,"icon":19},"Installation","/en/getting-started/installation","en/01.getting-started/3.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":6},"Quick Start","/en/getting-started/quick-start","en/01.getting-started/4.quick-start",{"title":25,"path":26,"stem":27,"icon":28},"Comparison","/en/getting-started/comparison","en/01.getting-started/5.comparison","i-lucide-scale",{"title":30,"path":31,"stem":32,"icon":33},"Coming from TanStack Query","/en/getting-started/coming-from-tanstack-query","en/01.getting-started/6.coming-from-tanstack-query","i-lucide-arrow-left-right",{"title":35,"path":36,"stem":37,"icon":38},"Migrating from Other Libraries","/en/getting-started/migrating-from-other-libraries","en/01.getting-started/7.migrating-from-other-libraries","i-lucide-arrow-right-left",{"title":40,"path":41,"stem":42,"icon":43},"AI Assistants","/en/getting-started/ai-assistants","en/01.getting-started/8.ai-assistants","i-lucide-sparkles",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"Core Concepts","i-lucide-book-open","/en/core-concepts","en/02.core-concepts",[51,56,61,66,71,76,81,86],{"title":52,"path":53,"stem":54,"icon":55},"Queries","/en/core-concepts/queries","en/02.core-concepts/1.queries","i-lucide-database",{"title":57,"path":58,"stem":59,"icon":60},"Query Keys","/en/core-concepts/query-keys","en/02.core-concepts/2.query-keys","i-lucide-key-round",{"title":62,"path":63,"stem":64,"icon":65},"Query States","/en/core-concepts/query-states","en/02.core-concepts/3.query-states","i-lucide-activity",{"title":67,"path":68,"stem":69,"icon":70},"Caching","/en/core-concepts/caching","en/02.core-concepts/4.caching","i-lucide-layers-3",{"title":72,"path":73,"stem":74,"icon":75},"Stale-While-Revalidate","/en/core-concepts/stale-while-revalidate","en/02.core-concepts/5.stale-while-revalidate","i-lucide-timer-reset",{"title":77,"path":78,"stem":79,"icon":80},"Deduplication","/en/core-concepts/deduplication","en/02.core-concepts/6.deduplication","i-lucide-copy-minus",{"title":82,"path":83,"stem":84,"icon":85},"Mutations & Invalidation","/en/core-concepts/mutations","en/02.core-concepts/7.mutations","i-lucide-pencil-line",{"title":87,"path":88,"stem":89,"icon":90},"Offline & Network","/en/core-concepts/offline","en/02.core-concepts/8.offline","i-lucide-wifi-off",{"title":92,"icon":93,"path":94,"stem":95,"children":96,"page":44},"Flutter Integration","i-lucide-smartphone","/en/flutter-integration","en/03.flutter-integration",[97,102,107,112,117,122,127,132],{"title":98,"path":99,"stem":100,"icon":101},"Setup","/en/flutter-integration/setup","en/03.flutter-integration/1.setup","i-lucide-wrench",{"title":103,"path":104,"stem":105,"icon":106},"QoraScope","/en/flutter-integration/qora-scope","en/03.flutter-integration/2.qora-scope","i-lucide-share-2",{"title":108,"path":109,"stem":110,"icon":111},"QoraBuilder","/en/flutter-integration/qora-builder","en/03.flutter-integration/3.qora-builder","i-lucide-blocks",{"title":113,"path":114,"stem":115,"icon":116},"QoraMutationBuilder","/en/flutter-integration/qora-mutation-builder","en/03.flutter-integration/4.qora-mutation-builder","i-lucide-pencil",{"title":118,"path":119,"stem":120,"icon":121},"InfiniteQueryBuilder","/en/flutter-integration/infinite-query-builder","en/03.flutter-integration/5.infinite-query-builder","i-lucide-infinity",{"title":123,"path":124,"stem":125,"icon":126},"Hooks (flutter_hooks)","/en/flutter-integration/hooks","en/03.flutter-integration/6.hooks","i-lucide-anchor",{"title":128,"path":129,"stem":130,"icon":131},"Network Status Widgets","/en/flutter-integration/network-status","en/03.flutter-integration/7.network-status","i-lucide-wifi",{"title":133,"path":134,"stem":135,"icon":136},"Best Practices","/en/flutter-integration/best-practices","en/03.flutter-integration/8.best-practices","i-lucide-badge-check",{"title":138,"icon":139,"path":140,"stem":141,"children":142,"page":44},"Recipes","i-lucide-chef-hat","/en/recipes","en/04.recipes",[143,147,152,157,162,166,171,176,181,186,191],{"title":144,"path":145,"stem":146,"icon":85},"Mutations","/en/recipes/mutations","en/04.recipes/02.mutations",{"title":148,"path":149,"stem":150,"icon":151},"Optimistic Updates","/en/recipes/optimistic-updates","en/04.recipes/03.optimistic-updates","i-lucide-zap",{"title":153,"path":154,"stem":155,"icon":156},"Persistence","/en/recipes/persistence","en/04.recipes/04.persistence","i-lucide-hard-drive",{"title":158,"path":159,"stem":160,"icon":161},"Network Aware","/en/recipes/network-aware","en/04.recipes/05.network-aware","i-lucide-signal",{"title":163,"path":164,"stem":165,"icon":121},"Infinite Queries","/en/recipes/infinite-queries","en/04.recipes/06.infinite-queries",{"title":167,"path":168,"stem":169,"icon":170},"Dependent Queries","/en/recipes/dependent-queries","en/04.recipes/07.dependent-queries","i-lucide-link-2",{"title":172,"path":173,"stem":174,"icon":175},"Cancellation","/en/recipes/cancel-token","en/04.recipes/08.cancel-token","i-lucide-circle-slash",{"title":177,"path":178,"stem":179,"icon":180},"SSR Hydration","/en/recipes/ssr-hydration","en/04.recipes/09.ssr-hydration","i-lucide-server",{"title":182,"path":183,"stem":184,"icon":185},"Error Handling","/en/recipes/error-handling","en/04.recipes/10.error-handling","i-lucide-shield-alert",{"title":187,"path":188,"stem":189,"icon":190},"Testing","/en/recipes/testing","en/04.recipes/11.testing","i-lucide-flask-conical",{"title":192,"path":193,"stem":194},"Route-Level Prefetching","/en/recipes/prefetch","en/04.recipes/12.prefetch",{"title":196,"icon":197,"path":198,"stem":199,"children":200,"page":44},"API Reference","i-lucide-file-code","/en/api-reference","en/05.api-reference",[201,206,210,214,219,224,228,232],{"title":202,"path":203,"stem":204,"icon":205},"QoraClient","/en/api-reference/qora-client","en/05.api-reference/1.qora-client","i-lucide-cpu",{"title":207,"path":208,"stem":209,"icon":60},"QoraKey","/en/api-reference/qora-key","en/05.api-reference/2.qora-key",{"title":211,"path":212,"stem":213,"icon":65},"QoraState","/en/api-reference/qora-state","en/05.api-reference/3.qora-state",{"title":215,"path":216,"stem":217,"icon":218},"QoraOptions","/en/api-reference/qora-options","en/05.api-reference/4.qora-options","i-lucide-sliders-horizontal",{"title":220,"path":221,"stem":222,"icon":223},"QoraClientConfig","/en/api-reference/qora-client-config","en/05.api-reference/5.qora-client-config","i-lucide-settings-2",{"title":225,"path":226,"stem":227,"icon":85},"Mutation API","/en/api-reference/qora-mutation","en/05.api-reference/6.qora-mutation",{"title":229,"path":230,"stem":231,"icon":156},"PersistQoraClient","/en/api-reference/persist-qora-client","en/05.api-reference/7.persist-qora-client",{"title":233,"path":234,"stem":235,"icon":121},"Infinite Query","/en/api-reference/infinite-query","en/05.api-reference/8.infinite-query",{"title":237,"icon":238,"path":239,"stem":240,"children":241,"page":44},"DevTools","i-lucide-bug","/en/devtools","en/06.devtools",[242,247,252],{"title":243,"path":244,"stem":245,"icon":246},"Overview","/en/devtools/overview","en/06.devtools/1.overview","i-lucide-layout-dashboard",{"title":248,"path":249,"stem":250,"icon":251},"In-App Overlay","/en/devtools/overlay","en/06.devtools/2.overlay","i-lucide-layers",{"title":253,"path":254,"stem":255,"icon":256},"Data Serialization","/en/devtools/data-display","en/06.devtools/4.data-display","i-lucide-eye",{"title":258,"icon":259,"path":260,"stem":261,"children":262,"page":44},"Third Party","i-lucide-puzzle","/en/third-party","en/07.third-party",[263,268,272,277,282,286],{"title":264,"path":265,"stem":266,"icon":267},"With Provider","/en/third-party/with-provider","en/07.third-party/1.with-provider","i-lucide-plug",{"title":269,"path":270,"stem":271,"icon":111},"With BLoC","/en/third-party/with-bloc","en/07.third-party/2.with-bloc",{"title":273,"path":274,"stem":275,"icon":276},"With Riverpod","/en/third-party/with-riverpod","en/07.third-party/3.with-riverpod","i-lucide-droplets",{"title":278,"path":279,"stem":280,"icon":281},"With RxDart","/en/third-party/with-rxdart","en/07.third-party/4.with-rxdart","i-lucide-git-branch",{"title":283,"path":284,"stem":285},"With Dio","/en/third-party/with-dio","en/07.third-party/5.with-dio",{"title":287,"path":288,"stem":289,"icon":290},"With GraphQL","/en/third-party/with-graphql","en/07.third-party/6.with-graphql","i-lucide-binary",{"title":292,"icon":190,"path":293,"stem":294,"children":295,"page":44},"Examples","/en/examples","en/08.examples",[296,301,305,310,314,318,322],{"title":297,"path":298,"stem":299,"icon":300},"Case Study: User List & Detail","/en/examples/basic-query","en/08.examples/1.basic-query","i-lucide-play-circle",{"title":302,"path":303,"stem":304,"icon":151},"Case Study: Optimistic Mutations & Rollback","/en/examples/mutations-optimistic","en/08.examples/2.mutations-optimistic",{"title":306,"path":307,"stem":308,"icon":309},"Case Study: Infinite Scroll Feed","/en/examples/infinite-scroll","en/08.examples/3.infinite-scroll","i-lucide-scroll",{"title":311,"path":312,"stem":313,"icon":90},"Case Study: Offline-First Todo App","/en/examples/offline-first","en/08.examples/4.offline-first",{"title":315,"path":316,"stem":317,"icon":161},"Case Study: Network Awareness","/en/examples/network-awareness","en/08.examples/5.network-awareness",{"title":319,"path":320,"stem":321,"icon":126},"Case Study: Hooks Integration","/en/examples/hooks-integration","en/08.examples/6.hooks-integration",{"title":323,"path":324,"stem":325,"icon":326},"Case Study: Complete Todo App","/en/examples/complete-todo-app","en/08.examples/7.complete-todo-app","i-lucide-check-square",{"title":328,"icon":329,"path":330,"stem":331,"children":332,"page":44},"Help","i-lucide-life-buoy","/en/help","en/09.help",[333,338],{"title":334,"path":335,"stem":336,"icon":337},"FAQ","/en/help/faq","en/09.help/1.faq","i-lucide-circle-help",{"title":339,"path":340,"stem":341,"icon":101},"Troubleshooting","/en/help/troubleshooting","en/09.help/2.troubleshooting",{"id":343,"title":323,"body":344,"description":3375,"extension":3376,"links":3377,"meta":3378,"navigation":3379,"path":324,"seo":3380,"stem":325,"__hash__":3383},"docs_en/en/08.examples/7.complete-todo-app.md",{"type":345,"value":346,"toc":3357},"minimark",[347,352,360,371,374,378,485,487,491,494,504,509,1020,1045,1047,1051,1055,1063,1635,1659,1662,1669,2149,2166,2170,2180,2629,2639,2643,2652,2827,2832,2842,3081,3092,3094,3098,3131,3133,3137,3143,3150,3152,3156,3308,3310,3314,3353],[348,349,351],"h2",{"id":350},"objective","Objective",[353,354,355,359],"p",{},[356,357,358],"strong",{},"Context",": Real apps rarely use one feature in isolation. Authentication gates data access, infinite scroll fetches paginated server data, mutations need optimistic feedback with rollback, and connectivity is unreliable. Each concern is straightforward in isolation; the challenge is composing them without coupling.",[353,361,362,363,370],{},"This example combines every production-grade Qora capability into a single app against the live ",[364,365,369],"a",{"href":366,"rel":367},"https://jsonplaceholder.typicode.com",[368],"nofollow","JSONPlaceholder"," API. The result is a reference implementation: each pattern is self-contained and extractable to a real project.",[372,373],"hr",{},[348,375,377],{"id":376},"key-features-demonstrated","Key Features Demonstrated",[379,380,381,398,411,419,427,448,457,469,477],"ul",{},[382,383,384,393,394,397],"li",{},[356,385,386,389,390],{},[387,388,229],"code",{}," + ",[387,391,392],{},"HiveStorageAdapter",": write-through persistence; ",[387,395,396],{},"hydrate()"," warms the cache before the first frame",[382,399,400,410],{},[356,401,402,405,406,409],{},[387,403,404],{},"AuthService"," (",[387,407,408],{},"ValueNotifier",")",": user session state independent of Qora; gates which todo list is fetched",[382,412,413,418],{},[356,414,415],{},[387,416,417],{},"InfiniteQueryBuilder\u003CTodosPage, int>",": cursor-based pagination with a sentinel-item scroll trigger",[382,420,421,426],{},[356,422,423],{},[387,424,425],{},"InfiniteQueryController.refetch()",": pull-to-refresh that resets pagination and re-fetches from page 1",[382,428,429,432,433,435,436,439,440,443,444,447],{},[356,430,431],{},"Optimistic create with offline queue",": ",[387,434,113],{}," with ",[387,437,438],{},"offlineQueue: true",", ",[387,441,442],{},"optimisticResponse",", and a ",[387,445,446],{},"_pendingTodos"," local list for instant UI feedback",[382,449,450,453,454,456],{},[356,451,452],{},"Per-tile toggle and delete mutations",": independent ",[387,455,113],{}," per tile; toggling one item never blocks another",[382,458,459,464,465,468],{},[356,460,461],{},[387,462,463],{},"SimulatedConnectivityManager",": custom ",[387,466,467],{},"ConnectivityManager"," implementation for self-contained offline demo via an AppBar toggle",[382,470,471,476],{},[356,472,473],{},[387,474,475],{},"NetworkMode.offlineFirst",": stale cache served instantly on reconnect; background revalidation triggered automatically",[382,478,479,484],{},[356,480,481],{},[387,482,483],{},"NetworkStatusIndicator",": zero-boilerplate offline banner using the built-in widget",[372,486],{},[348,488,490],{"id":489},"architecture-highlights","Architecture Highlights",[353,492,493],{},"The app is organised into feature modules with a shared infrastructure core. Qora APIs surface only at the UI layer; models, API clients, and serializers have no Qora dependency:",[495,496,502],"pre",{"className":497,"code":499,"language":500,"meta":501},[498],"language-text","ex_07_complete_todo_app/lib/\n├── main.dart                                    ← bootstrap: PersistQoraClient, hydrate, QoraScope\n├── core/\n│   ├── auth/\n│   │   └── auth_service.dart                   ← ValueNotifier\u003CAuthUser?>, login/logout\n│   ├── connectivity/\n│   │   └── simulated_connectivity_manager.dart ← ConnectivityManager implementation (toggle button)\n│   └── persistence/\n│       ├── hive_storage_adapter.dart           ← StorageAdapter over Hive box\n│       └── serializers.dart                    ← registerAllSerializers()\n├── features/\n│   ├── auth/ui/login_screen.dart               ← form, calls AuthService.login()\n│   └── todos/\n│       ├── data/todo_api.dart                  ← JSONPlaceholder HTTP client\n│       ├── models/todo.dart                    ← Todo, TodosPage, CreateTodoInput, ToggleTodoInput\n│       └── ui/\n│           ├── todo_list_screen.dart           ← all Qora patterns composed together\n│           └── todo_tile.dart                  ← presentation widget, decoupled from mutations\n└── shared/widgets/offline_banner.dart          ← OfflineBannerWrapper using NetworkStatusIndicator\n","text","",[387,503,499],{"__ignoreMap":501},[505,506,508],"h3",{"id":507},"bootstrap-sequence","Bootstrap sequence",[495,510,515],{"className":511,"code":512,"filename":513,"language":514,"meta":501,"style":501},"language-dart shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","Future\u003Cvoid> main() async {\n  WidgetsFlutterBinding.ensureInitialized();\n\n  final storage = await HiveStorageAdapter.open('qora_cache');\n  final connectivity = SimulatedConnectivityManager();\n\n  final client = PersistQoraClient(\n    storage: storage,\n    persistDuration: const Duration(days: 30),\n    config: QoraClientConfig(\n      defaultOptions: const QoraOptions(\n        staleTime: Duration(minutes: 5),\n        networkMode: NetworkMode.offlineFirst,\n      ),\n      reconnectStrategy: const ReconnectStrategy(\n        maxConcurrent: 3,\n        jitter: Duration(milliseconds: 150),\n      ),\n      debugMode: kDebugMode,\n    ),\n  );\n\n  registerAllSerializers(client);\n  await client.hydrate(); // ← warm cache before first frame\n\n  runApp(\n    QoraScope(\n      client: client,\n      connectivityManager: connectivity,\n      lifecycleManager: FlutterLifecycleManager(qoraClient: client),\n      child: OfflineBannerWrapper(child: _App(auth: AuthService(), connectivity: connectivity)),\n    ),\n  );\n}\n","lib/main.dart","dart",[387,516,517,551,570,577,611,628,633,649,664,691,704,719,741,759,767,782,795,817,824,837,845,853,858,869,892,897,905,913,925,938,959,1000,1007,1014],{"__ignoreMap":501},[518,519,522,526,530,534,537,541,544,548],"span",{"class":520,"line":521},"line",1,[518,523,525],{"class":524},"sBMFI","Future",[518,527,529],{"class":528},"sTEyZ","\u003C",[518,531,533],{"class":532},"spNyl","void",[518,535,536],{"class":528},"> ",[518,538,540],{"class":539},"s2Zo4","main",[518,542,543],{"class":528},"() ",[518,545,547],{"class":546},"s7zQu","async",[518,549,550],{"class":528}," {\n",[518,552,554,557,561,564,567],{"class":520,"line":553},2,[518,555,556],{"class":524},"  WidgetsFlutterBinding",[518,558,560],{"class":559},"sMK4o",".",[518,562,563],{"class":539},"ensureInitialized",[518,565,566],{"class":528},"()",[518,568,569],{"class":559},";\n",[518,571,573],{"class":520,"line":572},3,[518,574,576],{"emptyLinePlaceholder":575},true,"\n",[518,578,580,583,586,589,592,595,597,600,603,607,609],{"class":520,"line":579},4,[518,581,582],{"class":532},"  final",[518,584,585],{"class":528}," storage ",[518,587,588],{"class":559},"=",[518,590,591],{"class":546}," await",[518,593,594],{"class":524}," HiveStorageAdapter",[518,596,560],{"class":559},[518,598,599],{"class":539},"open",[518,601,602],{"class":528},"(",[518,604,606],{"class":605},"sfazB","'qora_cache'",[518,608,409],{"class":528},[518,610,569],{"class":559},[518,612,614,616,619,621,624,626],{"class":520,"line":613},5,[518,615,582],{"class":532},[518,617,618],{"class":528}," connectivity ",[518,620,588],{"class":559},[518,622,623],{"class":524}," SimulatedConnectivityManager",[518,625,566],{"class":528},[518,627,569],{"class":559},[518,629,631],{"class":520,"line":630},6,[518,632,576],{"emptyLinePlaceholder":575},[518,634,636,638,641,643,646],{"class":520,"line":635},7,[518,637,582],{"class":532},[518,639,640],{"class":528}," client ",[518,642,588],{"class":559},[518,644,645],{"class":524}," PersistQoraClient",[518,647,648],{"class":528},"(\n",[518,650,652,655,658,661],{"class":520,"line":651},8,[518,653,654],{"class":528},"    storage",[518,656,657],{"class":559},":",[518,659,660],{"class":528}," storage",[518,662,663],{"class":559},",\n",[518,665,667,670,672,675,678,681,683,687,689],{"class":520,"line":666},9,[518,668,669],{"class":528},"    persistDuration",[518,671,657],{"class":559},[518,673,674],{"class":532}," const",[518,676,677],{"class":524}," Duration",[518,679,680],{"class":528},"(days",[518,682,657],{"class":559},[518,684,686],{"class":685},"sbssI"," 30",[518,688,409],{"class":528},[518,690,663],{"class":559},[518,692,694,697,699,702],{"class":520,"line":693},10,[518,695,696],{"class":528},"    config",[518,698,657],{"class":559},[518,700,701],{"class":524}," QoraClientConfig",[518,703,648],{"class":528},[518,705,707,710,712,714,717],{"class":520,"line":706},11,[518,708,709],{"class":528},"      defaultOptions",[518,711,657],{"class":559},[518,713,674],{"class":532},[518,715,716],{"class":524}," QoraOptions",[518,718,648],{"class":528},[518,720,722,725,727,729,732,734,737,739],{"class":520,"line":721},12,[518,723,724],{"class":528},"        staleTime",[518,726,657],{"class":559},[518,728,677],{"class":524},[518,730,731],{"class":528},"(minutes",[518,733,657],{"class":559},[518,735,736],{"class":685}," 5",[518,738,409],{"class":528},[518,740,663],{"class":559},[518,742,744,747,749,752,754,757],{"class":520,"line":743},13,[518,745,746],{"class":528},"        networkMode",[518,748,657],{"class":559},[518,750,751],{"class":524}," NetworkMode",[518,753,560],{"class":559},[518,755,756],{"class":528},"offlineFirst",[518,758,663],{"class":559},[518,760,762,765],{"class":520,"line":761},14,[518,763,764],{"class":528},"      )",[518,766,663],{"class":559},[518,768,770,773,775,777,780],{"class":520,"line":769},15,[518,771,772],{"class":528},"      reconnectStrategy",[518,774,657],{"class":559},[518,776,674],{"class":532},[518,778,779],{"class":524}," ReconnectStrategy",[518,781,648],{"class":528},[518,783,785,788,790,793],{"class":520,"line":784},16,[518,786,787],{"class":528},"        maxConcurrent",[518,789,657],{"class":559},[518,791,792],{"class":685}," 3",[518,794,663],{"class":559},[518,796,798,801,803,805,808,810,813,815],{"class":520,"line":797},17,[518,799,800],{"class":528},"        jitter",[518,802,657],{"class":559},[518,804,677],{"class":524},[518,806,807],{"class":528},"(milliseconds",[518,809,657],{"class":559},[518,811,812],{"class":685}," 150",[518,814,409],{"class":528},[518,816,663],{"class":559},[518,818,820,822],{"class":520,"line":819},18,[518,821,764],{"class":528},[518,823,663],{"class":559},[518,825,827,830,832,835],{"class":520,"line":826},19,[518,828,829],{"class":528},"      debugMode",[518,831,657],{"class":559},[518,833,834],{"class":528}," kDebugMode",[518,836,663],{"class":559},[518,838,840,843],{"class":520,"line":839},20,[518,841,842],{"class":528},"    )",[518,844,663],{"class":559},[518,846,848,851],{"class":520,"line":847},21,[518,849,850],{"class":528},"  )",[518,852,569],{"class":559},[518,854,856],{"class":520,"line":855},22,[518,857,576],{"emptyLinePlaceholder":575},[518,859,861,864,867],{"class":520,"line":860},23,[518,862,863],{"class":539},"  registerAllSerializers",[518,865,866],{"class":528},"(client)",[518,868,569],{"class":559},[518,870,872,875,878,880,883,885,888],{"class":520,"line":871},24,[518,873,874],{"class":546},"  await",[518,876,877],{"class":528}," client",[518,879,560],{"class":559},[518,881,882],{"class":539},"hydrate",[518,884,566],{"class":528},[518,886,887],{"class":559},";",[518,889,891],{"class":890},"sHwdD"," // ← warm cache before first frame\n",[518,893,895],{"class":520,"line":894},25,[518,896,576],{"emptyLinePlaceholder":575},[518,898,900,903],{"class":520,"line":899},26,[518,901,902],{"class":539},"  runApp",[518,904,648],{"class":528},[518,906,908,911],{"class":520,"line":907},27,[518,909,910],{"class":524},"    QoraScope",[518,912,648],{"class":528},[518,914,916,919,921,923],{"class":520,"line":915},28,[518,917,918],{"class":528},"      client",[518,920,657],{"class":559},[518,922,877],{"class":528},[518,924,663],{"class":559},[518,926,928,931,933,936],{"class":520,"line":927},29,[518,929,930],{"class":528},"      connectivityManager",[518,932,657],{"class":559},[518,934,935],{"class":528}," connectivity",[518,937,663],{"class":559},[518,939,941,944,946,949,952,954,957],{"class":520,"line":940},30,[518,942,943],{"class":528},"      lifecycleManager",[518,945,657],{"class":559},[518,947,948],{"class":524}," FlutterLifecycleManager",[518,950,951],{"class":528},"(qoraClient",[518,953,657],{"class":559},[518,955,956],{"class":528}," client)",[518,958,663],{"class":559},[518,960,962,965,967,970,973,975,978,981,983,986,988,991,993,995,998],{"class":520,"line":961},31,[518,963,964],{"class":528},"      child",[518,966,657],{"class":559},[518,968,969],{"class":524}," OfflineBannerWrapper",[518,971,972],{"class":528},"(child",[518,974,657],{"class":559},[518,976,977],{"class":524}," _App",[518,979,980],{"class":528},"(auth",[518,982,657],{"class":559},[518,984,985],{"class":524}," AuthService",[518,987,566],{"class":528},[518,989,990],{"class":559},",",[518,992,935],{"class":528},[518,994,657],{"class":559},[518,996,997],{"class":528}," connectivity))",[518,999,663],{"class":559},[518,1001,1003,1005],{"class":520,"line":1002},32,[518,1004,842],{"class":528},[518,1006,663],{"class":559},[518,1008,1010,1012],{"class":520,"line":1009},33,[518,1011,850],{"class":528},[518,1013,569],{"class":559},[518,1015,1017],{"class":520,"line":1016},34,[518,1018,1019],{"class":528},"}\n",[353,1021,1022,1024,1025,1028,1029,1032,1033,1036,1037,1040,1041,1044],{},[387,1023,404],{}," extends ",[387,1026,1027],{},"ValueNotifier\u003CAuthUser?>",". ",[387,1030,1031],{},"_App"," listens to it with ",[387,1034,1035],{},"ValueListenableBuilder"," and navigates between ",[387,1038,1039],{},"LoginScreen"," and ",[387,1042,1043],{},"TodoListScreen"," reactively, no routing library, no extra state management layer.",[372,1046],{},[348,1048,1050],{"id":1049},"implementation-details","Implementation Details",[505,1052,1054],{"id":1053},"infinite-scroll-with-sentinel-pattern","Infinite scroll with sentinel pattern",[353,1056,1057,1059,1060,657],{},[387,1058,118],{}," fetches paginated todos from JSONPlaceholder. The sentinel pattern avoids a separate scroll controller and ",[387,1061,1062],{},"notificationPredicate",[495,1064,1067],{"className":511,"code":1065,"filename":1066,"language":514,"meta":501,"style":501},"InfiniteQueryBuilder\u003CTodosPage, int>(\n  queryKey: ['todos', auth.id],\n  fetcher: (page) => api.getTodosPage(auth.id, page),\n  options: InfiniteQueryOptions(\n    initialPageParam: 1,\n    getNextPageParam: (last, all) => last.hasMore ? all.length + 1 : null,\n  ),\n  builder: (context, state, controller) {\n    // ... state handling\n    final allItems = [\n      ..._pendingTodos, // optimistic items first\n      ...state.data!.flatten((p) => p.todos),\n    ];\n\n    return RefreshIndicator(\n      onRefresh: () => controller.refetch(),\n      child: ListView.builder(\n        itemCount: allItems.length + 1, // +1 for sentinel\n        itemBuilder: (context, index) {\n          if (index == allItems.length) {\n            // Sentinel: auto-fetch next page when scrolled into view\n            if (state.hasNextPage && !state.isFetchingNextPage) {\n              controller.fetchNextPage();\n            }\n            return state.isFetchingNextPage\n                ? const _PageLoader()\n                : const SizedBox.shrink();\n          }\n          final todo = allItems[index];\n          return todo.isOptimistic\n              ? TodoTile(todo: todo) // optimistic: no mutation callbacks\n              : _MutableTodoTile(todo: todo, api: api, auth: auth);\n        },\n      ),\n    );\n  },\n)\n","lib/features/todos/ui/todo_list_screen.dart",[387,1068,1069,1086,1111,1146,1158,1170,1217,1223,1243,1248,1261,1273,1306,1313,1317,1327,1351,1367,1390,1404,1422,1427,1453,1467,1472,1484,1497,1516,1521,1536,1549,1568,1601,1608,1614,1621,1629],{"__ignoreMap":501},[518,1070,1071,1073,1075,1078,1080,1083],{"class":520,"line":521},[518,1072,118],{"class":524},[518,1074,529],{"class":528},[518,1076,1077],{"class":524},"TodosPage",[518,1079,439],{"class":528},[518,1081,1082],{"class":524},"int",[518,1084,1085],{"class":528},">(\n",[518,1087,1088,1091,1093,1096,1099,1101,1104,1106,1109],{"class":520,"line":553},[518,1089,1090],{"class":528},"  queryKey",[518,1092,657],{"class":559},[518,1094,1095],{"class":528}," [",[518,1097,1098],{"class":605},"'todos'",[518,1100,990],{"class":559},[518,1102,1103],{"class":528}," auth",[518,1105,560],{"class":559},[518,1107,1108],{"class":528},"id]",[518,1110,663],{"class":559},[518,1112,1113,1116,1118,1121,1124,1127,1129,1132,1134,1136,1139,1141,1144],{"class":520,"line":572},[518,1114,1115],{"class":528},"  fetcher",[518,1117,657],{"class":559},[518,1119,1120],{"class":528}," (page) ",[518,1122,1123],{"class":559},"=>",[518,1125,1126],{"class":528}," api",[518,1128,560],{"class":559},[518,1130,1131],{"class":539},"getTodosPage",[518,1133,980],{"class":528},[518,1135,560],{"class":559},[518,1137,1138],{"class":528},"id",[518,1140,990],{"class":559},[518,1142,1143],{"class":528}," page)",[518,1145,663],{"class":559},[518,1147,1148,1151,1153,1156],{"class":520,"line":579},[518,1149,1150],{"class":528},"  options",[518,1152,657],{"class":559},[518,1154,1155],{"class":524}," InfiniteQueryOptions",[518,1157,648],{"class":528},[518,1159,1160,1163,1165,1168],{"class":520,"line":613},[518,1161,1162],{"class":528},"    initialPageParam",[518,1164,657],{"class":559},[518,1166,1167],{"class":685}," 1",[518,1169,663],{"class":559},[518,1171,1172,1175,1177,1180,1182,1185,1187,1190,1192,1195,1198,1201,1203,1206,1209,1211,1214],{"class":520,"line":630},[518,1173,1174],{"class":528},"    getNextPageParam",[518,1176,657],{"class":559},[518,1178,1179],{"class":528}," (last",[518,1181,990],{"class":559},[518,1183,1184],{"class":528}," all) ",[518,1186,1123],{"class":559},[518,1188,1189],{"class":528}," last",[518,1191,560],{"class":559},[518,1193,1194],{"class":528},"hasMore ",[518,1196,1197],{"class":559},"?",[518,1199,1200],{"class":528}," all",[518,1202,560],{"class":559},[518,1204,1205],{"class":528},"length ",[518,1207,1208],{"class":559},"+",[518,1210,1167],{"class":685},[518,1212,1213],{"class":559}," :",[518,1215,1216],{"class":559}," null,\n",[518,1218,1219,1221],{"class":520,"line":635},[518,1220,850],{"class":528},[518,1222,663],{"class":559},[518,1224,1225,1228,1230,1233,1235,1238,1240],{"class":520,"line":651},[518,1226,1227],{"class":528},"  builder",[518,1229,657],{"class":559},[518,1231,1232],{"class":528}," (context",[518,1234,990],{"class":559},[518,1236,1237],{"class":528}," state",[518,1239,990],{"class":559},[518,1241,1242],{"class":528}," controller) {\n",[518,1244,1245],{"class":520,"line":666},[518,1246,1247],{"class":890},"    // ... state handling\n",[518,1249,1250,1253,1256,1258],{"class":520,"line":693},[518,1251,1252],{"class":532},"    final",[518,1254,1255],{"class":528}," allItems ",[518,1257,588],{"class":559},[518,1259,1260],{"class":528}," [\n",[518,1262,1263,1266,1268,1270],{"class":520,"line":706},[518,1264,1265],{"class":559},"      ...",[518,1267,446],{"class":528},[518,1269,990],{"class":559},[518,1271,1272],{"class":890}," // optimistic items first\n",[518,1274,1275,1277,1280,1282,1285,1288,1291,1294,1296,1299,1301,1304],{"class":520,"line":721},[518,1276,1265],{"class":559},[518,1278,1279],{"class":528},"state",[518,1281,560],{"class":559},[518,1283,1284],{"class":528},"data",[518,1286,1287],{"class":559},"!.",[518,1289,1290],{"class":539},"flatten",[518,1292,1293],{"class":528},"((p) ",[518,1295,1123],{"class":559},[518,1297,1298],{"class":528}," p",[518,1300,560],{"class":559},[518,1302,1303],{"class":528},"todos)",[518,1305,663],{"class":559},[518,1307,1308,1311],{"class":520,"line":743},[518,1309,1310],{"class":528},"    ]",[518,1312,569],{"class":559},[518,1314,1315],{"class":520,"line":761},[518,1316,576],{"emptyLinePlaceholder":575},[518,1318,1319,1322,1325],{"class":520,"line":769},[518,1320,1321],{"class":546},"    return",[518,1323,1324],{"class":524}," RefreshIndicator",[518,1326,648],{"class":528},[518,1328,1329,1332,1334,1337,1339,1342,1344,1347,1349],{"class":520,"line":784},[518,1330,1331],{"class":528},"      onRefresh",[518,1333,657],{"class":559},[518,1335,1336],{"class":528}," () ",[518,1338,1123],{"class":559},[518,1340,1341],{"class":528}," controller",[518,1343,560],{"class":559},[518,1345,1346],{"class":539},"refetch",[518,1348,566],{"class":528},[518,1350,663],{"class":559},[518,1352,1353,1355,1357,1360,1362,1365],{"class":520,"line":797},[518,1354,964],{"class":528},[518,1356,657],{"class":559},[518,1358,1359],{"class":524}," ListView",[518,1361,560],{"class":559},[518,1363,1364],{"class":539},"builder",[518,1366,648],{"class":528},[518,1368,1369,1372,1374,1377,1379,1381,1383,1385,1387],{"class":520,"line":819},[518,1370,1371],{"class":528},"        itemCount",[518,1373,657],{"class":559},[518,1375,1376],{"class":528}," allItems",[518,1378,560],{"class":559},[518,1380,1205],{"class":528},[518,1382,1208],{"class":559},[518,1384,1167],{"class":685},[518,1386,990],{"class":559},[518,1388,1389],{"class":890}," // +1 for sentinel\n",[518,1391,1392,1395,1397,1399,1401],{"class":520,"line":826},[518,1393,1394],{"class":528},"        itemBuilder",[518,1396,657],{"class":559},[518,1398,1232],{"class":528},[518,1400,990],{"class":559},[518,1402,1403],{"class":528}," index) {\n",[518,1405,1406,1409,1412,1415,1417,1419],{"class":520,"line":839},[518,1407,1408],{"class":546},"          if",[518,1410,1411],{"class":528}," (index ",[518,1413,1414],{"class":559},"==",[518,1416,1376],{"class":528},[518,1418,560],{"class":559},[518,1420,1421],{"class":528},"length) {\n",[518,1423,1424],{"class":520,"line":847},[518,1425,1426],{"class":890},"            // Sentinel: auto-fetch next page when scrolled into view\n",[518,1428,1429,1432,1435,1437,1440,1443,1446,1448,1450],{"class":520,"line":855},[518,1430,1431],{"class":546},"            if",[518,1433,1434],{"class":528}," (state",[518,1436,560],{"class":559},[518,1438,1439],{"class":528},"hasNextPage ",[518,1441,1442],{"class":559},"&&",[518,1444,1445],{"class":559}," !",[518,1447,1279],{"class":528},[518,1449,560],{"class":559},[518,1451,1452],{"class":528},"isFetchingNextPage) {\n",[518,1454,1455,1458,1460,1463,1465],{"class":520,"line":860},[518,1456,1457],{"class":528},"              controller",[518,1459,560],{"class":559},[518,1461,1462],{"class":539},"fetchNextPage",[518,1464,566],{"class":528},[518,1466,569],{"class":559},[518,1468,1469],{"class":520,"line":871},[518,1470,1471],{"class":528},"            }\n",[518,1473,1474,1477,1479,1481],{"class":520,"line":894},[518,1475,1476],{"class":546},"            return",[518,1478,1237],{"class":528},[518,1480,560],{"class":559},[518,1482,1483],{"class":528},"isFetchingNextPage\n",[518,1485,1486,1489,1491,1494],{"class":520,"line":899},[518,1487,1488],{"class":559},"                ?",[518,1490,674],{"class":532},[518,1492,1493],{"class":524}," _PageLoader",[518,1495,1496],{"class":528},"()\n",[518,1498,1499,1502,1504,1507,1509,1512,1514],{"class":520,"line":907},[518,1500,1501],{"class":559},"                :",[518,1503,674],{"class":532},[518,1505,1506],{"class":524}," SizedBox",[518,1508,560],{"class":559},[518,1510,1511],{"class":539},"shrink",[518,1513,566],{"class":528},[518,1515,569],{"class":559},[518,1517,1518],{"class":520,"line":915},[518,1519,1520],{"class":528},"          }\n",[518,1522,1523,1526,1529,1531,1534],{"class":520,"line":927},[518,1524,1525],{"class":532},"          final",[518,1527,1528],{"class":528}," todo ",[518,1530,588],{"class":559},[518,1532,1533],{"class":528}," allItems[index]",[518,1535,569],{"class":559},[518,1537,1538,1541,1544,1546],{"class":520,"line":940},[518,1539,1540],{"class":546},"          return",[518,1542,1543],{"class":528}," todo",[518,1545,560],{"class":559},[518,1547,1548],{"class":528},"isOptimistic\n",[518,1550,1551,1554,1557,1560,1562,1565],{"class":520,"line":961},[518,1552,1553],{"class":559},"              ?",[518,1555,1556],{"class":524}," TodoTile",[518,1558,1559],{"class":528},"(todo",[518,1561,657],{"class":559},[518,1563,1564],{"class":528}," todo) ",[518,1566,1567],{"class":890},"// optimistic: no mutation callbacks\n",[518,1569,1570,1573,1576,1578,1580,1582,1584,1586,1588,1590,1592,1594,1596,1599],{"class":520,"line":1002},[518,1571,1572],{"class":559},"              :",[518,1574,1575],{"class":524}," _MutableTodoTile",[518,1577,1559],{"class":528},[518,1579,657],{"class":559},[518,1581,1543],{"class":528},[518,1583,990],{"class":559},[518,1585,1126],{"class":528},[518,1587,657],{"class":559},[518,1589,1126],{"class":528},[518,1591,990],{"class":559},[518,1593,1103],{"class":528},[518,1595,657],{"class":559},[518,1597,1598],{"class":528}," auth)",[518,1600,569],{"class":559},[518,1602,1603,1606],{"class":520,"line":1009},[518,1604,1605],{"class":528},"        }",[518,1607,663],{"class":559},[518,1609,1610,1612],{"class":520,"line":1016},[518,1611,764],{"class":528},[518,1613,663],{"class":559},[518,1615,1617,1619],{"class":520,"line":1616},35,[518,1618,842],{"class":528},[518,1620,569],{"class":559},[518,1622,1624,1627],{"class":520,"line":1623},36,[518,1625,1626],{"class":528},"  }",[518,1628,663],{"class":559},[518,1630,1632],{"class":520,"line":1631},37,[518,1633,1634],{"class":528},")\n",[353,1636,1637,1639,1640,1643,1644,1647,1648,1651,1652,1655,1656,1658],{},[387,1638,446],{}," is a local ",[387,1641,1642],{},"List\u003CTodo>"," in the screen's ",[387,1645,1646],{},"State",". It holds optimistic items created while a mutation is in-flight or queued offline. These items are prepended to the list and rendered with the ",[387,1649,1650],{},"isOptimistic"," visual style in ",[387,1653,1654],{},"TodoTile",". When the mutation succeeds, the list is invalidated and ",[387,1657,446],{}," is cleared.",[505,1660,431],{"id":1661},"optimistic-create-with-offline-queue",[353,1663,1664,1665,1668],{},"The create mutation uses a ",[387,1666,1667],{},"TContext"," snapshot to enable rollback:",[495,1670,1672],{"className":511,"code":1671,"filename":1066,"language":514,"meta":501,"style":501},"QoraMutationBuilder\u003CTodo, CreateTodoInput, List\u003CTodo>>(\n  mutator: api.createTodo,\n  options: MutationOptions(\n    offlineQueue: true,\n    optimisticResponse: (input) => Todo.optimistic(title: input.title),\n    onMutate: (input) async {\n      // Snapshot current pending list for rollback\n      final snapshot = List\u003CTodo>.from(_pendingTodos);\n      setState(() => _pendingTodos.add(Todo.optimistic(title: input.title)));\n      return snapshot;\n    },\n    onSuccess: (_, __, ___) async {\n      setState(() => _pendingTodos.clear());\n      context.qora.invalidate(['todos', auth.id]);\n    },\n    onError: (_, __, snapshot) async {\n      // Restore pre-mutation pending list\n      setState(() => _pendingTodos = snapshot ?? []);\n    },\n  ),\n  builder: (context, state, mutate) {\n    return FloatingActionButton(\n      onPressed: state.isPending ? null : () => _showCreateDialog(mutate),\n      child: state.isPending\n          ? const CircularProgressIndicator(strokeWidth: 2)\n          : const Icon(Icons.add),\n    );\n  },\n)\n",[387,1673,1674,1700,1716,1727,1737,1772,1785,1790,1820,1859,1869,1876,1900,1920,1951,1957,1979,1984,2007,2013,2019,2036,2045,2078,2091,2111,2133,2139,2145],{"__ignoreMap":501},[518,1675,1676,1678,1680,1683,1685,1688,1690,1693,1695,1697],{"class":520,"line":521},[518,1677,113],{"class":524},[518,1679,529],{"class":528},[518,1681,1682],{"class":524},"Todo",[518,1684,439],{"class":528},[518,1686,1687],{"class":524},"CreateTodoInput",[518,1689,439],{"class":528},[518,1691,1692],{"class":524},"List",[518,1694,529],{"class":528},[518,1696,1682],{"class":524},[518,1698,1699],{"class":528},">>(\n",[518,1701,1702,1705,1707,1709,1711,1714],{"class":520,"line":553},[518,1703,1704],{"class":528},"  mutator",[518,1706,657],{"class":559},[518,1708,1126],{"class":528},[518,1710,560],{"class":559},[518,1712,1713],{"class":528},"createTodo",[518,1715,663],{"class":559},[518,1717,1718,1720,1722,1725],{"class":520,"line":572},[518,1719,1150],{"class":528},[518,1721,657],{"class":559},[518,1723,1724],{"class":524}," MutationOptions",[518,1726,648],{"class":528},[518,1728,1729,1732,1734],{"class":520,"line":579},[518,1730,1731],{"class":528},"    offlineQueue",[518,1733,657],{"class":559},[518,1735,1736],{"class":559}," true,\n",[518,1738,1739,1742,1744,1747,1749,1752,1754,1757,1760,1762,1765,1767,1770],{"class":520,"line":613},[518,1740,1741],{"class":528},"    optimisticResponse",[518,1743,657],{"class":559},[518,1745,1746],{"class":528}," (input) ",[518,1748,1123],{"class":559},[518,1750,1751],{"class":524}," Todo",[518,1753,560],{"class":559},[518,1755,1756],{"class":539},"optimistic",[518,1758,1759],{"class":528},"(title",[518,1761,657],{"class":559},[518,1763,1764],{"class":528}," input",[518,1766,560],{"class":559},[518,1768,1769],{"class":528},"title)",[518,1771,663],{"class":559},[518,1773,1774,1777,1779,1781,1783],{"class":520,"line":630},[518,1775,1776],{"class":528},"    onMutate",[518,1778,657],{"class":559},[518,1780,1746],{"class":528},[518,1782,547],{"class":546},[518,1784,550],{"class":528},[518,1786,1787],{"class":520,"line":635},[518,1788,1789],{"class":890},"      // Snapshot current pending list for rollback\n",[518,1791,1792,1795,1798,1800,1803,1805,1807,1810,1812,1815,1818],{"class":520,"line":651},[518,1793,1794],{"class":532},"      final",[518,1796,1797],{"class":528}," snapshot ",[518,1799,588],{"class":559},[518,1801,1802],{"class":524}," List",[518,1804,529],{"class":528},[518,1806,1682],{"class":524},[518,1808,1809],{"class":528},">",[518,1811,560],{"class":559},[518,1813,1814],{"class":539},"from",[518,1816,1817],{"class":528},"(_pendingTodos)",[518,1819,569],{"class":559},[518,1821,1822,1825,1828,1830,1833,1835,1838,1840,1842,1844,1846,1848,1850,1852,1854,1857],{"class":520,"line":666},[518,1823,1824],{"class":539},"      setState",[518,1826,1827],{"class":528},"(() ",[518,1829,1123],{"class":559},[518,1831,1832],{"class":528}," _pendingTodos",[518,1834,560],{"class":559},[518,1836,1837],{"class":539},"add",[518,1839,602],{"class":528},[518,1841,1682],{"class":524},[518,1843,560],{"class":559},[518,1845,1756],{"class":539},[518,1847,1759],{"class":528},[518,1849,657],{"class":559},[518,1851,1764],{"class":528},[518,1853,560],{"class":559},[518,1855,1856],{"class":528},"title)))",[518,1858,569],{"class":559},[518,1860,1861,1864,1867],{"class":520,"line":693},[518,1862,1863],{"class":546},"      return",[518,1865,1866],{"class":528}," snapshot",[518,1868,569],{"class":559},[518,1870,1871,1874],{"class":520,"line":706},[518,1872,1873],{"class":528},"    }",[518,1875,663],{"class":559},[518,1877,1878,1881,1883,1886,1888,1891,1893,1896,1898],{"class":520,"line":721},[518,1879,1880],{"class":528},"    onSuccess",[518,1882,657],{"class":559},[518,1884,1885],{"class":528}," (_",[518,1887,990],{"class":559},[518,1889,1890],{"class":528}," __",[518,1892,990],{"class":559},[518,1894,1895],{"class":528}," ___) ",[518,1897,547],{"class":546},[518,1899,550],{"class":528},[518,1901,1902,1904,1906,1908,1910,1912,1915,1918],{"class":520,"line":743},[518,1903,1824],{"class":539},[518,1905,1827],{"class":528},[518,1907,1123],{"class":559},[518,1909,1832],{"class":528},[518,1911,560],{"class":559},[518,1913,1914],{"class":539},"clear",[518,1916,1917],{"class":528},"())",[518,1919,569],{"class":559},[518,1921,1922,1925,1927,1930,1932,1935,1938,1940,1942,1944,1946,1949],{"class":520,"line":761},[518,1923,1924],{"class":528},"      context",[518,1926,560],{"class":559},[518,1928,1929],{"class":528},"qora",[518,1931,560],{"class":559},[518,1933,1934],{"class":539},"invalidate",[518,1936,1937],{"class":528},"([",[518,1939,1098],{"class":605},[518,1941,990],{"class":559},[518,1943,1103],{"class":528},[518,1945,560],{"class":559},[518,1947,1948],{"class":528},"id])",[518,1950,569],{"class":559},[518,1952,1953,1955],{"class":520,"line":769},[518,1954,1873],{"class":528},[518,1956,663],{"class":559},[518,1958,1959,1962,1964,1966,1968,1970,1972,1975,1977],{"class":520,"line":784},[518,1960,1961],{"class":528},"    onError",[518,1963,657],{"class":559},[518,1965,1885],{"class":528},[518,1967,990],{"class":559},[518,1969,1890],{"class":528},[518,1971,990],{"class":559},[518,1973,1974],{"class":528}," snapshot) ",[518,1976,547],{"class":546},[518,1978,550],{"class":528},[518,1980,1981],{"class":520,"line":797},[518,1982,1983],{"class":890},"      // Restore pre-mutation pending list\n",[518,1985,1986,1988,1990,1992,1995,1997,1999,2002,2005],{"class":520,"line":819},[518,1987,1824],{"class":539},[518,1989,1827],{"class":528},[518,1991,1123],{"class":559},[518,1993,1994],{"class":528}," _pendingTodos ",[518,1996,588],{"class":559},[518,1998,1797],{"class":528},[518,2000,2001],{"class":559},"??",[518,2003,2004],{"class":528}," [])",[518,2006,569],{"class":559},[518,2008,2009,2011],{"class":520,"line":826},[518,2010,1873],{"class":528},[518,2012,663],{"class":559},[518,2014,2015,2017],{"class":520,"line":839},[518,2016,850],{"class":528},[518,2018,663],{"class":559},[518,2020,2021,2023,2025,2027,2029,2031,2033],{"class":520,"line":847},[518,2022,1227],{"class":528},[518,2024,657],{"class":559},[518,2026,1232],{"class":528},[518,2028,990],{"class":559},[518,2030,1237],{"class":528},[518,2032,990],{"class":559},[518,2034,2035],{"class":528}," mutate) {\n",[518,2037,2038,2040,2043],{"class":520,"line":855},[518,2039,1321],{"class":546},[518,2041,2042],{"class":524}," FloatingActionButton",[518,2044,648],{"class":528},[518,2046,2047,2050,2052,2054,2056,2059,2061,2064,2066,2068,2070,2073,2076],{"class":520,"line":860},[518,2048,2049],{"class":528},"      onPressed",[518,2051,657],{"class":559},[518,2053,1237],{"class":528},[518,2055,560],{"class":559},[518,2057,2058],{"class":528},"isPending ",[518,2060,1197],{"class":559},[518,2062,2063],{"class":559}," null",[518,2065,1213],{"class":559},[518,2067,1336],{"class":528},[518,2069,1123],{"class":559},[518,2071,2072],{"class":539}," _showCreateDialog",[518,2074,2075],{"class":528},"(mutate)",[518,2077,663],{"class":559},[518,2079,2080,2082,2084,2086,2088],{"class":520,"line":871},[518,2081,964],{"class":528},[518,2083,657],{"class":559},[518,2085,1237],{"class":528},[518,2087,560],{"class":559},[518,2089,2090],{"class":528},"isPending\n",[518,2092,2093,2096,2098,2101,2104,2106,2109],{"class":520,"line":894},[518,2094,2095],{"class":559},"          ?",[518,2097,674],{"class":532},[518,2099,2100],{"class":524}," CircularProgressIndicator",[518,2102,2103],{"class":528},"(strokeWidth",[518,2105,657],{"class":559},[518,2107,2108],{"class":685}," 2",[518,2110,1634],{"class":528},[518,2112,2113,2116,2118,2121,2123,2126,2128,2131],{"class":520,"line":899},[518,2114,2115],{"class":559},"          :",[518,2117,674],{"class":532},[518,2119,2120],{"class":524}," Icon",[518,2122,602],{"class":528},[518,2124,2125],{"class":524},"Icons",[518,2127,560],{"class":559},[518,2129,2130],{"class":528},"add)",[518,2132,663],{"class":559},[518,2134,2135,2137],{"class":520,"line":907},[518,2136,842],{"class":528},[518,2138,569],{"class":559},[518,2140,2141,2143],{"class":520,"line":915},[518,2142,1626],{"class":528},[518,2144,663],{"class":559},[518,2146,2147],{"class":520,"line":927},[518,2148,1634],{"class":528},[353,2150,2151,2152,2154,2155,2158,2159,2162,2163,2165],{},"When offline, ",[387,2153,438],{}," causes the mutation to emit ",[387,2156,2157],{},"MutationSuccess(isOptimistic: true)"," immediately. The todo appears in the list with the pending style. On reconnect, the queue replays the mutation and ",[387,2160,2161],{},"onSuccess"," fires, which clears ",[387,2164,446],{}," and invalidates the cache to fetch the server-confirmed item.",[505,2167,2169],{"id":2168},"per-tile-toggle-and-delete","Per-tile toggle and delete",[353,2171,2172,2173,2175,2176,2179],{},"Each tile wraps two independent ",[387,2174,113],{}," instances. They are nested inside ",[387,2177,2178],{},"_MutableTodoTile",", not in the list screen, so their state is scoped to the tile widget lifecycle:",[495,2181,2183],{"className":511,"code":2182,"filename":1066,"language":514,"meta":501,"style":501},"class _MutableTodoTile extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return QoraMutationBuilder\u003CTodo, ToggleTodoInput, void>(\n      mutator: api.toggleTodo,\n      options: MutationOptions(\n        onSuccess: (_, __, ___) async =>\n            context.qora.invalidate(['todos', auth.id]),\n      ),\n      builder: (context, toggleState, toggle) {\n        return QoraMutationBuilder\u003Cvoid, int, void>(\n          mutator: api.deleteTodo,\n          options: MutationOptions(\n            onSuccess: (_, __, ___) async =>\n                context.qora.invalidate(['todos', auth.id]),\n          ),\n          builder: (context, deleteState, delete) {\n            return TodoTile(\n              todo: todo,\n              onToggle: toggleState.isPending ? null\n                  : () => toggle(ToggleTodoInput(id: todo.id, completed: !todo.completed)),\n              onDelete: deleteState.isPending ? null\n                  : () => delete(todo.id),\n            );\n          },\n        );\n      },\n    );\n  }\n}\n",[387,2184,2185,2200,2205,2221,2243,2259,2270,2292,2319,2325,2344,2365,2381,2392,2413,2440,2447,2466,2474,2485,2503,2549,2566,2586,2593,2600,2607,2614,2620,2625],{"__ignoreMap":501},[518,2186,2187,2190,2192,2195,2198],{"class":520,"line":521},[518,2188,2189],{"class":559},"class",[518,2191,1575],{"class":524},[518,2193,2194],{"class":559}," extends",[518,2196,2197],{"class":524}," StatelessWidget",[518,2199,550],{"class":528},[518,2201,2202],{"class":520,"line":553},[518,2203,2204],{"class":532},"  @override\n",[518,2206,2207,2210,2213,2215,2218],{"class":520,"line":572},[518,2208,2209],{"class":524},"  Widget",[518,2211,2212],{"class":539}," build",[518,2214,602],{"class":528},[518,2216,2217],{"class":524},"BuildContext",[518,2219,2220],{"class":528}," context) {\n",[518,2222,2223,2225,2228,2230,2232,2234,2237,2239,2241],{"class":520,"line":579},[518,2224,1321],{"class":546},[518,2226,2227],{"class":524}," QoraMutationBuilder",[518,2229,529],{"class":528},[518,2231,1682],{"class":524},[518,2233,439],{"class":528},[518,2235,2236],{"class":524},"ToggleTodoInput",[518,2238,439],{"class":528},[518,2240,533],{"class":532},[518,2242,1085],{"class":528},[518,2244,2245,2248,2250,2252,2254,2257],{"class":520,"line":613},[518,2246,2247],{"class":528},"      mutator",[518,2249,657],{"class":559},[518,2251,1126],{"class":528},[518,2253,560],{"class":559},[518,2255,2256],{"class":528},"toggleTodo",[518,2258,663],{"class":559},[518,2260,2261,2264,2266,2268],{"class":520,"line":630},[518,2262,2263],{"class":528},"      options",[518,2265,657],{"class":559},[518,2267,1724],{"class":524},[518,2269,648],{"class":528},[518,2271,2272,2275,2277,2279,2281,2283,2285,2287,2289],{"class":520,"line":635},[518,2273,2274],{"class":528},"        onSuccess",[518,2276,657],{"class":559},[518,2278,1885],{"class":528},[518,2280,990],{"class":559},[518,2282,1890],{"class":528},[518,2284,990],{"class":559},[518,2286,1895],{"class":528},[518,2288,547],{"class":546},[518,2290,2291],{"class":559}," =>\n",[518,2293,2294,2297,2299,2301,2303,2305,2307,2309,2311,2313,2315,2317],{"class":520,"line":651},[518,2295,2296],{"class":528},"            context",[518,2298,560],{"class":559},[518,2300,1929],{"class":528},[518,2302,560],{"class":559},[518,2304,1934],{"class":539},[518,2306,1937],{"class":528},[518,2308,1098],{"class":605},[518,2310,990],{"class":559},[518,2312,1103],{"class":528},[518,2314,560],{"class":559},[518,2316,1948],{"class":528},[518,2318,663],{"class":559},[518,2320,2321,2323],{"class":520,"line":666},[518,2322,764],{"class":528},[518,2324,663],{"class":559},[518,2326,2327,2330,2332,2334,2336,2339,2341],{"class":520,"line":693},[518,2328,2329],{"class":528},"      builder",[518,2331,657],{"class":559},[518,2333,1232],{"class":528},[518,2335,990],{"class":559},[518,2337,2338],{"class":528}," toggleState",[518,2340,990],{"class":559},[518,2342,2343],{"class":528}," toggle) {\n",[518,2345,2346,2349,2351,2353,2355,2357,2359,2361,2363],{"class":520,"line":706},[518,2347,2348],{"class":546},"        return",[518,2350,2227],{"class":524},[518,2352,529],{"class":528},[518,2354,533],{"class":532},[518,2356,439],{"class":528},[518,2358,1082],{"class":524},[518,2360,439],{"class":528},[518,2362,533],{"class":532},[518,2364,1085],{"class":528},[518,2366,2367,2370,2372,2374,2376,2379],{"class":520,"line":721},[518,2368,2369],{"class":528},"          mutator",[518,2371,657],{"class":559},[518,2373,1126],{"class":528},[518,2375,560],{"class":559},[518,2377,2378],{"class":528},"deleteTodo",[518,2380,663],{"class":559},[518,2382,2383,2386,2388,2390],{"class":520,"line":743},[518,2384,2385],{"class":528},"          options",[518,2387,657],{"class":559},[518,2389,1724],{"class":524},[518,2391,648],{"class":528},[518,2393,2394,2397,2399,2401,2403,2405,2407,2409,2411],{"class":520,"line":761},[518,2395,2396],{"class":528},"            onSuccess",[518,2398,657],{"class":559},[518,2400,1885],{"class":528},[518,2402,990],{"class":559},[518,2404,1890],{"class":528},[518,2406,990],{"class":559},[518,2408,1895],{"class":528},[518,2410,547],{"class":546},[518,2412,2291],{"class":559},[518,2414,2415,2418,2420,2422,2424,2426,2428,2430,2432,2434,2436,2438],{"class":520,"line":769},[518,2416,2417],{"class":528},"                context",[518,2419,560],{"class":559},[518,2421,1929],{"class":528},[518,2423,560],{"class":559},[518,2425,1934],{"class":539},[518,2427,1937],{"class":528},[518,2429,1098],{"class":605},[518,2431,990],{"class":559},[518,2433,1103],{"class":528},[518,2435,560],{"class":559},[518,2437,1948],{"class":528},[518,2439,663],{"class":559},[518,2441,2442,2445],{"class":520,"line":784},[518,2443,2444],{"class":528},"          )",[518,2446,663],{"class":559},[518,2448,2449,2452,2454,2456,2458,2461,2463],{"class":520,"line":797},[518,2450,2451],{"class":528},"          builder",[518,2453,657],{"class":559},[518,2455,1232],{"class":528},[518,2457,990],{"class":559},[518,2459,2460],{"class":528}," deleteState",[518,2462,990],{"class":559},[518,2464,2465],{"class":528}," delete) {\n",[518,2467,2468,2470,2472],{"class":520,"line":819},[518,2469,1476],{"class":546},[518,2471,1556],{"class":524},[518,2473,648],{"class":528},[518,2475,2476,2479,2481,2483],{"class":520,"line":826},[518,2477,2478],{"class":528},"              todo",[518,2480,657],{"class":559},[518,2482,1543],{"class":528},[518,2484,663],{"class":559},[518,2486,2487,2490,2492,2494,2496,2498,2500],{"class":520,"line":839},[518,2488,2489],{"class":528},"              onToggle",[518,2491,657],{"class":559},[518,2493,2338],{"class":528},[518,2495,560],{"class":559},[518,2497,2058],{"class":528},[518,2499,1197],{"class":559},[518,2501,2502],{"class":559}," null\n",[518,2504,2505,2508,2510,2512,2515,2517,2519,2522,2524,2526,2528,2530,2532,2535,2537,2539,2542,2544,2547],{"class":520,"line":847},[518,2506,2507],{"class":559},"                  :",[518,2509,1336],{"class":528},[518,2511,1123],{"class":559},[518,2513,2514],{"class":539}," toggle",[518,2516,602],{"class":528},[518,2518,2236],{"class":524},[518,2520,2521],{"class":528},"(id",[518,2523,657],{"class":559},[518,2525,1543],{"class":528},[518,2527,560],{"class":559},[518,2529,1138],{"class":528},[518,2531,990],{"class":559},[518,2533,2534],{"class":528}," completed",[518,2536,657],{"class":559},[518,2538,1445],{"class":559},[518,2540,2541],{"class":528},"todo",[518,2543,560],{"class":559},[518,2545,2546],{"class":528},"completed))",[518,2548,663],{"class":559},[518,2550,2551,2554,2556,2558,2560,2562,2564],{"class":520,"line":855},[518,2552,2553],{"class":528},"              onDelete",[518,2555,657],{"class":559},[518,2557,2460],{"class":528},[518,2559,560],{"class":559},[518,2561,2058],{"class":528},[518,2563,1197],{"class":559},[518,2565,2502],{"class":559},[518,2567,2568,2570,2572,2574,2577,2579,2581,2584],{"class":520,"line":860},[518,2569,2507],{"class":559},[518,2571,1336],{"class":528},[518,2573,1123],{"class":559},[518,2575,2576],{"class":539}," delete",[518,2578,1559],{"class":528},[518,2580,560],{"class":559},[518,2582,2583],{"class":528},"id)",[518,2585,663],{"class":559},[518,2587,2588,2591],{"class":520,"line":871},[518,2589,2590],{"class":528},"            )",[518,2592,569],{"class":559},[518,2594,2595,2598],{"class":520,"line":894},[518,2596,2597],{"class":528},"          }",[518,2599,663],{"class":559},[518,2601,2602,2605],{"class":520,"line":899},[518,2603,2604],{"class":528},"        )",[518,2606,569],{"class":559},[518,2608,2609,2612],{"class":520,"line":907},[518,2610,2611],{"class":528},"      }",[518,2613,663],{"class":559},[518,2615,2616,2618],{"class":520,"line":915},[518,2617,842],{"class":528},[518,2619,569],{"class":559},[518,2621,2622],{"class":520,"line":927},[518,2623,2624],{"class":528},"  }\n",[518,2626,2627],{"class":520,"line":940},[518,2628,1019],{"class":528},[353,2630,2631,2632,2634,2635,2638],{},"Toggling item A and deleting item B are completely independent operations. Each ",[387,2633,113],{}," has its own ",[387,2636,2637],{},"MutationController"," lifecycle. A server error on item A does not affect item B's state.",[505,2640,2642],{"id":2641},"graceful-degradation-on-infinite-query-failure","Graceful degradation on infinite query failure",[353,2644,2645,435,2648,2651],{},[387,2646,2647],{},"InfiniteFailure",[387,2649,2650],{},"previousData"," keeps the feed visible when a page load fails:",[495,2653,2655],{"className":511,"code":2654,"filename":1066,"language":514,"meta":501,"style":501},"case InfiniteFailure(:final error, :final previousData):\n  if (previousData != null) {\n    // Show stale data + error banner at top\n    return Column(\n      children: [\n        _ErrorBanner(error: error, onRetry: controller.fetchNextPage),\n        Expanded(child: _buildList(previousData, controller)),\n      ],\n    );\n  }\n  // No data at all: full error screen with retry\n  return _ErrorView(error: error, onRetry: controller.refetch);\n",[387,2656,2657,2687,2703,2708,2717,2726,2754,2776,2783,2789,2793,2798],{"__ignoreMap":501},[518,2658,2659,2662,2665,2667,2669,2672,2675,2677,2679,2681,2684],{"class":520,"line":521},[518,2660,2661],{"class":546},"case",[518,2663,2664],{"class":524}," InfiniteFailure",[518,2666,602],{"class":528},[518,2668,657],{"class":559},[518,2670,2671],{"class":532},"final",[518,2673,2674],{"class":528}," error",[518,2676,990],{"class":559},[518,2678,1213],{"class":559},[518,2680,2671],{"class":532},[518,2682,2683],{"class":528}," previousData)",[518,2685,2686],{"class":559},":\n",[518,2688,2689,2692,2695,2698,2700],{"class":520,"line":553},[518,2690,2691],{"class":546},"  if",[518,2693,2694],{"class":528}," (previousData ",[518,2696,2697],{"class":559},"!=",[518,2699,2063],{"class":559},[518,2701,2702],{"class":528},") {\n",[518,2704,2705],{"class":520,"line":572},[518,2706,2707],{"class":890},"    // Show stale data + error banner at top\n",[518,2709,2710,2712,2715],{"class":520,"line":579},[518,2711,1321],{"class":546},[518,2713,2714],{"class":524}," Column",[518,2716,648],{"class":528},[518,2718,2719,2722,2724],{"class":520,"line":613},[518,2720,2721],{"class":528},"      children",[518,2723,657],{"class":559},[518,2725,1260],{"class":528},[518,2727,2728,2731,2734,2736,2738,2740,2743,2745,2747,2749,2752],{"class":520,"line":630},[518,2729,2730],{"class":524},"        _ErrorBanner",[518,2732,2733],{"class":528},"(error",[518,2735,657],{"class":559},[518,2737,2674],{"class":528},[518,2739,990],{"class":559},[518,2741,2742],{"class":528}," onRetry",[518,2744,657],{"class":559},[518,2746,1341],{"class":528},[518,2748,560],{"class":559},[518,2750,2751],{"class":528},"fetchNextPage)",[518,2753,663],{"class":559},[518,2755,2756,2759,2761,2763,2766,2769,2771,2774],{"class":520,"line":635},[518,2757,2758],{"class":524},"        Expanded",[518,2760,972],{"class":528},[518,2762,657],{"class":559},[518,2764,2765],{"class":539}," _buildList",[518,2767,2768],{"class":528},"(previousData",[518,2770,990],{"class":559},[518,2772,2773],{"class":528}," controller))",[518,2775,663],{"class":559},[518,2777,2778,2781],{"class":520,"line":651},[518,2779,2780],{"class":528},"      ]",[518,2782,663],{"class":559},[518,2784,2785,2787],{"class":520,"line":666},[518,2786,842],{"class":528},[518,2788,569],{"class":559},[518,2790,2791],{"class":520,"line":693},[518,2792,2624],{"class":528},[518,2794,2795],{"class":520,"line":706},[518,2796,2797],{"class":890},"  // No data at all: full error screen with retry\n",[518,2799,2800,2803,2806,2808,2810,2812,2814,2816,2818,2820,2822,2825],{"class":520,"line":721},[518,2801,2802],{"class":546},"  return",[518,2804,2805],{"class":524}," _ErrorView",[518,2807,2733],{"class":528},[518,2809,657],{"class":559},[518,2811,2674],{"class":528},[518,2813,990],{"class":559},[518,2815,2742],{"class":528},[518,2817,657],{"class":559},[518,2819,1341],{"class":528},[518,2821,560],{"class":559},[518,2823,2824],{"class":528},"refetch)",[518,2826,569],{"class":559},[505,2828,2830],{"id":2829},"simulatedconnectivitymanager",[387,2831,463],{},[353,2833,2834,2835,2837,2838,2841],{},"A custom ",[387,2836,467],{}," that toggles between online and offline via an AppBar button. This removes any runtime dependency on ",[387,2839,2840],{},"connectivity_plus"," for the demo:",[495,2843,2846],{"className":511,"code":2844,"filename":2845,"language":514,"meta":501,"style":501},"class SimulatedConnectivityManager implements ConnectivityManager {\n  final _controller = StreamController\u003CNetworkStatus>.broadcast();\n  NetworkStatus _status = NetworkStatus.online;\n\n  @override\n  Stream\u003CNetworkStatus> get statusStream => _controller.stream;\n\n  @override\n  NetworkStatus get currentStatus => _status;\n\n  void toggle() {\n    _status = _status == NetworkStatus.online\n        ? NetworkStatus.offline\n        : NetworkStatus.online;\n    _controller.add(_status);\n  }\n\n  @override\n  void dispose() => _controller.close();\n}\n","lib/core/connectivity/simulated_connectivity_manager.dart",[387,2847,2848,2862,2890,2910,2914,2918,2947,2951,2955,2972,2976,2986,3004,3016,3029,3043,3047,3051,3055,3077],{"__ignoreMap":501},[518,2849,2850,2852,2854,2857,2860],{"class":520,"line":521},[518,2851,2189],{"class":559},[518,2853,623],{"class":524},[518,2855,2856],{"class":559}," implements",[518,2858,2859],{"class":524}," ConnectivityManager",[518,2861,550],{"class":528},[518,2863,2864,2866,2869,2871,2874,2876,2879,2881,2883,2886,2888],{"class":520,"line":553},[518,2865,582],{"class":532},[518,2867,2868],{"class":528}," _controller ",[518,2870,588],{"class":559},[518,2872,2873],{"class":524}," StreamController",[518,2875,529],{"class":528},[518,2877,2878],{"class":524},"NetworkStatus",[518,2880,1809],{"class":528},[518,2882,560],{"class":559},[518,2884,2885],{"class":539},"broadcast",[518,2887,566],{"class":528},[518,2889,569],{"class":559},[518,2891,2892,2895,2898,2900,2903,2905,2908],{"class":520,"line":572},[518,2893,2894],{"class":524},"  NetworkStatus",[518,2896,2897],{"class":528}," _status ",[518,2899,588],{"class":559},[518,2901,2902],{"class":524}," NetworkStatus",[518,2904,560],{"class":559},[518,2906,2907],{"class":528},"online",[518,2909,569],{"class":559},[518,2911,2912],{"class":520,"line":579},[518,2913,576],{"emptyLinePlaceholder":575},[518,2915,2916],{"class":520,"line":613},[518,2917,2204],{"class":532},[518,2919,2920,2923,2925,2927,2929,2932,2935,2937,2940,2942,2945],{"class":520,"line":630},[518,2921,2922],{"class":524},"  Stream",[518,2924,529],{"class":528},[518,2926,2878],{"class":524},[518,2928,536],{"class":528},[518,2930,2931],{"class":559},"get",[518,2933,2934],{"class":528}," statusStream ",[518,2936,1123],{"class":559},[518,2938,2939],{"class":528}," _controller",[518,2941,560],{"class":559},[518,2943,2944],{"class":528},"stream",[518,2946,569],{"class":559},[518,2948,2949],{"class":520,"line":635},[518,2950,576],{"emptyLinePlaceholder":575},[518,2952,2953],{"class":520,"line":651},[518,2954,2204],{"class":532},[518,2956,2957,2959,2962,2965,2967,2970],{"class":520,"line":666},[518,2958,2894],{"class":524},[518,2960,2961],{"class":559}," get",[518,2963,2964],{"class":528}," currentStatus ",[518,2966,1123],{"class":559},[518,2968,2969],{"class":528}," _status",[518,2971,569],{"class":559},[518,2973,2974],{"class":520,"line":693},[518,2975,576],{"emptyLinePlaceholder":575},[518,2977,2978,2981,2983],{"class":520,"line":706},[518,2979,2980],{"class":532},"  void",[518,2982,2514],{"class":539},[518,2984,2985],{"class":528},"() {\n",[518,2987,2988,2991,2993,2995,2997,2999,3001],{"class":520,"line":721},[518,2989,2990],{"class":528},"    _status ",[518,2992,588],{"class":559},[518,2994,2897],{"class":528},[518,2996,1414],{"class":559},[518,2998,2902],{"class":524},[518,3000,560],{"class":559},[518,3002,3003],{"class":528},"online\n",[518,3005,3006,3009,3011,3013],{"class":520,"line":743},[518,3007,3008],{"class":559},"        ?",[518,3010,2902],{"class":524},[518,3012,560],{"class":559},[518,3014,3015],{"class":528},"offline\n",[518,3017,3018,3021,3023,3025,3027],{"class":520,"line":761},[518,3019,3020],{"class":559},"        :",[518,3022,2902],{"class":524},[518,3024,560],{"class":559},[518,3026,2907],{"class":528},[518,3028,569],{"class":559},[518,3030,3031,3034,3036,3038,3041],{"class":520,"line":769},[518,3032,3033],{"class":528},"    _controller",[518,3035,560],{"class":559},[518,3037,1837],{"class":539},[518,3039,3040],{"class":528},"(_status)",[518,3042,569],{"class":559},[518,3044,3045],{"class":520,"line":784},[518,3046,2624],{"class":528},[518,3048,3049],{"class":520,"line":797},[518,3050,576],{"emptyLinePlaceholder":575},[518,3052,3053],{"class":520,"line":819},[518,3054,2204],{"class":532},[518,3056,3057,3059,3062,3064,3066,3068,3070,3073,3075],{"class":520,"line":826},[518,3058,2980],{"class":532},[518,3060,3061],{"class":539}," dispose",[518,3063,543],{"class":528},[518,3065,1123],{"class":559},[518,3067,2939],{"class":528},[518,3069,560],{"class":559},[518,3071,3072],{"class":539},"close",[518,3074,566],{"class":528},[518,3076,569],{"class":559},[518,3078,3079],{"class":520,"line":839},[518,3080,1019],{"class":528},[353,3082,3083,3084,3087,3088,3091],{},"The AppBar button calls ",[387,3085,3086],{},"connectivity.toggle()",". All active queries pause immediately (",[387,3089,3090],{},"FetchStatus.paused","), and any mutations fired while offline are queued. On toggle back to online, the queue replays automatically.",[372,3093],{},[348,3095,3097],{"id":3096},"validation-steps","Validation Steps",[3099,3100,3101,3104,3107,3110,3113,3116,3119,3122,3125,3128],"ol",{},[382,3102,3103],{},"Log in with any user ID (1–10) → todos load from JSONPlaceholder",[382,3105,3106],{},"Scroll to bottom → next page auto-loads via sentinel trigger",[382,3108,3109],{},"Pull to refresh → list resets and re-fetches from page 1",[382,3111,3112],{},"Add a todo → appears instantly in the list with pending style",[382,3114,3115],{},"Toggle the wifi icon in the AppBar → offline banner appears; existing todos remain visible",[382,3117,3118],{},"Add another todo while offline → appears with pending style; \"queued\" state on FAB",[382,3120,3121],{},"Toggle wifi back → queued mutation replays; list refreshes with server-confirmed data",[382,3123,3124],{},"Toggle a todo → checked/unchecked immediately; server sync in background",[382,3126,3127],{},"Delete a todo → removed immediately; server confirms in background",[382,3129,3130],{},"Force-kill and reopen the app → cached auth and todos restore on first frame (within stale window)",[372,3132],{},[348,3134,3136],{"id":3135},"source-code","Source Code",[495,3138,3141],{"className":3139,"code":3140,"language":500,"meta":501},[498],"examples/ex_07_complete_todo_app/\n├── lib/\n│   ├── main.dart\n│   ├── core/\n│   │   ├── auth/auth_service.dart\n│   │   ├── connectivity/simulated_connectivity_manager.dart\n│   │   └── persistence/\n│   │       ├── hive_storage_adapter.dart\n│   │       └── serializers.dart\n│   ├── features/\n│   │   ├── auth/ui/login_screen.dart\n│   │   └── todos/\n│   │       ├── data/todo_api.dart\n│   │       ├── models/todo.dart\n│   │       └── ui/\n│   │           ├── todo_list_screen.dart\n│   │           └── todo_tile.dart\n│   └── shared/widgets/offline_banner.dart\n└── pubspec.yaml\n",[387,3142,3140],{"__ignoreMap":501},[353,3144,3145],{},[364,3146,3149],{"href":3147,"rel":3148},"https://github.com/meragix/qora/tree/main/examples/ex_07_complete_todo_app",[368],"View full source code",[372,3151],{},[348,3153,3155],{"id":3154},"patterns-at-a-glance","Patterns at a glance",[3157,3158,3159,3175],"table",{},[3160,3161,3162],"thead",{},[3163,3164,3165,3169,3172],"tr",{},[3166,3167,3168],"th",{},"Pattern",[3166,3170,3171],{},"File",[3166,3173,3174],{},"Qora API",[3176,3177,3178,3198,3214,3230,3244,3265,3279,3294],"tbody",{},[3163,3179,3180,3184,3189],{},[3181,3182,3183],"td",{},"Bootstrap + persistence",[3181,3185,3186],{},[387,3187,3188],{},"main.dart",[3181,3190,3191,439,3193,439,3195],{},[387,3192,229],{},[387,3194,103],{},[387,3196,3197],{},".hydrate()",[3163,3199,3200,3203,3208],{},[3181,3201,3202],{},"Auth-gated query key",[3181,3204,3205],{},[387,3206,3207],{},"todo_list_screen.dart",[3181,3209,3210,3213],{},[387,3211,3212],{},"['todos', auth.id]"," key",[3163,3215,3216,3219,3223],{},[3181,3217,3218],{},"Infinite scroll sentinel",[3181,3220,3221],{},[387,3222,3207],{},[3181,3224,3225,439,3227],{},[387,3226,118],{},[387,3228,3229],{},"InfiniteQueryController",[3163,3231,3232,3235,3239],{},[3181,3233,3234],{},"Pull-to-refresh",[3181,3236,3237],{},[387,3238,3207],{},[3181,3240,3241],{},[387,3242,3243],{},"controller.refetch()",[3163,3245,3246,3249,3253],{},[3181,3247,3248],{},"Optimistic create + offline queue",[3181,3250,3251],{},[387,3252,3207],{},[3181,3254,3255,439,3257,439,3260,439,3262],{},[387,3256,113],{},[387,3258,3259],{},"offlineQueue",[387,3261,442],{},[387,3263,3264],{},"onMutate",[3163,3266,3267,3270,3274],{},[3181,3268,3269],{},"Per-tile independent mutations",[3181,3271,3272],{},[387,3273,3207],{},[3181,3275,3276,3277],{},"Nested ",[387,3278,113],{},[3163,3280,3281,3284,3289],{},[3181,3282,3283],{},"Simulated offline toggle",[3181,3285,3286],{},[387,3287,3288],{},"simulated_connectivity_manager.dart",[3181,3290,3291,3293],{},[387,3292,467],{}," interface",[3163,3295,3296,3299,3304],{},[3181,3297,3298],{},"Offline banner",[3181,3300,3301],{},[387,3302,3303],{},"offline_banner.dart",[3181,3305,3306],{},[387,3307,483],{},[372,3309],{},[348,3311,3313],{"id":3312},"related","Related",[379,3315,3316,3325,3334,3341],{},[382,3317,3318,432,3322,3324],{},[364,3319,3321],{"href":3320},"./offline-first","Offline-First Case Study",[387,3323,229],{}," deep-dive",[382,3326,3327,432,3331,3333],{},[364,3328,3330],{"href":3329},"./infinite-scroll","Infinite Scroll Case Study",[387,3332,118],{}," patterns",[382,3335,3336,3340],{},[364,3337,3339],{"href":3338},"./mutations-optimistic","Optimistic Mutations Case Study",": rollback protocol",[382,3342,3343,432,3347,439,3350],{},[364,3344,3346],{"href":3345},"./network-awareness","Network Awareness Case Study",[387,3348,3349],{},"NetworkMode",[387,3351,3352],{},"ReconnectStrategy",[3354,3355,3356],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":501,"searchDepth":553,"depth":553,"links":3358},[3359,3360,3361,3364,3371,3372,3373,3374],{"id":350,"depth":553,"text":351},{"id":376,"depth":553,"text":377},{"id":489,"depth":553,"text":490,"children":3362},[3363],{"id":507,"depth":572,"text":508},{"id":1049,"depth":553,"text":1050,"children":3365},[3366,3367,3368,3369,3370],{"id":1053,"depth":572,"text":1054},{"id":1661,"depth":572,"text":431},{"id":2168,"depth":572,"text":2169},{"id":2641,"depth":572,"text":2642},{"id":2829,"depth":572,"text":463},{"id":3096,"depth":553,"text":3097},{"id":3135,"depth":553,"text":3136},{"id":3154,"depth":553,"text":3155},{"id":3312,"depth":553,"text":3313},"A production-grade Flutter app combining every Qora capability: PersistQoraClient, infinite scroll with pagination, optimistic create/toggle/delete, offline mutation queue, and simulated connectivity toggling.","md",null,{},{"icon":326},{"title":3381,"description":3382},"Case Study: Complete Todo App - Qora","Full-stack Qora reference app. Covers PersistQoraClient hydration, InfiniteQueryBuilder with sentinel pattern, nested QoraMutationBuilder per tile, offline queue, and graceful degradation.","bVA6H4S7Uh0Yc7osLvBXBXD85T6w4nAvHBh_VDZiM0I",[3385,3387],{"title":319,"path":320,"stem":321,"description":3386,"icon":126,"children":-1},"Replace QoraBuilder widget nesting with flat, composable hook calls: useQuery, useMutation, useInfiniteQuery, and global fetch indicators in a HookWidget",{"title":334,"path":335,"stem":336,"description":3388,"icon":337,"children":-1},"Answers to common questions about Qora's API, cache semantics, and Flutter integration.",1794040403030]