فلوتر MDC-102: ساختار و چیدمان مواد

۱. مقدمه

logo_components_color_2x_web_96dp.png

کامپوننت‌های متریال (MDC) به توسعه‌دهندگان در پیاده‌سازی طراحی متریال کمک می‌کنند. MDC که توسط تیمی از مهندسان و طراحان UX در گوگل ایجاد شده است، ده‌ها کامپوننت رابط کاربری زیبا و کاربردی را ارائه می‌دهد و برای اندروید، iOS، وب و Flutter.material.io/develop در دسترس است.

در codelab MDC-101 ، شما از دو کامپوننت متریال برای ساخت یک صفحه ورود استفاده کردید: فیلدهای متنی و دکمه‌هایی با موج‌های جوهر. حالا بیایید با اضافه کردن ناوبری، ساختار و داده‌ها، این پایه را گسترش دهیم.

آنچه خواهید ساخت

در این آزمایشگاه کد، شما یک صفحه اصلی برای برنامه‌ای به نام Shrine ، یک برنامه تجارت الکترونیک که لباس و کالاهای خانگی می‌فروشد، خواهید ساخت. این برنامه شامل موارد زیر خواهد بود:

  • نوار برنامه برتر
  • یک لیست شبکه‌ای پر از محصولات

اندروید

آی‌او‌اس

اپلیکیشن تجارت الکترونیک با نوار بالای اپلیکیشن و جدولی پر از محصولات

اپلیکیشن تجارت الکترونیک با نوار بالای اپلیکیشن و جدولی پر از محصولات

اجزا و زیرسیستم‌های Flutter متریال در این آزمایشگاه کد

  • نوار برنامه بالا
  • شبکه‌ها
  • کارت‌ها

سطح تجربه خود را در توسعه فلاتر چگونه ارزیابی می‌کنید؟

تازه کار متوسط ماهر

۲. محیط توسعه فلاتر خود را تنظیم کنید

برای تکمیل این آزمایشگاه به دو نرم‌افزار نیاز دارید - SDK فلاتر و یک ویرایشگر .

شما می‌توانید codelab را با استفاده از هر یک از این دستگاه‌ها اجرا کنید:

  • یک دستگاه فیزیکی اندروید یا iOS که به رایانه شما متصل شده و روی حالت توسعه‌دهنده (Developer mode) تنظیم شده باشد.
  • شبیه‌ساز iOS (نیاز به نصب ابزارهای Xcode دارد).
  • شبیه‌ساز اندروید (نیاز به راه‌اندازی در اندروید استودیو دارد).
  • یک مرورگر (برای اشکال‌زدایی، کروم مورد نیاز است).
  • به عنوان یک برنامه دسکتاپ ویندوز ، لینوکس یا macOS . شما باید روی پلتفرمی که قصد استقرار آن را دارید، توسعه دهید. بنابراین، اگر می‌خواهید یک برنامه دسکتاپ ویندوز توسعه دهید، باید روی ویندوز توسعه دهید تا به زنجیره ساخت مناسب دسترسی داشته باشید. الزامات خاص سیستم عامل وجود دارد که به تفصیل در docs.flutter.dev/desktop پوشش داده شده است.

۳. اپلیکیشن شروع کدلب را دانلود کنید

ادامه از MDC-101؟

اگر MDC-101 را تکمیل کرده‌اید، کد شما باید برای این آزمایشگاه کد آماده شده باشد. به مرحله بعدی بروید: یک نوار برنامه در بالای صفحه اضافه کنید .

از صفر شروع کردن؟

اپلیکیشن استارتر codelab را دانلود کنید

برنامه‌ی آغازین در دایرکتوری material-components-flutter-codelabs-102-starter_and_101-complete/mdc_100_series قرار دارد.

... یا آن را از گیت‌هاب کلون کنید

برای کپی کردن این codelab از گیت‌هاب، دستورات زیر را اجرا کنید:

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 را روی دستگاه خود مشاهده کنید.

اندروید

آی‌او‌اس

صفحه ورود به سیستم با فیلدهای نام کاربری و رمز عبور، دکمه‌های لغو و بعدی

صفحه ورود به سیستم با فیلدهای نام کاربری و رمز عبور، دکمه‌های لغو و بعدی

حالا که صفحه ورود به سیستم خوب به نظر می‌رسد، بیایید برنامه را با برخی از محصولات پر کنیم.

۴. اضافه کردن نوار برنامه در بالای صفحه

