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 |
Dzięki Material Flutter możesz teraz bardziej niż kiedykolwiek dostosowywać charakterystyczny styl aplikacji. Niedawne rozszerzenie Material Design zapewnia projektantom i programistom większą elastyczność w zakresie wyrażania marki produktu.
W ćwiczeniach MDC-101 i MDC-102 używasz Material Flutter do tworzenia podstaw aplikacji o nazwie Shrine, czyli aplikacji e-commerce do sprzedaży odzieży i artykułów gospodarstwa domowego. Aplikacja zawiera ścieżkę użytkownika, która zaczyna się od ekranu logowania, a następnie prowadzi użytkownika do ekranu głównego, na którym wyświetlane są produkty.
Co utworzysz
W tym ćwiczeniu dostosujesz aplikację Shrine za pomocą:
- Kolor
- Typografia
- Wysokość
- Kształt
- Układ
Android | iOS |
|
|
|
|
Komponenty i podsystemy Material Flutter w tym laboratorium
- Motywy
- Typografia
- Wysokość
- Lista obrazów
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-102?
Jeśli udało Ci się ukończyć MDC-102, kod powinien być gotowy do użycia w tym ćwiczeniu. Przejdź do kroku: Zmień kolory.
Zaczynasz od zera?
Pobierz aplikację Codelabs
Aplikacja startowa znajduje się w katalogu material-components-flutter-codelabs-103-starter_and_102-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 103-starter_and_102-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 być widoczna strona logowania Shrine z poprzednich ćwiczeń.
Android | iOS |
|
|
Kliknij „Dalej”, aby wyświetlić stronę produktu.
Android | iOS |
|
|
4. Zmiana kolorów
Utworzono schemat kolorów reprezentujący markę Shrine, a projektant chce, abyś wdrożył ten schemat kolorów w aplikacji Shrine.
Na początek zaimportujmy te kolory do projektu.
Utwórz colors.dart
Utwórz nowy plik Dart w folderze lib o nazwie colors.dart. Zaimportuj material.dart i dodaj const Color wartości:
import 'package:flutter/material.dart';
const kShrinePink50 = Color(0xFFFEEAE6);
const kShrinePink100 = Color(0xFFFEDBD0);
const kShrinePink300 = Color(0xFFFBB8AC);
const kShrinePink400 = Color(0xFFEAA4A4);
const kShrineBrown900 = Color(0xFF442B2D);
const kShrineErrorRed = Color(0xFFC5032B);
const kShrineSurfaceWhite = Color(0xFFFFFBFA);
const kShrineBackgroundWhite = Colors.white;
Niestandardowa paleta kolorów
Ten motyw kolorystyczny został utworzony przez projektanta z użyciem spersonalizowanych kolorów (widocznych na obrazie poniżej). Zawiera kolory wybrane z marki Shrine i zastosowane w Material Theme Editor, który rozszerzył je, aby utworzyć pełniejszą paletę. (Te kolory nie pochodzą z palet kolorów Material z 2014 roku).
Edytor motywów Material uporządkował je w odcienie oznaczone numerami, w tym etykietami 50, 100, 200 … do 900 dla każdego koloru. Shrine używa tylko odcieni 50, 100 i 300 z różowej palety oraz odcienia 900 z brązowej palety.

