TaskFlow คือ App (To-do-List) บริหารจัดการงาน (Task Management App) ที่ออกแบบมาเพื่อช่วยให้ผู้ใช้สามารถ จัดลำดับความสำคัญ วางแผน และ ติดตามงานได้อย่างเป็นระบบ โดยเน้น UI ที่เรียบง่าย ใช้งานง่าย และพัฒนาด้วย Flutter
- Features
- Packages
- Tech Stack
- UI UX Design Figma
- Project Structure
- ⭐ Widget Lifecycle
- ⭐ Asynchronous
- ⭐ Flutter Structure Tools
- ⭐ Flutter DevTools
- ⭐ Running a Flutter app on a physical device for testing Flutter Devtools
- ⭐ Flutter Inspector
- ⭐ How to Configure and Deploy an App Manually
- 🚨 How to fix Gradle build errors
- 🚨 How to fix the launcher icon image not changing to the new image
- 📝 สร้าง / แก้ไข / ลบ Task ได้อย่างสะดวก
- 🎯 กำหนดระดับความสำคัญของงาน (High / Medium / Low)
- 🗓️ กำหนดตั้งเวลาของตารางงาน
- 📊 แสดงสถานะความคืบหน้าของงาน
- 🎨 ยิง API เพื่อดึงรูปภาพแมวมาใช้เป็นฉากพื้นหลัง
- 🔐 ระบบ Login / Logout
- 👤 ระบบสมัครสมาชิก (Sign Up)
- 🔍 ระบบค้นหางาน (Search)
- 🔑 ระบบ Reset Password
- 📱 รองรับ Mobile
- Packages/Library ที่ใช้ทำงาน ร่วมกับ Flutter
- Download ได้จาก Link นี้ : The official repository for Dart and Flutter packages.
-
- #ดึง icon Style แบบ iOS มาใช้งาน
-
- #ใช้สำหรับ รองรับหลายภาษา , เพื่อ Format(แปลง) ข้อมูล เช่น วันที่ เวลา ตัวเลข เงิน ให้ตรงกับประเทศ/ภาษา ตามภูมิภาคนั้น
-
- #HTTP client สำหรับเรียก REST API
-
- #ใช้จัดการ business logic และ state
-
- #ใช้สำหรับ เก็บข้อมูล ที่มีการเปลี่ยนแปลงในหน้า UI ลงไปในเครื่อง , กรณีที่ต้องการ เรียกข้อมูลนำมาใช้งานอีกครั้ง เช่น คำนวณ หรือ เปลี่ยนแปลงค่าใหม่
-
- #ใช้เพื่อเปรียบเทียบ object , ถ้าค่า object เหมือนกัน จะได้ไม่ต้อง rebuild UI ใหม่ทั้งหน้า
-
- #ใช้เขียน unit test สำหรับทดสอบ BLoC , ว่าการทำงานถูกต้องไหม , มี Bug หรือเปล่า
-
- #ใช้ mock object(สร้างข้อมูลปลอม) ตอน test , โดยไม่ต้องไปแตะของจริง , ใช้ร่วมกับ Unit Testing
-
- #ใช้สำหรับ ตรวจสอบข้อมูลที่ถูกป้อนเข้ามาในช่อง TextFormField , ว่าข้อมูลพิมพ์มาในช่องกรอกนั้น ถูกต้องตามเงื่อนไขไหม
-
- #เชื่อมต่อ Firbase กับ Flutter , เพื่อเรียกใช้เครื่องมือจาก Firebase
-
- #เรียกใช้เครื่องมือ ระบบ Login / Register ใน Firebase
-
- #เรียกใช้เครื่องมือ ฐานข้อมูล NoSQL ของ Firebase , ใช้เก็บข้อมูลแบบ real-time
-
- #ใช้เชื่อมต่อ bloc เข้ากับ Flutter ในหน้า UI
-
- #เรียกใช้งาน Font ของ Google มาใช้งานใน Flutter
-
- #เรียกใช้ เครื่องมือ ทำให้รูปภาพมีความโปร่งใสมากขึ้น
-
- #ใช้สร้าง App Icon อัตโนมัติ
-
- Framework: Flutter
- Language: Dart
- Database: Firebase
- State Management: Bloc
- Design: Figma
- Version Control: Git & GitHub
Project TaskFlow ได้มีการออกแบบหน้า UI ล่วงหน้าด้วย Figma เพื่อกำหนดโครงสร้างหน้าจอ (Layout), Component และ User Flow ก่อนนำไปพัฒนาเป็นแอปด้วย Flutter
- 4-Column Grid For Mobile
- 8-Column Grid For Tablet
- 12-Column Grid For Website
Project TaskFlow ออกแบบ UI โดยใช้ 4-Column Grid System เพื่อให้เลย์เอาต์มีความเป็นระเบียบ รองรับการพัฒนาเป็น Mobile Application ได้ง่าย และสอดคล้องกับหลัก Responsive Design
🟥 Columns (Red area):
- คือพื้นที่ “คอลัมน์จริง” ที่ใช้วางเนื้อหา , เช่น Card, Text, Button, List, Image
🟩 Gutters (Light green area):
- เป็นช่องเว้นระยะห่างระหว่างคอลัมน์ , ช่วยให้ เนื้อหาไม่ติดและชิดกันมาก และ ทำให้อ่านง่าย ,
🟪 Margins (Light purple area):
- ขอบซ้าย–ขวาของหน้าจอ , คือ ระยะห่างของขอบ ระหว่างหน้าจอกับ Column แรก/สุดท้าย , ป้องกันไม่ให้เนื้อหาชิดขอบจอเกินไป
- ช่วยให้การจัดวางองค์ประกอบมีความสมดุล , เป็นระเบียบเรียบร้อย
- รองรับการขยาย Layout ในอนาคต
- Splash Screen
- SignIn Screen
- SignUp Screen
- Forget Password Screen
- Welcome Screen
- Create Task Screen
- EditTask Screen
- Search Screen
- Setting Profile Screen
- See All Tasks Screen
-
ใช้ Component Library (UI Kit) เพื่อลดความซ้ำซ้อน
-
กำหนดสี, Typography และ Spacing ให้สม่ำเสมอทั้งแอป
-
ออกแบบให้รองรับ Mobile เป็นหลัก (Mobile-first)
-
-
⭐ AUDIT (ตรวจสอบ)
- ตรวจสอบ งาน design จาก designers , ก่อนจะนำไปเขียน Code จริง
-
⭐ DESIGN & DOCUMENTATION
- ออกแบบ + ทำเอกสาร
- สร้าง “ระบบ” ให้คนอื่นนำไปใช้ต่อได้ ไม่ใช่แค่ ออกแบบสวยอย่างเดียว
-
⭐ QA (Quality Assurance)
- ตรวจสอบคุณภาพของงาน ตั้งแต่ ออกแบบ → เขียน Code → ก่อนปล่อยใช้งานจริง
- 📌 เพื่อให้มั่นใจว่า “ของที่ปล่อยไป ไม่พัง ไม่มั่ว ไม่หลุดตามมาตรฐาน”
-
⭐ MAINTAIN (ดูแลระยะยาว)
- ค่อยติดตาม ดูแล-แก้ไข-ปรับปรุง ของงาน, หลังจากที่ ปล่อย App ไปบน PlayStore แล้ว
- ดูแล และ ติดตามผล ในระยะยาว หลังปล่อยใช้งานเรียบร้อย
-
-
-
Auto Layout
- ทำให้ UI ขยาย / หด ได้อัตโนมัติ , โดยไม่ต้องไปปรับเองทุกจุด
-
Components
- ใช้สร้าง ของที่นำกลับมาใช้ซ้ำได้ (reuse)
-
Variables
- ค่ากลาง (Reusable values) ที่เรากำหนดไว้ครั้งเดียว แล้วหยิบค่ากลาง นำไปใช้ซ้ำกับหลายๆที่ , หลาย ๆ ส่วนของ Design
-
Variants
- Component เดียว , แต่แสดงหน้าตา UI หลาย “สถานะ” , "หลาย Style" ,"หลายแบบ" (state / version)
-
-
- default
- hover
- pressed
- disabled
-
🔘 default
- สภาพ ปกติของปุ่ม , โดยยังไม่มีการ แตะ , กด หรือะไร
-
🖱️ hover
- เลื่อนเมาส์ มาวางเหนือปุ่ม (ยังไม่กด)
- ตัวอย่าง:
- สีปุ่ม จะดูเข้มขึ้นทันที
- 👉 บอกผู้ใช้ว่า “ปุ่มนี้กดได้นะ”
-
👆 pressed (หรือ active)
- ขณะกำลังกดปุ่มอยู่ (คลิกค้าง / แตะ)
-
🚫 disabled
- ปุ่มถูกปิดใช้งาน
- กดไม่ได้
- ตัวอย่าง:
- สีเทา
- ตัวอักษรจาง
-
-
โครงสร้าง Project TaskFlow ถูกออกแบบตามแนวคิด Clean Architecture + BLoC Pattern เพื่อให้ Code แยกหน้าที่ชัดเจนดูแลรักษาและทดสอบได้ง่าย
lib/
│
├─ main.dart
├─ app_route.dart
│
└─ src/
├─ bloc/
│ ├─ sign_in/
│ ├─ sign_up/
│ ├─ sign_out/
│ ├─ forget_pass/
│ ├─ search/
│ ├─ week_calendar/
│ ├─ cat_api_image/
│ └─ app_bloc_observer.dart
│
├─ data/
│ ├─ models/
│ ├─ api_repository.dart
│ ├─ auth_repository.dart
│ └─ task_repository.dart
│
└─ pages/
├─ splash_page.dart
├─ sign_in_page.dart
├─ sign_up_page.dart
├─ forget_password_page.dart
├─ welcome_page.dart
├─ create_task_page.dart
├─ edit_task_page.dart
├─ search_page.dart
├─ see_all_list.dart
└─ setting_profile_page.dart
test/
│
├─ forget_pass_bloc_test.dart
├─ search_bloc_test.dart
└─ widget_test.dart -
- เป็นจุดเริ่มต้นเรียกใช้ Function การทำงาน ของ App Flutter
- ใช้ตั้งค่าเริ่มต้นต่าง ๆ ของ App , เช่น ไม่ว่าจะเป็นการกำหนด Routing ในการเปลี่ยนสลับไปยังหน้าเมนูอื่นๆ
-
- ใช้สำหรับ จัดการเส้นทางการนำทาง (Navigation / Routes) ของ App
- รวม ชื่อ route ทั้งหมดไว้ที่เดียว
- จัดการการเปลี่ยนหน้า (Page Navigation)
- ลดการเขียน
Navigator.push()ซ้ำ ๆ ทีละหน้าแทน - ทำให้ Code เป็นระเบียบและแก้ง่าย
-
-
คือ Folder ที่เก็บ Business Logic ของ App ตามสถาปัตยกรรม BLoC (Business Logic Component) ทำหน้าที่แยก “ตรรกะการทำงาน” ออกจาก “หน้าจอ UI” ให้ Code เป็นระเบียบ ทดสอบง่าย และดูแลง่าย
-
จะประกอบไปด้วย Files ต่างๆที่ทำงานเชื่อมต่อกับ Bloc เช่น , ระบบ Login , ระบบ Register , ระบบการจัดการปฎิทิน วัน/เดือน/ปี
-
- ควบคุมการทำงานหลัก รับ event → ประมวลผล → ส่ง state ไปแสดงผลหน้าจอ UI
-
- เหตุการณ์ที่เกิดจากผู้ใช้หรือระบบ (เช่น กดปุ่ม, โหลดข้อมูล)
-
- สถานะของหน้าจอ (เช่น กำลังโหลด, โหลดสำเร็จ, error)
-
graph TD
User[User] --> UI[UI Widgets]
subgraph BLoC_Layer [BLoC Layer]
Event[Event]
Bloc[BLoC]
State[State]
end
subgraph Data_Layer [Data Layer]
Repository[Repository]
DataSource[API / Firebase / Server]
end
UI -->|dispatch| Event
Event --> Bloc
Bloc -->|emit| State
State --> UI
Bloc -->|request data| Repository
Repository --> DataSource
DataSource --> Repository
Repository --> Bloc
-
สรุป :
เหตุผลที่นำ Bloc มาใช้ใน Project , เพื่อแยกการทำงานหน้าที่ให้มันชัดเจน
- UI (pages/widgets) → แสดงผลอย่างเดียว
- BLoC → เอาไว้จัดการ Logic / State
- Repository (data) → เอาไว้จัดการข้อมูล (API, Firebase, DB)
- BLoC ช่วยให้ rebuild UI เท่าที่จำเป็น , ไม่ทำให้ UI rebuild ทั้งหน้าโดยไม่จำเป็น โดย ทำให้ App มีการทำงาน Performance ที่เสถียรดีขึ้น
-
- ใช้ debug / log การทำงานของ Bloc ทั้งแอป
- ดูว่า event ไหนถูกเรียก และ state เปลี่ยนเป็นอะไร
-
- เป็น Folder ที่รับผิดชอบเรื่อง , การดึงข้อมูล , การบันทึกข้อมูล
- การแปลงข้อมูลให้อยู่ในรูปแบบที่ App ใช้งานได้ โดย , ไม่เกี่ยวกับ UI โดยตรง
-
- คือ Folder ที่ใช้เก็บ โครงสร้างข้อมูล (Data Model / Entity) ของ App พูดง่าย ๆ คือเป็น พิมพ์เขียวของข้อมูล ที่ App ใช้งาน
- กำหนดรูปแบบข้อมูล
- แปลงข้อมูลระหว่าง , JSON ↔ Object (จาก API / Firebase) , - เป็นชนิดข้อมูลที่ BLoC สามารถเรียกใช้งาน
-
- เป็น File ที่รวม Function การทำงาน ไว้ใช้ การเรียก API
- ทำหน้าที่ แปลง JSON → Model , เพื่อให้ Bloc สามารถนำข้อมูลไปใช้งานได้ง่าย
- หาก API มีการเปลี่ยนแปลง หรือแก้ไข ก็จะไม่กระทบ BLoC / UI โดยตรง ,
-
- รวม Function ที่ใช้จัดการ ระบบ Login / สมัครสมาชิก / ออกจากระบบ / Reset รหัสผ่าน โดยใช้ Firebase Authentication และ เรียกใ้ช Firestore เข้ามาทำงาน
- จะไม่เรียกใช้ Function ผ่านการทำงาน Bloc / UI โดยตรง , แต่ให้ไปเรียก Repository ที่ทำหน้าที่เป็นตัวกลางแทน
-
- รวมฟังก์ชันที่ใช้จัดการข้อมูล Task ใน Firestore , เช่น เพิ่ม, ดึง, แก้ไข, ลบ
-
- Folder ที่เก็บหน้าจอ (Screens / Pages) ของ App
- ไม่ควรมี Function logic หนัก ๆ หรือเรียก API โดยตรง ในหน้า UI
- สามารถใข้เชื่อมต่อกับ BLoC ได้
-
-
🚀
splash_page.dart- หน้าแรกสุดของ App - แสดง Logo / Loading -
🔐
sign_in_page.dart- หน้าเข้าสู่ระบบ -
📝
sign_up_page.dart- หน้าสมัครสมาชิก -
🔁
forget_password_page.dart- หน้าขอ Reset รหัสผ่าน -
👋
welcome_page.dart- หน้า แสดงภาพรวมของ Task ทั้งหมด -
➕
create_task_page.dart- หน้าสร้าง Task ใหม่ -
🛠️ edit_task_page.dart - หน้าแก้ไข ข้อมูล Task
-
🔍
search_page.dart- หน้าค้นหา Task -
📋
see_all_list.dart- หน้าแสดงรายการ Task ทั้งหมด -
⚙️
setting_profile_page.dart- หน้าตั้งค่า Profile - หน้าสำหรับ Logout
-
-
-
เป็น Folder ที่รวมการทำงานในส่วนของ Unit Testing ใช้สำหรับทดสอบ Code , ตรวจสอบหา Bug , ข้อผิดพลาดของการทำงานในระบบ
-
ตรวจสอบว่า function ทำงานถูกต้องไหม
-
ใช้ตรวจสอบว่าเมื่อ
- ส่ง Event เข้าไป , ให้ Bloc ประมวลผล , แล้ว State ที่ได้ออกมาถูกต้องหรือไม่
-
- ใช้สำหรับ ทดสอบ กรณีที่ ระบบ ได้ทำการ Reset Password ไปยัง Email สำเร็จ หรือไม่ หากส่งสำเร็จ จะให้แสดงผลลัพธ์อะไรกลับออกมา
- ใช้สำหรับ ทดสอบ กรณีที่ Internet ล่ม , server error แล้วระบบ ได้ทำการ Reset Password ไปยัง Email ไม่สำเร็จ หากส่งไม่สำเร็จ จะให้แสดงผลลัพธ์อะไรกลับออกมา
-
- ใช้สำหรับ ทดสอบ กรณีที่ หากเป็นสถานะตอนเริ่มต้น , ข้อความในช่อง ค้นหา ต้องว่าง , ต้องไม่มีการแสดงผลลัพธ์ในการค้นหา
- ใช้สำหรับ ทดสอบ กรณีที่ เราใส่ชื่อ Task ในช่องค้นหา , แล้วชื่อ Task มันมีอยู่ในระบบไหม , แล้ว แสดงผลลัพธ์ในช่องค้นหาไหม ,
- ใช้สำหรับ ทดสอบ ลองส่ง Event ชื่อ ClearSearch() เข้าไปใน Bloc , ข้อความในช่องค้นหาชื่อ Task , จะถูกล้างเป็น ค่าว่างใน ช่องค้นหาไหม
-
- ใช้สำหรับ ทดสอบ หน้าจอและ widget ของแอป เพื่อให้มั่นใจว่า UI แสดงผลและทำงานถูกต้อง
- มีการ เพิ่ม Repository ใน widget_test.dart เพราะ MyApp ถูกออกแบบให้ต้องรับ dependency(ทำงานแบบพึ่งพาอาศัยกัน) ให้ทำงานใน App ได้ , ไม่งั้นจะ Error
-
-
วงจรชีวิตของ widget ตั้งแต่ :
- สร้าง → แสดงผล → อัปเดต → ถูกทำลาย
-
- ช่วย ป้องกัน bug และ memory leak
- ช่วย ปรับปรุง performance (ไม่ rebuild เกินจำเป็น)
-
-
StatelessWidget
- ใช้แสดงผลอย่างเดียว ไม่มีการเปลี่ยนแปลงค่า State
- Build แค่ครั้งเดียว
-
StatefulWidget
- มี Lifecycle , มีการเปลี่ยนแปลงข้อมูล หรือ ค่า state ในหน้า UI บ่อยๆ
-
-
createState() ↓ initState() ↓ build() ↓ setState() → build() (วนซ้ำ) ↓ dispose()-
🔹 createState()
- สร้าง State object , ถูกเรียก ครั้งเดียว , ใช้ State นี้เก็บข้อมูลค่าที่มีการเปลี่ยนแปลง
-
🔹 initState()
- ถูกเรียก ครั้งเดียว
- ใช้สำหรับเตรียมค่าเริ่มต้น เช่น :
- เรียก API ครั้งแรก
- กำหนดค่า controller
-
🔹 build()
- สร้าง / อัปเดต การเปลี่ยนแปลงหน้า UI , จากค่า state ปัจจุบัน
- 📌 สำคัญมาก:
build ต้อง ทำงานเร็ว และ ไม่มี logic หนัก
-
🔹 setState()
- แจ้ง Flutter ว่า หน้า UI จะมีการ Update ข้อมูลใหม่น่ะ
- Flutter จะเก็บค่า state ที่มีการเปลี่ยนแปลง
- แล้วไปเรียก
build()ใหม่
-
🔹 dispose()
-
เป็นขั้นตอนสุดท้าย ใช้ ปิด / ล้างค่า / ยกเลิกการทำงานของ resource ต่าง ๆ
-
ใช้เมื่อ มีการเปลี่ยนหน้า , หรือปิดการใช้งาน App
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } /// Widget หลักของแอป class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( home: CounterPage(), ); } } /// StatefulWidget /// - ใช้เมื่อมี state ที่เปลี่ยนแปลงได้ class CounterPage extends StatefulWidget { const CounterPage({super.key}); /// 1️⃣ createState() @override State<CounterPage> createState() { print('createState()'); return _CounterPageState(); } } /// State class class _CounterPageState extends State<CounterPage> { int counter = 0; /// 2️⃣ initState() @override void initState() { super.initState(); print('initState()'); counter = 0; // กำหนดค่าเริ่มต้น } /// 3️⃣ build() @override Widget build(BuildContext context) { print('build()'); return Scaffold( appBar: AppBar( title: const Text('Widget Lifecycle Demo'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( 'Counter value:', style: TextStyle(fontSize: 18), ), Text( '$counter', style: const TextStyle(fontSize: 32), ), const SizedBox(height: 20), /// ปุ่มกดเพื่อเรียก setState() ElevatedButton( onPressed: () { /// 4️⃣ setState() setState(() { counter++; print('setState() → counter = $counter'); }); }, child: const Text('Increment'), ), ], ), ), ); } /// 5️⃣ dispose() @override void dispose() { print('dispose()'); super.dispose(); } } ```
-
-
-
ไม่ต้องรอให้คำสั่งใดคำสั่งหนึ่งเสร็จก่อน ถึงจะไปทำคำสั่งถัดไปได้
-
เหมาะกับงาน
- เรียก API
- อ่าน File
- เข้าถึงฐานข้อมูล
- หน่วงเวลา (
delay)
-
ประโยชน์
- 👉 ช่วยให้ ** App ไม่ค้าง / UI ไม่หยุดทำงาน**
-
🔹 Future
- เดียวจะมีค่าที่จะได้มาในอนาคตน่ะ , แต่ตอนนี้ยังไม่มีค่า
-
🔹 async
- ใช้ประกาศว่า Function นี้ ทำงานแบบ Asynchronous น่ะ
-
🔹 await
-
รอคำสั่งในบรรทัดนี้ ทำงานให้เสร็จ , แล้วค่อยไปทำงานคำสั่งในบรรทัดถัดไป
-
Future<void> loadData() async { print('Loading...'); await Future.delayed(Duration(seconds: 2)); print('Data loaded'); }
-
-
เป็น เมนู ที่ใช้สำหรับ ดูโครงสร้าง Widget Tree ของแอป Flutter แบบเป็นลำดับชั้น ใน Android Studio Code
-
ทำงานร่วมกับ Flutter Inspector
-
MaterialApp └─ Scaffold └─ AppBar └─ Column ├─ Text ├─ ElevatedButton └─ ListView- ดู โครงสร้าง Widget ทั้งหมด
- ตรวจว่า layout ซ้อนลึกเกินไปหรือไม่
- หา widget ที่ทำให้ UI ช้า
-
- View -> Tool Windows -> Structure
-
-
ในตัวอย่างต่อไปนี้ ภาพหน้าจอแรกจะแสดงไอคอน 3 อันพร้อมป้ายกำกับ (label) และภาพหน้าจอที่สองจะแสดงโครงร่างการจัดวางของแถว (rows) และคอลัมน์ (columns)
-
Container ใช้สำหรับ :
- เพิ่ม padding (ระยะด้านใน)
- เพิ่ม margin (ระยะด้านนอก)
- ใส่ border
- ใส่ สีพื้นหลัง
- ปรับขนาด ฯลฯ
-
Row ทั้งแถวถูกวางไว้ใน Container เพื่อเพิ่ม padding รอบ ๆ แถว
-
Column + Container ใช้เพื่อจัดระยะห่าง Layout , เนื่องจาก Column ไม่มี padding / margin , จึงต้องครอบด้วย Container
-
Text แต่ละอันถูกวางไว้ใน Container เพื่อเพิ่ม margin
-
-
เป็นเครื่องมือสำหรับ Debug วิเคราะห์ และปรับปรุงประสิทธิภาพ App Flutter
- ช่วยตรวจสอบ Performance (ประสิทธิภาพการทำงานของ App)
- ดูว่า function ไหน ทำให้ CPU ทำงานหนักจนเกินไป
- ตรวจสอบ memory usage (หน่วยความจำใช้ไปเท่าไร)
- ดู log จาก App แบบ real-time
-
-
- เปิด Visual Studio Code ขึ้นมา
- ไปที่ เมนู Run and Debug
- ทำการคลิก create launch.json file.
-
ให้ทำการเปิด File launch.json , แล้วแก้ไข "name": เป็น Debug , Profile , Release
// Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/? linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Debug", "request": "launch", "type": "dart" }, { "name": "Profile", "request": "launch", "type": "dart", "flutterMode": "profile" }, { "name": "Release", "request": "launch", "type": "dart", "flutterMode": "release" } ] }-
Debug mode
- เป็นโหมดการทำงานขณะพัฒนาที่เน้นการค้นหาและแก้ไขข้อผิดพลาด (Debug) โดยมี Feature สำคัญเช่น Hot Reload เพื่อดูผลลัพธ์ Code ที่แก้ทันที, แสดง Debugger เพื่อตรวจสอบ Code ทีละบรรทัด
- 📌 สามารถใช้ DevTools ใช้ได้เต็มรูปแบบ
- ข้อเสีย:
- App จะ ช้า และ File ใหญ่ กว่าปกติมาก
- คำเตือน:
⚠️ - ห้ามวัดความเร็ว Performance App ในโหมดนี้เด็ดขาด เพราะมันช้าเป็นปกติครับ
- ควรใช้ตอนไหน:
- เขียน Code /แก้บั๊ก
-
Profile mode
- เป็นโหมดที่ เน้นใช้ วัดประสิทธิภาพ (Performance) ใน App เป็นหลัก
- 📌 DevTools เหมาะที่สุดในโหมดนี้
- การทำงาน:
- จะคอมไพล์ Code คล้ายกับ Release Mode (เพื่อให้ได้ความเร็วที่ใกล้เคียงความจริงที่สุด)
- แต่จะ เปิดระบบ Tracing ไว้ เพื่อให้เครื่องมือ DevTools สามารถเข้าไปจับข้อมูลได้ (เช่น วัด FPS, ดูการใช้ Memory, ดู CPU)
- ข้อจำกัด:
- ❌ ห้าม Run บน Simulator/Emulator (ผลที่ได้จะไม่ตรงความจริง) ต้อง Run บนเครื่องจริงเท่านั้น
- ❌ ไม่มี Hot Reload
- ควรใช้ตอนไหน:
- เมื่อคุณรู้สึกว่า "App กระตุก", "เลื่อนหน้าจอไม่ลื่น", หรือ "กินแบตเตอรี่" ให้ Run โหมดนี้แล้วเปิด DevTools เพื่อหาสาเหตุครับ
-
Release mode
- โหมดนี้ มีไว้เพื่อ "ส่ง App ขึ้นไปยัง Store" (Google Play / App Store) หรือส่งให้ผู้ใช้ ได้ใช้งานจริง
- การทำงาน:
- เร็วที่สุด & เล็กที่สุด: Code จะถูกบีบอัด (Minified) และตัด Function Debugging/Tracing ออกทั้งหมด
- ข้อควรระวัง:
- ❌ Flutter DevTools จะไม่ทำงาน: คุณจะ Debug ดูตัวแปร หรือดู Log ไม่ได้แล้ว (หรือดูได้ยากมาก)
- ❌ Error บางอย่างอาจไม่โชว์: ในโหมด Debug ถ้ามี Error จอจะแดง (Red Screen of Death) แต่ใน Release App อาจจะแค่ค้างหรือปิดไปเลย (Crash)
- *ควรใช้ตอนไหน:
- ขั้นตอนสุดท้ายเมื่อทดสอบเสร็จแล้ว และต้องการ Build File
.apkหรือ.ipa
- ขั้นตอนสุดท้ายเมื่อทดสอบเสร็จแล้ว และต้องการ Build File
-
ให้ทำการโหลด Extension Flutter DevTools มาไว้ใน VS code , แล้วทำการ Run emulator เพื่อทดสอบ App
-
-
- Run App Flutter ก่อน
- ทำการ เลือก Emulator / มือถือจริง แล้วกด
▶️ Run - หรือใช้โหมดที่เหมาะกับการวัด Performance:
flutter run --profile
⚠️ DevTools จะไม่ทำงาน ถ้า App ยังไม่ Run - ไปที่เมนู:
- View → Tool Windows → Flutter DevTool
- ✅ ถ้าเชื่อมต่อสำเร็จ จะเห็น:
- Performance
- CPU Profiler
- Memory
- Network
-
-
🔴 Performance
- ใช้สำหรับ วิเคราะห์ประสิทธิภาพ การทำงาน ของ App
-
🔴 CPU Profiler
- ดูว่า Code ส่วนไหนที่ให้ CPU ทำงานหนัก / ช้า
- เหมาะเมื่อ:
- FPS ตก
- Animation ไม่ลื่น
- มี logic หนักใน
build()หรือsetState()
-
🔴 Memory
- ดูการใช้หน่วยความจำ (RAM)
- เหมาะเมื่อ:
- App ใช้ RAM เพิ่มเรื่อย ๆ
- App crash หลังใช้งานนาน
- หรือสงสัยลืมปิด Controller / StreamBuilder
-
🔴 Network
- ตรวจสอบการเรียก API และความเร็วอินเทอร์เน็ต
- เหมาะเมื่อ:
- โหลดข้อมูลช้า
- API ถูกเรียกหลายครั้งโดยไม่ตั้งใจ
-
🟢 สีของแท่งกราฟ
-
🟢 -> 🟦 UI (UI Thread) → build หนัก
- เวลาที่ Flutter ใช้ :
- สร้าง widget tree และ คำนวณ Layout
- ตัดสินใจว่า “ต้องวาดหน้า UI อะไรบ้าง”
- Run
build()
- ==📌 ถ้าสีฟ้านี้สูง → แปลว่า build หนัก / logic เยอะ / rebuild บ่อย==
-
🟢 -> 🔵 Raster (GPU Thread) → วาดภาพหนัก
- เวลาที่ Flutter ใช้ :
- ใช้ GPU แปลง UI ทั้งหมด
(ข้อความ, ไอคอน, รูปภาพ, สี, เงา)
ให้กลายเป็น จุดสีเล็ก ๆ (pixels) ไปแสดงภาพบนหน้าจอ
- ==📌ถ้าสีนี้สูง → มักเกิดจาก: ออกแบบ UI มีความซับซ้อนจนเกินไป==
-
🟢 -> 🟥 Jank
- frame ที่ใช้เวลานานเกินไป → จนทำให้ App กระตุก
- เกิดเมื่อ UI + Raster รวมกันเกิน 16.7 ms
-
-
🟠 FPS = 43 FPS (avg) หมายถึงอะไร
- ค่าเฉลี่ย Framerate = 43 เฟรมต่อวินาที
- น้อยกว่า 60 → App เริ่มจะกระตุก
-
🟠 เส้น 16.7 ms (เส้นอ้างอิง)
- ถ้าแท่งสูงเกินเส้นนี้ → เฟรมหลุด → กระตุก
-
🌸 Frame Analysis
- ใช้วิเคราะห์ความลื่นไหลของการแสดงผลหน้าจอ (FPS / Jank)
- ดูอะไรได้บ้าง:
-
เวลา render แต่ละ frame
-
แยกเป็น:
- UI thread → build / layout
- Raster thread → วาดภาพ
-
หา frame ที่เกิน 16.7 ms (60 FPS)
-
-
🌸 Timeline Events
- ดูเหตุการณ์ทั้งหมดที่เกิดขึ้นตามเวลา
-
🟣 Rebuild Stats
- ดูว่า widget ไหนถูก rebuild บ่อย
- 🟣 Count widget builds
- เมื่อเปิด ✔️ จะนับจำนวนครั้งที่ widget ถูก rebuild
- ใช้หาตัวต้นเหตุของ Performance Drop
- 🟣 Widget
- ชื่อ widget
- 🟣 Latest frame
- rebuild ใน frame ล่าสุด
- 🟣 Latest frame
- rebuild รวมทั้งหมด
- ==👉 ถ้า widget rebuild จำนวนครั้ง บ่อยเกินไป = App ทำงานช้า==
-
-
-
-
ต้องเปิดโหมดนักพัฒนา (Developer Options) ก่อน
-
-
หยิบมือถือ Android เข้าไปที่ Settings (การตั้งค่า)
-
ไปที่ About Phone (เกี่ยวกับโทรศัพท์)
-
ไปที่ Software information (ข้อมูลซอฟต์แวร์)
-
หาคำว่า Build Number (หมายเลขรุ่น)
-
จิ้มที่ Build Number รัวๆ ประมาณ 7 ครั้ง จนกระทั่งมีข้อความขึ้นว่า "You are now a developer!"
-
-
- ในบางมือถือรุ่นใหม่ จะมี Feature เป็น ตัว Block อัตโนมัติ , เพื่อความปลอดภัยของมือถือ จึงทำให้ไม่สามารถ เปิด USB Debuggin ได้
- ให้ไปทำการ ปิด ตัว Block อัตโนมัติ , โดยไปที่ Security & Privacy (ความปลอดภัยและความเป็นส่วนตัว)
- หาคำว่า Auto Blocker (ตัวบล๊อคอัตโนมัติ) , แล้วทำการ ปิดทิ้ง
- กลับไปที่ เมนู Developer mode
- ทำการ เปิด USB Debugging
- 💡 กรณีที่ไม่สามารถเปิด USB Debugging
- สาย USB อาจมีผล: สายชาร์จบางเส้น "ชาร์จไฟได้อย่างเดียว ส่งข้อมูลไม่ได้" ถ้าเสียบแล้วคอมนิ่งสนิท ลองเปลี่ยนสายดูครับ
- ถอดสาย USB ออกแล้วเสียบใหม่
- ปรับโหมดการเชื่อมต่อ USB บนมือถือเป็น PTP หรือ MTP , โดยไปที่
- เปิดเครื่องโทรศัพท์
- ไปที่ Developer mode
- เลือก Default USB configuration (การกำหนดค่า USB เริ่มต้น)
- กดเข้าไปแล้วเลือก MTP (ถ่ายโอนไฟล์) หรือ PTP (ถ่ายโอนรูปภาพ)
-
- เสียบสาย USB จากมือถือ เข้า คอมพิวเตอร์
- สำคัญมาก: ที่หน้าจอมือถือจะมี Popup เด้งขึ้นมาถามว่า "Allow USB debugging?" ให้กด Allow (อนุญาต)
-
- เมื่อตั้งค่าเสร็จแล้ว ให้กลับมาที่ Android Studio หรือ VS Code ครับ
- วิธีเช็คว่าคอมมองเห็นมือถือหรือยัง: เปิด Terminal ในโปรเจกต์ แล้วพิมพ์:
- flutter devices
- ถ้าสำเร็จ จะมีชื่อมือถือของคุณโผล่ขึ้นมาครับ (เช่น
SM A528BหรือiPhone of ...) - Run ผ่าน Terminal :
-
แบบธรรมดา (Debug):
- flutter run
-
แบบทดสอบ Performance (Profile):
- flutter run --profile
-
แบบตัวจริง (Release):
- flutter run --release
-
ทิ้งท้าย , ไม่จำเป็น ต้องเปิด หรือ Download App จาก Play Store ที่เราได้ทำการ Upload ไปยังบน PlayStore , เพื่อมาทดสอบครับ
- แอปจาก Play Store เป็น Release build
- ❌ ไม่สามารถเชื่อมต่อ DevTools ได้
-
-
-
เป็นเครื่องมือ , ตรวจสอบและวิเคราะห์โครงสร้าง UI ของ App Flutter แบบ Real-time
-
สามารถดูโครงสร้าง Widget Tree ได้
-
ตรวจสอบว่า widget ไหนซ้อนอยู่กับอะไร
-
Check ขนาด (size), padding, margin, alignment
-
หา layout ที่ล้นจอ / จัดตำแหน่งผิด
-
ใช้ตรวจสอบปัญหา Performance / ความเร็ว (เบื้องต้น)
-
-
🔴 Select widget mode (โหมดเลือก Widget) 🔍
- เมื่อกดปุ่มนี้ แล้วไปจิ้มที่หน้าจอมือถือตรงส่วนไหนก็ได้ ในโปรแกรม (VS Code/Android Studio) จะกระโดดไปไฮไลท์ Code ของ Widget ตัวนั้นให้ทันที
- ประโยชน์
- เอาไว้หาว่า "ปุ่มนี้มันอยู่ไฟล์ไหนนะ?" หรือ "กล่องนี้มันซ้อนอยู่ใน Column ไหน"
-
🔴 Show implementation widgets
- ไว้แสดงไส้ในของ Widget
- ปกติเวลาเราเขียนโค้ด เราจะใช้ Widget สำเร็จรูปที่ Flutter เตรียมมาให้ เช่น
Container,Text, หรือRaisedButton, แต่ในความเป็นจริง "เบื้องหลัง" ของ Widget พวกนี้ มันถูกสร้างขึ้นมาจาก Widget ย่อยๆ อีกหลายตัวประกอบร่างกัน -
-
แบบปกติ (ปิด Show implementation widgets):
- ใน Tree จะเห็นแค่บรรทัดเดียว:
Text - ข้อดี: ดูง่าย รู้เรื่องว่าตรงนี้คือข้อความ
- ใน Tree จะเห็นแค่บรรทัดเดียว:
-
แบบละเอียด (เปิด Show Implementation widgets):
- ใน Tree จะเห็นยาวเหยียด:
Text→RichText→RawGestureDetector→_RenderObjectWidget - ข้อดี: เห็นการทำงานลึกๆ ว่า Flutter วาดข้อความยังไง
- ใน Tree จะเห็นยาวเหยียด:
-
🛠️ จะเปิดใช้ก็ต่อเมื่อ:
- อยากรู้วิธีการเขียน: อยากศึกษาว่า Flutter เขียน Widget ตัวนี้ขึ้นมายังไง (เพื่อจะไปเขียน Widget เลียนแบบเอง)
- แก้บั๊กขั้นสูง: เมื่อเกิดปัญหาแปลกๆ ที่แก้ไม่ได้ และสงสัยว่าเป็นที่ตัว Framework ของ Flutter เอง (ซึ่งเกิดขึ้นน้อยมาก)
-
-
🔴 Slow animations 🐢
- ทำให้ Animation ทั้งหมดใน App ช้าลง 5 เท่า
- ประโยชน์
- เอาไว้เช็คความเนียนของ Animation ว่ามันขยับถูกทางไหม หรือมีจังหวะไหนกระตุกหรือเปล่า
-
🔴 Show guidelines (Debug Paint) 📏
- จะวาดเส้นขอบ, เส้นลูกศร, และแถบสีฟ้า/เขียว ทับลงบนหน้าจอ เพื่อบอกว่า Widget แต่ละตัวมีระยะห่างกันเท่าไร (Margin/Padding)
- ประโยชน์:
- ได้ใช้บ่อยมาก! เวลาจัดหน้าจอไม่ตรง หรือเว้นวรรคผิด กดปุ่มนี้จะเห็นเลยว่าใครกินที่เกิน หรือใครเบียดใครอยู่
-
🔴 Show baselines 📝
- ขีดเส้นสีเขียวที่ "ฐาน" ของตัวหนังสือ
- ประโยชน์:
- เอาไว้ ตรวจสอบ เวลาเราวาง Icon คู่กับ Text แล้วอยากรู้ว่ามันวางอยู่บนระนาบเดียวกันเป๊ะๆ หรือเปล่า
-
🔴 Highlight repaints 🌈 (ใช้ ตรวจสอบ Performance / ความเร็ว )
- ทำหน้าที่:
- เมื่อไหร่ก็ตามที่มีการเปลี่ยนแปลงบนหน้าจอ (เช่น ตัวเลขวิ่ง, สีกระพริบ) มันจะตี "กรอบสี่เหลี่ยมสีรุ้ง" ล้อมรอบจุดนั้น
- ใช้ทำอะไร
- ✅ ตรวจสอบปัญหา Performance / ความเร็ว
- ถ้า กดปุ่มเล็ก ๆ แต่กรอบสีรุ้งดันกระพริบทั้งหน้าจอ
→ แปลว่า repaint เกินจำเป็น ❌ - ควร repaint แค่ widget ของส่วนที่มีการเปลี่ยนแปลงตรงจุดนั้นๆ
- ทำหน้าที่:
-
🔴 Highlight oversized images 🖼️ (สำคัญเรื่อง Memory)
- 👉 ตรวจจับรูปภาพที่ “ใหญ่เกินความจำเป็น” เมื่อถูกแสดงบนหน้าจอ
- ใช้เพื่อ แก้ปัญหา Performance และ Memory โดยเฉพาะ
- หน้าที่:
- ถ้ารูปภาพไหนที่คุณโหลดมา "รูปจริงมีขนาดใหญ่ เช่น 4000×3000 px" แต่เอามาแสดงผลใน "กรอบเล็กนิดเดียว" ภาพนั้นจะถูก กลับหัว (Flip) และกลับสี (Invert) ให้ดูน่าเกลียดทันที
- ประโยชน์:
- เตือนสติ! เช่น คุณเอารูป 4K มาใส่ในกรอบ icon เล็กๆ 50x50 pixel มันเปลือง RAM เครื่องมาก ถ้าเห็นภาพกลับหัว ให้ไปลดขนาดรูปก่อนนำมาใช้ครับ
-
สรุปสั้นๆ:
- ถ้าจะแก้ UI เบี้ยว -> ใช้ Select widget mode คู่กับ Show guidelines
- ถ้าแอปกระตุก -> ใช้ Highlight repaints กับ Highlight oversized images
- ถ้า Animation แปลก -> ใช้ Slow animations
-
🟠 กรอบสีส้ม: Widget Tree (โครงสร้างต้นไม้)
- หน้าที่:
- แสดงให้เห็นว่า Widget ตัวไหนเป็น "พ่อ" (Parent) และตัวไหนเป็น "ลูก" (Child) ซ้อนกันอยู่อย่างไร
- หน้าที่:
-
🟢 กรอบสีเขียว: View Tabs (เมนูเลือกมุมมอง)
-
🟢 Widget properties:
- แสดงค่าการตั้งค่าต่างๆ ที่เราเขียนไว้ใน Code เช่น สี, ขนาด, ข้อความ
-
🟢 Render object:
- แสดงข้อมูลทางเทคนิคที่ Flutter ใช้คำนวณการวาดภาพบนหน้าจอ (อันนี้จะลึกหน่อย ใช้ตอนแก้บั๊กยากๆ)
-
🟢 Flex explorer:
- เป็นเครื่องมือพิเศษที่จะโผล่มาเมื่อเราเลือก Widget ประเภท
RowหรือColumnเพื่อช่วยจำลองการจัดวางตำแหน่ง (Alignment) แบบให้เห็นภาพ
- เป็นเครื่องมือพิเศษที่จะโผล่มาเมื่อเราเลือก Widget ประเภท
-
-
🌸 กรอบสีชมพู: View Tabs (เมนูเลือกมุมมอง)
- 🌸 ส่วนบน (แผนภาพสีฟ้า/เทา): Layout Visualizer
- แสดง "ขนาดจริง" ของ Widget (กว้าง x สูง)
- แสดง Padding/Margin (พื้นที่ว่างรอบๆ)
- ช่วยให้ดูรู้ทันทีว่า Widget นี้กินพื้นที่หน้าจอไปเท่าไหร่ (ในภาพคือ
Columnกว้าง 409.4 สูง 689.0)
- 🌸 ส่วนล่าง (ตารางข้อมูล): Properties Table
- แสดง "ค่าตัวแปร" ทั้งหมดของ Widget นั้น
- เช่น
mainAxisAlignment: start(เริ่มเรียงจากด้านบน),crossAxisAlignment: center(จัดกึ่งกลางแนวนอน)
- เช่น
- ช่วยให้เราเช็คได้ว่า สิ่งที่เราเขียนโค้ดไป มันแสดงผลออกมาเป็นค่าอะไรกันแน่
- แสดง "ค่าตัวแปร" ทั้งหมดของ Widget นั้น
- 🌸 ส่วนบน (แผนภาพสีฟ้า/เทา): Layout Visualizer
-
-
-
ให้ไปทำการ Download และ ติตตั้ง Library : flutter_launcher_icons
- Download ได้จาก Link นี้ : The official repository for Dart and Flutter packages.
-
แล้วทำการ Config ตามรูปภาพที่ส่งไป :
-
ไปที่ Path :
- android/app/src/main/AndroidManifest.xml
-
File AndroidManifest.xml คือ :
- เป็น File กำหนดตัวตนและพฤติกรรมของ App Android
- ระบบ Android จะอ่าน File นี้ ก่อน Run App เสมอ
- ใช้บอก Android ว่า:
- App ชื่ออะไร
- ใช้ Icon ตัวไหน
- Activity ไหนเป็นตัวเริ่ม App- ขอ permission อะไร
- รองรับ feature อะไรบ้าง
-
ให้ทำการแก้ไข android:label= เป็นชื่อ "TaskFlow" ที่ App เราเคยสร้าง
-
ให้ไปที่ Path :
- android/app/build.gradle.kts
-
File build.gradle.kts คือ :
- 👉 เป็น File ตั้งค่าการ Build Project Android ที่เขียนด้วยภาษา Kotlin
- 👉 App จะถูก build ยังไง ใช้ Android เวอร์ชันไหน ใช้แพ็กเกจอะไรบ้าง
- ทำการแก้ไขตาม File ภาพ :
- คำสั่ง namespace = "com.suporn.todolist" ✅ ใช้สำหรับสร้าง Code ใน Andriod
- คำสั่ง applicationId = "com.suporn.todolist ✅ ใช้สำหรับ ตัว App , ในการ Upload App ไปยัง Playstore
-
คำสั่ง minSdk = flutter.minSdkVersion
- minSdk = Minimum SDK Version คือ version Android ต่ำสุด ที่ App สามารถติดตั้งได้
- flutter.minSdkVersion = ค่า version Android ต่ำสุด ที่ Flutter ตั้งให้โดยอัตโนมัติ
-
คำสั่ง targetSdk = flutter.targetSdkVersion
- targetSdk = Target SDK Version คือ ระบุ version Android ใหม่ล่าสุด , ให้สามารถทำงานทดสอบร่วกับ App ได้
- flutter.targetSdkVersion = ค่า version Android ใหม่ล่าสุด ที่ Flutter ตั้งให้โดย
-
คำสั่ง targetSdk = flutter.targetSdkVersion
-
คำสั่ง versionName
- เป็น ชื่อเวอร์ชันที่ผู้ใช้เห็น
- เป็น string
- แสดงใน Play Store และหน้า About
-
ไปที่ Path :
- android/app/src/main/kotlin/com/example/todolist/MainActivity.kt
-
File MainActivity.kt คือ :
- ✅ File นี้ , ทำหน้าที่เป็น สะพานเชื่อม Flutter ↔ Android
- ให้ทำการแก้ไข ตาม File ภาพ
- ✅ namespace ใน file build.gradle.kts กับ package ชื่อควรตรงกัน
-
ไปที่ Path :
- android/app/google-services.json
- package_name ใน file : google-services.json , ชื่อต้องตรงกับ
package_nameในbuild.gradle.ktsและ ต้องตรงกับใน Firebase - ถ้าไม่ตรง จะเกิด ❌
- Firebase เชื่อมต่อไม่ติด
- App จะใช้งานไม่ได้
-
-
-
ให้ทำการสร้าง File key.properties ไว้ใน Path :
- android\key.properties
-
แล้วทำการ Copy คำสั่ง ไปไว้ใน File key.properties :
storePassword=<password-from-previous-step> keyPassword=<password-from-previous-step> keyAlias=upload storeFile=<keystore-file-location>
-
ทำการตั้ง Password ใน :
- storePassword
- keyPassword
-
ทำการเปิด Terminal ใน VS code หรือ Android Studio ขึ้นมา
-
แล้ว Run คำสั่ง ตามนี้ :
& "C:\Program Files\Android\Android Studio\jbr\bin\keytool.exe" -genkey -v ` -keystore $env:USERPROFILE\upload-keystore.jks ` -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 ` -alias upload
-
ให้เรียกแบบ ระบุ path ตรง ๆ ใน PowerShell แบบนี้ครับ
- keytool เป็นเครื่องมือที่มากับ JDK (Java Development Kit)
- แต่เราใช้ JDK ที่มาจาก Android Studio
-
หมายเหตุสำคัญ
- ต้องมี & นำหน้า (PowerShell ใช้เรียกไฟล์ .exe)
- เครื่องหมาย ` คือขึ้นบรรทัดใหม่ใน PowerShell
-
จากนั้น มันจะถามหา keyPassword ใน file key.properties , ให้ทำการใส่รหัสผ่านลงไป
-
ระบบ สร้าง keystore สำเร็จแล้ว
- File ถูกบันทึกที่ - C:\Users\John\upload-keystore.jks
-
เมื่อได้ File upload-keystore.jks มาแล้ว , ให้ทำการย้าย File upload-keystore.jks ไปไว้ Path :
- android\app\upload-keystore.jks
-
แล้วทำการเปิด File key.properties ขึ้นมาอีกครั้ง
- ไปแก้ไขตรงคำสั่ง ตรง storeFile= ให้เป็น ../app/upload-keystore.jks
-
-
-
ไปที่ Path :
- android/app/build.gradle.kts
-
ทำการ Import Library ด้วยคำสั่ง :
import java.util.Properties import java.io.FileInputStream
-
import java.util.Properties
- ใช้สำหรับ อ่านไฟล์ .properties เช่น key.properties
- เก็บข้อมูลแบบ key=value
-
import java.io.FileInputStream
- ใช้ เปิด File จริง ในพื้นที่ local บนเครื่องคอมเรา
- FileInputStream = อ่านข้อมูลจากไฟล์
- ใช้เปิด key.properties เพื่อส่งให้ Properties.load()
-
-
เขียนคำสั่ง อ่าน File key.properties , เพื่อนำข้อมูลไปใช้ Signing Android
val keystoreProperties = Properties() //✅ เตรียมตัวแปรไว้เก็บค่าจากไฟล์ val key.propertiesval keystorePropertiesFile = rootProject.file("key.properties") if (keystorePropertiesFile.exists()) { keystoreProperties.load(FileInputStream(keystorePropertiesFile)) }
-
กำหนดข้อมูลการ (Signing Configuration) สำหรับ Build แบบ Release
signingConfigs { create("release") { //✅ สร้าง config ชื่อ release //📌 ค่าทั้งหมดมาจาก key.properties keyAlias = keystoreProperties["keyAlias"] as String keyPassword = keystoreProperties["keyPassword"] as String storeFile = keystoreProperties["storeFile"]?.let { file(it) } storePassword = keystoreProperties["storePassword"] as String } } -
เพิ่มคำสั่ง สำหรับ ปล่อย App ตัวจริง
buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. //📌 บรรทัดแรก (debug) → ใช้แค่ชั่วคราว / ตัวอย่าง //signingConfig = signingConfigs.getByName("debug") //📌 บรรทัดที่สอง (release) → ใช้สำหรับปล่อย App ตัวจริง signingConfig = signingConfigs.getByName("release") } } -
จากนั้นเปิด Terminal ขึ้นมา , แล้วใช้คำสั่งตามนี้
flutter build appbundle --release
-
เป็นคำสั่งสำหรับ
- 👉 สร้างไฟล์ Android App Bundle (.aab)
- 👉 ในโหมด Release
-
ใช้ทำอะไร
- ไฟล์ .aab คือไฟล์ที่ Google Play Store ต้องการ
- ใช้สำหรับ อัปโหลดแอปขึ้น Play Store
-
- วิธีแก้ไข , กรณี Error เวลา Build ด้วย Gradle: ถูกล็อก (file locking)** หรือ cache ของ Gradle เสีย/ค้าง
- หากใช้คำสั่ง flutter run แล้วเกิด Error ลักษณะแบบนี้
Suppressed: java.lang.Exception: Storage[...] registration stack trace java.lang.IllegalStateException: Storage for [...] is already registered
- หรือ
java.lang.AssertionError: Could not delete caches dir yourProject\build\kotlin\compileDebugTestingKotlin
-
- ไปที่ Folder android
- ใช้คำสั่งตามนี้
- cd android
- ./gradlew --stop
- หรือจะ Run ด้วยคำสั่ง ./gradlew clean
-
- เปิด Task Manager
- ปิด process ที่ชื่อ java.exe ทั้งหมด
-
- เข้าไปที่ Folder android
- หาชื่อ Folder .gradle , แล้วทำการลบทิ้ง เพื่อเคลียร์ cache ของ Gradle เสีย/ค้าง ออกไปให้หมด
-
- เปิด Android Studio
- ไปที่เมนู File > Invalidate Caches...
- ติ๊กช่อง (โดยเฉพาะ Clear file system cache and Local History)
- กด Invalidate and Restart
-
- cd ..
- flutter clean
- flutter pub get
- flutter run
-
Suppressed: java.lang.IllegalArgumentException: this and base files have different roots:
-
- ใช้คำสั่ง flutter clean , ลบไฟล์ build และ cache เก่าทั้งหมดของ Flutter
- เปิด Folder
android/ใน File Project เรา , ด้วย Android Studio - ให้ Android Studio ทำ Gradle Sync , - 👉 ยังไม่ต้อง รัน
flutter pub get - เปิดเมนู File ใน Android Studio , แล้วเลือก Sync Project with Gradle Files
- เหตุผล - บางปัญหาเกิดจากฝั่ง Android โดยตรง
-
กลับมาที่ root ของ Flutter project , โดยใช้คำสั่ง cd ..
-
Run คำสั่ง:
- flutter pub get - flutter runผลลัพธ์ : ดึง package ใหม่ทั้งหมด , - build App ใหม่ด้วย environment ที่สะอาดแล้ว
-
- แก้ปัญหา build / Gradle / Flutter error ได้ โดยการย้าย Folder Project ไปไว้ใน Drive เดียวกัน
-
- Project อยู่ที่ - D:\my_flutter_project` -
แต่ Android SDK / Java / Gradle / Flutter
ส่วนใหญ่อยู่ใน- C:\ (เช่น C:\Android\Sdk)` -
- Gradle / Kotlin daemon มีปัญหาเรื่อง - file lock - cache - incremental build
- โดยเฉพาะบน Windows
เมื่อโปรเจกต์อยู่คนละ drive กับ SDK
-
- ย้ายโปรเจกต์จาก
D:→C:
- เช่น:C:\Projects\my_flutter_project`
- ย้ายโปรเจกต์จาก
-
จากนั้น build ใหม่
- flutter clean
- flutter run
-
- แก้ปัญหา build / Gradle / Flutter error ได้ โดยการย้าย Folder Project ไปไว้ใน Drive เดียวกัน
-
-
- เปิด Android Studio ขึ้นมา
- ไปที่ Folder Project เรา , แล้วไปตาม path : android/app/src/main/res
- จากนั้นให้ลบ File ตาม ภาพนี้
- จากนั้น build ใหม่
> - flutter clean
> - flutter run











