MDC-102 Flutter: struktura i układ materiału

1. Wprowadzenie

logo_components_color_2x_web_96dp.png

Material Komponenty (MDC) pomagają deweloperom wdrażać interfejs Material Design. MDC, stworzona przez zespół inżynierów i projektantów UX w Google, zawiera dziesiątki pięknych i funkcjonalnych komponentów interfejsu. Jest dostępny na Androida, iOS, internet oraz Flutter.material.io/develop

W ćwiczeniu z programowania MDC-101 do utworzenia strony logowania wykorzystano 2 komponenty materiałowe: pola tekstowe i przyciski z marszowymi odcieniami atramentu. Rozwińmy te podstawy, dodając nawigację, strukturę i dane.

Co utworzysz

W ramach tego ćwiczenia w Codelabs utworzysz ekran główny aplikacji o nazwie Shrine, która umożliwia sprzedaż odzieży i artykułów wyposażenia domu. Będą one zawierać:

  • Górny pasek aplikacji
  • Lista siatki pełna produktów

Android

iOS

aplikacja e-commerce z górnym paskiem aplikacji i siatką pełną produktów

aplikacja e-commerce z górnym paskiem aplikacji i siatką pełną produktów

Komponenty i podsystemy Material Flutter dostępne w tym ćwiczeniu z programowania

  • Górny pasek aplikacji
  • Siatki
  • Karty

Jak oceniasz swój poziom doświadczenia w programowaniu w usłudze Flutter?

Początkujący Poziom średnio zaawansowany Biegły
.

2. Konfigurowanie środowiska programistycznego Flutter

Aby ukończyć ten moduł, potrzebujesz 2 oprogramowania: pakietu SDK Flutter i edytora.

Ćwiczenie z programowania możesz uruchomić na dowolnym z tych urządzeń:

  • Fizyczne urządzenie z Androidem lub iOS podłączone do komputera i ustawione w trybie programisty.
  • Symulator iOS (wymaga zainstalowania narzędzi Xcode).
  • Emulator Androida (wymaga skonfigurowania Android Studio).
  • Przeglądarka (do debugowania wymagany jest Chrome).
  • Aplikacja komputerowa w systemie Windows, Linux lub macOS Programowanie należy tworzyć na platformie, na której zamierzasz wdrożyć usługę. Jeśli więc chcesz opracować aplikację komputerową dla systemu Windows, musisz to zrobić w tym systemie, aby uzyskać dostęp do odpowiedniego łańcucha kompilacji. Istnieją wymagania związane z konkretnymi systemami operacyjnymi, które zostały szczegółowo omówione na stronie docs.flutter.dev/desktop.

3. Pobierz aplikację startową w Codelabs

Przechodzisz z MDC-101?

Jeśli masz ukończone MDC-101, Twój kod powinien być gotowy do wykonania tego ćwiczenia z programowania. Przejdź od razu do kroku: Dodawanie górnego paska aplikacji.

Zaczynasz od zera?

Pobierz startową aplikację Codelabs

Aplikacja startowa znajduje się w katalogu material-components-flutter-codelabs-102-starter_and_101-complete/mdc_100_series.

...lub skopiuj je z GitHuba

Aby skopiować to ćwiczenia z programowania z GitHuba, uruchom te polecenia:

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

Otwieranie projektu i uruchamianie aplikacji

  1. Otwórz projekt w wybranym edytorze.
  2. Postępuj zgodnie z instrukcjami „Uruchom aplikację” w artykule Rozpocznij: jazdę próbną dla wybranego edytora.

Gotowe! Na urządzeniu powinna wyświetlić się strona logowania do Shrine z ćwiczenia z programowania MDC-101.

Android

iOS

strona logowania z polami nazwy użytkownika i hasła oraz przyciskami Anuluj i Dalej

strona logowania z polami nazwy użytkownika i hasła oraz przyciskami Anuluj i Dalej

Teraz gdy ekran logowania wygląda dobrze, dodajmy do aplikacji kilka produktów.

4. Dodaj górny pasek aplikacji

Jeśli teraz klikniesz przycisk „Dalej”, , pojawi się ekran główny z napisem „Udało Ci się!”. Wspaniale. Jednak teraz użytkownik nie musi podejmować żadnych działań ani nie ma żadnego wglądu w to, gdzie się znajduje w aplikacji. Aby Ci pomóc, dodajmy nawigację.

Material Design zapewnia dużą wygodę korzystania z nawigacji. Jednym z najbardziej widocznych komponentów jest pasek aplikacji u góry.

Aby zapewnić użytkownikom nawigację i zapewnić szybki dostęp do innych działań, dodajmy górny pasek aplikacji.

