المحركات والعارض
يقرأ Impeller الخلفية، ويحتاج Skia إلى التقاط. والعناصر نفسها.
يحتاج الانكسار إلى البكسلات التي خلف الزجاج. يستطيع Impeller أن يناول الشيدر الخلفيةَ الحية، ولا يستطيع Skia ذلك. هذا الفارق وحده هو القصة كلها، وLiquidGlassView هو الجواب عنه.
| الإعداد | ما تفعله العدسة |
|---|---|
| Impeller — الافتراضي على iOS و Android | تكسر الخلفية الحية: كل ما رسمه تطبيقك خلفها. بلا عارض ولا عنصر خلفية ولا إعداد. |
Skia داخل LiquidGlassView | تكسر backgroundWidget الملتقَط للعارض، أينما جلست العدسة داخل child الخاص به. |
| Skia بلا عارض | تتراجع إلى سطح مصنفر — ضبابية ولون وحافة — وتسجّل تنبيه تنقيح واحداً. |
LiquidGlassView#
العارض هو خط الالتقاط: يحوّل backgroundWidget إلى نسيج ثم يسلّم هذا النسيج لكل عدسة داخل child. قائمة معاملاته الكاملة مُولَّدة من الشيفرة في صفحة الواجهة، وهذه هي التي ستمدّ يدك إليها فعلاً.
| المعامل | الافتراضي | ما يتحكم به |
|---|---|---|
backgroundWidget | — | الطبقة التي تُلتقط. العدسات تكسر هذه، لا child. |
child | null | واجهتك. أي شيء هنا يمكن أن يحمل عدسة، على أي عمق. |
pixelRatio | 1.0 | دقة الالتقاط. ما دون 1 أرخص وأنعم، والزجاج يخفي قدراً مدهشاً من ذلك. |
realTimeCapture | true | إعادة الالتقاط كل إطار. أطفئه لخلفية لا تتحرك. |
useSync | true | يُبقي الالتقاط متزامناً مع الإطار قيد البناء. إطفاؤه أرخص وقد يتأخر إطاراً واحداً. |
refreshRate | deviceRefreshRate | يحدّ من تواتر الالتقاط، بمعزل عن معدّل الشاشة. |
useImpellerBackdrop | null | يتجاوز الاكتشاف التلقائي للمحرك. اتركه فارغاً إلا إن كنت تختبر المسار الآخر عن قصد. |
controller | null | LiquidGlassViewController، من أجل captureOnce() — لقطة واحدة عند إشارتك — ولقيادة التكيّف يدوياً. |
adaptiveSampling | null | على المحركين. يشغّل التقاط الإضاءة الصغير الذي يقرأه LiquidGlassAdaptivity. والفارغ يعني ألا معاينة إطلاقاً، فترتدّ الأسطح المتكيّفة إلى تخمين. والتقاط واحد يخدم كل سطح متكيّف في العارض، وهو مقيّد بالخمول: شاشة ساكنة لا تعاين شيئاً. |
batch | true | على Impeller فقط. يضع عدسات child على قراءة واحدة مشتركة للخلفية، وعدسات backgroundWidget على أخرى. وهو خامل على مسار الالتقاط الذي يقرأ أصلاً مرة واحدة لكل عارض. انظر الحزمة. |
regionCapture | false | يلتقط مستطيل كل عدسة بدل الخلفية كلها — مكسبٌ حين تجلس عدسة صغيرة على خلفية كبيرة، وخسارةٌ حين تنتشر عدسات كثيرة عليها. على مسار useSync في Skia فقط. |
التقاط واحد أم كل إطار#
يرسم realTimeCapture: true الخلفيةَ في كل إطار. وهذا صحيح لقائمة تنساب أو فيديو أو تدرّج متحرك — أي شيء يتحرك حقاً. وهو هدر خالص لخلفية لا تتغيّر أبداً.
حين تكون الخلفية ساكنة، خذ لقطة واحدة واكسرها إلى الأبد. تحريك عدسة فوق صورة ثابتة لا يكلّف شيئاً إضافياً: يُعاد تشغيل الشيدر، ولا يُعاد رسم أي شيء.
final controller = LiquidGlassViewController();
LiquidGlassView(
controller: controller,
backgroundWidget: const MyBackground(),
realTimeCapture: false,
child: const MyGlassUI(),
);
// Re-snapshot by hand after the background actually changes:
await controller.captureOnce();يشغّل عرض هذه الصفحة خلفيةً لا تكفّ عن الانزلاق. اضبط الالتقاط على مجمَّد فيظل الزجاج يكسر أشرطةً تجاوزها الزمن بينما تمضي الأشرطة حوله — والفارق بينهما هو مقدار قِدَم اللقطة، وcapture once يغلقه.
متى يريد Impeller عارضاً رغم ذلك#
على Impeller لا تحتاج العدسة إلى شيء: تقرأ الخلفية الحية أينما جلست. فيبدو العارض إجراءً شكلياً يخصّ Skia — وهو كذلك بالنسبة للانكسار. لكنه ليس كذلك لبقية ما يوفّره، واثنان منها يستحقان أن تمدّ يدك إليهما على Impeller أيضاً.
- `adaptiveSampling` — وهو غير اختياري. يحكم
LiquidGlassAdaptivityعلى الخلفية بقراءة بكسلاتها، وقراءة البكسلات تعني التقاطاً. ولا التقاط لدى Impeller: فهو لا يأخذ واحداً أبداً. فـLiquidGlassViewمضبوطاً عليهadaptiveSamplingهو الطريق ليجد الزجاج المتكيّف ما يحكم عليه على كلا المحركين — وبدونه يرتدّ كل سطح متكيّف إلى التخمين من سِمة التطبيق. وهو التقاط واحد ضئيل (بنسبة بكسل0.05، ثماني مرات في الثانية) تتشاركه كل الأسطح المتكيّفة في العارض، ويتوقف تماماً ما دامت الشاشة ساكنة. - `batch` — خاص بـImpeller بطبعه. يحزم العارض عدساته افتراضياً، وهو توفير خالص على Impeller: فمسار الالتقاط يقرأ أصلاً مرة واحدة لكل عارض، بينما على Impeller تنسخ كل عدسة الخلفية لنفسها ما لم تقل الحزمة غير ذلك.
- شجرة واحدة، محركان. لا يكلّف العارض على Impeller شيئاً وراء العنصر نفسه، فكتابة الصفحة داخله تعني أن الشيفرة ذاتها تعمل على الهاتف وعلى الويب بلا تفرّع.
ترجم الشيدرات قبل الإطار الأول#
هذا ليس عن الالتقاط، ولا عن المحرك أيضاً — بل ينطبق على الاثنين. فبرامج الزجاج تُترجَم مرة واحدة في كل تشغيل للتطبيق، على يد أول عدسة تُركَّب، والعدسة التي لم يصل برنامجها بعد ترسم بديلها المصنفر حتى يصل. وLiquidGlassShaders.ensureLoaded() في main() ينقل تلك الترجمة إلى ما قبل الإطار الأول.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Every program at once: the lens's, and the blender's.
await LiquidGlassShaders.ensureLoaded();
runApp(const MyApp());
}إعدادات مقترحة#
- للحالة العامة:
useSync: trueوpixelRatio: 0.8–1.0. - عندما يسبق الأداء:
useSync: falseوpixelRatio: 0.5–0.7. - خلفية ساكنة:
realTimeCapture: falseيتفوّق على كل المقابض الأخرى مجتمعة.
التعليقات
التعليقات هي مناقشات GitHub — يمكنك الرد من أيٍّ من المكانين.