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- تنزيل تطبيق بدء الدروس التطبيقية حول الترميز

هل تريد المتابعة من 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 أنماط تنقل تضمن درجة عالية من قابلية الاستخدام. أحد المكوّنات الأكثر وضوحًا هو شريط التطبيق العلوي.

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

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

في 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 كعنوان

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

إضافة رمز برمجي بادئة

أثناء استخدام 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 كعنوان
ورمز لقائمة الهامبرغر

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

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

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

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

هناك مساحة لاثنين من الرموز الإضافية.

إضافتها إلى مثيل 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

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

شريط تطبيق مع عنوان 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. (يجب عدم الخلط بينه وبين فئة الأداة الفعلية التي تُسمى الحاوية).

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

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 في المستقبل

أوافق بشدة أوافق ليست دقيقة ولا غير دقيقة لا أوافق لا أوافق أبدًا