Dodawanie widżetu AppBar

W narzędziu home.dart dodaj do Scaffold pasek aplikacji i usuń zaznaczony element const:

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

Dodanie paska AppBar do pola appBar: Scaffold daje nam bezpłatny, idealny układ, który utrzymuje pasek aplikacji na górze strony, a jej treść pod spodem.

Dodawanie widżetu tekstowego

W home.dart dodaj tytuł do paska aplikacji:

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

Zapisz projekt.

Android

iOS

pasek aplikacji z tytułem Shrine

pasek aplikacji z tytułem Shrine

Wiele pasków aplikacji ma przycisk obok tytułu. Dodajmy ikonę menu w aplikacji.

Dodaj wiodący przycisk IconButton

Będąc w trybie home.dart, ustaw parametr IconButton w polu leading: paska aplikacji. (Umieść go przed polem title:, aby imitować kolejność od początku do końca):

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

Zapisz projekt.

Android

iOS

pasek aplikacji z tytułem Shrine i ikoną menu z hamburgerami

pasek aplikacji z tytułem Shrine i ikoną menu z hamburgerami

Ikona menu (znana również jako „hamburger”) wyświetli się dokładnie tam, gdzie się spodziewasz.

Możesz też dodać przyciski na końcu tytułu. W usłudze Flutter są to tzw. „działania”.

Dodaj działania

Pozostały jeszcze 2 Ikona.

Dodaj je do instancji AppBar po tytule:

// 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');
    },
  ),
],

Zapisz projekt. Twój ekran główny powinien wyglądać tak:

Android

iOS

pasek aplikacji z tytułem „Shrine” i ikoną menu oraz ikonami na końcu wyszukiwania i dostosowania.

pasek aplikacji z tytułem „Shrine” i ikoną menu oraz ikonami na końcu wyszukiwania i dostosowania.

Teraz aplikacja ma przycisk początkowy, tytuł i 2 działania po prawej stronie. Na pasku aplikacji jest też wyświetlana wysokość, a subtelny cień informuje o tym, że znajduje się ona w innej warstwie niż jej zawartość.

5. Dodawanie karty w siatce

Aplikacja ma już strukturę, więc uporządkujmy treści, umieszczając je na kartach.

Dodawanie widoku siatki

Zacznijmy od dodania jednej karty poniżej górnego paska aplikacji. Widżet Karta ma za mało informacji, by umieścić go w widocznych miejscach, więc chcemy umieścić go w widżecie GridView.

Zastąp Środek w treści Scaffold za pomocą widoku siatki:

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

Rozpakujmy ten kod. Widok GridView wywołuje konstruktor count(), ponieważ liczba wyświetlanych elementów jest zliczana, a nie nieskończona. Potrzebuje jednak więcej informacji, aby zdefiniować układ.

Wartość crossAxisCount: określa liczbę elementów. Potrzebujemy 2 kolumn.

Pole padding: zapewnia miejsce po wszystkich 4 stronach widoku GridView. Oczywiście nie widać dopełnienia na końcu ani na dole, bo obok nich nie ma jeszcze dzieci GridView.

Pole childAspectRatio: określa rozmiar elementów na podstawie współczynnika proporcji (szerokość x wysokość).

Domyślnie widok GridView tworzy kafelki o tym samym rozmiarze.

Mamy jedną kartę, ale jest pusta. Dodajmy do naszej karty widżety podrzędne.

Układ treści

Karty powinny zawierać obszary na obraz, tytuł i dodatkowy tekst.

Zaktualizuj elementy podrzędne widoku 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'),
            ],
          ),
        ),
      ],
    ),
  )
],

Ten kod dodaje widżet kolumny służący do układania widżetów podrzędnych w pionie.

Pole crossAxisAlignment: field określa CrossAxisAlignment.start, co oznacza „wyrównaj tekst do początkowej krawędzi”.

Widżet AspectRatio decyduje o kształcie obrazu, niezależnie od jego rodzaju.

Opcja Dopełnienie sprawia, że tekst jest nieco z boku.

Dwa widżety Text są ułożone w pionie, a między nimi ma 8 punktów (SizedBox). Tworzymy kolejną kolumnę, aby umieścić je w dopełnieniem.

Zapisz projekt.

Android

iOS

pojedynczy element z obrazem, tytułem i dodatkowym tekstem

pojedynczy element z obrazem, tytułem i dodatkowym tekstem

Na tym podglądzie widać, że karta jest wstawiona od krawędzi, z zaokrąglonymi rogami i cień (odzwierciedlający wysokość karty). Cały kształt jest nazywany „kontenerem” w Material. (Nie należy mylić z rzeczywistą klasą widżetu o nazwie Container).

