1. Wprowadzenie
| Komponenty Material (MDC) pomagają deweloperom wdrażać Material Design. MDC zostało stworzone przez zespół inżynierów i projektantów UX w Google. Zawiera dziesiątki atrakcyjnych i funkcjonalnych komponentów interfejsu, które są dostępne na platformach Android, iOS, internetowej i Flutter.material.io/develop |
W kursie MDC-101 do utworzenia strony logowania użyto 2 komponentów Material: pól tekstowych i przycisków z efektem rozchodzenia się fali atramentu. Teraz rozbudujmy tę podstawę, dodając nawigację, strukturę i dane.
Co utworzysz
W tym laboratorium kodowania utworzysz ekran główny aplikacji Shrine, czyli aplikacji do handlu elektronicznego, w której sprzedawane są ubrania i artykuły gospodarstwa domowego. Będzie ona zawierać:
- górny pasek aplikacji,
- Lista produktów w formie siatki
Android | iOS |
|
|
Komponenty i podsystemy Material Flutter w tym laboratorium
- Górny pasek aplikacji
- Siatki
- karty;
Jak oceniasz swój poziom doświadczenia w programowaniu w Flutterze?
2. Konfigurowanie środowiska programistycznego Fluttera
Aby ukończyć ten moduł, potrzebujesz 2 programów: pakietu SDK Flutter i edytora.
Codelab możesz uruchomić na dowolnym z tych urządzeń:
- fizyczne urządzenie z Android lub iOS podłączone do komputera i ustawione w trybie deweloperskim;
- Symulator iOS (wymaga zainstalowania narzędzi Xcode).
- Android Emulator (wymaga konfiguracji w Android Studio).
- przeglądarka (do debugowania wymagana jest Chrome);
- Jako aplikacja komputerowa na Windows, Linux lub macOS. Musisz tworzyć aplikację na platformie, na której zamierzasz ją wdrożyć. Jeśli chcesz opracować aplikację na komputery z systemem Windows, musisz to zrobić na komputerze z tym systemem, aby mieć dostęp do odpowiedniego łańcucha kompilacji. Istnieją wymagania dotyczące poszczególnych systemów operacyjnych, które są szczegółowo opisane na stronie docs.flutter.dev/desktop.
3. Pobierz aplikację startową do ćwiczeń z programowania
Kontynuujesz naukę z MDC-101?
Jeśli masz już za sobą MDC-101, kod powinien być gotowy do tego ćwiczenia. Przejdź do kroku: Dodaj górny pasek aplikacji.
Zaczynasz od zera?
Pobieranie aplikacji do ćwiczeń z programowania
Aplikacja startowa znajduje się w katalogu material-components-flutter-codelabs-102-starter_and_101-complete/mdc_100_series.
...lub sklonuj go z GitHub
Aby skopiować ten codelab 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
Otwórz projekt i uruchom aplikację
- Otwórz projekt w wybranym edytorze.
- Postępuj zgodnie z instrukcjami w sekcji „Uruchamianie aplikacji” w artykule Wprowadzenie: testowanie dotyczącym wybranego edytora.
Gotowe! Na urządzeniu powinna pojawić się strona logowania Shrine z samouczka MDC-101.
Android | iOS |
|
|
Ekran logowania wygląda już dobrze, więc teraz wypełnimy aplikację produktami.
4. Dodawanie górnego paska aplikacji
Jeśli teraz klikniesz przycisk „Dalej”, zobaczysz ekran główny z napisem „Udało Ci się!”. Wspaniale. Ale teraz użytkownik nie może podjąć żadnych działań ani nie wie, gdzie w aplikacji się znajduje. Aby mu pomóc, musimy dodać nawigację.
Material Design oferuje wzorce nawigacji, które zapewniają wysoki poziom użyteczności. Jednym z najbardziej widocznych elementów jest górny pasek aplikacji.
Aby zapewnić nawigację i szybki dostęp do innych działań, dodajmy górny pasek aplikacji.
Dodawanie widżetu AppBar
W home.dart dodaj AppBar do Scaffold i usuń wyróżniony element const:
return const Scaffold(
// TODO: Add app bar (102)
appBar: AppBar(
// TODO: Add buttons and title (102)
),
Dodanie elementu AppBar do pola appBar: elementu Scaffold zapewnia nam idealny układ bez dodatkowych kosztów, ponieważ element AppBar znajduje się u góry strony, a treść pod nim.
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 |
|
|
Wiele pasków aplikacji ma przycisk obok tytułu. Dodajmy do aplikacji ikonę menu.
Dodawanie ikony IconButton na początku
W sekcji home.dart ustaw IconButton dla pola leading: AppBar. (Umieść go przed polem title:, aby zachować kolejność od lewej do prawej):
// TODO: Add buttons and title (102)
leading: IconButton(
icon: const Icon(
Icons.menu,
semanticLabel: 'menu',
),
onPressed: () {
print('Menu button');
},
),
Zapisz projekt.
Android | iOS |
|
|
Ikona menu (tzw. „hamburger”) pojawia się w oczekiwanym miejscu.
Możesz też dodać przyciski po prawej stronie tytułu. W Flutterze są one nazywane „działaniami”.
Dodawanie działań
Możesz dodać jeszcze 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 |
|
|
Aplikacja ma teraz przycisk wiodący, tytuł i 2 działania po prawej stronie. Pasek aplikacji wyświetla też wysokość za pomocą subtelnego cienia, który pokazuje, że znajduje się on na innej warstwie niż treść.
5. Dodawanie karty w siatce
Aplikacja ma już pewną strukturę, więc uporządkujmy treści, umieszczając je na kartach.
Dodawanie widoku GridView
Zacznijmy od dodania jednej karty pod górnym paskiem aplikacji. Sam widżet Card nie ma wystarczających informacji, aby wyświetlić się w widocznym miejscu, więc umieścimy go w widżecie GridView.
Zastąp element Center w treści elementu Scaffold elementem 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()],
),
Przeanalizujmy ten kod. Widok GridView wywołuje konstruktor count(), ponieważ liczba wyświetlanych przez niego elementów jest policzalna, a nie nieskończona. Potrzebuje jednak więcej informacji, aby określić układ.
Wartość crossAxisCount: określa liczbę elementów w poprzek. Chcemy mieć 2 kolumny.
Pole padding: zapewnia miejsce po wszystkich 4 stronach widoku GridView. Oczywiście nie widać dopełnienia po prawej stronie ani u dołu, ponieważ nie ma jeszcze elementów podrzędnych widoku GridView.
Pole childAspectRatio: określa rozmiar produktów na podstawie współczynnika proporcji (szerokość do wysokości).
Domyślnie widok GridView tworzy kafelki o jednakowym rozmiarze.
Mamy jedną kartę, ale jest ona pusta. Dodajmy widżety podrzędne do naszej karty.
Rozmieszczenie treści
Karty powinny zawierać obszary na obraz, tytuł i tekst dodatkowy.
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 Column, który służy do pionowego rozmieszczania widżetów podrzędnych.
Znak crossAxisAlignment: field określa CrossAxisAlignment.start, co oznacza „wyrównaj tekst do krawędzi początkowej”.
Widżet AspectRatio określa kształt obrazu niezależnie od tego, jaki obraz jest dostarczany.
Wypełnienie nieco przesuwa tekst z boku.
Dwa widżety Text są ułożone pionowo, a między nimi jest 8 punktów pustego miejsca (SizedBox). Tworzymy kolejną kolumnę, aby umieścić w niej elementy w ramach dopełnienia.
Zapisz projekt.
Android | iOS |
|
|
W tym podglądzie widać, że karta jest odsunięta od krawędzi, ma zaokrąglone rogi i cień (który wskazuje wysokość karty). Cały kształt w Material Design nazywa się „kontenerem”. (Nie myl tego z rzeczywistą klasą widżetu o nazwie Container).
Karty są zwykle wyświetlane w kolekcji z innymi kartami. Ułóżmy je w kolekcję w siatce.
6. Tworzenie kolekcji kart
Gdy na ekranie znajduje się kilka kart, są one grupowane w 1 lub więcej kolekcji. Karty w kolekcji są współpłaszczyznowe, co oznacza, że znajdują się na tej samej wysokości (chyba że zostaną podniesione lub przeciągnięte, ale w tym przypadku nie będziemy tego robić).
Powielanie karty w kolekcji
Obecnie karta jest tworzona w wierszu pola children: elementu GridView. To dużo zagnieżdżonego kodu, który może być trudny do odczytania. Wyodrębnijmy ją do funkcji, która może generować dowolną liczbę pustych kart i zwracać listę kart.
Utwórz nową funkcję prywatną powyżej funkcji build() (pamiętaj, że funkcje zaczynają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ć tym nowym kodem wszystko, co znajduje się w widoku 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
),
Zapisz projekt.
Android | iOS |
|
|
Karty są widoczne, ale jeszcze nic nie wyświetlają. Teraz możesz dodać dane produktów.
Dodawanie danych produktów
Aplikacja zawiera produkty ze zdjęciami, nazwami i cenami. Dodajmy go do widżetów, które już mamy na karcie.
Następnie w home.dart zaimportuj nowy pakiet i niektóre pliki, które udostępniliś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ń _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: nie zostanie jeszcze skompilowany ani uruchomiony. Mamy jeszcze jedną zmianę.
Zmień też funkcję build(), aby przekazywała BuildContext do _buildGridCards(), zanim spróbujesz skompilować kod:
// 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ę.
Android | iOS |
|
|
Zauważ, że nie dodajemy żadnych spacji pionowych między kartami. Domyślnie mają one 4 punkty marginesu u góry i u dołu.
Zapisz projekt.
Dane o produkcie są wyświetlane, ale wokół zdjęć jest dodatkowe miejsce. Obrazy są domyślnie rysowane z wartością BoxFit równą .scaleDown (w tym przypadku). Zmieńmy to na .fitWidth, aby nieco powiększyć obraz i usunąć dodatkowe odstępy.
Dodaj do obrazu pole fit: o wartości BoxFit.fitWidth:
// TODO: Adjust the box size (102)
fit: BoxFit.fitWidth,
Android | iOS |
|
|
Nasze produkty wyświetlają się teraz w aplikacji bez zarzutu.
7. Gratulacje!
Nasza aplikacja ma podstawowy proces, który prowadzi użytkownika od ekranu logowania do ekranu głównego, na którym można wyświetlać produkty. W kilku wierszach kodu dodaliśmy górny pasek aplikacji (z tytułem i 3 przyciskami) oraz karty (do prezentowania treści aplikacji). Ekran główny jest teraz prosty i funkcjonalny, ma podstawową strukturę i zawiera treści, które można wykorzystać.
Dalsze kroki
Użyliśmy już 4 podstawowych komponentów z biblioteki Material Flutter: górnego paska aplikacji, karty, pola tekstowego i przycisku. Więcej informacji znajdziesz w katalogu widżetów komponentów Material.
Aplikacja jest w pełni funkcjonalna, ale nie wyraża jeszcze żadnej konkretnej marki ani punktu widzenia. W module MDC-103: Material Design Theming with Color, Shape, Elevation and Type dostosujemy styl tych komponentów, aby odzwierciedlały żywą, nowoczesną markę.
















