Flutter — สร้าง Boilerplate Project ที่สมบูรณ์แบบตั้งแต่เริ่มต้น

เครดิต: Nguyễn Thành Minh (นักพัฒนา Android)
เราทุกคนเคยผ่านมาแล้ว — ที่ฉันหมายถึงคือเราทุกคนพยายามสร้างโครงการสำเร็จรูปสำหรับการทำงานหรือเป็นโครงการส่วนตัว โครงการสำเร็จรูปที่ดีจะช่วยเราประหยัดเวลาได้มากด้วยการแก้ปัญหาทั่วไป เช่น การเรียก API การประมวลผลข้อมูลจากฐานข้อมูล และบางทีที่สำคัญที่สุด — ทำให้โค้ดของเราเป็นระเบียบและสอดคล้องกัน เพื่อให้ผู้คนมีเวลาพยายามทำความเข้าใจโค้ดของเราได้ง่ายขึ้น ซีรีส์นี้มีวัตถุประสงค์เพื่อแบ่งปันโครงการสำเร็จรูปที่ฉันสร้าง ใช้ และบำรุงรักษาในช่วงสองปีที่ผ่านมา ไม่เพียงเท่านั้น ฉันจะแบ่งปันวิธีการเขียนโค้ด ตลอดจนวิธีแก้ไขปัญหาทั่วไปที่อาจเกิดขึ้น จากที่กล่าวมานี่คือตอนที่ 1: สำรวจโครงการ
ดูบน Github

1. คุณสมบัติ
คุณสมบัติที่สร้างขึ้นในโครงการนี้:
- สถาปัตยกรรม: สถาปัตยกรรมที่สะอาด
- การจัดการสถานะ: flutter_bloc
- การนำทาง: auto_route
- DI: get_it ฉีดได้
- REST API: dio
- GraphQL: อาร์ทิมิส , graphql_flutter
- ฐานข้อมูล: objectbox
- ค่ากำหนดที่ใช้ร่วมกัน: encrypted_shared_preferences
- ชั้นข้อมูล: แช่แข็ง
- ผ้าสำลี: dart_code_metrics , flutter_lints
- CI/CD: Github Actions, Bitbucket Pipelines
- การทดสอบหน่วย: ม็อกเทล , bloc_test
- เพจ: infinite_scroll_pagination
- ยูทิลิตี้ : rxdart , dartx , async
- ตัว สร้างเนื้อหา: flutter_gen_runner , flutter_launcher_icons , flutter_native_splash
- คุณสมบัติอื่นๆ: คำขอ API ที่ล้มเหลวในการลองใหม่อัตโนมัติ, รีเฟรชโทเค็น,...
ในการรันโปรเจ็กต์นี้ตามที่ตั้งใจไว้ เวอร์ชั่น Flutter ของคุณต้องเป็น (ตรงเป๊ะ) 3.3.9 และMelos เวอร์ชั่น 2.8.0จะต้องติดตั้ง หากคุณต้องการมี Git Convention สำหรับทีมของคุณ คุณอาจติดตั้งlefthook ฉันจะพูดถึง Melos และ Lefthook ในตอนหลังของซีรีส์นี้ สำหรับตอนนี้ ต่อไปนี้เป็นวิธีการเรียกใช้โปรเจ็กต์
หมายเหตุ: ฉันใช้ VSCode และ macOS ดังนั้นฉันจึงไม่สามารถรับประกันได้ว่ามันจะทำงานบน Android Studio หรือ Windows ด้วย
2.1. การติดตั้งเมลอส
Clean Architecture ต้องการให้เราแบ่งโครงการออกเป็นหลายโมดูล (แพ็คเกจ) ซึ่งเป็นเหตุผลว่าทำไมเราจึงต้องการเครื่องมือสำหรับจัดการแพ็คเกจ — Melos ในการติดตั้ง Melos:
dart pub global activate melos 2.8.0
export PATH="$PATH:<path to flutter>/flutter/bin"
export PATH="$PATH:<path to flutter>/flutter/bin/cache/dart-sdk/bin"
export PATH="$PATH:~/.pub-cache/bin"
หลังจากติดตั้ง Melos แล้ว คุณจะต้องเรียกใช้คำสั่งmake gen_env
หรือdart run tools/gen_env.dart
. นี่เป็นเครื่องมือที่ฉันสร้างขึ้นเอง เพื่อสร้างenv
โฟลเดอร์เพื่อให้เราสามารถประกาศความลับ เช่น Google API_KEY, ข้อมูลรับรองความถูกต้องพื้นฐาน ฯลฯ เครื่องมือนี้จะอ่านค่าใน.env
ไฟล์ แล้วใส่ลงในdart-define
แฟล็กเมื่อแอพ Flutter วิ่งหรือสร้าง

2.3. กำลังสร้างไฟล์
เมื่อเราได้env
โฟลเดอร์แล้ว เราก็แค่เรียกใช้คำmake sync
สั่ง นี่คือชวเลขสำหรับสามคำสั่ง:
melos bootstrap
melos run l10n
melos run force_build_all
ถัดไป คำสั่งmelos run l10n
ช่วยสร้างคลาสS
จาก.arb
ไฟล์ในresources
โมดูล

