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, stworzony przez zespół inżynierów i projektantów UX w Google, obejmuje dziesiątki pięknych i funkcjonalnych komponentów interfejsu. Jest dostępny na Androida, iOS, internet i Flutter.material.io/develop

W tym kursie (w języku angielskim) dowiesz się, jak utworzyć stronę logowania, korzystając z 2 komponentów Material Design: pól tekstowych i przycisków z efektem falowania. 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 – aplikacji e-commerce, 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
  • Siatka
  • Karty

Jak oceniasz swój poziom doświadczenia w programowaniu w Flutterze?

Początkujący Średnio zaawansowany Zaawansowany

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 Android lub iOS podłączone do komputera i ustawione w trybie programisty.
  • Symulator iOS (wymaga zainstalowania narzędzi Xcode).
  • Emulator Androida (wymaga skonfigurowania w Android Studio).
  • Przeglądarka (do debugowania wymagany jest Chrome).
  • jako aplikacja na komputer z systemem Windows, Linux lub macOS; Musisz tworzyć aplikację na platformie, na której planujesz ją wdrożyć. Jeśli więc chcesz tworzyć aplikacje na komputery z systemem Windows, musisz to robić w Windowsie, 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

Czy kontynuujesz na podstawie szkolenia MDC-101?

Jeśli ukończyłeś/ukończyłaś kurs MDC-101, Twój kod powinien być gotowy do użycia w tym ćwiczeniu. Przejdź do kroku: Dodawanie górnego paska aplikacji.

Zaczynasz od zera?

Pobieranie aplikacji startowej codelab

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. Wykonaj instrukcje „Uruchamianie aplikacji” opisane w artykule Pierwsze kroki: jazda testowa 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 już dobrze, wypełnijmy aplikację produktami.

4. Dodawanie paska aplikacji u góry

Jeśli teraz klikniesz przycisk „Dalej”, zobaczysz ekran główny z komunikatem „Gotowe!”. Wspaniale. Teraz użytkownik nie może wykonać żadnych działań ani nie wie, gdzie znajduje się w aplikacji. Aby mu pomóc, dodaj nawigację.

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

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

Dodawanie widżetu AppBar

home.dart dodaj do Scaffold element AppBar i usuń wyróżniony 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

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 nazwą Shrine

pasek aplikacji z nazwą Shrine

Wiele pasków aplikacji ma przycisk obok tytułu. Dodajmy ikonę menu w naszej 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 hamburgera

Ikona menu (inaczej „hamburger”) wyświetli się dokładnie tam, gdzie się spodziewasz.

Możesz też dodać przyciski na końcu tytułu. W Flutterze są one nazywane „działaniami”.

Dodaj działania

Jest miejsce na 2 przyciski IconButton.

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. 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

Teraz, gdy nasza aplikacja ma już pewną strukturę, uporządkujmy zawartość, umieszczając ją na kartach.

Dodawanie widoku siatki

Zacznijmy od dodania jednej karty poniżej górnego paska aplikacji. Widżet Karta nie ma wystarczających informacji, aby umieścić go w widocznych miejscach, dlatego 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. Aby zdefiniować układ, potrzebuje jednak więcej informacji.

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

Pole padding: zapewnia miejsce po wszystkich 4 stronach widoku siatki. Oczywiście nie widać wypełnienia po prawej ani u dołu, ponieważ obok tych elementów nie ma jeszcze żadnych podrzędnych elementów GridView.

Pole childAspectRatio: określa rozmiar elementów na podstawie współczynnika proporcji (szerokość na 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.

Odstęp przesuwa tekst trochę na bok.

Dwa widżety Text są ułożone pionowo z 8 punktami pustego miejsca między nimi (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 produkt z obrazem, tytułem i tekstem dodatkowym

W tym podglądzie widać, że karta jest wpuszczona od krawędzi, ma zaokrąglone rogi i cień (który wyraża jej wypukłość). W Material cały kształt jest nazywany „kontenerem”. (Nie należy mylić z rzeczywistą klasą widżetu o nazwie Container).

Karty zwykle wyświetlają się w kolekcji razem z innymi kartami. Utwórz kolekcję w siatce.

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ą współpłaskie, co oznacza, że mają tę samą wysokość (chyba że zostaną podniesione lub przeciągnięte, ale nie będziemy tego robić).

Powiel kartę, tworząc kolekcję

Obecnie nasza karta jest tworzona 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 w widoku siatki. 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 tekstem dodatkowym.

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

Karty są widoczne, ale nie wyświetlają jeszcze żadnych informacji. Teraz dodaj dane produktów.

Dodawanie danych produktów

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 udostępniliśmy do 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ą

siatki elementów z obrazem, tytułem produktu i ceną;

Zauważ, że nie dodajemy żadnej pionowej przestrzeni między kartami. Dzieje się tak, ponieważ domyślnie mają 4 punkty marginesu u góry i na dole.

Zapisz projekt.

Dane produktu są widoczne, ale wokół zdjęć jest dodatkowa przestrzeń. Obrazy są domyślnie rysowane z użyciem opcji BoxFit (w tym przypadku .scaleDown). 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

siatki elementów z przyciętym obrazem, tytułem produktu i ceną;

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

7. Gratulacje!

Nasza aplikacja ma podstawowy proces, który prowadzi użytkownika od ekranu logowania do ekranu głównego, na którym można przeglądać produkty. W zaledwie kilka linii kodu dodaliśmy górny pasek aplikacji (z tytułem i 3 przyciskami) oraz karty (do prezentacji treści 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: paska aplikacji, karty, pola tekstowego 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ę nadal używać komponentów Material Components

Zdecydowanie się zgadzam Zgadzam się Nie mam zdania Nie zgadzam się Zdecydowanie się nie zgadzam