همین الان، اگر روی دکمه «بعدی» کلیک کنید، می‌توانید صفحه اصلی را ببینید که می‌گوید «شما انجامش دادید!». عالیه! اما حالا کاربر ما هیچ کاری برای انجام دادن ندارد یا هیچ درکی از اینکه در کجای برنامه است، ندارد. برای کمک، وقت آن رسیده که ناوبری را اضافه کنیم.

طراحی متریال الگوهای ناوبری ارائه می‌دهد که درجه بالایی از کاربردپذیری را تضمین می‌کند. یکی از قابل مشاهده‌ترین اجزا، نوار بالای برنامه است.

برای فراهم کردن ناوبری و دسترسی سریع کاربران به سایر اقدامات، بیایید یک نوار برنامه در بالای صفحه اضافه کنیم.

افزودن ویجت 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 را در بالای صفحه و body را در زیر آن نگه می‌دارد.

اضافه کردن ویجت متن

در 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)

پروژه خود را ذخیره کنید.

اندروید

آی‌او‌اس

یک نوار برنامه با عنوان 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');
      },
    ),

پروژه خود را ذخیره کنید.

اندروید

آی‌او‌اس

یک نوار برنامه با عنوان Shrine و یک آیکون منوی همبرگری

یک نوار برنامه با عنوان Shrine و یک آیکون منوی همبرگری

آیکون منو (که با نام «همبرگر» نیز شناخته می‌شود) درست همان جایی که انتظار دارید، نمایش داده می‌شود.

همچنین می‌توانید دکمه‌هایی را به انتهای عنوان اضافه کنید. در فلاتر، به این دکمه‌ها «اکشن» می‌گویند.

افزودن اقدامات

جا برای دو 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');
    },
  ),
],

پروژه خود را ذخیره کنید. صفحه اصلی شما باید به این شکل باشد:

اندروید

آی‌او‌اس

یک نوار برنامه با عنوان Shrine و یک آیکون منوی همبرگری، و آیکون‌های جستجو و سفارشی‌سازی در انتهای آن

یک نوار برنامه با عنوان Shrine و یک آیکون منوی همبرگری، و آیکون‌های جستجو و سفارشی‌سازی در انتهای آن

حالا برنامه یک دکمه‌ی ابتدایی، یک عنوان و دو عمل در سمت راست دارد. نوار برنامه همچنین ارتفاع را با استفاده از یک سایه‌ی ظریف نمایش می‌دهد که نشان می‌دهد در لایه‌ای متفاوت از محتوا قرار دارد.

۵. یک کارت را در یک شبکه اضافه کنید

حالا که برنامه ما ساختار مشخصی دارد، بیایید محتوا را با قرار دادن در کارت‌ها سازماندهی کنیم.

اضافه کردن GridView

بیایید با اضافه کردن یک کارت در زیر نوار بالای برنامه شروع کنیم. ویجت کارت به تنهایی اطلاعات کافی برای نمایش در جایی که می‌توانیم آن را ببینیم، ندارد، بنابراین می‌خواهیم آن را در یک ویجت GridView محصور کنیم.

عبارت Center در بدنه Scaffold را با یک 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 فضا ایجاد می‌کند. البته شما نمی‌توانید padding را در سمت‌های انتهایی یا پایینی ببینید زیرا هنوز GridView child در کنار آنها وجود ندارد.

فیلد 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 تصمیم می‌گیرد که تصویر، صرف نظر از نوع تصویر ارائه شده، چه شکلی به خود بگیرد.

این لایه، متن را کمی از کنار به داخل می‌آورد.

دو ویجت متنی به صورت عمودی روی هم قرار گرفته‌اند و بین آنها ۸ نقطه فضای خالی وجود دارد ( SizedBox ). ما یک ستون دیگر ایجاد می‌کنیم تا آنها را درون Padding قرار دهیم.

پروژه خود را ذخیره کنید.

اندروید

آی‌او‌اس

یک آیتم واحد با تصویر، عنوان و متن ثانویه

یک آیتم واحد با تصویر، عنوان و متن ثانویه

در این پیش‌نمایش، می‌توانید ببینید که کارت از لبه به داخل کشیده شده است، با گوشه‌های گرد و یک سایه (که نشان‌دهنده‌ی ارتفاع کارت است). کل شکل در Material «کانتینر» نامیده می‌شود. (نباید با کلاس ویجت واقعی به نام Container اشتباه گرفته شود.)

کارت‌ها معمولاً در یک مجموعه با کارت‌های دیگر نشان داده می‌شوند. بیایید آنها را به صورت یک مجموعه در یک شبکه بچینیم.

۶. یک مجموعه کارت درست کنید