สุดท้าย คำสั่งmelos run force_build_all
ช่วยสร้างไฟล์.g.dart
, .freezed.dart
ฯลฯ บนแพ็คเกจทั้งหมดที่มีbuild_runner
ไลบรารี
ตอนนี้เรียกmake run_dev
ใช้แอพและสนุกได้เลย!
3. สถาปัตยกรรมโครงการ
สรุปแล้วเรามี 6 โมดูล (แพ็คเกจ)
- แอป
- โดเมน
- ข้อมูล
- ทรัพยากร
- ใช้ร่วมกัน
- โปรแกรมเริ่มต้น
3.1. โมดูลแอพ
นี่คือที่ที่เราจะเขียนโค้ด UI และ Bloc นอกจากนี้ยังเป็นที่ที่เราจะจัดเก็บทรัพยากรของเรา เช่น สี มิติ และลักษณะข้อความ นี่คือที่ที่เราจะประกาศฟังก์ชัน main() ของเราเป็นจุดเริ่มต้นเพื่อให้แอปของเราทำงาน

ทั้งสองโฟลเดอร์app_icon
และsplash
มีไฟล์ เรา app-icon.yaml
จึงsplash.yaml
สามารถกำหนดค่าไอคอนแอปและหน้าจอสแปลชได้ตามคำแนะนำของไลบรารีflutter_launcher_iconsและflutter_native_splash
app
โฟลเดอร์ประกอบด้วยซึ่งAppBloc
เป็น Bloc ที่จัดเตรียมไว้ในMaterialApp
ที่ซึ่งหน้าจอทั้งหมดในแอปสามารถดึงข้อมูลออกมาได้ หรืออีกนัยหนึ่งคือ มันเหมือนกับ Bloc ที่แบ่งปันไปยังทุกหน้าจอ คุณสามารถใช้AppBloc
สำหรับงานที่ต้องอัปเดต UI ของหน้าจอทั้งหมด เช่น การตั้งค่าภาษา การสลับโหมดมืด/โหมดสว่าง
โฟลเดอร์คือ ที่ ที่เราจะเขียนโค้ดคลาส พื้นฐานbase
เช่นBasePageState
, BaseBloc
, BaseEvent
และBaseState
โฟลเดอร์shared_view
นี้เป็นที่ที่ฉันจะเขียนโค้ด UI ที่ใช้ร่วมกันในหลายหน้าจอ จากนั้นจึงจะนำไปใช้กับโปรเจ็กต์อื่นๆ ได้ ตัวอย่างเช่น: app_text_field
เป็นcircle_image
มุมมองกลุ่มทั่วไปที่เห็นในหน้าจอเข้าสู่ระบบและลงทะเบียน
โฟลเดอร์common_view
นี้เป็นที่ที่ฉันจะเขียนโค้ด UI ที่ใช้ร่วมกันในหลายหน้าจอ จากนั้นจึงจะนำไปใช้กับโปรเจ็กต์อื่นๆ ได้ ตัวอย่างเช่น , common_dialog
, common_app_bar
สามารถcommon_scaffold
ใช้ซ้ำได้ในหลายโปรเจ็กต์ แม้ว่าเราอาจต้องการปรับแต่งเล็กน้อยเพื่อให้ตรงกับสไตล์
โฟลเดอร์config
คือที่ที่ฉันจะเรียกใช้ฟังก์ชันการกำหนดค่าสำหรับโมดูลแอป เช่นFirebase.initializeApp()
โฟลเดอร์di
นี้มีไว้สำหรับการตั้งค่า DI สำหรับโมดูลแอป
โฟลเดอร์exception_handler
จะดูแลข้อยกเว้นทั้งหมดในโครงการ
โฟลเดอร์helper
มีคลาส Helper ที่ใช้สำหรับโมดูลแอปโดยเฉพาะ
โฟลเดอร์navigation
คือที่ที่เราจะประกาศหน้าจอตลอดจนกล่องโต้ตอบและแผ่นงานด้านล่างที่ใช้ในแอป
โฟลเดอร์resource
คือที่ที่เราจะประกาศทรัพยากรสำหรับ UI เช่น สี มิติ และลักษณะข้อความ
โฟลเดอร์ui
คือที่ที่ฉันจะเขียนโค้ด UI และ Bloc สำหรับแต่ละหน้าจอ
โฟลเดอร์utils
นี้เป็นที่ที่ฉันจะเขียนโค้ดฟังก์ชัน utils ที่ใช้สำหรับโมดูลแอปโดยเฉพาะ
3.2. โมดูลโดเมน

