Dart Defines trong Flutter và sức mạnh của nó
Flutter & Dart . TutorialsContents
Chào mừng bạn đến với Fx Studio. Chúng ta đã cùng nhau phiêu lưu trong thế giới Flutter với Flavor. Tuy nhiên, bạn sẽ phải tốn kha khá các thao tác để đạt được mục đích. Do đó, chủ đề bài viết này sẽ hướng dẫn cho các bạn 1 cách khác để config, nó sẽ giúp mình giảm được vô số bước. Đó là Dart Defines.
Tác giả bài viết là bạn Hồng Vân. Mọi người có thể theo dõi thêm các bài viết từ GitHub chính chủ của bạn nhóe!
Còn nếu mọi việc đã ổn rồi, thì …
Bắt đầu thôi!
Chuẩn bị
Về mặt kiến thức, bạn cần thông thạo Flutter cơ bản trước nhóe. Còn về mặt tools & versions, mình sẽ sử dụng các công cụ như sau:
-
IDE:
-
Visual Studio Code version 1.67.0
-
Android Studio Chipmunk 2021.2.1
-
XCode version 13.3.1
-
-
Flutter SDK version 2.10.5
Ngoài ra, đây cũng là phần tiếp theo của 3 bài viết liên quan tới Flavor trong Flutter. Nên nếu bạn chưa đọc qua nó, thì có thể tham khảo ở link sau:
Dart Defines
Dart defines là các biến được tạo ra trong khoảng thời gian biên dịch chương trình (compile-time variables). Nếu bạn đang tìm kiếm những biến tương tự biến môi trường (environtment variables), thì Dart defines là những gì bạn cần! Và chúng được tạo ra bởi --dart-define
.
Vậy
--dart-define
là gì?
Nó là một tham số để truyền vào (pass) vào câu lệnh flutter build
hoặc flutter run
. Nó sẽ giúp chúng ta chuyển tiếp các biến môi trường. Điều đó có nghĩa là bạn có thể custom code của mình với bất kỳ giá trị nào bạn truyền qua tham số này.
Quá là hay phải không nào? Điều này khiến mình ngay lập tức nghĩ đến việc define các biến khác nhau cho flavor thay vì config khùng điên như ở bài trước của mình.
Ahihi!
Cú pháp
Trước khi đi vào ứng dụng cho flavor, mình sẽ demo nhỏ nhỏ để các bạn nắm được cách làm việc với --dart-define
nha!
- Bước 1: Chúng ta sẽ xây dựng 1 ứng dụng có UI đơn giản như sau:
- Bước 2: Các bạn define lớp
EnvironmentConfig
:
class EnvironmentConfig { static const APP_NAME = String.fromEnvironment('APP_NAME', defaultValue: 'awesomeApp'); static const APP_SUFFIX = String.fromEnvironment('APP_SUFFIX'); }
Giải thích 1 chút:
-
Chúng ta có thể truy cập các biến dart define thông qua hàm
fromEnvirontment(name, defaultValue)
. name sẽ là tên biến được define sau câu lệnh--dart-define=
, nếu nhưfromEnvirontment
không thể tìm thấy biến nào như vậy thì nó sẽ trả về defaultValue. -
Bạn sẽ bắt buộc phải khai báo biến là
const
, bởi vì hàmfromEnvironment
chỉ đảm bảo hoạt động khi các biến đó là hằng số.
Còn vì sao có thêm
static
phía trước? Bạn chỉ cần gõconst
không thôi là hiểu rõ! Ahihi!
- Bước 3: Sử dụng lớp
EnviromentConfig
:
Tới đây, mình sẽ gán 2 biến APP_NAME
và APP_SUFIX
vào trên UI:
Text( 'APP_NAME: ${EnvironmentConfig.APP_NAME}', style: Theme.of(context).textTheme.subtitle1, ), Text( 'APP_SUFFIX: ${EnvironmentConfig.APP_SUFFIX}', style: Theme.of(context).textTheme.subtitle1, ),
- Bước 4: Run và xem kết quả thôi nào!
Câu lệnh run như sau:
flutter run --dart-define=APP_NAME=demo --dart-define=APP_SUFFIX=.dev
(Kết quả thực thi nhóe!)
Ứng dụng vào Flutter Flavor
Nếu chưa biết Flutter Flavor là gì, bạn có thể tham khảo qua bài viết này của mình.
Còn đã biết rồi, thì bắt tay vào công việc thôi nào! Ý tưởng ở đây là mình sẽ config app có name và suffix khác nhau cho mỗi flavor nhé!
Cấu hình cho Android
- Bước 1: Định nghĩa và đọc các biến dart define:
Bạn vào file android/app/build.gradle
và thêm đoạn code này vào:
// 1 def dartEnvironmentVariables = [ APP_NAME: 'awesomeApp', APP_SUFFIX: null ]; // 2 if (project.hasProperty('dart-defines')) { // 3 dartEnvironmentVariables = dartEnvironmentVariables + project.property('dart-defines') .split(',') .collectEntries { entry -> // 4 def pair = new String(entry.decodeBase64(), 'UTF-8').split('=') [(pair.first()): pair.last()] } }
Giải thích code:
-
Chúng ta define các biến dart define theo dạng key-value. Key này sẽ giống nhau ở cả iOS, Android và Flutter. Còn value định nghĩa các giá trị default.
-
Flutter sẽ pass các biến dart define trong các thuộc tính của project với key là dart-defines
-
Chuyển đổi string từ
dart-defines
thành Map. -
Flutter Tool sẽ pass tất cả các Dart Defines như 1 string với dấu phẩy, ví dụ:
-
Ở Flutter 1.17:
APP_NAME=awesomeApp1,APP_SUFFIX=.dev
-
Ở Flutter 1.20:
APP_NAME%3Dawesome1,APP_SUFFIX%3D.dev
-
Ở Flutter 2.2:
REVGSU5FRVhBTVBMRV9BUFBfTkFNRT1hd2Vzb21lMg==,REVGSU5FRVhBTVBMRV9BUFBfU1VGRklYPS5kZXY=
Ở mỗi version, có cách mã hoá khác nhau, ở đây vì mình sử dụng Flutter 2.16 mã hoá base64 như ở Flutter 2.2, nên mình sẽ phải giải mã base64 ngược lại.
-
- Bước 2: Thay đổi app name và application id suffix
- Bạn tiếp tục vào file
android/app/build.gradle
và sửa ở phầndefaultConfig
như sau:
- Bạn tiếp tục vào file
defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.demo_dart_define" minSdkVersion flutter.minSdkVersion targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName // NOTE: ADD THESE LINES applicationIdSuffix dartEnvironmentVariables.APP_SUFFIX resValue "string", "app_name", dartEnvironmentVariables.APP_NAME }
-
- Bạn vào
android/app/src/main/AndroidManifest.xml
để gánapp_name
vào application label
- Bạn vào
<application android:label="@string/app_name" android:name="${applicationName}" android:icon="@mipmap/ic_launcher">
Bây giờ run và xem kết quả thôi. Câu lệnh như trên nhé!
flutter run --dart-define=APP_NAME=demo --dart-define=APP_SUFFIX=.dev
App name đã được thay đổi!
Cấu hình cho iOS
Bạn vẫn phải thực hiện trên XCode nha!
-
Bước 1: Bạn tạo file
Define-defaults.xcconfig
để define các cặp key-value tương tự như ở bước 1 của Android.- Click chuột phải vào
Runner/Flutter
chọn New File, nhập “con“” vào ô filter chọn Next và đặt tên tương ứng.
- Click chuột phải vào
-
- Bạn định nghĩa vào đó như sau:
APP_NAME=awesomeApp APP_SUFFIX=
-
Bước 2: import file đó vào
Debug.xcconfig
vàRelease.xcconfig
#include "Generated.xcconfig" #include "Define-defaults.xcconfig" #include "Define.xcconfig"
Giải thích: Define-defaults.xcconfig
là file bạn đã tạo ở bước 1 còn Define.xcconfig
để ghi các biến dart define sẽ là file được tạo ra trong quá trình biên dịch.
- Bước 3: Thay đổi app name và app bundle identifier trong file
Info.plist
- Bước 4: Chỉnh sửa scheme để đọc các biến dart define và ghi vào file
Define.xcconfig
- Click vào Scheme
Runner
-> Edit Scheme. Sau đó dưới tabBuild
chọn Pre-actions -> click dấu+
chọn New Run Script Action
- Click vào Scheme
-
- Tiếp theo, chúng ta sẽ viết đoạn code thực hiện việc đọc các biến dart define và ghi vào file
Define.xcconfig
. Đoạn code này sẽ được biên dịch trước khi XCode build app. Tương tự như Android là vẫn phải giải mã base64 nha:
- Tiếp theo, chúng ta sẽ viết đoạn code thực hiện việc đọc các biến dart define và ghi vào file
function entry_decode() { echo "${*}" | base64 --decode; } IFS=',' read -r -a define_items <<< "$DART_DEFINES" for index in "${!define_items[@]}" do define_items[$index]=$(entry_decode "${define_items[$index]}"); done printf "%s\n" "${define_items[@]}"|grep '^APP_' > ${SRCROOT}/Flutter/Define.xcconfig
-
- Nhớ chọn Provide build settings from là Runner nhoé! Nếu không thì các bạn sẽ gặp lỗi đấy.
flutter run --dart-define=APP_NAME=demo --dart-define=APP_SUFFIX=.dev
Lưu ý: Đoạn code trên sẽ giúp bạn tự generate ra file Define.xcconfig
khi bạn chạy câu lệnh có flutter run --dart-define=APP_...
nhưng điều gì sẽ xảy ra nếu bạn chỉ chạy lệnh flutter run
hoặc các biến dart define không chứa chữ “APP“? Bạn nghĩ nó sẽ lấy giá trị trong Define-defaults.xcconfig
phải không? Không đâu! Bạn sẽ gặp lỗi như vậy:
Theo mình thấy thì điều này khá khó chịu. Vậy nên, mình đề xuất các bạn hãy tạo luôn file Define.xcconfig
để nếu có không pass các biến dart define, thì chương trình vẫn chạy trơn tru.
Mở rộng thêm tính năng Customize Debug and Run của Visual Studio Code
Có lẽ sẽ có bạn thắc mắc rằng: “Không lẽ mỗi lần build bắt buộc phải build bằng command sao?” Hoặc rằng: “Nếu các biến dart define đó dài hay cần nhiều biến thì chẳng phải quá tốn công sao?”
Bây giờ, sẽ là lúc mình giúp bạn trả lời câu hỏi đó!
Nếu các bạn chưa biết về Customize Debug & Run của Visual Studio Code, thì tham khảo qua bài viết ở link này của mình. Còn nếu biết rồi, thì chúng ta bắt đầu thôi!
- Bạn vào tab
Run and Debug
và click vào create a launch.json file -> chọn Debbuger là Dart&Flutter nhé!.
- Vào file
.vscode/launch.json
, tạo 3 configurations tương ứng với 3 flavor là dev, staging và product.
{ "name": "dev", "request": "launch", "type": "dart", "args": ["--dart-define", "APP_NAME=[dev]demo", "--dart-define", "APP_SUFFIX=.dev"] }, { "name": "staging", "request": "launch", "type": "dart", "args": ["--dart-define", "APP_NAME=[stg]demo", "--dart-define", "APP_SUFFIX=.stg"] }, { "name": "prod", "request": "launch", "type": "dart", "args": ["--dart-define", "APP_NAME=demo"] },
- Run lên và xem kết quả thôi! Bạn có thể chọn flavor nào để build tuỳ thích nhé!
Kết quả nhận được như sau nhóe!
- dev
- staging
- product
Xem lại tên của 3 app để thấy sự khác nhau của mỗi config nhóe!
Tạm kết
Qua bài này bạn đã biết:
-
Khái niệm và cách làm việc với
--dart-define
trong Flutter -
Ứng dụng
--dart-define
vào Flutter flavor -
Mở rộng tính năng của
Customize Run and Debug
Nếu các bạn đã từng theo dõi các bài viết trước của mình, thì sẽ nhận ra có 1 cái liên quan đến Flutter flavor mà mình chưa nói đến. Đó là cái api url khác nhau cho từng flavor.
Câu trả lời đấy mình để dành cho các bạn trả lời đấy! Nếu muốn biết đáp án thì các bạn có thể liên hệ mình.
Okay! Tới đây, mình xin kết thúc bài viết về Dart Defines trong Flutter . Nếu có gì thắc mắc hay góp ý cho mình thì bạn có thể để lại bình luận hoặc gởi email theo trang Contact.
- Bạn có thể checkout source code tại đây.
- Nguồn tham khảo:
-
Follow me more:
- Facebook: https://www.facebook.com/van.may.750/
- Email : lehongvan.develop@gmail.com
- LinkedIn: https://www.linkedin.com/in/le-van-935231150/
Cảm ơn bạn đã đọc bài viết này!
Related Posts:
Written by Hồng Vân Lê
Leave a Reply Cancel reply
Fan page
Tags
Recent Posts
- Charles Proxy – Phần 1 : Giới thiệu, cài đặt và cấu hình
- Complete Concurrency với Swift 6
- 300 Bài code thiếu nhi bằng Python – Ebook
- Builder Pattern trong 10 phút
- Observer Pattern trong 10 phút
- Memento Pattern trong 10 phút
- Strategy Pattern trong 10 phút
- Automatic Reference Counting (ARC) trong 10 phút
- Autoresizing Masks trong 10 phút
- Regular Expression (Regex) trong Swift
You may also like:
Archives
- September 2024 (1)
- July 2024 (1)
- June 2024 (1)
- May 2024 (4)
- April 2024 (2)
- March 2024 (5)
- January 2024 (4)
- February 2023 (1)
- January 2023 (2)
- November 2022 (2)
- October 2022 (1)
- September 2022 (5)
- August 2022 (6)
- July 2022 (7)
- June 2022 (8)
- May 2022 (5)
- April 2022 (1)
- March 2022 (3)
- February 2022 (5)
- January 2022 (4)
- December 2021 (6)
- November 2021 (8)
- October 2021 (8)
- September 2021 (8)
- August 2021 (8)
- July 2021 (9)
- June 2021 (8)
- May 2021 (7)
- April 2021 (11)
- March 2021 (12)
- February 2021 (3)
- January 2021 (3)
- December 2020 (3)
- November 2020 (9)
- October 2020 (7)
- September 2020 (17)
- August 2020 (1)
- July 2020 (3)
- June 2020 (1)
- May 2020 (2)
- April 2020 (3)
- March 2020 (20)
- February 2020 (5)
- January 2020 (2)
- December 2019 (12)
- November 2019 (12)
- October 2019 (19)
- September 2019 (17)
- August 2019 (10)