MDC-102 Flutter: بنية المواد والتصميم

1. مقدمة

logo_components_color_2x_web_96dp.png

تساعد المكونات المادية (MDC) المطورين على تنفيذ التصميم المتعدد الأبعاد. يضم مركز MDC، الذي أنشأه فريق من المهندسين ومصممي تجربة المستخدم في Google، عشرات من مكونات واجهة المستخدم الجميلة والعملية، وهو متاح لأجهزة Android وiOS والويب وFlutter.material.io/develop

في درس تطبيقي حول الترميز MDC-101، استخدمت مكونَين من مكونات المواد لإنشاء صفحة تسجيل دخول، وهما الحقول النصية والأزرار التي تحتوي على تموجات الحبر. الآن دعنا نتوسع على هذا الأساس بإضافة التنقل والهيكل والبيانات.

ما الذي ستنشئه

في هذا الدرس التطبيقي حول الترميز، ستُنشئ شاشة رئيسية لتطبيق يُسمى Shrine، وهو تطبيق للتجارة الإلكترونية يبيع الملابس والسلع المنزلية. سيتضمّن المحتوى ما يلي:

  • شريط التطبيق العلوي
  • قائمة شبكية مليئة بالمنتجات

Android

iOS

تطبيق للتجارة الإلكترونية يتضمّن شريط تطبيق في أعلى الشاشة وشبكة مليئة بالمنتجات

تطبيق للتجارة الإلكترونية يتضمّن شريط تطبيق في أعلى الشاشة وشبكة مليئة بالمنتجات

مكوّنات Material Flutter والأنظمة الفرعية الخاصة بها في هذا الدرس التطبيقي حول الترميز

  • شريط التطبيق العلوي
  • الشبكات
  • البطاقات

ما هو تقييمك لمستوى خبرتك في تطوير Flutter؟

مبتدئ متوسط متقدّم

2. إعداد بيئة تطوير Flutter

لإكمال هذا التمرين، تحتاج إلى برنامجَين، وهما Flutter SDK ومحرِّر.

يمكنك تشغيل الدرس التطبيقي حول الترميز باستخدام أي من الأجهزة التالية:

  • جهاز Android أو iOS فعلي متصل بجهاز الكمبيوتر وتم ضبطه على "وضع مطور البرامج".
  • محاكي iOS (يتطلب تثبيت أدوات Xcode).
  • محاكي Android (يتطلب الإعداد في "استوديو Android")
  • متصفّح (يجب توفُّر متصفّح Chrome لتصحيح الأخطاء)
  • كتطبيق سطح المكتب الذي يعمل بنظام التشغيل Windows أو Linux أو macOS. يجب إجراء عملية التطوير على النظام الأساسي الذي تنوي نشر التطبيق عليه. لذلك، إذا أردت تطوير تطبيق متوافق مع أجهزة الكمبيوتر المكتبي التي تعمل بنظام التشغيل Windows، عليك تطويره على نظام التشغيل Windows للوصول إلى سلسلة الإنشاء المناسبة. هناك متطلبات خاصة بنظام التشغيل يتم تناولها بالتفصيل على docs.flutter.dev/desktop.

3- تنزيل تطبيق Codelab Starter

هل ستتابع من MDC-101؟

إذا أكملت الدورة التدريبية MDC-101، من المفترض أن تكون رمزك مُعدّة لاستخدامها في هذا الدليل التعليمي. انتقِل إلى الخطوة: إضافة شريط تطبيق في أعلى الشاشة.

هل تريد البدء من نقطة الصفر؟

تنزيل تطبيق "الدرس التطبيقي حول الترميز" للمبتدئين

يمكن العثور على تطبيق التشغيل الأولي في دليل material-components-flutter-codelabs-102-starter_and_101-complete/mdc_100_series.

...أو استنساخها من GitHub

لنسخ هذا الدليل التعليمي من GitHub، شغِّل الأوامر التالية:

git clone https://github.com/material-components/material-components-flutter-codelabs.git
cd material-components-flutter-codelabs/mdc_100_series
git checkout 102-starter_and_101-complete

فتح المشروع وتشغيل التطبيق

  1. افتح المشروع في المحرِّر الذي تختاره.
  2. اتّبِع تعليمات "تشغيل التطبيق" في البدء: تجربة التطبيق للمحرِّر الذي اخترته.

اكتمال عملية النقل بنجاح من المفترض أن تظهر لك صفحة تسجيل الدخول إلى Shrine من الدرس التطبيقي MDC-101 على جهازك.

Android

iOS

صفحة تسجيل الدخول التي تتضمّن حقلَي اسم المستخدم وكلمة المرور، وزرَّي الإلغاء والتالي

صفحة تسجيل الدخول التي تتضمّن حقلَي اسم المستخدم وكلمة المرور، وزرَّي الإلغاء والتالي

بعد أن أصبحت شاشة تسجيل الدخول جيدة، لنضيف بعض المنتجات إلى التطبيق.

4. إضافة شريط تطبيق في أعلى الشاشة

في الوقت الحالي، إذا نقرت على الزر "التالي"، ستظهر لك الشاشة الرئيسية التي تعرض الرسالة "لقد نجحت". رائع. لا يحتاج المستخدم الآن إلى اتّخاذ أي إجراءات، أو أي معرفة بالمكان الذي يتصفّحه في التطبيق. وللمساعدة في ذلك، حان الوقت لإضافة تفاصيل عن ميزات التنقّل.

يوفر Material Design أنماط تنقل تضمن درجة عالية من قابلية الاستخدام. إنّ شريط التطبيق العلوي هو أحد العناصر الأكثر ظهورًا.

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

إضافة تطبيق مصغّر لشريط التطبيقات

في home.dart، أضِف AppBar إلى Scaffold وأزِل رمز const المميّز:

return const Scaffold(
  // TODO: Add app bar (102)
  appBar: AppBar(
    // TODO: Add buttons and title (102)
  ),

تؤدي إضافة AppBar إلى حقل appBar: في Scaffold إلى الحصول على تصميم مثالي مجانًا، مع إبقاء AppBar أعلى الصفحة والنص أسفله.

إضافة تطبيق مصغّر للنص

في home.dart، أضِف عنوانًا إلى AppBar:

// TODO: Add app bar (102)
  appBar: AppBar(
    // TODO: Add buttons and title (102)
    title: const Text('SHRINE'),
    // TODO: Add trailing buttons (102)

احفظ مشروعك.

Android

iOS

شريط تطبيق يحمل عنوان Shrine

شريط تطبيق يحمل Shrine كعنوان

تحتوي العديد من أشرطة التطبيقات على زر بجانب العنوان. لنضيف رمز قائمة في تطبيقنا.

إضافة رمز IconButton في المقدمة

أثناء استخدام home.dart، يمكنك ضبط IconButton لحقل leading: في AppBar. (ضعه قبل حقل title: لمحاكاة الترتيب من الأعلى إلى الأسفل):

    // TODO: Add buttons and title (102)
    leading: IconButton(
      icon: const Icon(
        Icons.menu,
        semanticLabel: 'menu',
      ),
      onPressed: () {
        print('Menu button');
      },
    ),

احفظ مشروعك.

Android

iOS

شريط تطبيق يحمل عنوان "ضريح" مع رمز قائمة طعام هامبورغر

شريط تطبيق مع Shrine كعنوان
ورمز لقائمة الهامبرغر

يظهر رمز القائمة (المعروف أيضًا باسم "الهامبرغر") في المكان الذي تتوقعه فيه.

يمكنك أيضًا إضافة أزرار إلى الجهة اللاحقة من العنوان. ويُطلق على هذه الإجراءات في Flutter اسم "الإجراءات".

إضافة إجراءات

هناك مساحة لزرَّي IconButton إضافيَّين.

أضِفها إلى مثيل AppBar بعد العنوان:

// TODO: Add trailing buttons (102)
actions: <Widget>[
  IconButton(
    icon: const Icon(
      Icons.search,
      semanticLabel: 'search',
    ),
    onPressed: () {
      print('Search button');
    },
  ),
  IconButton(
    icon: const Icon(
      Icons.tune,
      semanticLabel: 'filter',
    ),
    onPressed: () {
      print('Filter button');
    },
  ),
],

احفظ مشروعك. من المفترض أن تظهر شاشتك الرئيسية على النحو التالي:

Android

iOS

شريط تطبيق يتضمّن عنوان &quot;Shrine&quot; ورمز قائمة طعام هامبورغر ورموز البحث والتخصيص

شريط تطبيق مع عنوان Shrine كعنوان ورمز قائمة الهامبرغر، بالإضافة إلى البحث اللاحقة ورموز تخصيص

يحتوي التطبيق الآن على زر بادئة وعنوان وإجراءين على الجانب الأيمن. يعرض شريط التطبيقات أيضًا الارتفاع باستخدام ظل خفيف يُظهر أنه على طبقة مختلفة عن المحتوى.

5- إضافة بطاقة في شبكة

الآن بعد أن أصبح تطبيقنا يتضمّن بعض البنية، لننظم المحتوى من خلال وضعه في بطاقات.

إضافة GridView

لنبدأ بإضافة بطاقة واحدة أسفل شريط التطبيقات العلوي. لا تحتوي أداة البطاقة وحدها على معلومات كافية لوضع نفسها في الأماكن التي يمكننا عرضها فيها، لذا نحتاج إلى تغليفها في أداة GridView.

استبدل المركز في نص السقالة بـ GridView:

// TODO: Add a grid view (102)
body: GridView.count(
  crossAxisCount: 2,
  padding: const EdgeInsets.all(16.0),
  childAspectRatio: 8.0 / 9.0,
  // TODO: Build a grid of cards (102)
  children: <Widget>[Card()],
),

لنفكّ رمز هذا الرمز. تستدعي GridView count() لأنّ عدد العناصر التي تعرِضها يمكن احتسابه وليس غير محدود. لكنها تحتاج إلى مزيد من المعلومات لتحديد تخطيطها.

وتحدّد السمة crossAxisCount: عدد العناصر المضمّنة. نريد عمودين.

يقدّم الحقل padding: مساحة على جميع الجوانب الأربعة من GridView. بطبيعة الحال، لا يمكنك رؤية المساحة المتروكة على الجوانب اللاحقة أو السفلية بسبب عدم وجود عناصر GridView بجانبها بعد.

يحدِّد الحقل childAspectRatio: حجم العناصر استنادًا إلى نسبة العرض إلى الارتفاع (العرض على الارتفاع).

ينشئ GridView تلقائيًا مربّعات بنفس الحجم.

لدينا بطاقة واحدة ولكنها فارغة. لنضيف التطبيقات المصغّرة الفرعية إلى بطاقتنا.

تنسيق المحتوى

يجب أن تتضمّن البطاقات مناطق للصورة والعنوان والنص الثانوي.

عدِّل عناصر GridView:

// TODO: Build a grid of cards (102)
children: <Widget>[
  Card(
    clipBehavior: Clip.antiAlias,
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        AspectRatio(
          aspectRatio: 18.0 / 11.0,
          child: Image.asset('assets/diamond.png'),
        ),
        Padding(
          padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Text('Title'),
              const SizedBox(height: 8.0),
              Text('Secondary Text'),
            ],
          ),
        ),
      ],
    ),
  )
],

تضيف هذه التعليمة البرمجية أداة عمود تُستخدم لتخطيط الأدوات الفرعية عموديًا.

يحدِّد الرمز crossAxisAlignment: field الرمز CrossAxisAlignment.start، ما يعني "محاذاة النص مع الحافة الأمامية".

تحدِّد أداة AspectRatio شكل الصورة بغض النظر عن نوع الصورة المقدَّمة.

