Flutter 101: Làm quen với Flutter
Xin chào tất cả các bạn đến với yeulaptrinh.vn, hôm nay mình sẽ bắt đầu một series mới về Flutter. Một cross-platform framework để phát triển ứng dụng cho thiết bị di động, web, desktop và thiết bị nhúng của Google.
Trước tiên, chúng ta sẽ điểm xem Flutter có gì ngon:
- Đầu tiên tất nhiên là vì nó là cross-platform framework. Code một lần có thể chạy trên nhiều nền tảng.
- Flutter ko sử dụng lại các thành phần native có sẵn mà tự vẽ lại hoàn toàn các thành phần UI dựa vào Canvas class, dẫn đến tính tùy biến rất cao
- Flutter không dùng Webview, không dùng bridge để tương tác với native code nên performance rất tốt
- Sử dụng Dart, một ngôn ngữ vừa suport Just-In-Time complier và Ahead-of-Time compiler, cho phép vừa có tính năng hot-reload vừa có hiệu năng cao khi release
Tất nhiên Flutter cũng có những yếu điểm của nó. Ví dụ như việc không sử dụng các thành phần native có sẵn thì khi OS ra một UI component mới thì mình phải build lại nó từ đầu hoặc chờ cộng đồng hỗ trợ. Cũng như Flutter cũng gặp khó khăn khi muốn tận dụng được một lượng lớn các native UI library có sẵn trên các nền tảng được.
Xong phần tổng quan một chút về Flutter, chúng ta sẽ bắt tay vào bước đầu tiên để có thể xây dựng một ứng dụng Flutter đó là cài đặt môi trường.
Cài đặt môi trường
Các bạn có thể tham khảo cách cài đặt Flutter cụ thể qua docs của Flutter ở đây, không quá phức tạp nhưng có khá nhiều bước. Nếu bạn chuyển từ các nền tảng khác (iOS, Android hay React Native) sang Flutter thì sẽ dễ hơn. Mình sẽ điểm nhanh các bước chính để cài đặt môi trường phát triển Flutter cho hai nền tảng iOS và Android trên MacOS (vì mình dùng MacOS)
- Tải về các file cần thiết: file cài đặt của phiên bản Flutter bạn muốn sử dụng (danh sách các phiên bản ở đây), Xcode (có thể download từ Appstore hoặc tải về file cài đặt ở đây) và Android Studio ở đây.
- Giải nén file zip của Flutter vào một folder nào đấy, thường mình sẽ đặt ở Documents, update PATH trên máy của bạn đến folder bin trong folder flutter vừa giải nén
export PATH="$PATH:[PATH_OF_FLUTTER_DIRECTORY]/bin"
- Kiểm tra lại xem đã export thành công chưa bằng cách
echo $PATH
trên terminal và kiểm tra xem flutter command đã hoạt đông chưa bằngwhich flutter
- Bạn có thể dùng câu lệnh
flutter doctor
trên terminal để kiểm tra trạng thái các cài đặt môi trường cần thiết cho Flutter. Phần nào chưa được cấu hình đúng thì sẽ có dấu x
- Sau khi bạn tải về Xcode và Android Studio thì tiến hành cài đặt, làm theo hướng dẫn và bạn sẽ có Android toolchain, Xcode, Android Studio sẵn sàng để sử dụng.
- Chọn một IDE để phát triển Flutter, ở đây các bạn có thể dùng VSCode/ Android Studio/ IntelliJ IDEA Community Edition có sẵn các plugin hỗ trợ Flutter
Về cơ bản sau các bước trên thì bạn đã có thể chạy được một ứng dụng Flutter đơn giản trên máy của bạn. Nếu bạn gặp vấn đề trong việc cài đặt thì bạn nên kiểm tra lại các biến môi trường như JAVA_HOME, ANDROID_SDK_ROOT hoặc có thể để lại comment, mình sẽ hỗ trợ.
Xây dựng ứng dụng Flutter đầu tiên
Xong phần cài đặt, chúng ta sẽ bắt đầu xây dựng ứng dụng Flutter đầu tiên. Mình đã sang ngay bên trang chủ của React Native với mục đích là xem ứng dụng đầu tiên của React Native thế nào để mình có thể xây dựng một ứng dụng giống hệt, giúp mọi người có thể có một sự so sánh cơ bản đầu tiên.
Bạn có thể dùng IDE hoặc Terminal để khởi tạo ứng dụng Flutter. Ở đây mình sẽ dùng Terminal để khởi tạo
flutter create rn_like_app
Kết quả bạn nhận được sẽ tương tự như sau:
Cấu trúc của một ứng dụng Flutter bình thường sẽ bao gồm các thư mục như sau
Ở đây mình tập trung vào việc phát triển ứng dụng trên thiết bị di động nên mình sẽ quan tâm đến 4 phần chính trong một project Flutter:
- Thư mực android và ios: nơi chứa các đoạn mã liên quan đến ứng dụng Android/ iOS của chúng ta. Trong này sẽ chứa các cấu hình cho ứng dụng Android và iOS, một số file native để giúp chúng ta có thể kết nối với các plug-in trong Flutter hoặc là có thể dùng để tương tác với Flutter code sau này
- Thư mục lib: là nơi chứa Dart code chính của ứng dụng Flutter
- pubspec.yaml: là nơi bạn khai báo các dependencies khi phát triển ứng dụng Flutter
Sau khi khởi tạo ứng dụng thành công, chúng ta sẽ chạy thử xem ứng dụng như thế nào. Bạn có thể dùng terminal để chạy như hướng dẫn lúc khởi tạo project hoặc dùng IDE để chạy ứng dụng. Ở đây dùng mình Visual Studio Code để chạy ứng dụng luôn.
Mở project bằng Visual Studio Code, mở file lib/main.dart
. Bạn sẽ thấy một menu cho phép chạy ứng dụng Flutter ở góc trên bên phải của IDE. Và ở góc dưới bên phải của IDE sẽ có một menu cho phép bạn thay đổi build destination của ứng dụng. Ở đây mình chọn là iOS simulator thì ứng dụng sau khi chạy sẽ như sau:
Khi chúng ta bấm vào nút ở góc dưới bên phải màn hình thì giá trị số lần bạn bấm nút sẽ thay đổi, mình sẽ quay lại ứng dụng này khi đến phần StatefulWidget.
Bây giờ chúng ta sẽ tạo UI giống với UI của ứng dụng HelloWorld của React Native bằng cách replace toàn bộ code trong file main.dart bằng đoạn code phía dưới
import 'package:flutter/material.dart';
void main() {
runApp(MyApp()); // 1
}
class MyApp extends StatelessWidget { // 2
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp( // 3
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: RNLikeHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class RNLikeHomePage extends StatelessWidget {
final String title;
RNLikeHomePage({Key? key, required this.title}) : super(key: key); // 4
@override
Widget build(BuildContext context) {
return Scaffold( // 5
appBar: AppBar(
title: Text(this.title),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Some text"),
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Some more text"),
Image.network(
"https://reactnative.dev/docs/assets/p_cat2.png",
width: 200,
height: 200,
)
],
),
),
TextField(
decoration: InputDecoration(hintText: "You can type in me"),
)
],
),
),
);
}
}
Nhìn sơ qua thì code có vẻ dài hơn kha khá so với React Native để cho ra cùng một UI nhỉ. Nhưng mà không sao dài hơn chưa chắc đã không tốt hơn. Mình tin là càng đi sâu mọi người sẽ càng thích Flutter. Bây giờ mình sẽ đi qua giải thích các thành phần trong đoạn code vừa rồi.
- (1) Hàm main là điểm bắt đầu của một ứng dụng Flutter, ở đây đơn giản chỉ là khởi tạo một StatelessWidget là MyApp. Ở các ứng dụng phức tạp hơn, hàm main có thể chứa các đoạn mã để khởi tạo các service khác trước khi MyApp được bắt đầu
- (2) Trong Flutter, tất cả các thành phần UI đều mà Widget. Widget như mà một cách chúng ta mô tả lại giao diện ứng dụng của bạn dựa trên trạng thái và cấu hình của widget đó. Khi mà trạng thái của một widget thay đổi, framework sẽ xác định xem mô tả của widget đấy có gì thay đổi và thực hiện những thay đổi đó một cách hiệu quả nhất có thể. Cụ thể ở đây, một custom widget tên là MyApp được sử dụng làm root của ứng dụng. Trong Flutter, các widget sẽ liên kết với nhau bằng một Widget tree. Hàm build là một trong những hàm quan trọng nhất trong vòng đời của widget, nó sẽ cung cấp “cấu hình”, “mô tả”, cách widget cha tổ chức các widget con để có được UI mong muốn. Hàm build sẽ trả về một widget là root của widget cha của nó.
- (3) MyApp sử dụng MaterialWidget, một widget được cung cấp sẵn của Flutter được xây dựng theo Material Design có chứa một số các widget hữu ích cho việc xây dựng ứng dụng của bạn như Navigator, giúp bạn quản lý luồng màn hình trong ứng dụng thông qua route.
- (4) RNLikeHomePage là StatelessWidget chính để thể hiện nội dung của app chúng ta ngày hôm nay. RNLikeHomePage nhận hai tham số là Key và chuỗi String. Trong đó key là một tham số tùy chọn, dùng để nhận diện một widget giúp cho Flutter có thể tối ưu hiệu suất của ứng dụng. Title có nhiệm vụ hiển thị mội chuỗi trên thanh app bar
- (5) Hàm build của RNLikeHomePage sẽ mô tả một UI tương tự như ứng dụng React Native phía trên với root Widget là Scaffold. Scaffold là một widget cung cấp cho chúng ta một mẫu UI theo Material design. Scaffold widget bao gồm hai phần chính là app bar và body như trong screenshot dưới đây. Phần appBar sẽ hiển thị một view chứa title mình truyền vào lúc khởi tạo RNLikeHomePage widget với background color blue được định nghĩa ở theme của MaterialApp. Phần body bao gồm một SingleChildScrollView ở ngoài cùng cho phép ứng dụng có thể cuộn nếu nội dung quá dài. Column cho phép các widget con được sắp xếp theo một hàng dọc. Text widget để hiển thị nội dung string. Image để hiển thị nội dung ảnh từ mạng và TextField là một widget cho phép nhập nội dung.
Hi vọng sau khi đọc bài blog này, các bạn sẽ có một cái nhìn ban đầu về một ứng dụng Flutter, cách viết UI code trong Flutter như thế nào. Bài tiếp theo chúng ta sẽ nói thêm về StatelessWidget và StatefulWidget.
Cảm ơn các bạn đã dành thời gian để đọc hết bài blog này. Nếu các bạn có góp ý gì về nội dung có thể để lại comment cho mình biết.