Professional Documents
Culture Documents
4
IT3100 – Lập trình hướng đối tượng
1.1 Giới thiệu Flutter
Đang có nhiều công ty phát triển các ứng dụng của mình
bằng Flutter. Có thể kể đến như là: Google, Alibaba,
Tencent,….
Dart có mục tiêu tổng hợp các lợi ích của hầu hết các
ngôn ngữ cấp cao với các tính năng ngôn ngữ, bao gồm
các tính năng sau:
- Công cụ hiệu quả (productive tooling): Bao gồm các
công cụ để phân tích mã, phát triển tích hợp các plugin
môi trường (IDE) và hệ sinh thái package lớn.
- Thu gom rác (garbage collection): Điều này quản lý
hoặc xử lý việc phân bổ bộ nhớ (chủ yếu là bộ nhớ bị
chiếm bởi các đối tượng không còn sử dụng).
- Chú thích kiểu (type annotations): Điều này dành cho
những người muốn bảo mật và nhất quán để kiểm soát
tất cả dữ liệu trong một ứng dụng.
- Nó cung cấp hiệu suất tốt nhất và các công cụ tốt hơn cho các
dự án quy mô lớn: Dart có công cụ hiện đại và ổn định được cung
cấp bởi các plugin IDE. Nó được thiết kế để có được hiệu suất tốt
nhất có thể trong khi vẫn giữ được cảm giác của một ngôn ngữ
động (dynamic language).
- Nó được tạo ra để trở nên mạnh mẽ và linh hoạt: Bằng cách giữ
cho chú thích kiểu (type annotations) là tùy chọn và thêm các tính
năng OOP, Dart cân bằng giữa hai thế giới của sự linh hoạt và sự
mạnh mẽ.
Dart là một ngôn ngữ đa nền tảng (cross-platform), hiện đại, có mục
đích chung (general-purpose), liên tục cải tiến các tính năng của nó,
làm cho nó trưởng thành và linh hoạt hơn. Đó là lý do tại sao đội
framework của Flutter đã chọn ngôn ngữ Dart để làm việc.
Mã Dart có thể được chạy trong môi trường hỗ trợ Dart. Một môi
trường có khả năng chạy Dart cung cấp các tính năng cần thiết cho
một ứng dụng, chẳng hạn như sau:
- Hệ thống thời gian chạy (runtime systems)
- Thư viện lõi của Dart
- Bộ thu gom rác (Garbage collectors)
Việc thực thi mã Dart hoạt động ở hai chế độ — biên dịch Just-In-
Time (JIT) hoặc biên dịch Ahead-Of-Time (AOT):
- Biên dịch JIT là nơi mã nguồn được tải và biên dịch thành mã máy
của Dart VM một cách nhanh chóng. Nó được sử dụng để chạy mã
trong giao diện dòng lệnh (command-line interface) hoặc khi bạn
đang phát triển một ứng dụng di động để sử dụng các tính năng
như gỡ lỗi (debugging) và tải lại nóng (hot reloading).
- Biên dịch AOT là nơi máy ảo Dart và mã của bạn được biên dịch
trước và VM hoạt động giống như một hệ thống thời gian chạy Dart
(Dart runtime system), cung cấp một bộ thu gom rác và các
phương pháp gốc (native methods) khác nhau từ bộ công cụ phát
triển phần mềm Dart (SDK) đến ứng dụng.
Dart góp phần vào tính năng nổi tiếng nhất của Flutter, tải lại nhanh
(hot reloading), dựa trên trình biên dịch Dart JIT, cho phép tương tác
nhanh với các hoán đổi mã trực tiếp (live code swap).
- Hàm main() là điểm bắt đầu của mọi ứng dụng Dart.
Hàm này là bắt buộc và cũng sẽ nhìn thấy nó trong mọi
ứng dụng Flutter. Mọi thứ bắt đầu với main().
- String name = “Dart”; là 1 dòng khai báo biến, ở đây
bạn đang khai báo 1 biến được gọi là name, kiểu String,
có giá trị là “Dart”. Lưu ý là ở đây sử dụng dấu (‘) hoặc
(“) đều được chấp nhận.
- print(“Hello $name!”); là dòng gọi phương thức in,
truyền vào 1 chuỗi. Điều đặc biệt là thay vì thực hiện
phép nối xâu như bình thường với toán tử “+”, thì ở đây
có thể sử dụng “$” để chèn 1 biến vào chuỗi. “$” có khả
năng tuyệt vời là giúp chúng ta chèn mọi loại biến vào
trong 1 chuỗi.
- Kiểu giá trị logic: bool – với 2 giá trị true và false
- Kiểu xâu kí tự: String
Ví dụ:
Ở đây chúng ta lại thấy sự xuất hiện của “$” như đã giới
thiệu ở phần trước.
Dart cung cấp cú pháp cho cấu trúc điều khiển rất giống
với các ngôn ngữ thông dụng khác (ví dụ, Java), đó là:
- if-else
- switch/case
- Vòng lặp với for, while và do-while
- break và continue
- Xử lý ngoại lệ với try/catch và throw
Vì sự tương tự đó nên sẽ không cung cấp chi tiết từng cú
pháp tại đây, đọc thêm tại:
https://dart.dev/guides/language/language-tour#control-
flow-statements
Tìm hiểu sâu và chi tiết hơn về function trong Dart tại đây:
https://www.tutorialspoint.com/dart_programming/dart_pro
gramming_functions.htm
- Dart được gọi là một ngôn ngữ lập trình hướng đối
tượng thực sự, “Dart is called a true object-oriented
language” – Google
- Và “In Dart, everything is an object”, ngay cả các hàm
cũng là các đối tượng, do đó lớp và đối tượng là những
phần quan trọng khi bạn làm việc với Dart và Flutter.
- Kế thừa (Inheritance):
+ Giống như Java, Dart chỉ cho phép đơn kế thừa.
+ Mặc dù chỉ cho phép đơn kế thừa, tuy nhiên khác
với Java, Dart lại có hỗ trợ đặc biệt với mixins – được sử
dụng để mô phỏng đa kế thừa và sử dụng lại mã.
+ Dart không có lớp final class, mọi lớp trong Dart
đều có thể được kế thừa bởi lớp khác.
- Đa hình (Polymorphism):
+ Dart cho phép ghi đè (overriding) một phương
thức cha để thay đổi hành vi ban đầu của chúng.
+ Khác Java, Dart không cho phép nạp chồng
phương thức (overloading). Không thể định nghĩa cùng 1
phương thức 2 lần với các đối số khác nhau.
Quan sát ở đây, name và surname không bắt đầu bởi “_”, do
đó chúng có thể được truy cập từ bên ngoài.
- Bạn có thể tạo ra các thể hiện của lớp Person từ hàm
main(), và set giá trị cho name và surname
Ở đây, dòng lệnh Person clack = Person(), được dùng để tạo một
thể hiện của lớp Person(), và kết quả là một đối tượng được chứa
trong biến clark. Cách viết trên là cách viết tắt của Person clack =
new Person(), từ khóa “new” có thể được lược bỏ tùy ý.
4.1.1 Embedder
- Đối với hệ điều hành, các ứng dụng Flutter được đóng
gói theo cách giống như bất kì ứng dụng gốc nào khác.
1 platform-specific embedder cung cấp 1 entrypoint,
phối hợp với hệ điều hành để truy cập vào các dịch vụ
như rendering surfaces, accessibility, input, và quản lý
message event loop.
- Embedder được viết bằng ngôn ngữ phù hợp với nền
tảng: hiện tại là Java và C++ cho Android, Objective-
C/Objecttive-C++ cho iOS và macOS, và C++ cho
Windows và Linux.
- Khi sử dụng embedder, mã Flutter có thể tích hợp vào
ứng dụng hiện có dưới dạng module, hoặc cũng có thể
là toàn bộ nội dung ứng dụng.
Nội dung
5.1 Widgets – Xây dựng Layout trong Flutter
5.2 Xử lý User Input và Gestures
5.3 Theming và Styling
5.4 Điều hướng trong Flutter
Stateless widgets
- Một UI thông thường sẽ bao gồm nhiều
widget, một trong số chúng sẽ không bao
giờ thay đổi thuộc tính sau khi được khởi
tạo. Chúng được gọi là không có trạng
thái (state), nghĩa là chúng không tự thay
đổi thông qua các hành động hoặc hành vi
nội bộ.
- Thay vào đó, chúng được thay đổi bởi các
sự kiện bên ngoài trên widget cha trong
cây widget. Hiều rõ hơn là, widget con
được mô tả từ widget cha mà không tự
thay đổi được nó.
- Các Stateless widgets chỉ có các thuộc
tính final được xác định trong quá trình
xây dựng, và đó là thứ duy nhất cần được
tạo trên màn hình thiết bị
Stateful widgets
- Không giống như Stateless
widget nhận mô tả từ widget
cha và duy trì điều đó trong
suốt thời gian tồn tại của nó,
Stateful widget thay đổi mô tả
của chúng một cách linh hoạt
trong thời gian tồn tại. Chúng
có một lớp State đại diện cho
trạng thái hiện tại của widget.
- Bằng cách giữ trạng thái của
widget trong 1 đối tượng State
riêng biệt, framework có thể
rebuild lại bất cứ khi nào cần
mà không làm mất trạng thái
liên quan đến nó.
- Một stateful widget có khả năng thay đổi diện mạo trong
suốt vòng đời của nó – nghĩa là nó cần được rebuild để
phản ánh những thay đổi đó. Những thay đổi đó xảy ra
khi thực thi phương thức _incrementCounter(), phương
thức được gọi mỗi khi button được nhấn.
- Lưu ý trong code trên về việc sử dụng thuộc tính
onPressed của widget FloatingActionWidget, thuộc tính
này nhận 1 hàm (ở đây là _incrementCounter()), và hàm
sẽ được thực thi khi nút được pressed (được nhấn)
- Vậy làm thế nào mà framework State biết được khi nào
widget con thay đổi và cần rebuild nó? setState là câu
trả lời. Bằng cách gọi phương thức setState, framework
được thông báo rằng nó cần phải rebuild widget con.
Row và Column
Container
Packing
ListView: Là một
widget dạng cột,
nhưng được trang
bị khả năng cuộn
khi nội dung quá
dài so với khả
năng hiển thị của
màn hình.
Stack: Các widget được sắp xếp chồng lên nhau. Như
trong hình dưới, Text widget có nội dung là “Mia B” chồng
lên trên 1 Image widget khác.
Tap
Double tap
Drag
Horizontal drag
Scale
Dưới đây cung cấp đường dẫn tới tài liệu để bạn có thể
nghiên cứu thêm về phần này.
https://flutter.dev/docs/development/ui/widgets/styling
https://api.flutter.dev/flutter/material/Theme-class.html
https://api.flutter.dev/flutter/material/MaterialApp-class.html
https://api.flutter.dev/flutter/cupertino/CupertinoApp-
class.html
Đây là phần Flutter nâng cao không nằm trong mục tiêu
của bộ slide này (mục tiêu ở mức tìm hiểu về công nghệ
Flutter).
Nên ở đây không trình bày, mà cung cấp tài liệu để bạn
đọc tự tìm hiểu và nghiên cứu. Tìm hiểu phần này tại các
chương 8 (Firebase Plugins), chương 9 (Developing Your
Own Flutter Plugin), chương 10 (Accessing Devices
Features from the Flutter App), chương 11 (Platform Views
and Map Integration) trong cuốn Flutter for Beginners phần
tài liệu tham khảo.
Phần này sẽ hướng dẫn từng bước cách xây dựng lên
ứng dụng chuyển đổi đơn vị đo.
Ứng dụng chuyển đổi đơn vị đo này sẽ cho phép người
dùng chọn một số đo hệ mét hoặc hệ Anh, và chuyển đổi
nó sang một thước đo khác. Ví dụ, người dùng có thể
chuyển đổi một khoảng cách với đơn vị mile (dặm), sang
khoảng cách với đơn vị km. Nó giúp cho người dùng hiểu
được tốc độ xe của mình, hoặc trọng lượng của thực
phẩm mà bạn mua trong siêu thị, ở một quốc gia dùng hệ
đo khác.
Kiến thức cần sử dụng: Kiến thức về State trong Flutter, và
TextField (xử lý với users input)
IDE sử dụng: Android Studio
Lúc này, màn hình ứng dụng hiển thị như sau:
Tiếp theo, trong body của build() method, xóa Text widget
và thay thế bằng TextField.
Hiện tại, TextField vẫn chưa làm gì, do đó, điều đầu tiên
chúng ta cần làm là đoc giá trị mà người dùng nhập vào
nó. Để đáp lại mỗi thay đổi trong nội dung của TextField,
sẽ sử dụng phương thức onChanged, và update State.
Ở đây, mỗi khi giá trị của TextField thay đổi (onChanged),
chúng ta sẽ kiểm tra xem là giá trị nhập vào đó có là 1 số
hay không. Nếu là 1 số, chúng ta sẽ thay đổi giá trị của
_numberFrom, bằng cách này, ta đã thực sự cập nhật
được state.
Để kiểm tra xem bản cập nhật này có xảy ra hay không,
hãy thêm 1 Text widget mà sẽ show ra nội dung của
TextEdit widget, và bọc lại 2 widget đó vào trong 1 Column
Trước khi thử lại app, hãy thêm 1 method vào MyAppState
class:
Phương thức này được gọi 1 lần cho mỗi đối tượng State
khi State được xây dựng. Đây cũng là nơi bạn khởi tạo
các giá trị có thể cần khi xây dựng các class. Ở đây, chúng
ta đã đặt giá trị ban đầu cho _numberFrom. Cũng lưu ý
rằng, phải luôn gọi super.initState() ở cuối phương thức
initState().
Bây giờ, khi bạn chạy thử app sẽ thấy rằng, khi nhập 1 số
vào TextField, sẽ tạo ra 1 bản sao ở Text widget.
Tiếp theo, chúng ta sẽ hoàn tất UI của app, đầu tiên là xây
dựng 1 widget khác, đó là DropdownButton.
Tiếp theo, là cách phản hồi lại với user input khi thay đổi
giá trị của DropdownButton.
- Tạo 1 String là _startMeasure ở trên cùng của
MyAppState, nó sẽ chứa giá trị item được chọn từ
DropdownButton.
- Sửa lại onChanged() bằng:
11. Widget cuối cùng của cột, là 1 Text dùng cho việc hiển
thị kết quả chuyển đổi
12. Và còn 1 công đoạn cuối cùng trước khi hoàn thiện UI
của chúng ta. Vì màn hình hiển thị của ứng dụng được
yêu cầu hiển thị 8 widget mà ta đã xây dựng, tuy nhiên rất
có thể màn hình không thể hiển thị cùng 1 lúc cả 8 widget.
Do đó, ta cần cho nó khả năng cuộn. Bằng cách đưa
Column vào trong 1 SingleChildScrollView.
UI của chúng ta đã chính thức hoàn tất. Tuy nhiên lúc này
khi bạn chọn đơn vị trong các DropdownButton hay nhấn
RaisedButton, ứng dụng vẫn chưa làm gì cả. Bây giờ
chính là lúc xây dựng phần logic của app.
Ở đây, chúng ta thấy rõ sự tiện lợi khi biểu thị mội đơn
đơn vị thành 1 con số.
Bây giờ, chúng ta cần phải xây dựng phương thức convert
để chuyển đổi giá trị, sử dụng formulas và _measureMap
- Cuối cùng, cần gọi đến phương thức convert() mỗi khi
người dùng tap vào “Convert” button. Sửa lại code của
RaisedButton như sau:
- Để hiện thị kết quả, sửa lại Text widget cuối cùng, thứ
mà sẽ chứa message tới người dùng
Toàn bộ code của phần 7 này, bạn có thể tham khảo tại:
https://github.com/PacktPublishing/Flutter-
Projects/tree/master/ch_02