Expense Tracker App
แอปจัดการรายจ่ายจากคอร์ส Flutter Academind Section 5-6 — เป็นแอปแรกที่ประกอบ concept หลายตัวเข้าด้วยกัน
Widget Tree
graph TD MaterialApp --> Scaffold Scaffold --> AppBar["AppBar + IconButton"] Scaffold --> Column Column --> Chart["Chart (FractionallySizedBox)"] Column --> ExpensesList["ExpensesList (ListView.builder)"] ExpensesList --> Dismissible["Dismissible + ValueKey"] Dismissible --> ExpenseItem ExpenseItem --> Card Card --> Row Row --> ColLeft["Column (title + date)"] Row --> Spacer Row --> RowRight["Row (icon + amount)"]
สิ่งที่เรียนจาก Section 5
UI & Layout
- AppBar +
actions— แถบบน + ปุ่ม IconButton เปิด modal - Card + Padding + Row + Spacer — layout ของแต่ละ expense item
- ListView.builder — สร้าง item เฉพาะที่เห็นบนจอ ประหยัด memory (ต่างจาก Column ที่สร้างทุกตัว)
- Stack — ซ้อน widget ทับกัน
State Management
- StatefulWidget — จัดการ
_registeredExpenseslist - setState() — บอก Flutter ให้ rebuild UI เมื่อ list เปลี่ยน
User Input
- TextEditingController +
dispose()— จัดการ input title/amount (ต้อง dispose ไม่งั้น memory leak) - DropdownButton — เลือกหมวดหมู่ expense
- showDatePicker +
async/await— เลือกวันที่ - Validation —
double.tryParse(),.trim(),.isEmpty, logical operators (||,&&)
Dialogs & Overlays
- showModalBottomSheet — ฟอร์มเพิ่ม expense (เปิดจากด้านล่าง)
- showDialog + AlertDialog — แจ้ง validation error
- ScaffoldMessenger + SnackBar — แจ้งเตือน + Undo ลบ expense (
persist: false) - Navigator.pop() — ปิด modal/overlay
Swipe to Delete
- Dismissible + ValueKey — ปัดลบ item ออกจาก list
- SnackBar Undo — กดย้อนกลับเพิ่ม item คืนได้
Data Modeling
- enum
Category+categoryIconsmap — หมวดหมู่แบบ type-safe - Initializer list (
:) — auto-generateidด้วย UUID v4 - ExpenseBucket +
forCategorynamed constructor — จัดกลุ่มค่าใช้จ่ายสำหรับกราฟ
Chart
- FractionallySizedBox — bar สูงตามสัดส่วน (0.0–1.0) ของค่าใช้จ่ายสูงสุด
- ExpenseBucket +
totalExpensesgetter — คำนวณยอดรวมต่อหมวด
สิ่งที่เรียนจาก Section 5 (Theming)
- ThemeData().copyWith() + ColorScheme.fromSeed() — กำหนด theme ทั้งแอปจากจุดเดียว
- Dark mode —
ThemeData.dark().copyWith()+kDarkColorScheme - MediaQuery — เช็ค
platformBrightnessสำหรับ dark/light kprefix — convention ตั้งชื่อ global/constant variables (เช่นkColorScheme)- CardThemeData — Flutter เวอร์ชันใหม่ใช้แทน
CardTheme
สิ่งที่เรียนจาก Section 6 (Responsive & Adaptive)
Widget Size Constraints
- Widget ถูกกำหนดขนาดจาก parent constraints + child preferences
- ปัญหา: unconstrained ซ้อน unconstrained (เช่น ListView ใน Column) → ล้นจอ
- Expanded แก้ปัญหา — เปลี่ยนจาก infinity เป็น “เท่าที่เหลือ”
- ใช้ DevTools → Layout Explorer ดู constraints ของแต่ละ widget
Responsive Layout
- MediaQuery.of(context).size.width — เช็คขนาดจอ เลือก Column vs Row
- LayoutBuilder — รู้ constraints ของ parent (ต่างจาก MediaQuery ที่ดูจอทั้งหมด)
- Collection if/else —
if (width >= 600) Row(...) else Column(...)ใน list ไม่ต้องมี{} - เปลี่ยน layout ตาม width: Title+Amount แถวเดียว, Dropdown+Date แถวเดียว
Keyboard & Scroll
- MediaQuery.of(context).viewInsets.bottom — รู้ว่า keyboard กินพื้นที่เท่าไหร่
- SingleChildScrollView — ทำให้ modal scroll ได้เมื่อ keyboard เปิด
- SizedBox(height: double.infinity) — บังคับ modal เต็มจอ
SafeArea
- useSafeArea: true — ใน
showModalBottomSheetหลีก notch/camera อัตโนมัติ - Scaffold ใช้ SafeArea ภายในอยู่แล้ว — modal ต้องเปิดเอง
Adaptive (ปรับตาม Platform)
Platform.isIOSจากdart:io— เช็คว่ารันบน iOS หรือ Android- CupertinoAlertDialog จาก
cupertino.dart— dialog แบบ iOS - Flutter ปรับบางอย่างอัตโนมัติ: AppBar title จัดกลางบน iOS, font ต่างกัน
Key Points
- แอปแรกที่ประกอบ concept หลายตัว: State, Input, Theme, Layout, Overlay, Validation
ListView.builder+Dismissibleคือ pattern มาตรฐานสำหรับ swipe-to-delete listTextEditingControllerต้องdispose()เสมอ — ไม่งั้น memory leakThemeData().copyWith()ตั้ง style จากจุดเดียว ไม่ต้อง hardcode สีทั่วแอป- Validation ใช้
tryParse(ไม่ throw) + logical operators รวมเงื่อนไข FractionallySizedBoxใช้สัดส่วน 0.0–1.0 แทนค่า pixel ตายตัวExpandedแก้ปัญหา unconstrained ซ้อน unconstrained (ListView ใน Column, Chart ใน Row)LayoutBuilderดู parent constraints /MediaQueryดูขนาดจอ — เลือกใช้ตามสถานการณ์Platform.isIOS+CupertinoAlertDialogทำให้ dialog ดู native บน iOS
Related
- Flutter — framework ที่ใช้สร้างแอปนี้
- Widgets — reference ของทุก widget ที่ใช้
- Dart — ภาษาที่เขียน (enum, initializer list, async/await, tryParse)
- UUID — ใช้ v4 สร้าง unique ID
- Responsive Design — Section 6 ปรับ layout ตามขนาดจอ
- Flutter Academind Complete — สรุปคอร์สทั้งหมด