الدفعة
أي عدد من العدسات، بقراءة واحدة للخلفية — على Impeller.
تعمل العدسة على Impeller بقراءة ما رُسم خلفها ثم تشغيل شيدر الزجاج فوق تلك النسخة. والقراءة هي الجزء الغالي، وهي لكل عدسة: شريط أدوات من ثمانية أزرار زجاجية يعني ثماني قراءات في الإطار، وست عشرة مع الضبابية. لُفَّها بـ LiquidGlassBatch فتُوسَم كلها بمفتاح خلفية مشترك واحد، فيأخذ المحرك النسخة مرة واحدة ويعاينها كل عضو.
LiquidGlassBatch(
child: Column(
children: [
for (final action in actions)
LiquidGlassLens(
style: buttonStyle,
child: ActionButton(action),
),
],
),
)ولا يتغيّر شيء آخر في الأعضاء: يحتفظ كلٌّ بشكله وأسلوبه وابنه وتكيّفه واستجابته للمس. ويبقون ألواحاً زجاجية منفصلة — الدفعة تجعلهم يتشاركون القراءة فحسب. ويُعثر على الأعضاء بالسياق فيجلسون على أي عمق، وتنضم المكوّنات بالطريقة نفسها: الزر وزر الإجراء العائم والشريط العلوي وLiquidGlassSheet الموضوعة في الشجرة — أيٌّ منها داخل الدفعة يتشارك قراءتها بلا أي توصيل. أما ما يعرضه showLiquidGlassSheet وshowLiquidGlassDialog فمسار خارج شجرة كل دفعة، فيأخذ قراءة خاصة به ما لم يطلب الاستدعاء الانضمام إلى واحدة — انظر الملاحظة أدناه. ويحتفظ شريط التبويب دائماً بقراءة خاصة به، حتى داخل الدفعة: فتُرسم كبسولته وحبّته المتحرّكة نظيفةً في كل إطار من انتقال الحبّة، وقراءة واحدة للشريط كله لا تكاد تكلّف شيئاً.
| المعامل | الافتراضي | ما يفعله |
|---|---|---|
child | — | الشجرة الفرعية التي يتشارك أحفادها من LiquidGlassLens القراءة. |
enabled | true | اقلبه إلى false فتعود كل عدسة إلى قراءة خاصة بها — السلوك ما قبل الدفعة، وهو المقارنة التي تقيس بها ما توفّره الدفعة. |
mode | null | كيف يتشارك العضو الذي يضبّبه المحرك القراءة — انظر أدناه. null يستخدم LiquidGlassConfiguration.batchMode. |
blurMethod | null | أين يضبّب الأعضاء: بتمويه المحرك الغاوسي أو بشيدرهم. null يستخدم LiquidGlassConfiguration.batchBlurMethod. |
shaderBlurDispersion | null | هل يحتفظ تضبيب الشيدر لدى العضو بـ dispersion أسلوبه. null يستخدم LiquidGlassConfiguration.batchShaderBlurDispersion. |
أين يضبّب الأعضاء#
يمكن للعضو ذي الضبابية أن يضبّب في موضعين. مع LiquidGlassBlurMethod.shader تضبّب العدسة الخلفية وهي تعاينها في شيدرها، فلا يحتاج التضبيب مروراً خاصاً به ويبقى العضو على قراءة الدفعة الواحدة. ويحمل الصقيع حبيبات دقيقة بدل تمويه المحرك الغاوسي الأملس تماماً. ومع LiquidGlassBlurMethod.engine — وهو الافتراضي — يضبّبه تمويه المحرك، ويقرّر mode الدفعة كيف يتشارك ذلك التضبيب القراءة:
| الوضع | ما يكلّفه العضو المضبَّب |
|---|---|
separatePass (الافتراضي) | مروران: التضبيب مرور مفتاحي خاص به فوق نسخة الدفعة، ويأخذ الشيدر مروراً ثانياً بقراءة خاصة به فوقه. قراءة واحدة للدفعة وواحدة لكل عضو؛ وقد يُحسب التضبيب المتساوي مرة واحدة لهم جميعاً. |
singlePass | مرور مفتاحي واحد: يسري التضبيب داخل مرور الشيدر عبر ImageFilter.compose. والدفعة كلها تكلّف قراءة واحدة — لكن انظر خلل Flutter أدناه. |
العضو بلا ضبابية، أو بتضبيب الشيدر، أو بالزجاج الخفيف مرورٌ مفتاحي واحد في الوضعين. ويحتفظ الخالط بـ useEngineBlur الخاص به ويُجمَّع دائماً كـ singlePass. وكل معامل يُترك null يأخذ قيمة الدفعة المقابلة من LiquidGlassConfiguration، فسطر واحد في main() يضبطها لكل دفعة في التطبيق — بما فيها دفعات العارض والهيكل:
void main() {
// Batched lenses blur with the engine's Gaussian, in one pass each.
LiquidGlassConfiguration.batchBlurMethod = LiquidGlassBlurMethod.engine;
LiquidGlassConfiguration.batchMode = LiquidGlassBatchMode.singlePass;
runApp(const MyApp());
}دفعة أم خالط؟#
يرسم الخالط أعضاءه بـشيدر واحد: ظلّ واحد ومادة واحدة وقراءة واحدة — وثمانية أعضاء على الأكثر، لأن كل ما يرسمه يجب أن يتّسع في ذلك الشيدر. ومع smoothness: 0 لا يصهرهم أصلاً؛ فهو مرور واحد للطقم، وهذا ما كان LiquidGlassGroup المهمَل. أما الدفعة فلا تشارك سوى القراءة. يبقى الأعضاء مرورات منفصلة بمظاهرهم، ولا حدّ لعددهم. فمدّ يدك إلى الخالط حين ينبغي أن تُقرأ العدسات قطعةَ زجاج واحدة، وإلى الدفعة حين تكون كثيرة فحسب. وهما يتداخلان: الخالط داخل دفعة عضوٌ كأي عضو، يُرسم من نسخة الدفعة.
القاعدة الوحيدة: يجب ألا يتراكب الأعضاء#
يعاين كل عضو النسخة نفسها، المأخوذة قبل أن يرسم أيٌّ منهم — فلا يستطيع عضو أن يرى زجاج عضو آخر. وحيث يتراكب اثنان يكسر الأعلى ما كان خلف الأسفل، فتُقرأ الطبقتان كعدسة واحدة. والقوائم والشبكات وصفوف عناصر التحكم لا تقع في هذا أبداً. أما العدسة التي يجب أن تتراكب مع أخرى فمكانها خارج الدفعة:
LiquidGlassBatch(
child: Column(
children: [
Expanded(child: cards), // shared read
LiquidGlassBatch.exclude( // its own read again
child: myTabBar,
),
],
),
)والأمر نفسه للزجاج داخل نافذة منبثقة. لنفترض أن زراً زجاجياً في نافذة منبثقة، وأن النافذة والصفحة داخل دفعة واحدة. لا يرى الزر النافذةَ التي خلفه: بل يكسر المحتوى الذي لُفّت حوله الدفعة، كأن النافذة غير موجودة. ولتجعل الزر يكسر ما خلفه فعلاً، أخرجه من الدفعة بـ LiquidGlassBatch.exclude (أو enabled: false). وإن حملت النافذة مجموعة من هذه الأزرار، فضع المجموعة في دفعة خاصة بها — LiquidGlassBatch داخل النافذة — فتتشارك قراءة واحدة تُؤخذ بعد رسم النافذة.
// Inside the pop-up, which sits inside the page's batch:
LiquidGlassBatch.exclude( // one button: its own read
child: glassButton,
)
LiquidGlassBatch( // a group: one read of its own,
child: Row(children: glassButtons), // taken over the pop-up
)أين ينطبق#
المفتاح المشترك آلية Impeller، فالدفعة هي ما يغيّر الكلفة هناك. أما على مسار التقاط Skia والويب فإن LiquidGlassView يلتقط خلفيته مرة واحدة لكل عدسة بداخله أصلاً، فالدفعة خاملة لا خاطئة — والشجرة نفسها تعمل على الواجهتين.
طبقة زجاج واحدة، تطفو فوق الصفحة#
توجد الدفعة لأن قراءة الخلفية هي الشيء الغالي الذي يفعله Impeller، وتشاركها هو أكبر توفير منفرد متاح على ذلك المحرك — عدسات كثيرة بثمن واحدة. لكن يجدر قول ما الدفعة من أجله، لأن التخطيطات التي تجعلها رخيصة هي نفسها التخطيطات الصحيحة.
الزجاج طبقة تحكّم. وينبغي في كل الحالات تقريباً أن يكون طبقة واحدة تطفو فوق الصفحة — شريطاً أو طقم عناصر تحكم أو صفَّ إجراءات — والمحتوى يمرّ تحتها. وهذا ما وُجدت المادة من أجله: أن ترى ما تحت الزجاج، ولهذا يكسر بدل أن يحجب.
التعليقات
التعليقات هي مناقشات GitHub — يمكنك الرد من أيٍّ من المكانين.