[{"data":1,"prerenderedAt":2505},["ShallowReactive",2],{"navigation_docs_fr":3,"-fr-examples-basic-query":342,"-fr-examples-basic-query-surround":2500},[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":297,"body":344,"description":2491,"extension":2492,"links":2493,"meta":2494,"navigation":2495,"path":298,"seo":2496,"stem":299,"__hash__":2499},"docs_fr/fr/08.examples/1.basic-query.md",{"type":345,"value":346,"toc":2476},"minimark",[347,352,369,375,378,382,473,475,479,486,496,524,775,785,787,791,796,806,843,866,1803,1830,1836,1846,1888,1898,1906,1929,2360,2373,2375,2379,2382,2417,2419,2423,2429,2438,2440,2444,2472],[348,349,351],"h2",{"id":350},"objectif","Objectif",[353,354,355,359,360,364,365,368],"p",{},[356,357,358],"strong",{},"Contexte"," : Chaque écran alimenté par le réseau doit gérer les cinq mêmes préoccupations : état de chargement, état d'erreur, données en cache, rafraîchissement en arrière-plan et connectivité. Avec un ",[361,362,363],"code",{},"FutureBuilder"," brut ou un ",[361,366,367],{},"setState"," manuel, chaque écran réimplémente cette logique indépendamment. Le code est verbeux, incohérent et fragile sous navigation.",[353,370,371,372,374],{},"Cet exemple montre comment ",[361,373,108],{}," consolide ces préoccupations en un seul builder déclaratif. Le cache SWR de Qora permet aux utilisateurs de rouvrir un écran visité sans requête réseau supplémentaire.",[376,377],"hr",{},[348,379,381],{"id":380},"fonctionnalités-démontrées","Fonctionnalités démontrées",[383,384,385,404,412,417,434,443,452,461],"ul",{},[386,387,388,393,394,396,397,400,401],"li",{},[356,389,390,391],{},"Configuration ",[361,392,103],{}," : ",[361,395,202],{}," injecté à la racine avec ",[361,398,399],{},"FlutterLifecycleManager"," et ",[361,402,403],{},"FlutterConnectivityManager",[386,405,406,411],{},[356,407,408],{},[361,409,410],{},"QoraBuilder\u003CT>"," : fetche au montage, s'abonne aux changements d'état, dispose proprement",[386,413,414,416],{},[356,415,72],{}," : données en cache affichées instantanément à la re-navigation ; refetch en arrière-plan uniquement quand périmé",[386,418,419,425,426,429,430,433],{},[356,420,421,424],{},[361,422,423],{},"FetchStatus"," comme second axe UI"," : distingue le fetch actif (",[361,427,428],{},"fetching","), la pause hors ligne (",[361,431,432],{},"paused",") et l'état inactif sans aucun flag booléen personnalisé",[386,435,436,442],{},[356,437,438,439],{},"Dégradation gracieuse via ",[361,440,441],{},"previousData"," : les données périmées restent visibles pendant le rafraîchissement en arrière-plan et lors d'erreurs légères",[386,444,445,393,448,451],{},[356,446,447],{},"Clés de requête par élément",[361,449,450],{},"['users', userId]"," donne à chaque utilisateur une entrée de cache séparée et indépendante",[386,453,454,460],{},[356,455,456,457],{},"Prédicat ",[361,458,459],{},"invalidateWhere"," : un seul appel invalide simultanément la liste et tous les écrans de détail ouverts",[386,462,463,393,466,469,470,472],{},[356,464,465],{},"Pull-to-refresh",[361,467,468],{},"RefreshIndicator"," câblé à ",[361,471,459],{},", aucun état manuel requis",[376,474],{},[348,476,478],{"id":477},"points-darchitecture","Points d'architecture",[353,480,481,482,485],{},"Les widgets ne possèdent jamais de données : ils déclarent ",[356,483,484],{},"ce qu'ils veulent"," et Qora décide de fetcher, servir depuis le cache ou attendre la connectivité :",[487,488,494],"pre",{"className":489,"code":491,"language":492,"meta":493},[490],"language-text","ex_01_basic_query/lib/\n├── main.dart                    ← QoraClient + QoraScope à la racine\n├── models/\n│   └── user.dart                ← classe de données pure, sans dépendance Qora\n├── services/\n│   └── fake_api.dart            ← fonctions async (latence simulée 2 s + ~30 % mode échec)\n└── screens/\n    ├── user_list_screen.dart    ← QoraBuilder\u003CList\u003CUser>>, key: ['users']\n    └── user_detail_screen.dart  ← QoraBuilder\u003CUser>, key: ['users', userId]\n","text","",[361,495,491],{"__ignoreMap":493},[353,497,498,500,501,504,505,508,509,512,513,516,517,516,520,523],{},[361,499,202],{}," est configuré une seule fois à la racine. Les écrans y accèdent via ",[361,502,503],{},"context.qora"," : le raccourci ",[361,506,507],{},"BuildContextExtension"," vers ",[361,510,511],{},"QoraScope.of(context)",". Pas de ",[361,514,515],{},"Provider",", pas de ",[361,518,519],{},"ref",[361,521,522],{},"context.read"," :",[487,525,530],{"className":526,"code":527,"filename":528,"language":529,"meta":493,"style":493},"language-dart shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","final client = QoraClient(\n  config: const QoraClientConfig(\n    defaultOptions: QoraOptions(\n      staleTime: Duration(minutes: 5),\n      gcTime: Duration(minutes: 10),\n    ),\n    debugMode: kDebugMode,\n  ),\n);\n\nrunApp(\n  QoraScope(\n    client: client,\n    lifecycleManager: FlutterLifecycleManager(qoraClient: client),\n    connectivityManager: FlutterConnectivityManager(),\n    child: const MyApp(),\n  ),\n);\n","lib/main.dart","dart",[361,531,532,556,573,586,612,633,641,654,662,670,677,686,694,707,728,744,761,768],{"__ignoreMap":493},[533,534,537,541,545,549,553],"span",{"class":535,"line":536},"line",1,[533,538,540],{"class":539},"spNyl","final",[533,542,544],{"class":543},"sTEyZ"," client ",[533,546,548],{"class":547},"sMK4o","=",[533,550,552],{"class":551},"sBMFI"," QoraClient",[533,554,555],{"class":543},"(\n",[533,557,559,562,565,568,571],{"class":535,"line":558},2,[533,560,561],{"class":543},"  config",[533,563,564],{"class":547},":",[533,566,567],{"class":539}," const",[533,569,570],{"class":551}," QoraClientConfig",[533,572,555],{"class":543},[533,574,576,579,581,584],{"class":535,"line":575},3,[533,577,578],{"class":543},"    defaultOptions",[533,580,564],{"class":547},[533,582,583],{"class":551}," QoraOptions",[533,585,555],{"class":543},[533,587,589,592,594,597,600,602,606,609],{"class":535,"line":588},4,[533,590,591],{"class":543},"      staleTime",[533,593,564],{"class":547},[533,595,596],{"class":551}," Duration",[533,598,599],{"class":543},"(minutes",[533,601,564],{"class":547},[533,603,605],{"class":604},"sbssI"," 5",[533,607,608],{"class":543},")",[533,610,611],{"class":547},",\n",[533,613,615,618,620,622,624,626,629,631],{"class":535,"line":614},5,[533,616,617],{"class":543},"      gcTime",[533,619,564],{"class":547},[533,621,596],{"class":551},[533,623,599],{"class":543},[533,625,564],{"class":547},[533,627,628],{"class":604}," 10",[533,630,608],{"class":543},[533,632,611],{"class":547},[533,634,636,639],{"class":535,"line":635},6,[533,637,638],{"class":543},"    )",[533,640,611],{"class":547},[533,642,644,647,649,652],{"class":535,"line":643},7,[533,645,646],{"class":543},"    debugMode",[533,648,564],{"class":547},[533,650,651],{"class":543}," kDebugMode",[533,653,611],{"class":547},[533,655,657,660],{"class":535,"line":656},8,[533,658,659],{"class":543},"  )",[533,661,611],{"class":547},[533,663,665,667],{"class":535,"line":664},9,[533,666,608],{"class":543},[533,668,669],{"class":547},";\n",[533,671,673],{"class":535,"line":672},10,[533,674,676],{"emptyLinePlaceholder":675},true,"\n",[533,678,680,684],{"class":535,"line":679},11,[533,681,683],{"class":682},"s2Zo4","runApp",[533,685,555],{"class":543},[533,687,689,692],{"class":535,"line":688},12,[533,690,691],{"class":551},"  QoraScope",[533,693,555],{"class":543},[533,695,697,700,702,705],{"class":535,"line":696},13,[533,698,699],{"class":543},"    client",[533,701,564],{"class":547},[533,703,704],{"class":543}," client",[533,706,611],{"class":547},[533,708,710,713,715,718,721,723,726],{"class":535,"line":709},14,[533,711,712],{"class":543},"    lifecycleManager",[533,714,564],{"class":547},[533,716,717],{"class":551}," FlutterLifecycleManager",[533,719,720],{"class":543},"(qoraClient",[533,722,564],{"class":547},[533,724,725],{"class":543}," client)",[533,727,611],{"class":547},[533,729,731,734,736,739,742],{"class":535,"line":730},15,[533,732,733],{"class":543},"    connectivityManager",[533,735,564],{"class":547},[533,737,738],{"class":551}," FlutterConnectivityManager",[533,740,741],{"class":543},"()",[533,743,611],{"class":547},[533,745,747,750,752,754,757,759],{"class":535,"line":746},16,[533,748,749],{"class":543},"    child",[533,751,564],{"class":547},[533,753,567],{"class":539},[533,755,756],{"class":551}," MyApp",[533,758,741],{"class":543},[533,760,611],{"class":547},[533,762,764,766],{"class":535,"line":763},17,[533,765,659],{"class":543},[533,767,611],{"class":547},[533,769,771,773],{"class":535,"line":770},18,[533,772,608],{"class":543},[533,774,669],{"class":547},[353,776,777,780,781,784],{},[361,778,779],{},"staleTime: 5 minutes"," signifie : si vous naviguez vers un écran visité il y a moins de 5 minutes, ",[356,782,783],{},"aucun appel réseau ne se déclenche"," : le cache sert les données à la première frame. Après 5 minutes, les données en cache sont affichées immédiatement pendant que Qora revalide en arrière-plan.",[376,786],{},[348,788,790],{"id":789},"détails-dimplémentation","Détails d'implémentation",[792,793,795],"h3",{"id":794},"la-matrice-détat-à-deux-dimensions","La matrice d'état à deux dimensions",[353,797,798,799,802,803,523],{},"La plupart des solutions de gestion d'état donnent un axe : ",[361,800,801],{},"loading / data / error",". Qora en donne ",[356,804,805],{},"deux",[383,807,808,829],{},[386,809,810,393,813,393,816,819,820,819,823,819,826],{},[361,811,812],{},"QoraState\u003CT>",[356,814,815],{},"ce que vous avez",[361,817,818],{},"Initial",", ",[361,821,822],{},"Loading",[361,824,825],{},"Success",[361,827,828],{},"Failure",[386,830,831,393,833,393,836,819,838,819,840],{},[361,832,423],{},[356,834,835],{},"ce que Qora fait en ce moment",[361,837,428],{},[361,839,432],{},[361,841,842],{},"idle",[353,844,845,846,848,849,851,852,854,855,858,859,862,863,523],{},"Ces deux axes sont indépendants. Un widget peut être dans l'état ",[361,847,825],{}," (données visibles) pendant que ",[361,850,423],{}," est ",[361,853,428],{}," (revalidation SWR en arrière-plan en cours) : l'utilisateur voit ses données et une bannière \"Mise à jour...\" discrète, mais sans spinner ni écran vide. Le ",[361,856,857],{},"UserListScreen"," affiche toutes les combinaisons pertinentes sans un seul ",[361,860,861],{},"bool _isLoading"," ou ",[361,864,865],{},"bool _isOffline",[487,867,870],{"className":526,"code":868,"filename":869,"language":529,"meta":493,"style":493},"QoraBuilder\u003CList\u003CUser>>(\n  queryKey: const ['users'],\n  fetcher: FakeApi.getUsers,\n  options: const QoraOptions(\n    staleTime: Duration(minutes: 5),\n    gcTime: Duration(minutes: 10),\n  ),\n  builder: (context, state, fetchStatus) {\n\n    // ── Axe 2: FetchStatus → bannière de statut en haut ───────────────\n    final banner = switch (fetchStatus) {\n      FetchStatus.fetching => const _StatusBanner(\n          icon: Icons.sync,\n          label: 'Mise à jour…',\n          color: Colors.blue,\n        ),\n      FetchStatus.paused => const _StatusBanner(\n          icon: Icons.wifi_off,\n          label: 'Hors ligne : données en cache affichées',\n          color: Colors.orange,\n        ),\n      FetchStatus.idle => const SizedBox.shrink(),\n    };\n\n    // ── Axe 1: QoraState → contenu principal ──────────────────────────\n    return switch (state) {\n\n      // Premier chargement sans cache, ou hors ligne sans cache du tout\n      Initial() || Loading(previousData: null) =>\n          const Center(child: CircularProgressIndicator()),\n\n      // Erreur critique : rien à afficher\n      Failure(:final error, previousData: null) => _ErrorScreen(\n          error: error,\n          onRetry: () => context.qora.invalidateWhere(\n            (key) => key.firstOrNull == 'users',\n          ),\n        ),\n\n      // Données disponibles dans TOUS les cas restants :\n      //   Success        → données fraîches\n      //   Loading(data)  → données périmées, SWR en cours\n      //   Failure(data)  → rafraîchissement échoué, données périmées toujours affichées\n      _ => Column(\n          children: [\n            banner,\n            if (state is Failure\u003CList\u003CUser>>)\n              _ErrorBanner(error: state.error), // erreur légère : liste toujours visible\n            Expanded(\n              child: RefreshIndicator(\n                onRefresh: () async {\n                  context.qora.invalidateWhere(\n                    (key) => key.firstOrNull == 'users',\n                  );\n                  await Future\u003Cvoid>.delayed(const Duration(milliseconds: 300));\n                },\n                child: ListView.builder(\n                  itemCount: state.dataOrNull?.length ?? 0,\n                  itemBuilder: (_, i) => _UserTile(user: state.dataOrNull![i]),\n                ),\n              ),\n            ),\n          ],\n        ),\n    };\n  },\n)\n","lib/screens/user_list_screen.dart",[361,871,872,890,911,929,942,961,980,986,1007,1011,1017,1034,1054,1071,1083,1100,1107,1124,1139,1151,1167,1174,1200,1208,1213,1219,1230,1235,1241,1270,1292,1297,1303,1337,1349,1376,1400,1408,1415,1420,1426,1432,1438,1444,1457,1468,1476,1502,1525,1533,1546,1562,1578,1598,1606,1648,1656,1674,1703,1743,1751,1759,1767,1775,1782,1789,1797],{"__ignoreMap":493},[533,873,874,876,879,882,884,887],{"class":535,"line":536},[533,875,108],{"class":551},[533,877,878],{"class":543},"\u003C",[533,880,881],{"class":551},"List",[533,883,878],{"class":543},[533,885,886],{"class":551},"User",[533,888,889],{"class":543},">>(\n",[533,891,892,895,897,899,902,906,909],{"class":535,"line":558},[533,893,894],{"class":543},"  queryKey",[533,896,564],{"class":547},[533,898,567],{"class":539},[533,900,901],{"class":543}," [",[533,903,905],{"class":904},"sfazB","'users'",[533,907,908],{"class":543},"]",[533,910,611],{"class":547},[533,912,913,916,918,921,924,927],{"class":535,"line":575},[533,914,915],{"class":543},"  fetcher",[533,917,564],{"class":547},[533,919,920],{"class":551}," FakeApi",[533,922,923],{"class":547},".",[533,925,926],{"class":543},"getUsers",[533,928,611],{"class":547},[533,930,931,934,936,938,940],{"class":535,"line":588},[533,932,933],{"class":543},"  options",[533,935,564],{"class":547},[533,937,567],{"class":539},[533,939,583],{"class":551},[533,941,555],{"class":543},[533,943,944,947,949,951,953,955,957,959],{"class":535,"line":614},[533,945,946],{"class":543},"    staleTime",[533,948,564],{"class":547},[533,950,596],{"class":551},[533,952,599],{"class":543},[533,954,564],{"class":547},[533,956,605],{"class":604},[533,958,608],{"class":543},[533,960,611],{"class":547},[533,962,963,966,968,970,972,974,976,978],{"class":535,"line":635},[533,964,965],{"class":543},"    gcTime",[533,967,564],{"class":547},[533,969,596],{"class":551},[533,971,599],{"class":543},[533,973,564],{"class":547},[533,975,628],{"class":604},[533,977,608],{"class":543},[533,979,611],{"class":547},[533,981,982,984],{"class":535,"line":643},[533,983,659],{"class":543},[533,985,611],{"class":547},[533,987,988,991,993,996,999,1002,1004],{"class":535,"line":656},[533,989,990],{"class":543},"  builder",[533,992,564],{"class":547},[533,994,995],{"class":543}," (context",[533,997,998],{"class":547},",",[533,1000,1001],{"class":543}," state",[533,1003,998],{"class":547},[533,1005,1006],{"class":543}," fetchStatus) {\n",[533,1008,1009],{"class":535,"line":664},[533,1010,676],{"emptyLinePlaceholder":675},[533,1012,1013],{"class":535,"line":672},[533,1014,1016],{"class":1015},"sHwdD","    // ── Axe 2: FetchStatus → bannière de statut en haut ───────────────\n",[533,1018,1019,1022,1025,1027,1031],{"class":535,"line":679},[533,1020,1021],{"class":539},"    final",[533,1023,1024],{"class":543}," banner ",[533,1026,548],{"class":547},[533,1028,1030],{"class":1029},"s7zQu"," switch",[533,1032,1033],{"class":543}," (fetchStatus) {\n",[533,1035,1036,1039,1041,1044,1047,1049,1052],{"class":535,"line":688},[533,1037,1038],{"class":551},"      FetchStatus",[533,1040,923],{"class":547},[533,1042,1043],{"class":543},"fetching ",[533,1045,1046],{"class":547},"=>",[533,1048,567],{"class":539},[533,1050,1051],{"class":551}," _StatusBanner",[533,1053,555],{"class":543},[533,1055,1056,1059,1061,1064,1066,1069],{"class":535,"line":696},[533,1057,1058],{"class":543},"          icon",[533,1060,564],{"class":547},[533,1062,1063],{"class":551}," Icons",[533,1065,923],{"class":547},[533,1067,1068],{"class":1029},"sync",[533,1070,611],{"class":547},[533,1072,1073,1076,1078,1081],{"class":535,"line":709},[533,1074,1075],{"class":543},"          label",[533,1077,564],{"class":547},[533,1079,1080],{"class":904}," 'Mise à jour…'",[533,1082,611],{"class":547},[533,1084,1085,1088,1090,1093,1095,1098],{"class":535,"line":730},[533,1086,1087],{"class":543},"          color",[533,1089,564],{"class":547},[533,1091,1092],{"class":551}," Colors",[533,1094,923],{"class":547},[533,1096,1097],{"class":543},"blue",[533,1099,611],{"class":547},[533,1101,1102,1105],{"class":535,"line":746},[533,1103,1104],{"class":543},"        )",[533,1106,611],{"class":547},[533,1108,1109,1111,1113,1116,1118,1120,1122],{"class":535,"line":763},[533,1110,1038],{"class":551},[533,1112,923],{"class":547},[533,1114,1115],{"class":543},"paused ",[533,1117,1046],{"class":547},[533,1119,567],{"class":539},[533,1121,1051],{"class":551},[533,1123,555],{"class":543},[533,1125,1126,1128,1130,1132,1134,1137],{"class":535,"line":770},[533,1127,1058],{"class":543},[533,1129,564],{"class":547},[533,1131,1063],{"class":551},[533,1133,923],{"class":547},[533,1135,1136],{"class":543},"wifi_off",[533,1138,611],{"class":547},[533,1140,1142,1144,1146,1149],{"class":535,"line":1141},19,[533,1143,1075],{"class":543},[533,1145,564],{"class":547},[533,1147,1148],{"class":904}," 'Hors ligne : données en cache affichées'",[533,1150,611],{"class":547},[533,1152,1154,1156,1158,1160,1162,1165],{"class":535,"line":1153},20,[533,1155,1087],{"class":543},[533,1157,564],{"class":547},[533,1159,1092],{"class":551},[533,1161,923],{"class":547},[533,1163,1164],{"class":543},"orange",[533,1166,611],{"class":547},[533,1168,1170,1172],{"class":535,"line":1169},21,[533,1171,1104],{"class":543},[533,1173,611],{"class":547},[533,1175,1177,1179,1181,1184,1186,1188,1191,1193,1196,1198],{"class":535,"line":1176},22,[533,1178,1038],{"class":551},[533,1180,923],{"class":547},[533,1182,1183],{"class":543},"idle ",[533,1185,1046],{"class":547},[533,1187,567],{"class":539},[533,1189,1190],{"class":551}," SizedBox",[533,1192,923],{"class":547},[533,1194,1195],{"class":682},"shrink",[533,1197,741],{"class":543},[533,1199,611],{"class":547},[533,1201,1203,1206],{"class":535,"line":1202},23,[533,1204,1205],{"class":543},"    }",[533,1207,669],{"class":547},[533,1209,1211],{"class":535,"line":1210},24,[533,1212,676],{"emptyLinePlaceholder":675},[533,1214,1216],{"class":535,"line":1215},25,[533,1217,1218],{"class":1015},"    // ── Axe 1: QoraState → contenu principal ──────────────────────────\n",[533,1220,1222,1225,1227],{"class":535,"line":1221},26,[533,1223,1224],{"class":1029},"    return",[533,1226,1030],{"class":1029},[533,1228,1229],{"class":543}," (state) {\n",[533,1231,1233],{"class":535,"line":1232},27,[533,1234,676],{"emptyLinePlaceholder":675},[533,1236,1238],{"class":535,"line":1237},28,[533,1239,1240],{"class":1015},"      // Premier chargement sans cache, ou hors ligne sans cache du tout\n",[533,1242,1244,1247,1250,1253,1256,1259,1261,1264,1267],{"class":535,"line":1243},29,[533,1245,1246],{"class":551},"      Initial",[533,1248,1249],{"class":543},"() ",[533,1251,1252],{"class":547},"||",[533,1254,1255],{"class":551}," Loading",[533,1257,1258],{"class":543},"(previousData",[533,1260,564],{"class":547},[533,1262,1263],{"class":547}," null",[533,1265,1266],{"class":543},") ",[533,1268,1269],{"class":547},"=>\n",[533,1271,1273,1276,1279,1282,1284,1287,1290],{"class":535,"line":1272},30,[533,1274,1275],{"class":539},"          const",[533,1277,1278],{"class":551}," Center",[533,1280,1281],{"class":543},"(child",[533,1283,564],{"class":547},[533,1285,1286],{"class":551}," CircularProgressIndicator",[533,1288,1289],{"class":543},"())",[533,1291,611],{"class":547},[533,1293,1295],{"class":535,"line":1294},31,[533,1296,676],{"emptyLinePlaceholder":675},[533,1298,1300],{"class":535,"line":1299},32,[533,1301,1302],{"class":1015},"      // Erreur critique : rien à afficher\n",[533,1304,1306,1309,1312,1314,1316,1319,1321,1324,1326,1328,1330,1332,1335],{"class":535,"line":1305},33,[533,1307,1308],{"class":551},"      Failure",[533,1310,1311],{"class":543},"(",[533,1313,564],{"class":547},[533,1315,540],{"class":539},[533,1317,1318],{"class":543}," error",[533,1320,998],{"class":547},[533,1322,1323],{"class":543}," previousData",[533,1325,564],{"class":547},[533,1327,1263],{"class":547},[533,1329,1266],{"class":543},[533,1331,1046],{"class":547},[533,1333,1334],{"class":551}," _ErrorScreen",[533,1336,555],{"class":543},[533,1338,1340,1343,1345,1347],{"class":535,"line":1339},34,[533,1341,1342],{"class":543},"          error",[533,1344,564],{"class":547},[533,1346,1318],{"class":543},[533,1348,611],{"class":547},[533,1350,1352,1355,1357,1360,1362,1365,1367,1370,1372,1374],{"class":535,"line":1351},35,[533,1353,1354],{"class":543},"          onRetry",[533,1356,564],{"class":547},[533,1358,1359],{"class":543}," () ",[533,1361,1046],{"class":547},[533,1363,1364],{"class":543}," context",[533,1366,923],{"class":547},[533,1368,1369],{"class":543},"qora",[533,1371,923],{"class":547},[533,1373,459],{"class":682},[533,1375,555],{"class":543},[533,1377,1379,1382,1384,1387,1389,1392,1395,1398],{"class":535,"line":1378},36,[533,1380,1381],{"class":543},"            (key) ",[533,1383,1046],{"class":547},[533,1385,1386],{"class":543}," key",[533,1388,923],{"class":547},[533,1390,1391],{"class":543},"firstOrNull ",[533,1393,1394],{"class":547},"==",[533,1396,1397],{"class":904}," 'users'",[533,1399,611],{"class":547},[533,1401,1403,1406],{"class":535,"line":1402},37,[533,1404,1405],{"class":543},"          )",[533,1407,611],{"class":547},[533,1409,1411,1413],{"class":535,"line":1410},38,[533,1412,1104],{"class":543},[533,1414,611],{"class":547},[533,1416,1418],{"class":535,"line":1417},39,[533,1419,676],{"emptyLinePlaceholder":675},[533,1421,1423],{"class":535,"line":1422},40,[533,1424,1425],{"class":1015},"      // Données disponibles dans TOUS les cas restants :\n",[533,1427,1429],{"class":535,"line":1428},41,[533,1430,1431],{"class":1015},"      //   Success        → données fraîches\n",[533,1433,1435],{"class":535,"line":1434},42,[533,1436,1437],{"class":1015},"      //   Loading(data)  → données périmées, SWR en cours\n",[533,1439,1441],{"class":535,"line":1440},43,[533,1442,1443],{"class":1015},"      //   Failure(data)  → rafraîchissement échoué, données périmées toujours affichées\n",[533,1445,1447,1450,1452,1455],{"class":535,"line":1446},44,[533,1448,1449],{"class":543},"      _ ",[533,1451,1046],{"class":547},[533,1453,1454],{"class":551}," Column",[533,1456,555],{"class":543},[533,1458,1460,1463,1465],{"class":535,"line":1459},45,[533,1461,1462],{"class":543},"          children",[533,1464,564],{"class":547},[533,1466,1467],{"class":543}," [\n",[533,1469,1471,1474],{"class":535,"line":1470},46,[533,1472,1473],{"class":543},"            banner",[533,1475,611],{"class":547},[533,1477,1479,1482,1485,1488,1491,1493,1495,1497,1499],{"class":535,"line":1478},47,[533,1480,1481],{"class":1029},"            if",[533,1483,1484],{"class":543}," (state ",[533,1486,1487],{"class":547},"is",[533,1489,1490],{"class":551}," Failure",[533,1492,878],{"class":543},[533,1494,881],{"class":551},[533,1496,878],{"class":543},[533,1498,886],{"class":551},[533,1500,1501],{"class":543},">>)\n",[533,1503,1505,1508,1511,1513,1515,1517,1520,1522],{"class":535,"line":1504},48,[533,1506,1507],{"class":551},"              _ErrorBanner",[533,1509,1510],{"class":543},"(error",[533,1512,564],{"class":547},[533,1514,1001],{"class":543},[533,1516,923],{"class":547},[533,1518,1519],{"class":543},"error)",[533,1521,998],{"class":547},[533,1523,1524],{"class":1015}," // erreur légère : liste toujours visible\n",[533,1526,1528,1531],{"class":535,"line":1527},49,[533,1529,1530],{"class":551},"            Expanded",[533,1532,555],{"class":543},[533,1534,1536,1539,1541,1544],{"class":535,"line":1535},50,[533,1537,1538],{"class":543},"              child",[533,1540,564],{"class":547},[533,1542,1543],{"class":551}," RefreshIndicator",[533,1545,555],{"class":543},[533,1547,1549,1552,1554,1556,1559],{"class":535,"line":1548},51,[533,1550,1551],{"class":543},"                onRefresh",[533,1553,564],{"class":547},[533,1555,1359],{"class":543},[533,1557,1558],{"class":1029},"async",[533,1560,1561],{"class":543}," {\n",[533,1563,1565,1568,1570,1572,1574,1576],{"class":535,"line":1564},52,[533,1566,1567],{"class":543},"                  context",[533,1569,923],{"class":547},[533,1571,1369],{"class":543},[533,1573,923],{"class":547},[533,1575,459],{"class":682},[533,1577,555],{"class":543},[533,1579,1581,1584,1586,1588,1590,1592,1594,1596],{"class":535,"line":1580},53,[533,1582,1583],{"class":543},"                    (key) ",[533,1585,1046],{"class":547},[533,1587,1386],{"class":543},[533,1589,923],{"class":547},[533,1591,1391],{"class":543},[533,1593,1394],{"class":547},[533,1595,1397],{"class":904},[533,1597,611],{"class":547},[533,1599,1601,1604],{"class":535,"line":1600},54,[533,1602,1603],{"class":543},"                  )",[533,1605,669],{"class":547},[533,1607,1609,1612,1615,1617,1620,1623,1625,1628,1630,1633,1635,1638,1640,1643,1646],{"class":535,"line":1608},55,[533,1610,1611],{"class":1029},"                  await",[533,1613,1614],{"class":551}," Future",[533,1616,878],{"class":543},[533,1618,1619],{"class":539},"void",[533,1621,1622],{"class":543},">",[533,1624,923],{"class":547},[533,1626,1627],{"class":682},"delayed",[533,1629,1311],{"class":543},[533,1631,1632],{"class":539},"const",[533,1634,596],{"class":551},[533,1636,1637],{"class":543},"(milliseconds",[533,1639,564],{"class":547},[533,1641,1642],{"class":604}," 300",[533,1644,1645],{"class":543},"))",[533,1647,669],{"class":547},[533,1649,1651,1654],{"class":535,"line":1650},56,[533,1652,1653],{"class":543},"                }",[533,1655,611],{"class":547},[533,1657,1659,1662,1664,1667,1669,1672],{"class":535,"line":1658},57,[533,1660,1661],{"class":543},"                child",[533,1663,564],{"class":547},[533,1665,1666],{"class":551}," ListView",[533,1668,923],{"class":547},[533,1670,1671],{"class":682},"builder",[533,1673,555],{"class":543},[533,1675,1677,1680,1682,1684,1686,1689,1692,1695,1698,1701],{"class":535,"line":1676},58,[533,1678,1679],{"class":543},"                  itemCount",[533,1681,564],{"class":547},[533,1683,1001],{"class":543},[533,1685,923],{"class":547},[533,1687,1688],{"class":543},"dataOrNull",[533,1690,1691],{"class":547},"?.",[533,1693,1694],{"class":543},"length ",[533,1696,1697],{"class":547},"??",[533,1699,1700],{"class":604}," 0",[533,1702,611],{"class":547},[533,1704,1706,1709,1711,1714,1716,1719,1721,1724,1727,1729,1731,1733,1735,1738,1741],{"class":535,"line":1705},59,[533,1707,1708],{"class":543},"                  itemBuilder",[533,1710,564],{"class":547},[533,1712,1713],{"class":543}," (_",[533,1715,998],{"class":547},[533,1717,1718],{"class":543}," i) ",[533,1720,1046],{"class":547},[533,1722,1723],{"class":551}," _UserTile",[533,1725,1726],{"class":543},"(user",[533,1728,564],{"class":547},[533,1730,1001],{"class":543},[533,1732,923],{"class":547},[533,1734,1688],{"class":543},[533,1736,1737],{"class":547},"!",[533,1739,1740],{"class":543},"[i])",[533,1742,611],{"class":547},[533,1744,1746,1749],{"class":535,"line":1745},60,[533,1747,1748],{"class":543},"                )",[533,1750,611],{"class":547},[533,1752,1754,1757],{"class":535,"line":1753},61,[533,1755,1756],{"class":543},"              )",[533,1758,611],{"class":547},[533,1760,1762,1765],{"class":535,"line":1761},62,[533,1763,1764],{"class":543},"            )",[533,1766,611],{"class":547},[533,1768,1770,1773],{"class":535,"line":1769},63,[533,1771,1772],{"class":543},"          ]",[533,1774,611],{"class":547},[533,1776,1778,1780],{"class":535,"line":1777},64,[533,1779,1104],{"class":543},[533,1781,611],{"class":547},[533,1783,1785,1787],{"class":535,"line":1784},65,[533,1786,1205],{"class":543},[533,1788,669],{"class":547},[533,1790,1792,1795],{"class":535,"line":1791},66,[533,1793,1794],{"class":543},"  }",[533,1796,611],{"class":547},[533,1798,1800],{"class":535,"line":1799},67,[533,1801,1802],{"class":543},")\n",[353,1804,1805,1806,1809,1810,1812,1813,1819,1820,1822,1823,1825,1826,1829],{},"Le pattern ",[361,1807,1808],{},"Loading(previousData: null)"," est une déstructuration Dart 3 sur la classe sealed : il correspond à ",[361,1811,822],{}," ",[356,1814,1815,1816,1818],{},"uniquement quand ",[361,1817,441],{}," est null",", c'est-à-dire le tout premier chargement sans cache. Tout ",[361,1821,822],{}," ultérieur (revalidation SWR en arrière-plan) porte ",[361,1824,441],{}," et tombe dans le bras ",[361,1827,1828],{},"_",", gardant la liste visible. C'est pourquoi les utilisateurs ne voient jamais un écran vide ou un spinner plein écran lors de la re-navigation.",[792,1831,1833,1835],{"id":1832},"invalidatewhere-un-appel-toutes-les-clés-liées",[361,1834,459],{}," : un appel, toutes les clés liées",[353,1837,1838,1839,1842,1843,1845],{},"Après un pull-to-refresh, ",[361,1840,1841],{},"['users']"," (la liste) et chaque ",[361,1844,450],{}," (écrans de détail ouverts) ont besoin de données fraîches. Un prédicat les couvre tous :",[487,1847,1849],{"className":526,"code":1848,"filename":869,"language":529,"meta":493,"style":493},"context.qora.invalidateWhere((key) => key.firstOrNull == 'users');\n// Invalide : ['users'], ['users', '1'], ['users', '2'], …\n",[361,1850,1851,1883],{"__ignoreMap":493},[533,1852,1853,1856,1858,1860,1862,1864,1867,1869,1871,1873,1875,1877,1879,1881],{"class":535,"line":536},[533,1854,1855],{"class":543},"context",[533,1857,923],{"class":547},[533,1859,1369],{"class":543},[533,1861,923],{"class":547},[533,1863,459],{"class":682},[533,1865,1866],{"class":543},"((key) ",[533,1868,1046],{"class":547},[533,1870,1386],{"class":543},[533,1872,923],{"class":547},[533,1874,1391],{"class":543},[533,1876,1394],{"class":547},[533,1878,1397],{"class":904},[533,1880,608],{"class":543},[533,1882,669],{"class":547},[533,1884,1885],{"class":535,"line":558},[533,1886,1887],{"class":1015},"// Invalide : ['users'], ['users', '1'], ['users', '2'], …\n",[353,1889,1890,1891,1893,1894,1897],{},"Chaque ",[361,1892,108],{}," actif abonné à une clé correspondante transite vers ",[361,1895,1896],{},"Loading(previousData: data)"," et refetche. Les données existantes restent visibles : aucun écran vide nulle part dans la pile de navigation.",[792,1899,1901,1902,1905],{"id":1900},"cache-par-élément-et-updatedat-sur-lécran-de-détail","Cache par élément et ",[361,1903,1904],{},"updatedAt"," sur l'écran de détail",[353,1907,1890,1908,1911,1912,393,1915,1917,1918,1921,1922,1925,1926,523],{},[361,1909,1910],{},"UserDetailScreen"," utilise une ",[356,1913,1914],{},"clé paramétrée",[361,1916,450],{},". Cela donne à chaque utilisateur sa propre entrée de cache indépendante. L'écran lit également ",[361,1919,1920],{},"state.updatedAt"," (le ",[361,1923,1924],{},"DateTime"," du dernier fetch réussi) pour afficher un horodatage \"dernier chargement il y a X minutes\" en direct, rendant le comportement SWR ",[356,1927,1928],{},"visible à l'utilisateur",[487,1930,1933],{"className":526,"code":1931,"filename":1932,"language":529,"meta":493,"style":493},"QoraBuilder\u003CUser>(\n  queryKey: ['users', userId],       // ex. ['users', '1']\n  fetcher: () => FakeApi.getUser(userId),\n  options: const QoraOptions(staleTime: Duration(minutes: 5)),\n  builder: (context, state, fetchStatus) {\n    return switch (state) {\n      Initial() || Loading(previousData: null) =>\n          const Center(child: CircularProgressIndicator()),\n      Failure(:final error, previousData: null) => _ErrorScreen(error),\n      _ => Column(\n          children: [\n            if (fetchStatus == FetchStatus.paused)\n              const _Banner(label: 'Hors ligne : données en cache affichées'),\n            if (state is Failure\u003CUser>)\n              _Banner(label: 'Rafraîchissement échoué : ${state.error}'),\n            if (fetchStatus == FetchStatus.fetching)\n              const LinearProgressIndicator(minHeight: 2),\n            Expanded(\n              child: _UserDetailView(\n                user: state.dataOrNull!,\n                updatedAt: state.updatedAt, // ← horodatage depuis l'état Success\n                isStale: state is! Success\u003CUser>,\n              ),\n            ),\n          ],\n        ),\n    };\n  },\n)\n","lib/screens/user_detail_screen.dart",[361,1934,1935,1946,1966,1988,2015,2031,2039,2059,2075,2106,2116,2124,2141,2160,2177,2211,2226,2245,2251,2262,2278,2296,2320,2326,2332,2338,2344,2350,2356],{"__ignoreMap":493},[533,1936,1937,1939,1941,1943],{"class":535,"line":536},[533,1938,108],{"class":551},[533,1940,878],{"class":543},[533,1942,886],{"class":551},[533,1944,1945],{"class":543},">(\n",[533,1947,1948,1950,1952,1954,1956,1958,1961,1963],{"class":535,"line":558},[533,1949,894],{"class":543},[533,1951,564],{"class":547},[533,1953,901],{"class":543},[533,1955,905],{"class":904},[533,1957,998],{"class":547},[533,1959,1960],{"class":543}," userId]",[533,1962,998],{"class":547},[533,1964,1965],{"class":1015},"       // ex. ['users', '1']\n",[533,1967,1968,1970,1972,1974,1976,1978,1980,1983,1986],{"class":535,"line":575},[533,1969,915],{"class":543},[533,1971,564],{"class":547},[533,1973,1359],{"class":543},[533,1975,1046],{"class":547},[533,1977,920],{"class":551},[533,1979,923],{"class":547},[533,1981,1982],{"class":682},"getUser",[533,1984,1985],{"class":543},"(userId)",[533,1987,611],{"class":547},[533,1989,1990,1992,1994,1996,1998,2001,2003,2005,2007,2009,2011,2013],{"class":535,"line":588},[533,1991,933],{"class":543},[533,1993,564],{"class":547},[533,1995,567],{"class":539},[533,1997,583],{"class":551},[533,1999,2000],{"class":543},"(staleTime",[533,2002,564],{"class":547},[533,2004,596],{"class":551},[533,2006,599],{"class":543},[533,2008,564],{"class":547},[533,2010,605],{"class":604},[533,2012,1645],{"class":543},[533,2014,611],{"class":547},[533,2016,2017,2019,2021,2023,2025,2027,2029],{"class":535,"line":614},[533,2018,990],{"class":543},[533,2020,564],{"class":547},[533,2022,995],{"class":543},[533,2024,998],{"class":547},[533,2026,1001],{"class":543},[533,2028,998],{"class":547},[533,2030,1006],{"class":543},[533,2032,2033,2035,2037],{"class":535,"line":635},[533,2034,1224],{"class":1029},[533,2036,1030],{"class":1029},[533,2038,1229],{"class":543},[533,2040,2041,2043,2045,2047,2049,2051,2053,2055,2057],{"class":535,"line":643},[533,2042,1246],{"class":551},[533,2044,1249],{"class":543},[533,2046,1252],{"class":547},[533,2048,1255],{"class":551},[533,2050,1258],{"class":543},[533,2052,564],{"class":547},[533,2054,1263],{"class":547},[533,2056,1266],{"class":543},[533,2058,1269],{"class":547},[533,2060,2061,2063,2065,2067,2069,2071,2073],{"class":535,"line":656},[533,2062,1275],{"class":539},[533,2064,1278],{"class":551},[533,2066,1281],{"class":543},[533,2068,564],{"class":547},[533,2070,1286],{"class":551},[533,2072,1289],{"class":543},[533,2074,611],{"class":547},[533,2076,2077,2079,2081,2083,2085,2087,2089,2091,2093,2095,2097,2099,2101,2104],{"class":535,"line":664},[533,2078,1308],{"class":551},[533,2080,1311],{"class":543},[533,2082,564],{"class":547},[533,2084,540],{"class":539},[533,2086,1318],{"class":543},[533,2088,998],{"class":547},[533,2090,1323],{"class":543},[533,2092,564],{"class":547},[533,2094,1263],{"class":547},[533,2096,1266],{"class":543},[533,2098,1046],{"class":547},[533,2100,1334],{"class":551},[533,2102,2103],{"class":543},"(error)",[533,2105,611],{"class":547},[533,2107,2108,2110,2112,2114],{"class":535,"line":672},[533,2109,1449],{"class":543},[533,2111,1046],{"class":547},[533,2113,1454],{"class":551},[533,2115,555],{"class":543},[533,2117,2118,2120,2122],{"class":535,"line":679},[533,2119,1462],{"class":543},[533,2121,564],{"class":547},[533,2123,1467],{"class":543},[533,2125,2126,2128,2131,2133,2136,2138],{"class":535,"line":688},[533,2127,1481],{"class":1029},[533,2129,2130],{"class":543}," (fetchStatus ",[533,2132,1394],{"class":547},[533,2134,2135],{"class":551}," FetchStatus",[533,2137,923],{"class":547},[533,2139,2140],{"class":543},"paused)\n",[533,2142,2143,2146,2149,2152,2154,2156,2158],{"class":535,"line":696},[533,2144,2145],{"class":539},"              const",[533,2147,2148],{"class":551}," _Banner",[533,2150,2151],{"class":543},"(label",[533,2153,564],{"class":547},[533,2155,1148],{"class":904},[533,2157,608],{"class":543},[533,2159,611],{"class":547},[533,2161,2162,2164,2166,2168,2170,2172,2174],{"class":535,"line":709},[533,2163,1481],{"class":1029},[533,2165,1484],{"class":543},[533,2167,1487],{"class":547},[533,2169,1490],{"class":551},[533,2171,878],{"class":543},[533,2173,886],{"class":551},[533,2175,2176],{"class":543},">)\n",[533,2178,2179,2182,2184,2186,2189,2192,2196,2198,2201,2204,2207,2209],{"class":535,"line":730},[533,2180,2181],{"class":551},"              _Banner",[533,2183,2151],{"class":543},[533,2185,564],{"class":547},[533,2187,2188],{"class":904}," 'Rafraîchissement échoué : ",[533,2190,2191],{"class":543},"${",[533,2193,2195],{"class":2194},"sHdIc","state",[533,2197,923],{"class":543},[533,2199,2200],{"class":2194},"error",[533,2202,2203],{"class":543},"}",[533,2205,2206],{"class":904},"'",[533,2208,608],{"class":543},[533,2210,611],{"class":547},[533,2212,2213,2215,2217,2219,2221,2223],{"class":535,"line":746},[533,2214,1481],{"class":1029},[533,2216,2130],{"class":543},[533,2218,1394],{"class":547},[533,2220,2135],{"class":551},[533,2222,923],{"class":547},[533,2224,2225],{"class":543},"fetching)\n",[533,2227,2228,2230,2233,2236,2238,2241,2243],{"class":535,"line":763},[533,2229,2145],{"class":539},[533,2231,2232],{"class":551}," LinearProgressIndicator",[533,2234,2235],{"class":543},"(minHeight",[533,2237,564],{"class":547},[533,2239,2240],{"class":604}," 2",[533,2242,608],{"class":543},[533,2244,611],{"class":547},[533,2246,2247,2249],{"class":535,"line":770},[533,2248,1530],{"class":551},[533,2250,555],{"class":543},[533,2252,2253,2255,2257,2260],{"class":535,"line":1141},[533,2254,1538],{"class":543},[533,2256,564],{"class":547},[533,2258,2259],{"class":551}," _UserDetailView",[533,2261,555],{"class":543},[533,2263,2264,2267,2269,2271,2273,2275],{"class":535,"line":1153},[533,2265,2266],{"class":543},"                user",[533,2268,564],{"class":547},[533,2270,1001],{"class":543},[533,2272,923],{"class":547},[533,2274,1688],{"class":543},[533,2276,2277],{"class":547},"!,\n",[533,2279,2280,2283,2285,2287,2289,2291,2293],{"class":535,"line":1169},[533,2281,2282],{"class":543},"                updatedAt",[533,2284,564],{"class":547},[533,2286,1001],{"class":543},[533,2288,923],{"class":547},[533,2290,1904],{"class":543},[533,2292,998],{"class":547},[533,2294,2295],{"class":1015}," // ← horodatage depuis l'état Success\n",[533,2297,2298,2301,2303,2306,2309,2312,2314,2316,2318],{"class":535,"line":1176},[533,2299,2300],{"class":543},"                isStale",[533,2302,564],{"class":547},[533,2304,2305],{"class":543}," state ",[533,2307,2308],{"class":547},"is!",[533,2310,2311],{"class":551}," Success",[533,2313,878],{"class":543},[533,2315,886],{"class":551},[533,2317,1622],{"class":543},[533,2319,611],{"class":547},[533,2321,2322,2324],{"class":535,"line":1202},[533,2323,1756],{"class":543},[533,2325,611],{"class":547},[533,2327,2328,2330],{"class":535,"line":1210},[533,2329,1764],{"class":543},[533,2331,611],{"class":547},[533,2333,2334,2336],{"class":535,"line":1215},[533,2335,1772],{"class":543},[533,2337,611],{"class":547},[533,2339,2340,2342],{"class":535,"line":1221},[533,2341,1104],{"class":543},[533,2343,611],{"class":547},[533,2345,2346,2348],{"class":535,"line":1232},[533,2347,1205],{"class":543},[533,2349,669],{"class":547},[533,2351,2352,2354],{"class":535,"line":1237},[533,2353,1794],{"class":543},[533,2355,611],{"class":547},[533,2357,2358],{"class":535,"line":1243},[533,2359,1802],{"class":543},[353,2361,2362,2363,819,2366,2368,2369,2372],{},"Naviguez vers un utilisateur, revenez, rouvrez : les données apparaissent à la ",[356,2364,2365],{},"première frame",[361,2367,1904],{}," affiche l'heure du fetch d'origine, et un ",[361,2370,2371],{},"LinearProgressIndicator"," de 2 px se déclenche uniquement si les données étaient périmées. L'appel réseau se produit silencieusement en arrière-plan.",[376,2374],{},[348,2376,2378],{"id":2377},"étapes-de-validation","Étapes de validation",[353,2380,2381],{},"L'exemple inclut un guide interactif pour rendre le comportement du cache observable :",[2383,2384,2385,2392,2399,2402,2408,2414],"ol",{},[386,2386,2387,2388,2391],{},"Ouvrir ",[356,2389,2390],{},"Liste d'utilisateurs"," → attendre 2 s pour le chargement initial",[386,2393,2394,2395,2398],{},"Revenir en arrière et rouvrir → ",[356,2396,2397],{},"instantané depuis le cache",", sans appel réseau",[386,2400,2401],{},"Pull-to-refresh → la liste périmée reste visible ; la bannière bleue \"Mise à jour...\" apparaît simultanément",[386,2403,2404,2405],{},"Ouvrir un utilisateur → noter l'horodatage ",[356,2406,2407],{},"\"Dernier chargement\"",[386,2409,2410,2411,2413],{},"Revenir et rouvrir dans les 5 min → ",[356,2412,2397],{},", horodatage inchangé",[386,2415,2416],{},"Activer le mode avion → bannière orange \"Hors ligne\" ; la liste reste entièrement utilisable",[376,2418],{},[348,2420,2422],{"id":2421},"code-source","Code source",[487,2424,2427],{"className":2425,"code":2426,"language":492,"meta":493},[490],"examples/ex_01_basic_query/\n├── lib/\n│   ├── main.dart                    ← configuration QoraClient + QoraScope\n│   ├── models/user.dart             ← classe de données pure\n│   ├── services/fake_api.dart       ← API simulée (délai 2 s, ~30 % mode échec)\n│   └── screens/\n│       ├── user_list_screen.dart    ← QoraBuilder + matrice FetchStatus × QoraState\n│       └── user_detail_screen.dart  ← clé par élément, updatedAt, previousData\n└── pubspec.yaml\n",[361,2428,2426],{"__ignoreMap":493},[353,2430,2431],{},[2432,2433,2437],"a",{"href":2434,"rel":2435},"https://github.com/meragix/qora/tree/main/examples/ex_01_basic_query",[2436],"nofollow","Voir le code source complet",[376,2439],{},[348,2441,2443],{"id":2442},"voir-aussi","Voir aussi",[383,2445,2446,2452,2458,2464],{},[386,2447,2448,2451],{},[2432,2449,21],{"href":2450},"../getting-started/quick-start",": présentation conceptuelle pas à pas de la même fonctionnalité",[386,2453,2454,2457],{},[2432,2455,108],{"href":2456},"../flutter-integration/qora-builder",": API widget complète",[386,2459,2460,2463],{},[2432,2461,72],{"href":2462},"../core-concepts/stale-while-revalidate",": fonctionnement du modèle de fraîcheur du cache",[386,2465,2466,2469,2470],{},[2432,2467,62],{"href":2468},"../core-concepts/query-states",": référence de la classe sealed ",[361,2471,211],{},[2473,2474,2475],"style",{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}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);}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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 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 .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":493,"searchDepth":558,"depth":558,"links":2477},[2478,2479,2480,2481,2488,2489,2490],{"id":350,"depth":558,"text":351},{"id":380,"depth":558,"text":381},{"id":477,"depth":558,"text":478},{"id":789,"depth":558,"text":790,"children":2482},[2483,2484,2486],{"id":794,"depth":575,"text":795},{"id":1832,"depth":575,"text":2485},"invalidateWhere : un appel, toutes les clés liées",{"id":1900,"depth":575,"text":2487},"Cache par élément et updatedAt sur l'écran de détail",{"id":2377,"depth":558,"text":2378},{"id":2421,"depth":558,"text":2422},{"id":2442,"depth":558,"text":2443},"Une fonctionnalité liste-détail complète : cache SWR, état UI à deux axes avec FetchStatus, clés de requête par élément, pull-to-refresh et dégradation gracieuse. Zéro boilerplate de gestion d'état.","md",null,{},{"icon":300},{"title":2497,"description":2498},"Étude de cas : Liste et détail utilisateur - Qora","Écrans liste et détail de niveau production avec QoraBuilder. Couvre le cache SWR, FetchStatus, la dégradation gracieuse via previousData, les clés par élément et invalidateWhere.","RyQrTWKFRNSdrLi9RAIrrUCfvN_yZ34POXcUksXqSZM",[2501,2503],{"title":287,"path":288,"stem":289,"description":2502,"icon":290,"children":-1},"Envelopper les clients GraphQL graphql ou ferry dans des fetchers Qora pour une gestion fluide des requêtes avec stale-while-revalidate, invalidation de cache et pagination.",{"title":302,"path":303,"stem":304,"description":2504,"icon":151,"children":-1},"Une implémentation complète de mise à jour optimiste : setQueryData pour un retour UI immédiat, restoreQueryData pour le rollback automatique en cas d'échec, et invalidateWhere pour la réconciliation serveur.",1794040411337]