Każdy kolorowy parametr widżetu jest mapowany na kolor z tych schematów. Na przykład kolor dekoracji pola tekstowego, gdy aktywnie przyjmuje ono dane wejściowe, powinien być kolorem podstawowym motywu. Jeśli ten kolor nie jest dostępny (trudno go odróżnić od tła), użyj innego.
Teraz, gdy mamy już kolory, których chcemy użyć, możemy zastosować je w interfejsie. Zrobimy to, ustawiając wartości widżetu ThemeData, który zastosujemy do instancji MaterialApp na górze hierarchii widżetów.
Dostosowywanie ThemeData.light()
Flutter zawiera kilka wbudowanych motywów. Jednym z nich jest jasny motyw. Zamiast tworzyć widżet ThemeData od zera, skopiujemy jasny motyw i zmienimy wartości, aby dostosować je do naszej aplikacji.
Zaimportuj colors.dart w app.dart.
import 'colors.dart';
Następnie dodaj do pliku app.dart te elementy poza zakresem klasy ShrineApp:
// TODO: Build a Shrine Theme (103)
final ThemeData _kShrineTheme = _buildShrineTheme();
ThemeData _buildShrineTheme() {
final ThemeData base = ThemeData.light(useMaterial3: true);
return base.copyWith(
colorScheme: base.colorScheme.copyWith(
primary: kShrinePink100,
onPrimary: kShrineBrown900,
secondary: kShrineBrown900,
error: kShrineErrorRed,
),
// TODO: Add the text themes (103)
// TODO: Decorate the inputs (103)
);
}
Teraz ustaw theme: na końcu funkcji build() w aplikacji ShrineApp (w widżecie MaterialApp) jako nowy motyw:
// TODO: Customize the theme (103)
theme: _kShrineTheme, // New code
Zapisz projekt. Ekran logowania powinien teraz wyglądać tak:
Android | iOS |
|
|
5. Modyfikowanie typografii i stylów etykiet
Oprócz zmian kolorów projektant podał też konkretną typografię, której mamy używać. Motyw ThemeData w Flutterze zawiera 3 motywy tekstu. Każdy motyw tekstowy to zbiór stylów tekstu, takich jak „nagłówek” i „tytuł”. Użyjemy w aplikacji kilku stylów i zmienimy niektóre wartości.
Dostosowywanie motywu tekstu
Aby zaimportować czcionki do projektu, musisz dodać je do pliku pubspec.yaml.
W pliku pubspec.yaml dodaj ten kod bezpośrednio po tagu flutter::
# TODO: Insert Fonts (103)
fonts:
- family: Rubik
fonts:
- asset: fonts/Rubik-Regular.ttf
- asset: fonts/Rubik-Medium.ttf
weight: 500
Teraz możesz uzyskać dostęp do czcionki Rubik i jej używać.
Rozwiązywanie problemów z plikiem pubspec
Jeśli skopiujesz i wkleisz powyższą deklarację, podczas uruchamiania polecenia pub get mogą wystąpić błędy. Jeśli pojawią się błędy, zacznij od usunięcia początkowych białych znaków i zastąpienia ich spacjami z 2-znakowym wcięciem. (Dwa spacje przed
fonts:
, cztery spacje przed
family: Rubik
itd.).
Jeśli zobaczysz komunikat Mapping values are not allowed here (Mapowanie wartości jest tutaj niedozwolone), sprawdź wcięcie w wierszu, w którym występuje problem, oraz w wierszach powyżej.
W polu login.dart zmień te wartości w Column():
Column(
children: <Widget>[
Image.asset('assets/diamond.png'),
const SizedBox(height: 16.0),
Text(
'SHRINE',
style: Theme.of(context).textTheme.headlineSmall,
),
],
)
W polu app.dart dodaj po polu _buildShrineTheme() te informacje:
// TODO: Build a Shrine Text Theme (103)
TextTheme _buildShrineTextTheme(TextTheme base) {
return base
.copyWith(
headlineSmall: base.headlineSmall!.copyWith(
fontWeight: FontWeight.w500,
),
titleLarge: base.titleLarge!.copyWith(
fontSize: 18.0,
),
bodySmall: base.bodySmall!.copyWith(
fontWeight: FontWeight.w400,
fontSize: 14.0,
),
bodyLarge: base.bodyLarge!.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16.0,
),
)
.apply(
fontFamily: 'Rubik',
displayColor: kShrineBrown900,
bodyColor: kShrineBrown900,
);
}
Pobiera TextTheme i zmienia wygląd nagłówków, tytułów i napisów.
Zastosowanie fontFamily w ten sposób powoduje wprowadzenie zmian tylko w wartościach skali typograficznej określonych w copyWith() (nagłówek, tytuł, podpis).
W przypadku niektórych czcionek ustawiamy niestandardową grubość czcionki w przyrostach co 100: w500 (grubość 500) odpowiada średniej, a w400 – zwykłej.
Używanie nowych tematów
Dodaj te tematy do _buildShrineTheme po wystąpieniu błędu:
// TODO: Add the text themes (103)
textTheme: _buildShrineTextTheme(base.textTheme),
textSelectionTheme: const TextSelectionThemeData(
selectionColor: kShrinePink100,
),
Zapisz projekt. Tym razem uruchom ponownie aplikację (tzw. szybkie ponowne uruchomienie), ponieważ zmodyfikowaliśmy czcionki.
Android | iOS |
|
|
Tekst na ekranie logowania i ekranie głównym wygląda inaczej – część tekstu jest napisana czcionką Rubik, a część jest wyświetlana na brązowo zamiast na czarno lub biało. Ikony też są renderowane na brązowo.
Zmniejszanie tekstu
Etykiety są za duże.
W sekcji home.dart zmień children: najbardziej wewnętrznej kolumny:
// TODO: Change innermost Column (103)
children: <Widget>[
// TODO: Handle overflowing labels (103)
Text(
product.name,
style: theme.textTheme.button,
softWrap: false,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
const SizedBox(height: 4.0),
Text(
formatter.format(product.price),
style: theme.textTheme.bodySmall,
),
// End new code
],
Wyśrodkuj i upuść tekst
Chcemy wyśrodkować etykiety i wyrównać tekst do dołu każdej karty, a nie do dołu każdego obrazu.
Przenieś etykiety na koniec (dół) osi głównej i wyśrodkuj je:
// TODO: Align labels to the bottom and center (103)
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
Zapisz projekt.
Android | iOS |
|
|
Wygląda o wiele lepiej.
Nadawanie motywu polom tekstowym
Możesz też zastosować motyw dekoracji w polach tekstowych za pomocą InputDecorationTheme.
W polu app.dart w metodzie _buildShrineTheme() podaj wartość inputDecorationTheme::
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
),
Obecnie pola tekstowe mają dekorację filled. Usuńmy to. Usunięcie filled i określenie inputDecorationTheme spowoduje, że pola tekstowe będą miały styl konturu.
W polu login.dart usuń wartości filled: true:
// Remove filled: true values (103)
TextField(
controller: _usernameController,
decoration: const InputDecoration(
// Removed filled: true
labelText: 'Username',
),
),
const SizedBox(height: 12.0),
TextField(
controller: _passwordController,
decoration: const InputDecoration(
// Removed filled: true
labelText: 'Password',
),
obscureText: true,
),
Uruchomienie z pamięci. Ekran logowania powinien wyglądać tak, gdy pole Nazwa użytkownika jest aktywne (gdy w nim piszesz):
Android | iOS |
|
|
Wpisz tekst w polu tekstowym – obramowania i etykiety pływające będą wyświetlane w kolorze podstawowym. Ale nie możemy go łatwo zobaczyć. Nie jest dostępna dla osób, które mają problemy z rozróżnianiem pikseli o niewystarczającym kontraście kolorów. (Więcej informacji znajdziesz w artykule Kolor i ułatwienia dostępu w wytycznych Material Design).
W sekcji app.dart podaj focusedBorder: w sekcji inputDecorationTheme: :
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
),
Następnie w sekcji inputDecorationTheme: podaj floatingLabelStyle: :
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
floatingLabelStyle: TextStyle(
color: kShrineBrown900,
),
),
Na koniec ustawmy kolor drugorzędny zamiast podstawowego dla przycisku Anuluj, aby zwiększyć kontrast.
TextButton(
child: const Text('CANCEL'),
onPressed: () {
_usernameController.clear();
_passwordController.clear();
},
style: TextButton.styleFrom(
primary: Theme.of(context).colorScheme.secondary,
),
),
Zapisz projekt.
Android | iOS |
|
|
6. Dostosowywanie wysokości
Po nadaniu stronie stylu z określonymi kolorami i typografią, które pasują do Shrine, dostosujmy wysokość.
Zmiana wysokości przycisku DALEJ
Domyślna wysokość elementu ElevatedButton to 2. Podnieśmy ją wyżej.
W login.dart dodaj wartość style: do przycisku DALEJ:
ElevatedButton(
child: const Text('NEXT'),
onPressed: () {
Navigator.pop(context);
},
style: ElevatedButton.styleFrom(
foregroundColor: kShrineBrown900,
backgroundColor: kShrinePink100,
elevation: 8.0,
),
),
Zapisz projekt.
Android | iOS |
|
|
Dostosowywanie podniesienia karty
Obecnie karty leżą na białej powierzchni obok nawigacji witryny.
W sekcji home.dart dodaj wartość elevation: do sekcji Cards:
// TODO: Adjust card heights (103)
elevation: 0.0,
Zapisz projekt.
Android | iOS |
|
|
Cień pod kartami został usunięty.
7. Dodawanie kształtu
Shrine ma nowoczesny geometryczny styl, a elementy są w kształcie ośmiokąta lub prostokąta. Zastosujmy ten styl kształtu w przypadku kart na ekranie głównym oraz pól tekstowych i przycisków na ekranie logowania.
Zmienianie kształtów pól tekstowych na ekranie logowania
W app.dart zaimportuj ten plik:
import 'supplemental/cut_corners_border.dart';
W app.dart zmień motyw dekoracji pola tekstowego, aby używać obramowania z ściętymi rogami:
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: CutCornersBorder(),
focusedBorder: CutCornersBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
floatingLabelStyle: TextStyle(
color: kShrineBrown900,
),
),
Zmienianie kształtów przycisków na ekranie logowania
W login.dart dodaj fazowane prostokątne obramowanie przycisku ANULUJ:
TextButton(
child: const Text('CANCEL'),
onPressed: () {
_usernameController.clear();
_passwordController.clear();
},
style: TextButton.styleFrom(
foregroundColor: kShrineBrown900,
shape: const BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0)),
),
),
),
Przycisk TextButton nie ma widocznego kształtu, więc po co dodawać kształt obramowania? Animacja fali jest więc powiązana z tym samym kształtem, gdy go dotkniesz.
Teraz dodaj ten sam kształt do przycisku DALEJ:
ElevatedButton(
child: const Text('NEXT'),
onPressed: () {
Navigator.pop(context);
},
style: ElevatedButton.styleFrom(
foregroundColor: kShrineBrown900,
backgroundColor: kShrinePink100,
elevation: 8.0,
shape: const BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0)),
),
),
),
Aby zmienić kształt wszystkich przycisków, możemy też użyć symboli elevatedButtonTheme lub textButtonTheme w app.dart. To pozostawiamy jako wyzwanie dla ucznia.
Uruchomienie z pamięci.
Android | iOS |
|
|
8. Zmiana układu
Następnie zmieńmy układ, aby karty miały różne współczynniki proporcji i rozmiary, dzięki czemu każda z nich będzie wyglądać inaczej.
Zastąpienie widoku GridView widokiem AsymmetricView
Pliki dla układu asymetrycznego zostały już przez nas utworzone.
W pliku home.dart dodaj ten import:
import 'supplemental/asymmetric_view.dart';
Usuń _buildGridCards i zastąp body:
body: AsymmetricView(
products: ProductsRepository.loadProducts(Category.all),
),
Zapisz projekt.
Android | iOS |
|
|
Produkty przewijają się teraz w poziomie w wzorze przypominającym splot.
9. Wypróbuj inny motyw (opcjonalnie)
Kolor to skuteczny sposób na wyrażenie tożsamości marki, a niewielka zmiana koloru może mieć duży wpływ na wrażenia użytkowników. Aby to sprawdzić, zobaczmy, jak wyglądałaby witryna Shrine, gdyby schemat kolorów marki był nieco inny.
Modyfikowanie kolorów
W polu colors.dart dodaj ten kolor:
const kShrinePurple = Color(0xFF5D1049);
W polu app.dart zmień funkcję _buildShrineTheme() na tę:
ThemeData _buildShrineTheme() {
final ThemeData base = ThemeData.light();
return base.copyWith(
colorScheme: base.colorScheme.copyWith(
primary: kShrinePurple,
secondary: kShrinePurple,
error: kShrineErrorRed,
),
scaffoldBackgroundColor: kShrineSurfaceWhite,
textSelectionTheme: const TextSelectionThemeData(
selectionColor: kShrinePurple,
),
appBarTheme: const AppBarTheme(
foregroundColor: kShrineBrown900,
backgroundColor: kShrinePink100,
),
inputDecorationTheme: const InputDecorationTheme(
border: CutCornersBorder(),
focusedBorder: CutCornersBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrinePurple,
),
),
floatingLabelStyle: TextStyle(
color: kShrinePurple,
),
),
);
}
Uruchomienie z pamięci. Nowy motyw powinien się teraz wyświetlić.
Android | iOS |
|
|
Android | iOS |
|
|
Wynik jest zupełnie inny. Przywróćmy app.dart's _buildShrineTheme do stanu sprzed tego kroku. Możesz też pobrać kod startowy do ćwiczenia 104.
10. Gratulacje!
Masz już utworzoną aplikację, która jest zgodna ze specyfikacją projektu od projektanta.
Dalsze kroki
Używasz teraz tych elementów Material Flutter: motyw, typografia, cień i kształt. Więcej komponentów i podsystemów znajdziesz w bibliotece Material Flutter.
Przejrzyj pliki w katalogu supplemental, aby dowiedzieć się, jak utworzyliśmy asymetryczną siatkę układu z przewijaniem w poziomie.
Co zrobić, jeśli planowany projekt aplikacji zawiera elementy, które nie mają komponentów w bibliotece? W module MDC-104: Zaawansowane komponenty Material pokazujemy, jak tworzyć komponenty niestandardowe za pomocą biblioteki Material Flutter, aby uzyskać pożądany wygląd.


























