الاستخدام الكفء
اختر القطعة الصحيحة، وتتكفّل الكلفة بنفسها في الغالب.
معظم مشكلات أداء الزجاج ليست مشكلات ضبط، بل عنصرٌ يؤدي عملاً بُني له عنصر أرخص. هذه الصفحة هي ذلك القرار.
ابدأ مما تبنيه#
| ما تريده | مدّ يدك إلى | لماذا |
|---|---|---|
| زجاج يجب أن يكون رخيصاً، وجاهزاً للنشر | الزجاج الخفيف | أكفأ قطعة هنا: لا شيدر ولا التقاط ولا ميزانية عدسات. صقيع ولون وحافة مضاءة، مرسومة من هندسة اللوحة العادية. على Skia والويب ضع التطبيق كله عليه (LiquidGlassEngine.liteGlassOnSkia = true) وانشر — فلا خلفية تُلتقط ولا أداء يُقلقك. وعلى أيٍّ من المحركين، الزر الملوَّن أو البطاقة ذات الحافة التي لا تحتاج أن تثني شيئاً تضبط liteGlass: true في نمطها، بدل شيدر الخلفية على Impeller أو الخلفية الملتقَطة على Skia. |
| لوح واحد يطفو فوق صفحة | LiquidGlassLens | قراءة واحدة وشيدر واحد — ومروران على Impeller حين يضبّب. لا شيء يتفوّق عليه في الانكسار. |
| شريط وصفحة وتبويبات | LiquidGlassScaffold | على Skia يلتقط الجسم مرة واحدة لكل العدسات التي تحته. وعلى Impeller لا التقاط إطلاقاً، ويُجمّع عناصر واجهته في قراءة واحدة. والتكيّف موصول فيه على المحركين. |
| زجاجات كثيرة | الدفعة | عدسات كثيرة بكلفة قراءة واحدة — على Impeller، والزجاج الخفيف كذلك. أما على Skia فالعارض يلتقط مرة واحدة لها كلها أصلاً، فالدفعة هناك خاملة لا خاطئة. |
| عنقود ينبغي أن يُقرأ لوحاً واحداً | الانصهار مع smoothness: 0 | سطح واحد ومادة واحدة وقراءة واحدة — حتى ثمانية أعضاء. وكان LiquidGlassGroup هذا بعينه، وهو مهمَل الآن. |
| شكلان ينبغي أن يذوبا معاً | الانصهار | الجسر الكروي هو المقصود كله. |
| عنصر يتغيّر حجمه بتغيّر محتواه | التحوّل | يقيس الابن. والكتلتان مرورٌ واحد للمازج، لكن بشيدر أثقل من شيدر العدسة — وLiquidGlassMorphMotion.plain عدسة واحدة على نابض واحد، حين لا تحتاج الخصر. |
| مزلاج أو مفتاح | LiquidGlassSlider / LiquidGlassSwitch | مكتفيان بذاتيهما. على Impeller يقرأ المقبض الخلفية الحية ولا يُلتقط شيء؛ وعلى Skia يأخذ عارضهما الصغير لقطة واحدة للمسار ولا يعمل حياً إلا ما دام المقبض ممسوكاً. ولا تُلتقط صفحتك أبداً. |
الزجاج على Impeller: ما يكلّفه، بالترتيب#
الزجاج طبقة تحكّم عائمة: أشرطة وعناصر تحكّم وأزرار تطفو فوق المحتوى، والمحتوى ينساب تحتها. أبقِه خارج المحتوى نفسه — لا داخل قائمة أو شبكة تنساب. لهذا وُجدت المادة، وهذا أيضاً ما يُبقيها رخيصة.
على Impeller، العامل الذي يحدّد الكلفة هو عدد الزجاجات المضبَّبة على الشاشة. وهذه كلفة Impeller، لا كلفة شيدرات هذه الحزمة: فكل تضبيب مرورٌ فوق الخلفية. لذا حتى الزجاج الخفيف، الذي لا يستخدم أي شيدر، يحتاج دفعة متى كان في الصفحة أسطح مضبَّبة كثيرة. من الإعداد الأرخص إلى الأغلى:
| الإعداد | الكلفة |
|---|---|
| 1. دفعة، ضبابية `0` | الأكفأ. قراءة مشتركة واحدة بلا أي تضبيب: تحتمل الصفحة زجاجات كثيرة بلا كلفة رسم تُذكر. |
| 2. دفعة، تضبيب الشيدر | اضبط LiquidGlassConfiguration.batchBlurMethod على shader. يضبّب كل زجاج داخل شيدره، فتحتفظ الدفعة بقراءتها الواحدة. وتكبر الكلفة مع عدد الزجاجات ومع قيمة sigma — انظر نصيحة الضبابية أدناه. |
| 3. دفعة، تضبيب المحرك، مرور منفصل | الافتراضي. ليست قاعدة — وقد تتغيّر حين يصلح فريق Flutter خلل المرور الواحد (انظر الدفعة). ومع زجاجات كثيرة ليست فعّالة. ومع زجاجات قليلة لم تُختبر بعد، فما يتفوّق هناك ما يزال مفتوحاً. |
كم من الضبابية#
الضبابية موجودة لتليّن الخلفية لا لتخفيها. فقيمة sigma العالية تحفظ معدّل الإطارات لكنها تمحو الانكسار، فيكفّ الزجاج عن أن يُقرأ زجاجاً — مع تضبيب الشيدر ومع تضبيب المحرك على حدّ سواء. ويبدو الزجاج صحيحاً حين تترك الضبابية الانكسار واضحاً.
- من `2` إلى `5` في كل الحالات، ومن
2إلى3تفضيلاً، مع لونcolorفي النمط يحمل الصقيع. - زجاجات كثيرة: `2`. اختُبرت على صفحة Social Feed في تطبيق الأمثلة، التي تطفو فيها زجاجات كثيرة فوق محتوى ينساب: لا يُظهر زمن الرسم أي قفزات، ويبقى أقصاه ومتوسطه ثابتين حين يبدأ الانسياب.
السؤال الوحيد المهم على Skia#
هل تتحرك الخلفية فعلاً؟ على Impeller تقرأ العدسة الخلفية الحية ولا التقاط يُفكَّر فيه. أما على Skia والويب فالغالي هو رسم الخلفية ليجد الشيدر ما يقرؤه — وفعل ذلك ستين مرة في الثانية لتدرّج لا يتغيّر أبداً هو أشيع هدر في واجهة زجاجية.
// A background that never changes: one snapshot, refracted forever.
final controller = LiquidGlassViewController();
LiquidGlassView(
controller: controller,
backgroundWidget: const MyGradient(),
realTimeCapture: false,
child: const MyGlassUI(),
);
// …and again only when the background genuinely changed:
await controller.captureOnce();- تدرّج مرسوم أو صورة أو رسم ثابت ← التقاط واحد.
- قائمة تنساب أو فيديو أو خلفية متحركة ← أبقِه حياً.
- خلفية تتغيّر نادراً ← التقاط واحد، ثم
captureOnce()مرة أخرى حين تتغيّر فعلاً.
قائمة تحقّق قبل النشر#
- هل يلتقط شيء لا حاجة له بذلك؟
LiquidGlassViewواحد لكل شاشة، لا واحد لكل عنصر. والمزلاج والمفتاح يجلبان عارضهما الصغير: عارضٌ حول الصفحة لا بأس به، وعارضٌ حول كل عنصر لا حاجة إليه. وإن استغنت الصفحة عن الانكسار فالزجاج الخفيف لا يلتقط شيئاً إطلاقاً. - هل ما زال `pixelRatio` عند `1.0`؟ الزجاج يثني ويضبّب ما يقرؤه، فيخفي الالتقاط الناعم جيداً. و
0.7غير مرئي عادةً وأرخص بوضوح. - هل تؤدي الضبابية عملاً لا تراه؟ على Skia، الضبابية أغلى مقابض المظهر على مسار الالتقاط، وفوق
7تقريباً تكفّ عن الظهور كضبابية خلفية حقيقية. - هل `dispersion` مفعّل حيث لن يرى أحد الهالة؟ عند
0(الافتراضي) يكلّف شريط الثني قراءة خلفية واحدة لكل بكسل، وأي قيمة أخرى تضيف قراءات في ذلك الشريط. - هل تستحق الحبة المتحولة خط رسمها الثاني؟
LiquidGlassPillMode.impellerOnlyيمنحك الحبة الكاملة حيث تكون مجانية والإبراز الخفيف في كل مكان آخر — إلا إن كان التطبيق على الزجاج الخفيف، فهناك لا يكلّفbothشيئاً على Skia أيضاً. - هل تتراكب عدسات داخل دفعة؟ يجب ألا تفعل: فالزجاج في الدفعة لا يرى زجاجاً آخر فوقه أو تحته. والزر الزجاجي في نافذة منبثقة يكسر الصفحة تحت النافذة لا النافذة نفسها. أخرِجه بـ
LiquidGlassBatch.exclude، أو امنح مجموعة منهاLiquidGlassBatchخاصة بها داخل النافذة — انظر الدفعة. - هل التطبيق كله داخل دفعة واحدة؟ لا ينبغي ذلك. الدفعة لصفحة واحدة على الأكثر.
أخطاء تبدو كأنها عِلل#
| العَرَض | السبب |
|---|---|
| الزجاج ضبابية مصنفرة مسطّحة بلا ثني. | على Skia أو الويب بلا LiquidGlassView أب. لا يوجد ما يُكسر فيتراجع — ويقول ذلك مرة في وحدة التنقيح. والمظهر نفسه بلا تنبيه هو الزجاج الخفيف عن قصد، أو أول عدسة في التطبيق بينما يُترجَم شيدرها. |
| الزجاج يكسر نسخة قديمة من الصفحة. | realTimeCapture: false بلا captureOnce() بعد تغيّر الخلفية. |
| عدستان في دفعة تبدوان واحدة. | إنهما متراكبتان. فكل عضو يعاين النسخة نفسها المأخوذة قبل أن يرسم أيٌّ منهم، فلا ترى العليا زجاج السفلى. |
| عدسة داخل قائمة تسودّ بينما تُسحب القائمة إلى ما بعد نهايتها (Impeller). | يرسم تمدّد التمرير الزائد في Android العنصرَ القابل للتمرير في نسيج خاص به، يحمل ما رسمته القائمة لا الصفحة التي تحتها. وتحتفظ العدسة بمكانها وتكسر ذلك — سوداء حيثما كانت القائمة فارغة خلفها، ما دام السحب مستمراً. فإن ظهر ذلك، أزل مؤشّر التمرير الزائد عن القوائم التي تحمل عدسات. |
| التحوّل لا يتحرك حين يتغيّر المحتوى. | الأبناء بلا مفاتيح، فالابن الذي احتفظ بنوعه لم يُرَ جديداً. |
مجاني بحكم البناء#
touch: null— وهو الافتراضي — لا يضيف مستمعاً ولا مؤقتاً.visibility: falseيزيل كلفة الخلفية فوراً، ومعها الابن.- التكيّف لا يعاين إلا بينما يوجد شيء متكيّف على الشاشة، ببضعة كيلوبايتات وثماني مرات في الثانية على الأكثر — ولا يعاين إطلاقاً ما دامت لا إطارات تُرسم.
LiquidGlassSliderوLiquidGlassSwitchلا يلتقطان صفحتك أبداً: لا شيء إطلاقاً على Impeller، ولقطة واحدة لمسارهما على Skia.- على Impeller في Android وiOS تخبز العدسة محيطها في نسيج مرة واحدة وتقرؤه ما دامت تتحرك فحسب؛ والشكل الذي ما يزال يتغيّر يُخبز حين يثبت. و
LiquidGlassConfiguration.shapeCache = falseيعطّله. - الزجاج الخفيف — عدسة واحدة، أو التطبيق كله لكل محرك — لا يترجم شيدراً ولا يأخذ التقاطاً ولا يشغل مقعد عدسة.
التعليقات
التعليقات هي مناقشات GitHub — يمكنك الرد من أيٍّ من المكانين.