{"id":"l-rn-3-1","courseId":"react-native-apps","lang":"ar","kind":"lesson","summary":"حوّل الملفات داخل src/app إلى شاشات Trailhead باستخدام Expo Router، وغلّفها بـ stack أصلي عبر ملف layout، وتنقّل بينها باستخدام Link وكائن router.","html":"<p>على الويب، يُظهر شريط العنوان مكانك، ويمشي زرّ الرجوع عبر السجلّ. أما على الهاتف فلا يرى المستخدمون أيًّا منهما: يرون شاشات تنزلق من اليمين، وسهم رجوع أو إيماءة سحب، وtabs في الأسفل. وتحت السطح، ما زال Expo Router يعطي كل شاشة URL، وهذا ما يجعل الـ deep links والتنقّل المضبوط بالأنواع ونسخة الويب ممكنة. تحصل على النموذجين معًا.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">الملفات هي المسارات</h2>\n<p>يبني Expo Router نظام التنقّل من الملفات الموجودة في <code dir=\"ltr\">src/app</code>. مسار الملف هو المسار (route):</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Text</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code>src/app/\n  _layout.tsx          wraps everything below in a navigator\n  index.tsx            /\n  about.tsx            /about\n  settings/\n    _layout.tsx        wraps the settings screens\n    index.tsx          /settings\n    units.tsx          /settings/units\n  +not-found.tsx       any URL that doesn't match</code></pre></div>\n<p>كل ملف مسار يصدّر component بشكل افتراضي (default export). وهذا كل ما تعنيه الشاشة:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"tsx\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">src/app/about.tsx</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { Text, View } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'react-native'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> default</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> About</span><span style=\"color:#953800;--shiki-dark:#FFA657\">() </span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ flex: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, padding: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">16</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Trailhead keeps a private log of your hikes.&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  );</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>ولأن <strong>كل</strong> ملف في المجلّد يصبح مسارًا، أبقِ كل ما عداه في مكان آخر: <code dir=\"ltr\">src/components</code>، و<code dir=\"ltr\">src/lib</code>، و<code dir=\"ltr\">src/state</code>. component صفّ حُفظ خطأً في <code dir=\"ltr\">src/app</code> يصبح شاشة يستطيع أي أحد الوصول إليها عبر deep link.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">الـ layouts تغلّف ما يجاورها</h2>\n<p>يرسم ملف <code dir=\"ltr\">_layout.tsx</code> أداة تنقّل حول المسارات الموجودة في مجلّده. يستخدم الـ root layout في Trailhead <strong>Stack</strong>، وهو أداة التنقّل الأصلية التي تضيف الشاشات وتُزيلها بحركات المنصّة، مع السحب للرجوع على iOS وإيماءة الرجوع على مستوى النظام في Android:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"tsx\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">src/app/_layout.tsx</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { Stack } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'expo-router'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> default</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> RootLayout</span><span style=\"color:#953800;--shiki-dark:#FFA657\">() </span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Stack</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> screenOptions</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ headerTintColor: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'#2f6f4e'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Stack.Screen</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> name</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"index\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> options</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ title: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'Trailhead'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> /></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Stack.Screen</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> name</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"about\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> options</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ title: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'About'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> /></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Stack</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  );</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>تنطبق <code dir=\"ltr\">screenOptions</code> على كل شاشة في الـ stack؛ وكل <code dir=\"ltr\">Stack.Screen</code> يخصّص شاشة واحدة، تُطابَق باسم مسارها <code dir=\"ltr\">name</code>. لست مضطرًا إلى إدراج كل مسار: المسارات غير المدرجة تعمل مع ذلك بالخيارات الافتراضية.</p>\n<p>بضعة خيارات للترويسة تغطّي معظم الاحتياجات: <code dir=\"ltr\">title</code>، و<code dir=\"ltr\">headerShown: false</code> للشاشات التي ترسم شريطها العلوي بنفسها (مثل الخريطة بملء الشاشة من الدرس السابق)، و<code dir=\"ltr\">headerBackTitle</code> للتسمية بجوار سهم الرجوع في iOS، و<code dir=\"ltr\">headerRight</code> لزرّ مثل “Edit”. وعلى iOS يعطيك <code dir=\"ltr\">headerLargeTitle: true</code> العنوان الكبير القابل للطي الذي تراه في تطبيقات Apple نفسها، وهو يناسب قائمة رئيسية مثل سجلّ الرحلات.</p>\n<p>وحين يعتمد العنوان على البيانات (اسم الرحلة مثلًا)، ارسم <code dir=\"ltr\">&lt;Stack.Screen options={{ title: hike.name }} /&gt;</code> داخل الشاشة نفسها. لا يرسم شيئًا مرئيًا؛ بل يضبط ترويسة الشاشة التي هو فيها.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 250\" role=\"img\" aria-labelledby=\"l-rn-3-1-t1\"><defs><marker id=\"arrow-l-rn-3-1-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-rn-3-1-t1\">الملفات index.tsx وsettings/index.tsx وsettings/units.tsx تقابل العناوين / و/settings و/settings/units؛ والتنقّل يضيف كلًّا منها إلى stack، والرجوع يُزيل الشاشة العليا.</title>\n  <rect class=\"d-box\" x=\"20\" y=\"30\" width=\"220\" height=\"190\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"40\" y=\"58\">src/app/</text>\n  <text class=\"d-code\" x=\"50\" y=\"92\">index.tsx</text>\n  <text class=\"d-code\" x=\"50\" y=\"132\">settings/index.tsx</text>\n  <text class=\"d-code\" x=\"50\" y=\"172\">settings/units.tsx</text>\n  <path class=\"d-arrow\" d=\"M240 88 L300 88\" marker-end=\"url(#arrow-l-rn-3-1-1)\"/>\n  <path class=\"d-arrow\" d=\"M240 128 L300 128\" marker-end=\"url(#arrow-l-rn-3-1-1)\"/>\n  <path class=\"d-arrow\" d=\"M240 168 L300 168\" marker-end=\"url(#arrow-l-rn-3-1-1)\"/>\n  <text class=\"d-code\" x=\"310\" y=\"92\">/</text>\n  <text class=\"d-code\" x=\"310\" y=\"132\">/settings</text>\n  <text class=\"d-code\" x=\"310\" y=\"172\">/settings/units</text>\n  <rect class=\"d-box-primary\" x=\"480\" y=\"150\" width=\"170\" height=\"40\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"565\" y=\"175\" text-anchor=\"middle\">/</text>\n  <rect class=\"d-box-primary\" x=\"480\" y=\"104\" width=\"170\" height=\"40\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"565\" y=\"129\" text-anchor=\"middle\">/settings</text>\n  <rect class=\"d-box-accent\" x=\"480\" y=\"58\" width=\"170\" height=\"40\" rx=\"8\"/>\n  <text class=\"d-label-strong\" x=\"565\" y=\"83\" text-anchor=\"middle\">/settings/units</text>\n  <text class=\"d-label-muted\" x=\"565\" y=\"40\" text-anchor=\"middle\">Stack (الأعلى هو الظاهر)</text>\n  <text class=\"d-label-muted\" x=\"565\" y=\"215\" text-anchor=\"middle\">push يضيف · back يُزيل</text>\n</svg><figcaption>الملفات تصبح مسارات، والـ Stack في الـ layout يحمل الشاشات التي زرتها</figcaption></figure>\n<h2 id=\"sec-3\" tabindex=\"-1\">التنقّل بين الشاشات</h2>\n<p>لكل ما ينقر عليه المستخدم، استخدم <code dir=\"ltr\">Link</code>. يرسم نصًّا افتراضيًا؛ ومع <code dir=\"ltr\">asChild</code> يسلّم التنقّل إلى الـ component القابل للضغط الخاص بك:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"tsx\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">src/app/index.tsx</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { Link } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'expo-router'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { Pressable, Text, View } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'react-native'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> default</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> HikeLog</span><span style=\"color:#953800;--shiki-dark:#FFA657\">() </span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ flex: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, padding: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">16</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, gap: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">12</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Link</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> href</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/about\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>About Trailhead&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Link</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Link</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> href</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/settings\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> asChild</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Pressable</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> accessibilityRole</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"button\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">          &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Settings&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Pressable</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Link</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  );</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>ومن الكود، بعد حفظ أو تسجيل دخول، استخدم الكائن <code dir=\"ltr\">router</code>:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"tsx\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">TSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { router } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'expo-router'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">router.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">push</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'/settings/units'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">); </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// add a screen on top</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">router.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">back</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();                  </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// pop the top screen</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">router.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">replace</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'/'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);            </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// swap the current screen; no way back to it</span></span></code></pre></div>\n<p><code dir=\"ltr\">push</code> هو الانتقال الأمامي المعتاد. و<code dir=\"ltr\">replace</code> للمسارات التي لا معنى للعودة فيها: بعد الشاشات التعريفية، وبعد تسجيل الدخول، وبعد تأكيد «تم الحفظ!». أما <code dir=\"ltr\">back</code> فهو ما يفعله سهم الرجوع في الترويسة وإيماءة السحب أصلًا؛ استدعه بنفسك بعد حفظ نموذج ليعود المستخدم إلى حيث جاء. وهناك أيضًا <code dir=\"ltr\">router.navigate</code>، الذي يذهب إلى مسار، وإن كان ذلك المسار موجودًا في الـ stack أصلًا، يعود إليه بدلًا من إضافة نسخة مكرّرة.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>الإضافة في حلقة مفرغة</span></p>\n<p>زرّ “Done” يستدعي <code dir=\"ltr\">router.push('/')</code> بعد تعديل رحلة يكدّس نسخة ثانية من السجلّ فوق الأولى. افعل ذلك بضع مرات، وستمشي إيماءة الرجوع عبر خمس قوائم متطابقة. بعد إنهاء مهمّة، ارجع <strong>back</strong> (أو استخدم <code dir=\"ltr\">replace</code>)، ولا تتقدّم بـ push إلى حيث جئت.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">الـ stack ليس سجلّ المتصفّح</h2>\n<p>من المغري أن تعامل الـ stack كسجلّ المتصفّح، لكن هناك فرقًا واحدًا مهمًّا: <strong>كل شاشة في الـ stack تبقى مركّبة</strong>. حين تضيف شاشة تفاصيل الرحلة، يحتفظ السجلّ تحتها بالـ state وموضع التمرير، وتستمر الـ effects الخاصة به في العمل. ولهذا يكون الرجوع فوريًا، ولهذا أيضًا لا يُعاد تشغيل <code dir=\"ltr\">useEffect</code> في الشاشة حين تعود إليها. وحين تحتاج إلى تحديث البيانات في كل مرة تعود فيها الشاشة إلى الواجهة، يوفّر Expo Router الـ hook <code dir=\"ltr\">useFocusEffect</code>، الذي يعمل حين تحصل الشاشة على التركيز (focus) وينظّف حين تفقده.</p>\n<h2 id=\"sec-5\" tabindex=\"-1\">شاشة «غير موجود» ولماذا ما زالت الـ URLs مهمّة</h2>\n<p>يُرسم <code dir=\"ltr\">+not-found.tsx</code> لأي URL لا يطابق مسارًا، وهذا يحدث على الجوّال غالبًا عبر deep links من رسائل بريد قديمة أو من شاشة غُيّر اسمها. أعطه رسالة ودودة و<code dir=\"ltr\">Link</code> يعود إلى الرئيسية.</p>\n<p>والتفكير بالـ URLs يؤتي ثماره لاحقًا في هذا القسم: يستطيع إشعار أن يفتح <code dir=\"ltr\">/hikes/42</code> مباشرة، ويستطيع رابط في رسالة أن يفتح التطبيق على الشاشة الصحيحة، وتعمل المسارات نفسها على الويب إن بنيت Trailhead له يومًا.</p>\n<p>في الدرس التالي ستضيف tabs في أسفل التطبيق، وشاشة تفاصيل لكل رحلة، وتمرّر معرّف الرحلة عبر الـ URL.</p>\n","toc":[{"id":"sec-1","text":"الملفات هي المسارات","level":2},{"id":"sec-2","text":"الـ layouts تغلّف ما يجاورها","level":2},{"id":"sec-3","text":"التنقّل بين الشاشات","level":2},{"id":"sec-4","text":"الـ stack ليس سجلّ المتصفّح","level":2},{"id":"sec-5","text":"شاشة «غير موجود» ولماذا ما زالت الـ URLs مهمّة","level":2}],"takeaways":["كل ملف داخل <code dir=\"ltr\">src/app</code> هو مسار (route)، ومساره هو الـ URL؛ أبقِ الـ components والـ hooks والأدوات المساعدة خارج ذلك المجلّد.","يغلّف ملف <code dir=\"ltr\">_layout.tsx</code> المسارات المجاورة له بأداة تنقّل (navigator)، مثل <code dir=\"ltr\">Stack</code> أصلي.","استخدم <code dir=\"ltr\">&lt;Link href&gt;</code> للتنقّل الذي ينقر عليه المستخدم، واستخدم <code dir=\"ltr\">router.push</code> و<code dir=\"ltr\">router.replace</code> و<code dir=\"ltr\">router.back</code> من الكود.","<code dir=\"ltr\">push</code> يضيف شاشة إلى الـ stack، و<code dir=\"ltr\">back</code> يُزيلها، و<code dir=\"ltr\">replace</code> يستبدل الشاشة الحالية فلا يستطيع المستخدم العودة إليها."],"further":[{"title":"Expo Router introduction","url":"https://docs.expo.dev/router/introduction/"},{"title":"Core concepts of file-based routing","url":"https://docs.expo.dev/router/basics/core-concepts/"},{"title":"Navigating between pages","url":"https://docs.expo.dev/router/basics/navigation/"}],"quiz":[{"q":"<p>أضفت <code dir=\"ltr\">src/app/HikeRow.tsx</code> ليحوي component صفّ قائمة قابلًا لإعادة الاستخدام. ماذا يحدث؟</p>","options":[{"text":"لا شيء خاص؛ يعامل Expo Router الملفات التي في اسمها <code dir=\"ltr\">Screen</code> فقط على أنها مسارات.","why":"لا توجد قاعدة تسمية كهذه. كل ملف في مجلّد التطبيق مسار."},{"text":"يسجّله Expo Router مسارًا على <code dir=\"ltr\">/HikeRow</code>، فيصبح component مخصّص لإعادة الاستخدام شاشةً يمكن التنقّل إليها.","why":"صحيح. ضع الـ components المشتركة في <code dir=\"ltr\">src/components</code> (أو في أي مكان خارج <code dir=\"ltr\">src/app</code>)."},{"text":"يفشل البناء، لأن ملفات المسارات يجب أن تكون بأحرف صغيرة.","why":"حالة الأحرف لا تجعل الملف خارج المسارات. سيُسجَّل مع ذلك على <code dir=\"ltr\">/HikeRow</code>."}],"answer":1},{"q":"<p>بعد أن ينهي المستخدم الشاشات التعريفية (onboarding)، ينتقل Trailhead إلى سجلّ الرحلات. يجب ألا يعيده زرّ الرجوع إلى الشاشات التعريفية. أيّ استدعاء يناسب؟</p>","options":[{"text":"<code dir=\"ltr\">router.push('/')</code>","why":"<code dir=\"ltr\">push</code> يترك الشاشات التعريفية تحتها، فيعود الرجوع إليها."},{"text":"<code dir=\"ltr\">router.back()</code>","why":"هذا يذهب إلى ما كان قبل الشاشات التعريفية، وهو ليس سجلّ الرحلات."},{"text":"<code dir=\"ltr\">router.replace('/')</code>","why":"صحيح. يستبدل <code dir=\"ltr\">replace</code> الشاشة الحالية بالجديدة، فلا يبقى شيء يُرجَع إليه."}],"answer":2},{"q":"<p>أين يوضع عنوان الترويسة لشاشة <code dir=\"ltr\">/settings</code>؟</p>","options":[{"text":"في الـ layout، <code dir=\"ltr\">&lt;Stack.Screen name=&quot;settings&quot; options={{ title: 'Settings' }} /&gt;</code>، أو يُضبط من الشاشة نفسها بالـ component ذاته.","why":"صحيح. يمكن أن تعيش الخيارات في الـ layout (جيد للعناوين الثابتة) أو أن تُرسم داخل الشاشة (جيد حين تعتمد على البيانات)."},{"text":"في <code dir=\"ltr\">app.json</code> تحت <code dir=\"ltr\">expo.routes.settings.title</code>.","why":"لا يوجد مفتاح كهذا. خيارات التنقّل تعيش في الـ layouts والشاشات."},{"text":"في عنصر <code dir=\"ltr\">&lt;title&gt;</code> أعلى الـ JSX الخاص بالشاشة.","why":"هذا HTML. الترويسات الأصلية تُضبط عبر خيارات أداة التنقّل."}],"answer":0}],"exercise":{"kind":"fill","language":"text","template":"# URL                       file that renders it\n/                           src/app/{{0}}.tsx\n/settings                   src/app/settings/{{1}}.tsx\n/settings/units             src/app/settings/{{2}}.tsx\n(Stack around settings/*)   src/app/settings/{{3}}.tsx\n(any unknown URL)           src/app/{{4}}.tsx\n\n# In code, after the units form saves, return to /settings:\nrouter.{{5}}();","blanks":[{"answers":["index"]},{"answers":["index"]},{"answers":["units"]},{"answers":["_layout"]},{"answers":["+not-found"]},{"answers":["back","dismiss"]}],"title":"اربط عناوين Trailhead بالملفات","prompt":"<p>املأ أسماء الملفات (دون <code dir=\"ltr\">.tsx</code>) ليرسم Expo Router كل URL من الملف الصحيح، ويغلّف شاشات الإعدادات بـ layout خاص بها، ويعرض شاشة ودودة للعناوين غير المعروفة. ثم اختر دالة <code dir=\"ltr\">router</code> التي تعود من <code dir=\"ltr\">/settings/units</code> إلى <code dir=\"ltr\">/settings</code> بعد الحفظ دون تكديس شاشة مكرّرة.</p>\n","hints":["المسار الخاص بالمجلّد نفسه (<code dir=\"ltr\">/</code> أو <code dir=\"ltr\">/settings</code>) يأتي من ملف بالاسم نفسه في الموضعين.","لملفات الـ layout وملف «غير موجود» بادئات خاصة: شرطة سفلية وعلامة زائد.","سهم الرجوع في الترويسة يفعل الشيء نفسه الذي تفعله الدالة التي تحتاج إليها."],"explanation":"<p>يرسم <code dir=\"ltr\">index.tsx</code> الـ URL الخاص بالمجلّد نفسه، لذا يأتي كلٌّ من <code dir=\"ltr\">/</code> و<code dir=\"ltr\">/settings</code> من ملف <code dir=\"ltr\">index</code>. ويصبح <code dir=\"ltr\">units.tsx</code> داخل <code dir=\"ltr\">settings/</code> المسار <code dir=\"ltr\">/settings/units</code>. يغلّف <code dir=\"ltr\">_layout.tsx</code> المسارات المجاورة له بأداة تنقّل، ويلتقط <code dir=\"ltr\">+not-found.tsx</code> العناوين غير المطابقة. بعد الحفظ، يُزيل <code dir=\"ltr\">router.back()</code> شاشة الوحدات ويكشف شاشة الإعدادات التي كانت تحتها أصلًا؛ أما <code dir=\"ltr\">push('/settings')</code> فسيكدّس نسخة ثانية.</p>\n","checkLabels":{}},"words":638,"readMinutes":4}