هر زمان که چندین کارت در یک صفحه نمایش داده شوند، در یک یا چند مجموعه گروه‌بندی می‌شوند. کارت‌های یک مجموعه هم‌سطح هستند، به این معنی که کارت‌ها ارتفاع ثابت یکسانی با یکدیگر دارند (مگر اینکه کارت‌ها برداشته یا کشیده شوند، اما ما در اینجا این کار را انجام نخواهیم داد).

کارت را در یک مجموعه ضرب کنید

در حال حاضر، کارت ما به صورت درون‌خطی از فیلد children: در GridView ساخته شده است. این مقدار زیادی کد تودرتو است که خواندن آن را دشوار می‌کند. بیایید آن را در تابعی استخراج کنیم که بتواند هر تعداد کارت خالی که می‌خواهیم تولید کند و لیستی از کارت‌ها را برگرداند.

یک تابع خصوصی جدید بالای تابع build() ایجاد کنید (به یاد داشته باشید توابعی که با زیرخط شروع می‌شوند، API خصوصی هستند):

// 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
),

پروژه خود را ذخیره کنید.

اندروید

آی‌او‌اس

شبکه‌ای از آیتم‌ها با تصویر، عنوان و متن ثانویه

شبکه‌ای از آیتم‌ها با تصویر، عنوان و متن ثانویه

کارت‌ها آنجا هستند، اما هنوز چیزی نشان نمی‌دهند. اکنون زمان اضافه کردن داده‌های محصول است.

اضافه کردن داده‌های محصول

این برنامه چند محصول با تصاویر، نام‌ها و قیمت‌ها دارد. بیایید این موارد را به ویجت‌هایی که از قبل در کارت داریم اضافه کنیم.

سپس، در 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
),

برنامه را فوراً مجدداً راه‌اندازی کنید.

اندروید

آی‌او‌اس

شبکه‌ای از اقلام با تصویر، عنوان محصول و قیمت

شبکه‌ای از اقلام با تصویر، عنوان محصول و قیمت

ممکن است متوجه شده باشید که ما هیچ فضای عمودی بین کارت‌ها اضافه نمی‌کنیم. دلیلش این است که آنها به طور پیش‌فرض ۴ نقطه حاشیه در بالا و پایین خود دارند.

پروژه خود را ذخیره کنید.

داده‌های محصول نمایش داده می‌شوند، اما تصاویر فضای اضافی در اطراف خود دارند. تصاویر به طور پیش‌فرض (در این مورد) با BoxFit با مقدار .scaleDown ترسیم می‌شوند. بیایید آن را به .fitWidth تغییر دهیم تا کمی بزرگنمایی شوند و فضای خالی اضافی حذف شود.

یک فیلد fit: به تصویر با مقدار BoxFit.fitWidth اضافه کنید:

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

اندروید

آی‌او‌اس

شبکه‌ای از اقلام با تصویر برش خورده، عنوان محصول و قیمت

محصولات ما اکنون به طور کامل در برنامه نمایش داده می‌شوند!

۷. تبریک می‌گویم!

برنامه ما یک جریان ساده دارد که کاربر را از صفحه ورود به سیستم به صفحه اصلی می‌برد، جایی که می‌توان محصولات را مشاهده کرد. تنها با چند خط کد، یک نوار برنامه در بالای صفحه (با یک عنوان و سه دکمه) و کارت‌هایی (برای ارائه محتوای برنامه) اضافه کردیم. صفحه اصلی ما اکنون ساده و کاربردی است، با ساختاری ساده و محتوای کاربردی.

مراحل بعدی

با نوار برنامه بالا، کارت، فیلد متنی و دکمه، اکنون از چهار کامپوننت اصلی از کتابخانه Material Flutter استفاده کرده‌ایم! می‌توانید با مراجعه به کاتالوگ ویجت‌های کامپوننت‌های Material ، اطلاعات بیشتری کسب کنید.

اگرچه برنامه ما کاملاً در حال کار است، اما هنوز هیچ برند یا دیدگاه خاصی را بیان نمی‌کند. در MDC-103: تم‌بندی طراحی متریال با رنگ، شکل، ارتفاع و نوع ، ما سبک این اجزا را برای بیان یک برند پر جنب و جوش و مدرن سفارشی خواهیم کرد.

من توانستم این آزمایشگاه کد را با مقدار قابل توجهی از زمان و تلاش تکمیل کنم.

کاملاً موافقم موافق خنثی مخالف کاملاً مخالفم

من دوست دارم در آینده به استفاده از کامپوننت‌های متریال ادامه دهم.

کاملاً موافقم موافق خنثی مخالف کاملاً مخالفم