1. Giới thiệu
| Thành phần Material (MDC) giúp nhà phát triển triển khai Material Design. Được tạo bởi một nhóm kỹ sư và nhà thiết kế trải nghiệm người dùng tại Google, MDC có hàng chục thành phần giao diện người dùng đẹp mắt và có chức năng, đồng thời có sẵn cho Android, iOS, web và Flutter.material.io/develop |
Giờ đây, bạn có thể dùng Material Flutter để tuỳ chỉnh phong cách đặc trưng của ứng dụng hơn bao giờ hết. Việc mở rộng gần đây của Material Design giúp các nhà thiết kế và nhà phát triển có thêm sự linh hoạt để thể hiện thương hiệu của sản phẩm.
Trong các lớp học lập trình MDC-101 và MDC-102,bạn đã dùng Material Flutter để xây dựng các thành phần cơ bản của một ứng dụng có tên là Shrine, một ứng dụng thương mại điện tử bán quần áo và đồ gia dụng. Ứng dụng này có một quy trình dành cho người dùng bắt đầu bằng màn hình đăng nhập, sau đó đưa người dùng đến màn hình chính hiển thị sản phẩm.
Sản phẩm bạn sẽ tạo ra
Trong lớp học lập trình này, bạn sẽ tuỳ chỉnh ứng dụng Shrine bằng cách sử dụng:
- Màu
- Kiểu chữ
- Độ cao
- Hình dạng
- Bố cục
Android | iOS |
|
|
|
|
Các thành phần và hệ thống con Material Flutter trong lớp học lập trình này
- Giao diện
- Kiểu chữ
- Độ cao
- Danh sách hình ảnh
Bạn đánh giá thế nào về mức độ kinh nghiệm của mình trong việc phát triển bằng Flutter?
2. Thiết lập môi trường phát triển Flutter
Bạn cần có 2 phần mềm để hoàn thành bài thực hành này: Flutter SDK và một trình chỉnh sửa.
Bạn có thể chạy lớp học lập trình này bằng bất kỳ thiết bị nào sau đây:
- Một thiết bị Android hoặc iOS thực được kết nối với máy tính và được đặt ở Chế độ nhà phát triển.
- Trình mô phỏng iOS (bạn cần cài đặt các công cụ Xcode).
- Trình mô phỏng Android (cần thiết lập trong Android Studio).
- Một trình duyệt (bạn cần có Chrome để gỡ lỗi).
- Dưới dạng ứng dụng máy tính cho Windows, Linux hoặc macOS. Bạn phải phát triển trên nền tảng mà bạn dự định triển khai. Vì vậy, nếu muốn phát triển một ứng dụng máy tính cho Windows, bạn phải phát triển trên Windows để truy cập vào chuỗi bản dựng thích hợp. Có những yêu cầu cụ thể theo hệ điều hành được đề cập chi tiết trên docs.flutter.dev/desktop.
3. Tải ứng dụng khởi đầu của lớp học lập trình xuống
Tiếp tục từ MDC-102?
Nếu bạn đã hoàn thành MDC-102, thì mã của bạn sẽ sẵn sàng cho lớp học lập trình này. Chuyển đến bước: Thay đổi màu sắc.
Bạn muốn bắt đầu từ đầu?
Tải ứng dụng khởi đầu của lớp học lập trình xuống
Ứng dụng khởi đầu nằm trong thư mục material-components-flutter-codelabs-103-starter_and_102-complete/mdc_100_series.
...hoặc sao chép từ GitHub
Để sao chép lớp học lập trình này từ GitHub, hãy chạy các lệnh sau:
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
Mở dự án và chạy ứng dụng
- Mở dự án trong trình chỉnh sửa mà bạn chọn.
- Làm theo hướng dẫn để "Chạy ứng dụng" trong phần Bắt đầu: Lái thử cho trình chỉnh sửa bạn chọn.
Thành công! Bạn sẽ thấy trang đăng nhập Shrine của các lớp học lập trình trước trên thiết bị của mình.
Android | iOS |
|
|
Nhấp vào "Tiếp theo" để xem trang sản phẩm.
Android | iOS |
|
|
4. Thay đổi màu sắc
Một bảng phối màu đã được tạo để thể hiện thương hiệu Shrine và nhà thiết kế muốn bạn triển khai bảng phối màu đó trên ứng dụng Shrine
Để bắt đầu, hãy nhập những màu đó vào dự án của chúng ta.
Tạo colors.dart
Tạo một tệp dart mới trong lib có tên colors.dart. Nhập material.dart và thêm các giá trị const Color:
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;
Bảng màu tuỳ chỉnh
Chủ đề màu sắc này đã được nhà thiết kế tạo bằng các màu sắc tuỳ chỉnh (như trong hình ảnh bên dưới). Tệp này chứa những màu được chọn từ thương hiệu của Shrine và áp dụng cho Material Theme Editor. Nhờ đó, bảng màu được mở rộng hơn. (Những màu này không có trong bảng màu Material năm 2014.)
Trình chỉnh sửa giao diện Material đã sắp xếp các màu này thành các sắc độ được gắn nhãn bằng số, bao gồm cả nhãn 50, 100, 200, ... đến 900 của mỗi màu. Shrine chỉ sử dụng các sắc độ 50, 100 và 300 trong mẫu màu hồng và 900 trong mẫu màu nâu.