Karty zwykle wyświetlają się w kolekcji razem z innymi kartami. Rozmieść je w siatce jako kolekcję.

6. Utwórz kolekcję kart

Jeśli na ekranie znajduje się wiele kart, są one zgrupowane w jedną lub więcej kolekcji. Karty w kolekcji są umieszczone w jednej płaszczyźnie, co oznacza, że mają takie same ukształtowanie w miejscu spoczynku (chyba że karty zostaną podniesione lub przeciągnięte – w tym przypadku nie będziemy tego robić).

Powiel kartę, tworząc kolekcję

Obecnie karta jest zbudowana w polu children: widoku GridView. To dużo zagnieżdżonego kodu, który może być trudny do odczytania. Wyodrębnijmy go do funkcji, która może wygenerować dowolną liczbę pustych kart i zwraca listę kart.

Utwórz nową funkcję prywatną nad funkcją build() (pamiętaj, że funkcje rozpoczynające się od podkreślenia są prywatnym interfejsem 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;
}

Przypisz wygenerowane karty do pola children widoku GridView. Pamiętaj, aby zastąpić całą zawartość obiektu GridView nowym kodem:

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

Zapisz projekt.

Android

iOS

siatka elementów z obrazem, tytułem i dodatkowym tekstem

siatka elementów z obrazem, tytułem i dodatkowym tekstem

Karty są, ale na razie nic nie widać. Teraz dodaj dane produktów.

Dodawanie danych o produktach

Aplikacja zawiera produkty ze zdjęciami, nazwami i cenami. Dodajmy ten element do widżetów, które są już na karcie,

Następnie w home.dart zaimportuj nowy pakiet i kilka plików, które dostarczyliśmy dla modelu danych:

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

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

Na koniec zmień parametr _buildGridCards(), aby pobrać informacje o produkcie, i użyj tych danych na kartach:

// 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();
}

UWAGA: narzędzie nie będzie jeszcze kompilować ani uruchamiać. Mamy jeszcze jedną zmianę.

Zanim spróbujesz skompilować, zmień też funkcję build() tak, aby przekazywać parametr BuildContext na _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
),

Uruchom ponownie aplikację na gorąco.

Android

iOS

siatka produktów ze zdjęciem, nazwą produktu i ceną

siatka produktów ze zdjęciem, nazwą produktu i ceną

Możesz zauważyć, że nie dodamy pionowego odstępu między kartami. Wynika to z faktu, że domyślnie u góry i na dole strony znajdują się 4 punkty marginesów.

Zapisz projekt.

Dane produktów pojawią się, ale wokół zdjęć będzie dodatkowa przestrzeń. Domyślnie obrazy są rysowane w BoxFit o wartości .scaleDown (w tym przypadku). Zmieńmy to na .fitWidth, żeby dzieci trochę powiększyły obraz i usunęły niepotrzebną spację.

Dodaj do obrazu pole fit: o wartości BoxFit.fitWidth:

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

Android

iOS

siatka produktów z przyciętym zdjęciem, nazwą produktu i ceną

Nasze produkty znakomicie wyświetlają się teraz w aplikacji.

7. Gratulacje!

Nasza aplikacja ma podstawowa procedurę, która przenosi użytkownika z ekranu logowania na ekran główny, na którym można zobaczyć produkty. W zaledwie kilku linijkach kodu dodaliśmy górny pasek aplikacji (z tytułem i 3 przyciskami) oraz karty (prezentujące zawartość aplikacji). Nasz ekran główny jest teraz prosty i funkcjonalny, ma podstawową strukturę i zawiera przydatne treści.

Dalsze kroki

Użyliśmy 4 podstawowych komponentów z biblioteki Material Flutter na górnym pasku aplikacji, karcie, polu tekstowym i przycisku. Więcej informacji znajdziesz w katalogu widżetów komponentów Material Design.

Aplikacja jest w pełni funkcjonalna, ale nie prezentuje jeszcze żadnej marki ani punktu widzenia. W poradniku MDC-103: Material Design Theming with Color, shape, Elevation and Type, dostosujemy styl tych komponentów, aby podkreślić żywą, nowoczesną markę.

Udało mi się ukończyć to ćwiczenia z programowania w rozsądny sposób i w rozsądny sposób

Całkowicie się zgadzam Zgadzam się Nie mam zdania Nie zgadzam się Całkowicie się nie zgadzam

Chcę w przyszłości nadal używać komponentów Material Komponenty

Całkowicie się zgadzam Zgadzam się Nie mam zdania Nie zgadzam się Całkowicie się nie zgadzam
.