تؤدي المساحة المتروكة إلى إدخال النص من الجانب قليلاً.

يتم تجميع تطبيقَي النص المصغّرَين عموديًا مع ترك 8 نقاط من المساحة الفارغة بينهما (SizedBox). نُنشئ عمودًا آخر لوضعها داخل "المسافة البادئة".

احفظ مشروعك.

Android

iOS

عنصر واحد يتضمّن صورة وعنوانًا ونصًا ثانويًا

عنصر واحد يتضمّن صورة وعنوانًا ونصًا ثانويًا

في هذه المعاينة، يمكنك رؤية أنّ البطاقة مضمّنة من الحافة، مع زوايا مستديرة وظلال (توضّح ارتفاع البطاقة). يُطلق على الشكل بأكمله اسم "الحاوية" في Material. (يجب عدم الخلط بين هذه الفئة وفئة التطبيق المصغّر الفعلية التي تُسمى Container).

تظهر البطاقات عادةً في مجموعة مع بطاقات أخرى. لنرتِّبها كمجموعة في شبكة.

6- إنشاء مجموعة بطاقات

عند وجود بطاقات متعددة في شاشة، يتم تجميعها معًا في مجموعة واحدة أو أكثر. تكون البطاقات في المجموعة على مستوى مستوٍ، ما يعني أنّ البطاقات تشترك في الارتفاع نفسه (ما لم يتم رفع البطاقات أو سحبها، ولكن لن نفعل ذلك هنا).

مضاعفة البطاقة في مجموعة

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

أنشئ دالة خاصة جديدة فوق دالة build() (تذكَّر أنّ الدوال التي تبدأ بشرطة سفلية هي واجهات برمجة تطبيقات خاصة):

// TODO: Make a collection of cards (102)
List<Card> _buildGridCards(int count) {
  List<Card> cards = List.generate(
    count,
    (int index) {
      return Card(
        clipBehavior: Clip.antiAlias,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            AspectRatio(
              aspectRatio: 18.0 / 11.0,
              child: Image.asset('assets/diamond.png'),
            ),
            Padding(
              padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: const <Widget>[
                  Text('Title'),
                  SizedBox(height: 8.0),
                  Text('Secondary Text'),
                ],
              ),
            ),
          ],
        ),
      );
    },
  );
  return cards;
}

أضِف البطاقات التي تمّ إنشاؤها إلى الحقل children في GridView. تذكَّر استبدال كل المحتوى المضمّن في GridView بهذا الرمز الجديد:

// TODO: Add a grid view (102)
body: GridView.count(
  crossAxisCount: 2,
  padding: const EdgeInsets.all(16.0),
  childAspectRatio: 8.0 / 9.0,
  children: _buildGridCards(10) // Replace
),

احفظ مشروعك.

Android

iOS

شبكة من العناصر مع صورة وعنوان ونص ثانوي

شبكة من العناصر التي تتضمّن صورة وعنوانًا ونصًا ثانويًا

البطاقات متوفّرة، لكنها لا تعرض أي شيء حتى الآن. حان الوقت الآن لإضافة بيانات المنتجات.

إضافة بيانات المنتج

يحتوي التطبيق على بعض المنتجات التي تحتوي على صور وأسماء وأسعار. لنضيف ذلك إلى التطبيقات المصغّرة المتوفّرة في البطاقة

بعد ذلك، في home.dart، استورِد حزمة جديدة وبعض الملفات التي وفّرناها لنموذج بيانات:

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

import 'model/product.dart';
import 'model/products_repository.dart';

أخيرًا، غيِّر _buildGridCards() لعرض معلومات المنتج، واستخدِم هذه البيانات في البطاقات:

// TODO: Make a collection of cards (102)

