MDC-103 Flutter: dopasowywanie motywu, kolor, kształt, wysokość i typ

1. Wprowadzenie

logo_components_color_2x_web_96dp.png

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-101MDC-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

Strona logowania do świątyni w odcieniach brązu i różu

Strona logowania do świątyni w odcieniach brązu i różu

Strona produktu Shrine z górnym paskiem aplikacji i asymetryczną, przewijaną w poziomie siatką pełną produktów w różowych odcieniach

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?

Początkujący Średnio zaawansowany Zaawansowany

2. Konfigurowanie środowiska programistycznego Fluttera

Aby ukończyć ten moduł, potrzebujesz 2 programów: pakietu SDK Flutteredytora.

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ę

  1. Otwórz projekt w wybranym edytorze.
  2. 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

strona logowania Shrine bez motywu,

strona logowania Shrine bez motywu,

Kliknij „Dalej”, aby wyświetlić stronę produktu.

Android

iOS

strona z siatką produktów Shrine bez motywu,

strona z siatką produktów Shrine bez motywu,

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.

d0362cb45c565a8e.jpeg 470b0e1c2669ae2.png

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

strona logowania do Shrine w różowo-brązowej kolorystyce,

strona logowania do Shrine w różowo-brązowej kolorystyce,

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

Strona siatki produktów Shrine z zastosowanymi motywami tekstowymi

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

Strona z siatką produktów w świątyni z różnym wyrównaniem tekstu

Strona z siatką produktów w świątyni z różnym wyrównaniem tekstu

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

Strona logowania do Shrine z polem nazwy użytkownika

Strona logowania do Shrine z polem nazwy użytkownika

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

Strona logowania w usłudze Shrine z dostępnym przyciskiem ANULUJ

Strona logowania w usłudze Shrine z dostępnym przyciskiem ANULUJ

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.

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

Strona logowania do świątyni z podświetlonym przyciskiem DALEJ

Strona logowania do świątyni z podświetlonym przyciskiem DALEJ

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

Strona z siatką produktów Shrine bez podniesienia dla każdej karty

Strona z siatką produktów Shrine bez podniesienia dla każdej karty

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';

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

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 textButtonThemeapp.dart. To pozostawiamy jako wyzwanie dla ucznia.

Uruchomienie z pamięci.

Android

iOS

Strona logowania w aplikacji Shrine z zastosowanym motywem kształtów

Strona logowania w aplikacji Shrine z zastosowanym motywem kształtów

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

Strona produktu Shrine z asymetrycznym układem z możliwością przewijania w poziomie

Strona produktu Shrine z asymetrycznym układem z możliwością przewijania w poziomie

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

Strona logowania do świątyni z motywem w odcieniach fioletu i różu

Strona logowania do świątyni z motywem w odcieniach fioletu i różu

Android

iOS

Strona produktu Shrine z motywem różowym

Strona produktu Shrine z motywem różowym

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.

Udało mi się ukończyć to ćwiczenie w rozsądnym czasie i przy rozsądnym nakładzie pracy.

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

Chcę nadal korzystać z komponentów Material

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