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