Mỗi tham số màu của một tiện ích được liên kết với một màu trong các bảng phối màu này. Ví dụ: màu cho các thành phần trang trí của trường văn bản khi trường này đang nhận dữ liệu đầu vào phải là màu chính của giao diện. Nếu màu đó không dễ nhìn (dễ thấy trên nền), hãy dùng một màu khác.
Giờ đây, khi đã có các màu muốn dùng, chúng ta có thể áp dụng các màu đó cho giao diện người dùng. Chúng ta sẽ thực hiện việc này bằng cách đặt các giá trị của một tiện ích ThemeData mà chúng ta áp dụng cho phiên bản MaterialApp ở đầu hệ phân cấp tiện ích.
Tuỳ chỉnh ThemeData.light()
Flutter có một số giao diện tích hợp sẵn. Giao diện sáng là một trong số đó. Thay vì tạo một tiện ích ThemeData từ đầu, chúng ta sẽ sao chép giao diện sáng và thay đổi các giá trị để tuỳ chỉnh cho ứng dụng của mình.
Hãy nhập colors.dart vào app.dart.
import 'colors.dart';
Sau đó, hãy thêm nội dung sau vào app.dart bên ngoài phạm vi của lớp 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)
);
}
Bây giờ, hãy đặt theme: ở cuối hàm build() của ShrineApp (trong tiện ích MaterialApp) thành giao diện mới của chúng ta:
// TODO: Customize the theme (103)
theme: _kShrineTheme, // New code
Lưu dự án. Màn hình đăng nhập của bạn hiện sẽ có dạng như sau:
Android | iOS |
|
|
5. Sửa đổi kiểu chữ và kiểu nhãn
Ngoài các thay đổi về màu sắc, nhà thiết kế cũng đã cung cấp cho chúng ta kiểu chữ cụ thể để sử dụng. ThemeData của Flutter có 3 giao diện văn bản. Mỗi chủ đề văn bản là một tập hợp các kiểu văn bản, chẳng hạn như "tiêu đề" và "tên". Chúng ta sẽ sử dụng một số kiểu cho ứng dụng và thay đổi một số giá trị.
Tuỳ chỉnh giao diện văn bản
Để nhập phông chữ vào dự án, bạn phải thêm phông chữ vào tệp pubspec.yaml.
Trong pubspec.yaml, hãy thêm nội dung sau ngay sau thẻ flutter::
# TODO: Insert Fonts (103)
fonts:
- family: Rubik
fonts:
- asset: fonts/Rubik-Regular.ttf
- asset: fonts/Rubik-Medium.ttf
weight: 500
Giờ đây, bạn có thể truy cập và sử dụng phông chữ Rubik.
Khắc phục sự cố về tệp pubspec
Bạn có thể gặp lỗi khi chạy pub get nếu cắt và dán khai báo ở trên. Nếu bạn gặp lỗi, hãy bắt đầu bằng cách xoá khoảng trắng ở đầu và thay thế bằng khoảng trắng bằng cách thụt lề 2 dấu cách. (Hai dấu cách trước
fonts:
, bốn khoảng trắng trước
family: Rubik
, v.v.)
Nếu bạn thấy thông báo Không được phép liên kết các giá trị tại đây, hãy kiểm tra thụt lề của dòng có vấn đề và thụt lề của các dòng phía trên.
Trong login.dart, hãy thay đổi nội dung sau trong Column():
Column(
children: <Widget>[
Image.asset('assets/diamond.png'),
const SizedBox(height: 16.0),
Text(
'SHRINE',
style: Theme.of(context).textTheme.headlineSmall,
),
],
)
Trong app.dart, hãy thêm nội dung sau vào _buildShrineTheme():
// 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,
);
}
Thao tác này sẽ lấy một TextTheme và thay đổi giao diện của tiêu đề, tên và chú thích.
Khi áp dụng fontFamily theo cách này, các thay đổi sẽ chỉ áp dụng cho các giá trị tỷ lệ kiểu chữ được chỉ định trong copyWith() (tiêu đề, tên, chú thích).
Đối với một số phông chữ, chúng tôi đang đặt một fontWeight tuỳ chỉnh, theo gia số 100: w500 (trọng số 500) tương ứng với trung bình và w400 tương ứng với thông thường.
Sử dụng giao diện văn bản mới
Thêm các giao diện sau vào _buildShrineTheme sau lỗi:
// TODO: Add the text themes (103)
textTheme: _buildShrineTextTheme(base.textTheme),
textSelectionTheme: const TextSelectionThemeData(
selectionColor: kShrinePink100,
),
Lưu dự án. Lần này, hãy khởi động lại ứng dụng (còn gọi là Khởi động lại nóng), vì chúng ta đã sửa đổi phông chữ.
Android | iOS |
|
|
Văn bản trên màn hình đăng nhập và màn hình chính trông khác nhau – một số văn bản dùng phông chữ Rubik, còn văn bản khác hiển thị bằng màu nâu thay vì màu đen hoặc trắng. Các biểu tượng cũng hiển thị màu nâu.
Thu nhỏ văn bản
Nhãn quá lớn.
Trong home.dart, hãy thay đổi children: của Cột trong cùng:
// 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
],
Căn giữa và thả văn bản
Chúng ta muốn căn giữa nhãn và căn chỉnh văn bản ở cuối mỗi thẻ, thay vì ở cuối mỗi hình ảnh.
Di chuyển nhãn đến cuối (dưới cùng) của trục chính và thay đổi để chúng được căn giữa::
// TODO: Align labels to the bottom and center (103)
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
Lưu dự án.
Android | iOS |
|
|
Như vậy trông đẹp hơn nhiều.
Tạo giao diện cho các trường văn bản
Bạn cũng có thể tạo giao diện cho phần trang trí trên các trường văn bản bằng InputDecorationTheme.
Trong app.dart, trong phương thức _buildShrineTheme(), hãy chỉ định một giá trị inputDecorationTheme::
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
),
Hiện tại, các trường văn bản có một thành phần trang trí filled. Hãy xoá nội dung đó. Việc xoá filled và chỉ định inputDecorationTheme sẽ giúp các trường văn bản có kiểu đường viền.
Trong login.dart, hãy xoá các giá trị 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,
),
Khởi động lại nóng. Màn hình đăng nhập của bạn sẽ có dạng như sau khi trường Tên người dùng đang hoạt động (khi bạn đang nhập vào trường này):
Android | iOS |
|
|
Nhập vào một trường văn bản – đường viền và nhãn nổi sẽ hiển thị bằng màu chính. Nhưng chúng ta không thể thấy được điều đó một cách dễ dàng. Những người gặp khó khăn trong việc phân biệt các pixel không có độ tương phản màu đủ cao sẽ không thể truy cập vào nội dung này. (Để biết thêm thông tin, hãy xem bài viết về Màu sắc và khả năng hỗ trợ tiếp cận trong Nguyên tắc về Material Design.)
Trong app.dart, hãy chỉ định một focusedBorder: trong inputDecorationTheme: :
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
),
Tiếp theo, hãy chỉ định một floatingLabelStyle: trong inputDecorationTheme: :
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
floatingLabelStyle: TextStyle(
color: kShrineBrown900,
),
),
Cuối cùng, hãy để nút Huỷ dùng màu phụ thay vì màu chính để tăng độ tương phản.
TextButton(
child: const Text('CANCEL'),
onPressed: () {
_usernameController.clear();
_passwordController.clear();
},
style: TextButton.styleFrom(
primary: Theme.of(context).colorScheme.secondary,
),
),
Lưu dự án.
Android | iOS |
|
|
6. Điều chỉnh độ cao
Bây giờ, bạn đã tạo kiểu cho trang bằng màu sắc và kiểu chữ cụ thể phù hợp với Shrine, hãy điều chỉnh độ nâng.
Thay đổi độ nâng của nút TIẾP THEO
Độ cao mặc định của ElevatedButton là 2. Hãy nâng cao mục tiêu đó.
Trong login.dart, hãy thêm giá trị style: vào ElevatedButton TIẾP THEO:
ElevatedButton(
child: const Text('NEXT'),
onPressed: () {
Navigator.pop(context);
},
style: ElevatedButton.styleFrom(
foregroundColor: kShrineBrown900,
backgroundColor: kShrinePink100,
elevation: 8.0,
),
),
Lưu dự án.
Android | iOS |
|
|
Điều chỉnh độ cao của thẻ
Hiện tại, các thẻ nằm trên một bề mặt màu trắng bên cạnh phần điều hướng của trang web.
Trong home.dart, hãy thêm giá trị elevation: vào Thẻ:
// TODO: Adjust card heights (103)
elevation: 0.0,
Lưu dự án.
Android | iOS |
|
|
Bạn đã xoá bóng đổ bên dưới các thẻ.
7. Thêm hình dạng
Ngôi đền có phong cách hình học độc đáo, xác định các phần tử bằng hình bát giác hoặc hình chữ nhật. Hãy triển khai kiểu dáng đó trong các thẻ trên màn hình chính, cũng như các trường văn bản và nút trên màn hình đăng nhập.
Thay đổi hình dạng của trường văn bản trên màn hình đăng nhập
Trong app.dart, hãy nhập tệp sau:
import 'supplemental/cut_corners_border.dart';
Vẫn trong app.dart, hãy sửa đổi giao diện trang trí trường văn bản để sử dụng đường viền góc cắt:
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: CutCornersBorder(),
focusedBorder: CutCornersBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
floatingLabelStyle: TextStyle(
color: kShrineBrown900,
),
),
Thay đổi hình dạng nút trên màn hình đăng nhập
Trong login.dart, hãy thêm một đường viền hình chữ nhật vát cho nút CANCEL:
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)),
),
),
),
TextButton không có hình dạng nào hiển thị, vậy tại sao lại thêm một hình dạng đường viền? Vì vậy, ảnh động gợn sóng sẽ liên kết với cùng một hình dạng khi được chạm vào.
Bây giờ, hãy thêm cùng một hình dạng vào nút TIẾP THEO:
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)),
),
),
),
Để thay đổi hình dạng của tất cả các nút, chúng ta cũng có thể sử dụng elevatedButtonTheme hoặc textButtonTheme trong app.dart. Đây là một thử thách dành cho người học!
Khởi động lại nóng.
Android | iOS |
|
|
8. Thay đổi bố cục
Tiếp theo, hãy thay đổi bố cục để hiển thị các thẻ ở nhiều tỷ lệ khung hình và kích thước, sao cho mỗi thẻ trông khác biệt với các thẻ khác.
Thay thế GridView bằng AsymmetricView
Chúng ta đã viết các tệp cho bố cục không đối xứng.
Trong home.dart, hãy thêm nội dung nhập sau:
import 'supplemental/asymmetric_view.dart';
Xoá _buildGridCards và thay thế bằng body:
body: AsymmetricView(
products: ProductsRepository.loadProducts(Category.all),
),
Lưu dự án.
Android | iOS |
|
|
Giờ đây, các sản phẩm sẽ cuộn theo chiều ngang theo một mẫu lấy cảm hứng từ kiểu dệt.
9. Thử dùng một chủ đề khác (Không bắt buộc)
Màu sắc là một cách hiệu quả để thể hiện thương hiệu của bạn, và một thay đổi nhỏ về màu sắc có thể ảnh hưởng lớn đến trải nghiệm người dùng. Để thử nghiệm, hãy xem giao diện của Shrine sẽ như thế nào nếu bảng phối màu của thương hiệu có chút thay đổi.
Sửa đổi màu sắc
Trong colors.dart, hãy thêm màu sau:
const kShrinePurple = Color(0xFF5D1049);
Trong app.dart, hãy thay đổi hàm _buildShrineTheme() thành hàm sau:
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,
),
),
);
}
Khởi động lại nóng. Giờ đây, giao diện mới sẽ xuất hiện.
Android | iOS |
|
|
Android | iOS |
|
|
Kết quả rất khác nhau! Hãy hoàn nguyên app.dart's _buildShrineTheme về trạng thái trước bước này. Hoặc tải mã khởi đầu của 104 xuống.
10. Xin chúc mừng!
Đến đây, bạn đã tạo một ứng dụng giống với các quy cách thiết kế của nhà thiết kế.
Các bước tiếp theo
Giờ đây, bạn đã sử dụng các thành phần Material Flutter sau đây: giao diện, kiểu chữ, độ cao và hình dạng. Bạn có thể khám phá thêm các thành phần và hệ thống con trong thư viện Material Flutter.
Khám phá các tệp trong thư mục supplemental để tìm hiểu cách chúng tôi tạo lưới bố cục bất đối xứng có thể cuộn theo chiều ngang.
Nếu thiết kế ứng dụng mà bạn dự định có chứa những phần tử không có thành phần trong thư viện thì sao? Trong MDC-104: Thành phần Material nâng cao, chúng tôi trình bày cách tạo các thành phần tuỳ chỉnh bằng thư viện Material Flutter để đạt được giao diện mong muốn.


























