Liquid Glass Easy

البداية

ثبّتها، وضَع عدسة واحدة على الشاشة.

تجلب Liquid Glass Easy مادة Liquid Glass من Apple — المادة التي يقوم عليها iOS 26 و iOS 27 — إلى Flutter: عدسة مبنية على شيدر تكسر ما خلفها وتكبّره وتلوّنه وتثنيه، وتستجيب للمس كجسمٍ مرن. ليست ضبابية فوقها طبقة بيضاء — المحتوى تحتها ينثني فعلاً.

العنصر في المركز هو LiquidGlassLens: قطعة زجاج واحدة. وحين تضع عدة قطع على شاشة، لُفَّها بـ LiquidGlassBatch فتتشارك قراءة واحدة للخلفية بدل أن تدفع كلٌّ ثمنها. وكل ما تبقّى في هذا الموقع إمّا طريقة لتنسيق العدسة، أو طريقة لجعلها تتفاعل، أو عنصر تحكم جاهز موصول بها سلفاً.

يأتي الزجاج بوزنين. العدسة تكسر الضوء عبر شيدر؛ أما الزجاج الخفيف فهو المظهر نفسه بلا شيدر — صقيع ولون وحافة، بلا انكسار — للصفحات كثيرة العدسات، والأجهزة الأبطأ، والويب. اقلبه هنا فينقلب كل عرض في هذا الموقع؛ والزر نفسه في رأس كل صفحة.

يبدّل كل عرض في هذا الموقع بين الزجاج الكامل والزجاج الخفيف. وهو الزر نفسه الموجود في رأس الصفحة.

التثبيت#

pubspec.yaml
dependencies:
  liquid_glass_easy: ^4.3.0
الطرفية
flutter pub get
Dart
import 'package:liquid_glass_easy/liquid_glass_easy.dart';

عدسة في أي مكان#

على Impeller — المُصيّر الافتراضي في Flutter على إصدارات iOS و Android الحديثة — لا تحتاج العدسة أي إعداد على الإطلاق. ضعها فوق واجهتك فتكسر كل ما رسمه تطبيقك خلفها.

Dart
Stack(
  fit: StackFit.expand,
  children: [
    Image.asset('assets/bg.jpg', fit: BoxFit.cover),
    Center(
      child: SizedBox(
        width: 260,
        height: 150,
        child: LiquidGlassLens(
          style: const LiquidGlassStyle(
            shape: LiquidGlassShape.squircle(cornerRadius: 44),
            refraction: LiquidGlassRefraction(
              distortion: 0.13,
              distortionWidth: 34,
            ),
          ),
          child: const Center(child: Text('Liquid Glass')),
        ),
      ),
    ),
  ],
)

تأخذ العدسة حجمها من التخطيط، فامنحها حجماً — عبر SizedBox أو Positioned أو ابن له حجم ذاتي. ويُقصّ الابن دائماً على شكل الزجاج، فأضِف Padding خاصاً بك لإزاحته للداخل.

أكثر من عدسة#

عدة عدسات هي ببساطة عدة عناصر LiquidGlassLens. وحين تحوي الشاشة كثيراً منها — قائمة بطاقات زجاجية، أو صفّ رقائق — لُفَّ ذلك الجزء من الشجرة بـ LiquidGlassBatch. على Impeller كانت كل عدسة ستقرأ الخلفية لنفسها؛ وداخل الدفعة تتشارك قراءة واحدة، ويحتفظ كلٌّ منها بشكله وأسلوبه وابنه.

Dart
const cardStyle = LiquidGlassStyle(
  shape: LiquidGlassShape.squircle(cornerRadius: 28),
  refraction: LiquidGlassRefraction(distortion: 0.1, distortionWidth: 26),
);

LiquidGlassBatch(
  child: ListView.builder(
    padding: const EdgeInsets.all(20),
    itemCount: 20,
    itemBuilder: (context, i) => Padding(
      padding: const EdgeInsets.only(bottom: 14),
      child: SizedBox(
        height: 110,
        child: LiquidGlassLens(
          style: cardStyle,
          child: Center(child: Text('Card $i')),
        ),
      ),
    ),
  ),
)

الدفعة توفير لـ Impeller. أما على Skia والويب فالعارض يلتقط أصلاً مرة واحدة لكل العدسات بداخله، فتعمل الشجرة نفسها هناك بلا تغيير ولا يبقى للدفعة ما تشاركه. ولا حدّ لعدد أعضاء الدفعة؛ وتجد التفاصيل في صفحة الدفعة، أما الانصهار فهو الطريقة الأخرى لجمع العدسات — حين ينبغي أن تنصهر في قطعة زجاج واحدة.

