โดยทั่วไปเวลาเราทำแอปแล้วต้องต่อเข้ากับ API หลังบ้าน เราก็จะต้องแยก environment เพื่อไม่ให้ข้อมูลสำหรับทดสอบกับของจริงปนกัน ส่วนจะแยกเป็นกี่ environment นั้นก็แล้วแต่ทีมจะกำหนดกันเองอีกที เช่น dev, staging, และ production เป็นต้น ซึ่งการแยก environment แบบนี้ใน Android จะเรียกว่า Flavor ส่วนใน iOS จะเรียกว่า Target/Scheme
แนวคิดของการแยก Environment ก็คือการแยก main file ที่เราจะใช้เรียกตอนรันโปรแกรมนั่นเอง โดยเราจะแยกไปตามแต่ละ environment เลย เช่น ถ้าเรามี 2 enviroment คือ dev กับ prod เราก็อาจสร้าง 2 main ไฟล์แยกกันดังนี้
- main_dev.dart
- main_prod.dart
ซึ่งแต่ละ main file ก็จะมี configuration ที่แตกต่างกัน โดยเราจะสร้าง configuration file แยกออกมา และส่งไปให้แต่ละ main file ใช้งานอีกที
เราลองมาดู main_dev.dart
, main_prod.dart
, และไฟล์ app_config.dart
กันก่อน
import 'package:flutter_flavors/config/app_config.dart';
import 'package:flutter_flavors/config/app_config_dev.dart';
import 'package:flutter_flavors/main_common.dart';
void main() {
final AppConfig appConfig = AppConfigDev();
runApp(MyApp(
appConfig,
));
}
import 'package:flutter_flavors/config/app_config.dart';
import 'package:flutter_flavors/config/app_config_prod.dart';
import 'package:flutter_flavors/main_common.dart';
void main() {
final AppConfig appConfig = AppConfigProd();
runApp(MyApp(
appConfig,
));
}
เราใช้ app_config.dart
สำหรับประกาศว่า configuration ควรจะต้องมี attribute อะไรให้ config บ้าง ในที่นี้คือมีค่าเดียว appName
import 'package:flutter/material.dart';
abstract class AppConfig {
String get appName;
}
มาถึงตรงนี้เราก็มีคลาสพร้อมใช้งานแล้ว ขั้นตอนต่อไปก็คือการ config แต่ละ environment โดยการ extends AppConfig
มาใช้งาน โดยการสร้างไฟล์ app_config_dev.dart
กับ app_config_prod.dart
แยกออกมาดังนี้
import 'package:flutter/material.dart';
import 'package:flutter_flavors/config/app_config.dart';
class AppConfigDev implements AppConfig {
final _appName = "My App(DEV)";
@override
String get appName => _appName;
}
import 'package:flutter/material.dart';
import 'package:flutter_flavors/config/app_config.dart';
class AppConfigProd implements AppConfig {
final _appName = "Dog Match";
@override
String get appName => _appName;
}
จากตัวอย่าง เราใช้วิธีส่ง configuration ลงไปให้ MyApp
ใช้ แต่จริงๆ แล้วเราสามารถใช้ library ที่ทำ dependency injection อย่าง Provider หรือ Flutter BLoC ก็ได้ ซึ่งนั่นทำให้เราสามารถอ้างอึง config ของเราได้จากที่ไหนในแอปก็ได้โดยสะดวก
แต่ในบทความนี้ไม่ได้ใช้ เพราะต้องการลดความซับซ้อนของโค้ดลง
ส่วนใน MyApp เราก็สามารถเรียกใช้ค่าใน config ได้เลยตรงๆ แบบนี้
import 'package:flutter/material.dart';
import 'package:flutter_flavors/config/app_config.dart';
class MyApp extends StatefulWidget {
MyApp(this.appConfig);
final AppConfig appConfig;
@override
_MyAppState createState() => _MyAppState(appConfig);
}
class _MyAppState extends State<DogApp> {
_MyAppState(this.appConfig);
final AppConfig appConfig;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(Text(appConfig.appName)),
)
)
}
}
ทีนี้เวลาเราต้องการรันแยก main file เราสามารถรันโดยใช้ -t
เพื่อระบุว่าจะให้ไฟล์ไหนเป็นไฟล์แรกได้เลยแบบนี้
$ flutter run -t lib/main_dev.dart
or
$ flutter run -t lib/main_prod.dart
ถ้าต้องการ config ให้รันแยกกันใน VS Code ให้เลือกเมนู Run → Add Configuration ดังนี้
จากนั้นก็ config แยกกันตาม environment ได้เลย
{
"version": "0.2.0",
"configurations": [
{
"name": "My App (DEV)",
"request": "launch",
"type": "dart",
"program": "lib/main_dev.dart",
},
{
"name": "My App",
"request": "launch",
"type": "dart",
"program": "lib/main_prod.dart",
}
]
}
เมื่อเราเข้าไปดูในเมนู Run ที่อยู่ทางซ้ายของ VS Code ก็จะเห็นคำสั่งตามชื่อที่เราตั้งไว้ในไฟล์ launch.json
อย่างไรก็ตามบทความนี้นำเสนอแค่การ config แยก environment อย่างง่ายๆ เท่านั้น หากต้องการ config แยกกันในระดับที่ละเอียดกว่านี้ เช่น มี App Icon, App Name, หรือ App ID ที่แตกต่างกันในแต่ละ environment เราสามารถอ่านเพิ่มเติมได้ที่ Creating flavors for Flutter
บทความนี้ใช้ Code และรูปจาก Rafael Delos Santos
1 comment
[…] Coding […]
Comments are closed.