// Replace this entire method
List<Card> _buildGridCards(BuildContext context) {
  List<Product> products = ProductsRepository.loadProducts(Category.all);

  if (products.isEmpty) {
    return const <Card>[];
  }

  final ThemeData theme = Theme.of(context);
  final NumberFormat formatter = NumberFormat.simpleCurrency(
      locale: Localizations.localeOf(context).toString());

  return products.map((product) {
    return Card(
      clipBehavior: Clip.antiAlias,
      // TODO: Adjust card heights (103)
      child: Column(
        // TODO: Center items on the card (103)
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          AspectRatio(
            aspectRatio: 18 / 11,
            child: Image.asset(
              product.assetName,
              package: product.assetPackage,
             // TODO: Adjust the box size (102)
            ),
          ),
          Expanded(
            child: Padding(
              padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
              child: Column(
               // TODO: Align labels to the bottom and center (103)
               crossAxisAlignment: CrossAxisAlignment.start,
                // TODO: Change innermost Column (103)
                children: <Widget>[
                 // TODO: Handle overflowing labels (103)
                 Text(
                    product.name,
                    style: theme.textTheme.titleLarge,
                    maxLines: 1,
                  ),
                  const SizedBox(height: 8.0),
                  Text(
                    formatter.format(product.price),
                    style: theme.textTheme.titleSmall,
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }).toList();
}

ملاحظة: لن يتم التجميع وتشغيله في الوقت الحالي. لدينا تغيير آخر.

غيِّر أيضًا الدالة build() لتمرير BuildContext إلى _buildGridCards() قبل محاولة الترجمة:

// TODO: Add a grid view (102)
body: GridView.count(
  crossAxisCount: 2,
  padding: const EdgeInsets.all(16.0),
  childAspectRatio: 8.0 / 9.0,
  children: _buildGridCards(context) // Changed code
),

أعِد تشغيل التطبيق فورًا.

Android

iOS

شبكة من السلع تتضمّن صورة وعنوان المنتج وسعره

شبكة من السلع تتضمّن صورة وعنوان المنتج وسعره

قد تلاحظ أنّنا لا نضيف أيّ مسافة عمودية بين البطاقات. ويعود السبب في ذلك إلى أنّها تحتوي تلقائيًا على 4 نقاط هامش في أعلى وأسفل الصفحة.

احفظ مشروعك.

تظهر بيانات المنتج، ولكن هناك مساحة إضافية حول الصور. يتم رسم الصور باستخدام BoxFit.scaleDown تلقائيًا (في هذه الحالة). لنغيّر ذلك إلى .fitWidth ليتم التكبير قليلاً وإزالة المسافة البيضاء الإضافية.

أضِف حقل fit: إلى الصورة مع القيمة BoxFit.fitWidth:

  // TODO: Adjust the box size (102)
  fit: BoxFit.fitWidth,

Android

iOS

شبكة من السلع تتضمّن صورة مُقتطعة وعنوان المنتج وسعره

وتظهر منتجاتنا الآن في التطبيق بشكل مثالي.

7- تهانينا!

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

الخطوات التالية

باستخدام شريط التطبيق العلوي والبطاقة وحقل النص والزر، استخدمنا الآن أربعة مكوّنات أساسية من مكتبة Material Flutter. يمكنك التعرّف على المزيد من المعلومات من خلال الانتقال إلى كتالوج التطبيقات المصغّرة لمكونات Material.

على الرغم من أنّ تطبيقنا يعمل بشكل كامل، لا يعبّر بعد عن أي علامة تجارية أو وجهة نظر معيّنة. في MDC-103: مظهر التصميم المتعدد الأبعاد حسب اللون والشكل والارتفاع والنوع، سنخصِّص نمط هذه المكوّنات للتعبير عن علامة تجارية عصرية ونابضة بالحياة.

تمكنتُ من إكمال هذا الدرس التطبيقي حول الترميز بقدرٍ معقول من الوقت والجهد

أوافق بشدة أوافق لا أوافق ولا أعارض لا أوافق لا أوافق أبدًا

أود مواصلة استخدام Material Components في المستقبل

أوافق بشدة أوافق لا أوافق ولا أعارض لا أوافق لا أوافق أبدًا