العدسة نفسها على Skia وعلى الويب#

لا يملك Skia شيدراً يقرأ الخلفية الحية، فلا تجد العدسة ما تقرؤه ما لم تناولها إياه بنفسك. لُفَّ محتواك بـ LiquidGlassView وامنحه backgroundWidget — يلتقط العارض تلك الخلفية، وتكسر كل عدسة داخل child صورةَ الالتقاط.

Dart
LiquidGlassView(
  backgroundWidget: const MyBackground(),
  child: Center(
    child: SizedBox(
      width: 300,
      height: 160,
      child: LiquidGlassLens(
        style: const LiquidGlassStyle(
          shape: LiquidGlassShape.squircle(cornerRadius: 40),
        ),
        child: const Center(child: Text('refracts the capture')),
      ),
    ),
  ),
)

الشجرة نفسها آمنة للنشر على Impeller، ولهذا تكتب واحدة فقط. يكتشف العارض المحرك بنفسه — بلا راية ولا if (Platform...) — وعلى Impeller لا يلتقط شيئاً إطلاقاً: يُرسم backgroundWidget حيث يُرسم دائماً، وتذهب العدسات داخل child مباشرة إلى خط خلفية Impeller فتكسر البكسلات الحية خلفها. مسار الالتقاط موجود فقط على المحرك الذي لا خيار آخر لديه.

بلا عارض على Skia#

لا شيء ينكسر. العدسة التي لا تجد خلفية تكسرها تتراجع إلى سطح مصنفر — ضبابية خلفية ولون وحافة — وتسجّل تنبيه تنقيح واحداً يخبرك أي عدسة كانت. فتحصل على لوح زجاجي مقنع بدل مستطيل فارغ.

الزجاج الخفيف: الطريق المختصر#

الزجاج الخفيف هو المادة نفسها بلا شيدر: صقيع ولون وحافة مضيئة، لكن بلا انكسار. لا شيء يُترجَم، ولا التقاط على Skia، ولا مقعد في ميزانية العدسات على Impeller. تصل إليه بثلاث طرق، ولا تحتاج أيٌّ منها تشغيلَ مفتاح أولاً.

كعنصر. LiquidGlassLite سطح قائم بذاته — بطاقة أو صف أو ورقة — لم يكن عدسة قط. امنحه شكلاً وضبابية ولوناً فيرسم زجاجاً خفيفاً، على أي محرك، في أي موضع من الشجرة.

Dart
LiquidGlassLite(
  shape: const LiquidGlassShape(cornerRadius: 22, borderWidth: 1.5),
  blur: const LiquidGlassBlur(sigmaX: 3, sigmaY: 3),
  color: const Color(0x33FFFFFF),
  child: child,
)

على عدسة واحدة. امنح أسلوب العدسة قيمة liteGlass فتُرسَم تلك العدسة خفيفةً بينما تبقى سائر العدسات على الشاشة زجاجاً كاملاً.

Dart
LiquidGlassLens(
  style: const LiquidGlassStyle(liteGlass: LiquidGlassLitePickup.backdrop),
  child: child,
)

للتطبيق كله. مفتاحان ساكنان على LiquidGlassEngine، واحد لكل محرك. شغّل أحدهما فترسم كل عدسة وكل عنصر — المزلاج والمفتاح والأزرار والأشرطة والورقة — زجاجاً خفيفاً تلقائياً، دون تغيير في أيٍّ منها. ضعه في main() قبل runApp فتُبنى العدسة الأولى خفيفةً من البداية. وكلاهما false افتراضياً: احذفهما أو أعدهما فيعود التطبيق زجاجاً كاملاً.

Dart
void main() {
  // Every lens and component in the app draws lite glass on this engine.
  LiquidGlassEngine.liteGlassOnSkia = true;      // Skia and the web
  LiquidGlassEngine.liteGlassOnImpeller = true;  // Impeller

  // Full glass again: set either one back to false (the default).
  // LiquidGlassEngine.liteGlassOnSkia = false;
  // LiquidGlassEngine.liteGlassOnImpeller = false;

  runApp(const MyApp());
}

سطر واحد في main() فتكون الشيدرات جاهزة#

يعمل الزجاج على شيدرات الأجزاء. استدعِ ensureLoaded قبل runApp فتُترجَم وتُحمَّل قبل أن تُبنى العدسة الأولى — جاهزةً لحظة ظهورها.

Dart
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await LiquidGlassShaders.ensureLoaded();
  runApp(const MyApp());
}

التعليقات

التعليقات هي مناقشات GitHub — يمكنك الرد من أيٍّ من المكانين.