เช่นเดียวกับโมดูลแอป โฟลเดอร์config
และdi
ให้บริการตามวัตถุประสงค์ที่คล้ายกันในโมดูลโดเมน
โฟลเดอร์entity
ประกอบด้วยคลาสเอนทิตี เช่น ผู้ใช้และการจอง
โฟลเดอร์navigation
มีAppNavigator
คลาสซึ่งรับผิดชอบpush
, replace
, และpop
หน้าจอ
โฟลเดอร์repository
คือตำแหน่งที่เราจะประกาศคลาส Abstract Repository ในโครงการ
โฟลเดอร์usecase
คือที่ที่เราจะประกาศ Use Cases ในโครงการ
3.3. โมดูลข้อมูล

ในทำนองเดียวกันโฟลเดอร์config
และdi
ให้บริการเพื่อจุดประสงค์เดียวกันที่นี่
โฟลเดอร์graphql
ประกอบด้วย.graphql
และschema.graphql
ถ้าโครงการของคุณใช้ GraphQL ไฟล์build.yaml
นี้ใช้สำหรับกำหนดค่าไฟล์ที่สร้างขึ้นซึ่งเกี่ยวข้องกับ GraphQL
โฟลเดอร์repository
มีคลาส Repository Implementation นอกจากนี้ยังมีหลายโฟลเดอร์:
converter
: สำหรับคลาสตัวแปลงการเข้ารหัสmapper
: สำหรับการเข้ารหัสคลาส Mapper ซึ่งจะใช้ในการแมปแบบจำลองข้อมูลเข้ากับเอนทิตีmodel
: สำหรับคลาสโมเดลข้อมูลการเข้ารหัสsource
: สำหรับการเข้ารหัสคลาสพื้นฐานเช่น RestApiClient, GraphQlApiClient รวมถึงการจัดหาแหล่งข้อมูลในโครงการเช่น AppApiService, AppDatabase, AppSharedPreferences, LocationDataSource

นี่เป็นโมดูลที่เรียบง่าย มีเฉพาะ.arb
ไฟล์ที่มีสตริงสำหรับการแปลเป็นภาษาท้องถิ่น โมดูลนี้แทรกเข้าไปในโมดูลอื่นอีกสองโมดูลapp
ดังนั้นdomain
คลาส UI, Bloc, Entity และ Use Case จึงสามารถใช้ได้
3.5. โมดูลที่ใช้ร่วมกัน

ตามชื่อที่แนะนำ โมดูลนี้จัดเตรียมค่าคงที่ คลาสข้อยกเว้นที่กำหนดเอง คลาสตัวช่วย มิกซ์อิน และฟังก์ชัน utils สำหรับโมดูลอื่นๆ ที่จะใช้
3.6. โมดูล Initializer

โมดูลนี้มีเฉพาะคลาส AppInitializer คลาสนี้รับผิดชอบในการรวบรวมการกำหนดค่าทั้งหมดจากโมดูลอื่นในinit()
ฟังก์ชัน ฟังก์ชันmain()
จะต้องเรียกใช้init()
ฟังก์ชันนี้เพื่อดึงการกำหนดค่าทั้งหมดจากโมดูลทั้งหมดเท่านั้น
3.7. โฟลเดอร์และไฟล์อื่นๆ
โฟลเดอร์.github
นี้มีไว้สำหรับกำหนดค่า CI/CD หากโปรเจ็กต์ของคุณใช้ Github Actions
โฟลเดอร์.lefthook
และไฟล์lefthook.yml
มีไว้สำหรับกำหนดค่าแบบแผน Git โดยใช้ไลบรารีของ lefthook
โฟลเดอร์.vscode
นี้มีไว้สำหรับประกาศการตั้งค่า VSCode ที่ใช้สำหรับโครงการนี้
โฟลเดอร์.env
มีไว้สำหรับประกาศความลับสำหรับแต่ละสภาพแวดล้อม
โฟลเดอร์tools
มีเครื่องมือที่ฉันสร้างขึ้น
ไฟล์analysis_options.yaml
นี้มีไว้สำหรับประกาศกฎ linter ของสองไลบรารีflutter_lintsและdart_code_metrics
ไฟล์bitbucket-pipelines.yml
นี้มีไว้สำหรับกำหนดค่า CI หากโครงการของคุณใช้ Bitbucket Pipelines
ไฟล์makefile
ติดตามคำสั่งทั้งหมดที่ใช้ในโครงการ ตัวอย่างเช่น คำสั่งmake format
ช่วยจัดรูปแบบรหัสทั้งหมดที่มี.dart
นามสกุลไฟล์ภายในโครงการ มีคำสั่งอื่นๆ อีกมากมายmake test
ที่เรียกใช้การทดสอบหน่วยในทุกโมดูล
ไฟล์melos.yaml
นี้ใช้สำหรับกำหนดค่า Melos
บทสรุป
ทำไมเราต้องสร้างโมดูลอื่นสำหรับการกำหนดค่าเริ่มต้น คลาสตัวทำแผนที่ เอนทิตี และกรณีการใช้งานคืออะไร ฉันจะพูดถึงสิ่งเหล่านี้ในส่วนที่สองของซีรี่ส์นี้ — Clean Architecture