Claude Code สำหรับการวนรอบ UI ใน Flutter: วงจรปฏิบัติที่จะเปลี่ยน user stories เป็น widget tree, การจัดการสเตต และการนำทาง โดยคงการเปลี่ยนแปลงเป็นโมดูลและตรวจสอบได้ง่าย

งาน UI ใน Flutter ที่เร็วมักเริ่มดี คุณปรับเลย์เอาต์ เพิ่มปุ่ม ย้ายฟิลด์ แล้วหน้าจอก็ดูดีขึ้นอย่างรวดเร็ว ปัญหาปรากฏหลังการวนสักรอบสองรอบ เมื่อความเร็วกลายเป็นกองการเปลี่ยนแปลงที่ไม่มีใครอยากรีวิว
ทีมมักเจอความล้มเหลวแบบเดียวกัน:
สาเหตุสำคัญคือวิธี "prompt ใหญ่ครั้งเดียว": อธิบายฟีเจอร์ทั้งหมด, ขอชุดหน้าจอทั้งหมด, แล้วรับเอาเอาต์พุตใหญ่ ผู้ช่วยพยายามช่วย แต่แตะหลายส่วนของโค้ดพร้อมกัน ซึ่งทำให้การเปลี่ยนยุ่ง ยากรีวิว และเสี่ยงต่อการรวมเข้ากับสาขาหลัก
ลูปที่ทำซ้ำได้แก้ปัญหานี้โดยบังคับให้ชัดเจนและจำกัดรัศมีความเสียหาย แทนที่จะบอกว่า "สร้างฟีเจอร์" ให้ทำซ้ำ ๆ ดังนี้: เลือก user story เดียว, สร้างสไลซ์ UI เล็กที่สุดที่พิสูจน์มันได้, เพิ่มเฉพาะสเตตที่ต้องการสำหรับสไลซ์นั้น, แล้วต่อการนำทางสำหรับเส้นทางเดียว ทุกการวนอยู่ในขนาดที่พอรีวิวได้ และข้อผิดพลาดย้อนกลับง่าย
เป้าหมายที่นี่คือเวิร์กโฟลว์เชิงปฏิบัติในการเปลี่ยน user stories เป็นหน้าจอที่เป็นรูปธรรม การจัดการสเตต และการไหลการนำทางโดยไม่เสียการควบคุม ถ้าทำดี คุณจะได้ชิ้น UI แบบโมดูล, diffs เล็กลง, และความประหลาดใจน้อยลงเมื่อความต้องการเปลี่ยน
User stories เขียนให้คนอ่านไม่ใช่ widget tree ก่อนจะสร้างอะไร ให้แปลงสตอรีเป็นสเปก UI เล็ก ๆ ที่บรรยายพฤติกรรมที่มองเห็นได้ "เสร็จ" ควรทดสอบได้: สิ่งที่ผู้ใช้เห็น แตะ และยืนยัน ไม่ใช่ว่าดีไซน์ "ดูทันสมัย"
วิธีง่าย ๆ ที่ทำให้ขอบเขตชัดคือแบ่งสตอรีเป็นสี่ถัง:
ถ้าสตอรียังรู้สึกคลุมเครือ ให้ตอบคำถามเหล่านี้เป็นภาษาง่าย ๆ:
เพิ่มข้อจำกัดตั้งแต่ต้นเพราะมันชี้นำทุกการตัดสินใจเลย์เอาต์: พื้นฐานธีม (สี, ระยะห่าง, แบบอักษร), การตอบสนอง (มือถือแนวตั้งก่อน แล้วขนาดแท็บเล็ต), และมาตรฐานการเข้าถึง เช่น ขนาดเป้าการแตะ, การขยายข้อความที่อ่านได้, และป้ายที่มีความหมายสำหรับไอคอน
สุดท้าย ตัดสินใจว่าส่วนไหนมั่นคงกับส่วนไหนปรับได้ เพื่อไม่ให้คุณหมุนโค้ดเบส รายการมั่นคงคือสิ่งที่ฟีเจอร์อื่นพึ่งพา เช่น ชื่อ route, โมเดลข้อมูล, และ API ที่มีอยู่ ส่วนที่ปรับได้ปลอดภัยกว่าที่จะวน เช่น โครงสร้างเลย์เอาต์, ข้อความสั้นๆ, และองค์ประกอบ widget ที่แน่นอน
ตัวอย่าง: "ในฐานะผู้ใช้ ฉันสามารถบันทึกไอเท็มเป็น Favorites จากหน้ารายละเอียดได้" สเปก UI ที่สร้างได้อาจเป็น:
นั่นเพียงพอที่จะสร้าง รีวิว และวนปรับ โดยไม่ต้องเดา
diffs เล็กไม่ได้หมายความทำงานช้า แต่มันทำให้การเปลี่ยนแต่ละครั้งรีวิวง่าย ย้อนกลับง่าย และยากที่จะพัง กฎง่าย: หนึ่งหน้าจอหรือหนึ่งปฏิสัมพันธ์ต่อการวน
เลือกสไลซ์ที่ค่อนข้างแคบก่อนเริ่ม "เพิ่ม empty state ให้หน้าจอ Orders" เป็นสไลซ์ที่ดี แต่ "รีเวิร์คทั้ง Orders flow" ไม่ใช่ เป้าหมายคือ diff ที่เพื่อนร่วมทีมอ่านเข้าใจในหนึ่งนาที
โครงโฟลเดอร์ที่มั่นคงช่วยกักการเปลี่ยนให้อยู่ในขอบเขต โครงเรียบง่ายแบบ feature-first ป้องกันการกระจาย widgets และ routes ทั่วแอป:
lib/
features/
orders/
screens/
widgets/
state/
routes.dart
เก็บ widgets ให้เล็กและประกอบกัน เมื่อ widget มีอินพุตและเอาต์พุตชัดเจน คุณสามารถเปลี่ยนเลย์เอาต์โดยไม่แตะสเตต และเปลี่ยนสเตตโดยไม่ต้องเขียน UI ใหม่ ชอบ widget ที่รับค่าธรรมดาและ callbacks แทน global state
ลูปที่รีวิวได้:
ตั้งกฎเข้ม: ทุกการเปลี่ยนต้องย้อนกลับหรือแยกได้ง่าย หลีกเลี่ยง refactor แทรกขณะวนหน้าจอ หากพบปัญหาไม่เกี่ยวข้อง ให้จดไว้แล้วแก้ในคอมมิตแยก
ถ้าเครื่องมือรองรับ snapshots และ rollback ใช้แต่ละสไลซ์เป็นจุดสแนปชอต บางแพลตฟอร์มเช่น Koder.ai รวมสแนปชอตและการย้อนกลับ ซึ่งช่วยให้การทดลองปลอดภัยขึ้นเมื่อคุณลองเปลี่ยน UI แบบกล้าได้กล้าเสีย
หนึ่งนิสัยเพิ่มเติมที่ช่วยให้การวนเริ่มต้นสงบ: ชอบเพิ่ม widget ใหม่แทนแก้ widget ที่แชร์อยู่แล้ว เพราะคอมโพเนนต์แชร์คือที่ที่การเปลี่ยนเล็ก ๆ กลายเป็น diffs ใหญ่
งาน UI ที่เร็วปลอดภัยเมื่อคุณแยกการคิดออกจากการพิมพ์ เริ่มจากแผน widget tree ชัดเจนก่อนสร้างโค้ด
ขอเฉพาะเค้าโครง widget tree เท่านั้น คุณต้องการชื่อ widget, ลำดับชั้น, และแต่ละส่วนแสดงอะไร ไม่มีโค้ดตอนนี้ นี่คือที่คุณจับสเตตที่ขาด, หน้าจอว่าง, และการเลือกเลย์เอาต์ที่แปลก ๆ ขณะที่ทุกอย่างยังแก้ได้ราคาถูก
ขอการแจกแจงคอมโพเนนต์พร้อมหน้าที่ ให้แต่ละ widget โฟกัส: หนึ่ง widget แสดง header อีกอันแสดง list อีกอันจัดการ empty/error UI ถ้าต้องการสเตตในภายหลัง ให้จดไว้ตอนนี้แต่ยังไม่ต้อง implement
สร้าง scaffold ของหน้าจอและ stateless widgets แยก pass เริ่มด้วยไฟล์หน้าจอเดียวที่มีเนื้อหาตัวแทนและ TODOs ชัดเจน เก็บอินพุตชัด (constructor params) เพื่อให้คุณต่อสเตตจริงภายหลังโดยไม่ต้องเขียน tree ใหม่
ทำ pass แยกสำหรับสไตลิงและรายละเอียดเลย์เอาต์: ระยะห่าง, typography, theming, การตอบสนอง ทำการสไตลิงเป็น diff ของตัวเองเพื่อให้การรีวิวง่าย
วางข้อจำกัดไว้ข้างหน้าเพื่อไม่ให้ผู้ช่วยคิด UI ที่คุณไม่สามารถปล่อยได้:
ตัวอย่างชัดเจน: สตอรีคือ "ในฐานะผู้ใช้ ฉันตรวจสอบไอเท็มที่บันทึกไว้และลบไอเท็มได้" ขอ widget tree ที่รวม app bar, list พร้อม row ของไอเท็ม, และ empty state แล้วขอการแจกแจงเช่น SavedItemsScreen, SavedItemTile, EmptySavedItems หลังจากนั้นค่อยสร้าง scaffold แบบ stateless พร้อมข้อมูลปลอม และสุดท้ายเพิ่มสไตลิง (divider, padding, ปุ่มลบชัดเจน) ใน pass แยก
การวน UI พังเมื่อ widget แต่ละตัวเริ่มตัดสินใจเอง ให้เก็บ widget tree ให้โง่: มันควรอ่านสเตตแล้วเรนเดอร์ ไม่ควรมีตรรกะธุรกิจ
เริ่มจากตั้งชื่อสเตตเป็นคำง่าย ๆ ส่วนใหญ่ฟีเจอร์ต้องการมากกว่า "loading" และ "done":
จากนั้นจดอีเวนต์ที่เปลี่ยนสเตต: แตะ, ส่งฟอร์ม, ดึงเพื่อรีเฟรช, ย้อนกลับ, retry, และ "ผู้ใช้แก้ฟิลด์" การทำนี้ล่วงหน้าป้องกันการเดาในภายหลัง
เลือกแนวทางสเตตหนึ่งแบบสำหรับฟีเจอร์และยึดตามนั้น เป้าหมายไม่ใช่ "รูปแบบที่ดีที่สุด" แต่เป็น diffs ที่สม่ำเสมอ
สำหรับหน้าจอเล็ก ๆ controller ง่าย ๆ (เช่น ChangeNotifier หรือ ValueNotifier) มักเพียงพอ วางตรรกะไว้ในที่เดียว:
ก่อนเพิ่มโค้ด ให้เขียนการเปลี่ยนสเตตเป็นประโยคภาษาอังกฤษ ตัวอย่างสำหรับหน้าจอเข้าสู่ระบบ:
"เมื่อผู้ใช้แตะ Sign in: ตั้งค่าเป็น Loading ถ้าอีเมลไม่ถูกต้อง: อยู่ใน Partial input และแสดงข้อความอินไลน์ ถ้ารหัสผิด: ตั้งเป็น Error พร้อมข้อความและเปิด Retry ถ้าสำเร็จ: ตั้งเป็น Success และนำทางไป Home"
จากนั้นสร้างโค้ด Dart ขั้นต่ำที่ตรงกับประโยคเหล่านั้น การรีวิวจะง่ายเพราะคุณสามารถเปรียบเทียบ diff กับกฎที่เขียนไว้
ทำให้การ validate ชัดเจน ตัดสินใจว่าจะทำอย่างไรเมื่อ input ไม่ถูกต้อง:
เมื่อคำตอบเหล่านี้ถูกเขียนแล้ว UI จะสะอาดและโค้ดสเตตก็จะเล็กลง
การนำทางที่ดีเริ่มจากแผนที่เล็ก ๆ ไม่ใช่กอง routes สำหรับแต่ละ user story ให้จดสี่ช่วงสำหรับแต่ละสตอรี่: จุดเข้ามา, ก้าวต่อไปที่น่าจะเกิดขึ้นที่สุด, วิธียกเลิก, และความหมายของ "ย้อนกลับ"
แผนที่ route เล็ก ๆ ควรตอบคำถามที่มักทำให้ต้องแก้ซ้ำ:
จากนั้นกำหนดพารามิเตอร์ที่ส่งระหว่างหน้าจอ ให้ชัดเจน: IDs (productId, orderId), filters (ช่วงวันที่, สถานะ), และ draft data (ฟอร์มที่กรอกบางส่วน) หากข้ามขั้นตอนนี้คุณจะลงเอยกับการยัดสเตตใน singletons หรือ rebuild หน้าจอเพื่อ "ค้นหา" คอนเท็กซ์
deep links สำคัญแม้จะยังไม่ปล่อยจริงในวันแรก ตัดสินใจว่าจะทำอย่างไรเมื่อผู้ใช้เข้ามากลางทาง: คุณสามารถโหลดข้อมูลที่ขาดหรือควรเปลี่ยนเส้นทางไปหน้าปลอดภัย?
ตัดสินใจด้วยว่าหน้าจอใดควรส่งผลลัพธ์กลับ เช่น หน้าจอ "Select Address" ส่งกลับ addressId และหน้าชำระเงินอัปเดตโดยไม่ต้องรีเฟรชทั้งหน้า เก็บรูปทรงผลลัพธ์ให้เล็กและมี type เพื่อให้การเปลี่ยนแปลงง่ายต่อการรีวิว
ก่อนเขียนโค้ด ให้ชี้จุด edge cases: การเปลี่ยนแปลงที่ยังไม่บันทึก (แสดง confirm dialog), ต้อง auth (หยุดแล้วกลับมาต่อหลังล็อกอิน), ข้อมูลหายหรือลบ (แสดงข้อผิดพลาดและทางออกชัดเจน)
เมื่อคุณวนเร็ว ความเสี่ยงจริงไม่ใช่ "UI ผิด" แต่มันคือ UI ที่รีวิวไม่ได้ หากเพื่อนร่วมทีมไม่บอกได้ว่าสิ่งใดเปลี่ยน ทำไมจึงเปลี่ยน และอะไรคงที่ ทุกการวนถัดไปจะช้าลง
กฎที่ช่วยได้: ล็อกอินเทอร์เฟซก่อน แล้วจึงให้ภายในเคลื่อนไหว รักษา props สาธารณะของ widget (อินพุต), โมเดล UI เล็ก ๆ, และ route arguments ให้คงที่ เมื่อมีชื่อและ type ชัดเจน คุณสามารถปรับโครง widget โดยไม่ทำลายส่วนอื่นของแอป
ขอแผนที่เป็นมิตรต่อ diff ก่อนสร้างโค้ด คุณต้องการแผนที่บอกว่าไฟล์ไหนจะเปลี่ยนและไฟล์ไหนต้องไม่ถูกแตะ แผนแบบนี้ทำให้รีวิวมีสมาธิและป้องกัน refactor โดยไม่ตั้งใจ
แพตเทิร์นที่ทำให้ diffs เล็ก:
สมมติสตอรีคือ "ในฐานะผู้ซื้อ ฉันแก้ที่อยู่จัดส่งจากหน้าชำระเงินได้" ล็อก route args ก่อน: CheckoutArgs(cartId, shippingAddressId) คงที่ จากนั้นวนภายในหน้าจอ เมื่อเลย์เอาต์นิ่งแล้ว แยกเป็น AddressForm, AddressSummary, และ SaveBar
หากการจัดการสเตตเปลี่ยน (เช่น การ validate ย้ายจาก widget ไป CheckoutController) การรีวิวจะอ่านง่าย: ไฟล์ UI เปลี่ยนแค่ rendering ส่วน controller แสดงการเปลี่ยนตรรกะในที่เดียว
วิธีที่เร็วที่สุดในการชะลอคือขอให้ผู้ช่วยเปลี่ยนทุกอย่างพร้อมกัน ถ้าคอมมิตหนึ่งแตะเลย์เอาต์ สเตต และการนำทาง ผู้รีวิวจะไม่รู้ว่าอะไรทำให้พัง และการย้อนกลับจะยุ่งยาก
นิสัยที่ปลอดภัยคือความตั้งใจเดียวต่อการวน: วางรูปแบบ widget tree, จากนั้นต่อสเตต, แล้วต่อการนำทาง
ปัญหาหนึ่งที่พบบ่อยคือให้โค้ดที่สร้างขึ้นใหม่คิดรูปแบบใหม่ในแต่ละหน้าจอ หน้าหนึ่งใช้ Provider อีกหน้าหนึ่งใช้ setState และหน้าที่สามแนะนำ controller แบบกำหนดเอง แอปจะไม่สอดคล้องในเวลาอันสั้น เลือกชุดแพตเทิร์นเล็ก ๆ และบังคับใช้
อีกข้อผิดพลาดคือใส่งาน async ไว้ตรง build() มันอาจดูโอเคในเดโมเร็ว ๆ แต่จะกระตุ้นการเรียกซ้ำเมื่อ rebuild เกิด flicker และบั๊กติดตามยาก ย้ายการเรียกไป initState(), view model, หรือ controller เฉพาะ และเก็บ build() ไว้เพื่อการเรนเดอร์
การตั้งชื่อเป็นกับดักเงียบ โค้ดที่คอมไพล์แต่ชื่ออ่านเหมือน Widget1, data2, หรือ temp ทำให้ refactor ในอนาคตเจ็บปวด ชื่อชัดยังช่วยให้ผู้ช่วยสร้างการเปลี่ยนแปลงตามเจตนาได้ดีขึ้น
การ์ดเรลที่จะป้องกันผลลัพธ์แย่ ๆ:
build()วิธีแก้บั๊ก UI แบบคลาสสิกคือเพิ่ม Container, Padding, Align, และ SizedBox ต่อไปจนดูถูกต้อง หลังการวนไม่กี่ครั้ง tree จะอ่านยาก
ถ้าปุ่มจัดตำแหน่งผิดก่อนลองลบ wrappers, ใช้พาเรนต์เลย์เอาต์เดียว, หรือ extract widget เล็ก ๆ ที่มี constraints ของตัวเอง
ตัวอย่าง: หน้าชำระเงินที่ราคารวมกระโดดเมื่อโหลด ผู้ช่วยอาจห่อแถวราคาใน widget เพิ่มเพื่อ "stabilize" มัน ทางแก้สะอาดคือจองพื้นที่ด้วย placeholder โหลดง่าย ๆ ขณะที่คงโครงสร้างแถวไว้
ก่อนคอมมิต ทำการผ่านสองนาทีเพื่อตรวจค่าผู้ใช้และปกป้องจากรีเกรชันที่คาดไม่ถึง เป้าหมายไม่ใช่ความสมบูรณ์แบบ แต่เพื่อให้การวนนี้รีวิวง่าย ทดสอบง่าย และย้อนกลับง่าย
อ่าน user story หนึ่งครั้ง แล้วตรวจรายการเหล่านี้กับแอปที่รันได้ (หรืออย่างน้อยกับ widget test ง่าย ๆ):
เช็คลิสต์ความเป็นจริง: ถ้าคุณเพิ่มหน้ารายละเอียด Order ใหม่ คุณควรสามารถ (1) เปิดจากรายการ, (2) เห็น spinner โหลด, (3) จำลองข้อผิดพลาด, (4) เห็น order ว่าง, และ (5) กด back เพื่อกลับไปยังรายการโดยไม่มีการกระโดดแปลก ๆ
ถ้าเวิร์กโฟลว์ของคุณรองรับสแนปชอตและการย้อนกลับ ให้ถ่ายสแนปชอตก่อนการเปลี่ยนเลย์เอาต์ใหญ่ ๆ บางแพลตฟอร์มเช่น Koder.ai รองรับสิ่งนี้และช่วยให้คุณวนได้เร็วขึ้นโดยไม่เสี่ยงสาขาหลัก
User story: "ในฐานะผู้ซื้อ ฉันเรียกดูสินค้า เปิดหน้ารายละเอียด บันทึกสินค้าลง Favorites และภายหลังดู Favorites ของฉัน" เป้าหมายคือย้ายจากคำพูดไปยังหน้าจอในสามขั้นเล็กที่รีวิวได้
การวน 1: โฟกัสเฉพาะหน้ารายการ browse สร้าง widget tree ที่เพียงพอให้เรนเดอร์แต่ไม่ผูกกับข้อมูลจริง: Scaffold กับ AppBar, ListView ของแถวตัวแทน, UI ชัดเจนสำหรับ loading และ empty state เก็บสเตตง่าย ๆ: loading (แสดง CircularProgressIndicator), empty (ข้อความสั้น ๆ และปุ่ม Try again), ready (แสดงรายการ)
การวน 2: เพิ่มหน้ารายละเอียดและการนำทาง ทำให้ชัดเจน: onTap push route และส่งอ็อบเจ็กต์พารามิเตอร์เล็ก ๆ (เช่น id, title) เริ่มหน้ารายละเอียดเป็น read-only มี title, คำอธิบายตัวแทน, และปุ่ม Favorite จุดประสงค์คือจับคู่สตอรี: list -> details -> back โดยไม่มีฟลอว์พิเศษ
การวน 3: แนะนำการอัปเดต favorites state และฟีดแบค UI เพิ่มแหล่งความจริงเดียวสำหรับ favorites (แม้อยู่ในหน่วยความจำ) แล้วเชื่อมทั้งสองหน้าจอ การแตะ Favorite อัปเดตไอคอนทันทีและแสดงการยืนยันเล็ก ๆ (เช่น SnackBar) แล้วเพิ่มหน้าจอ Favorites ที่อ่านสเตตเดียวกันและจัดการ empty state
diff ที่รีวิวได้มักจะเป็นแบบนี้:
browse_list_screen.dart: widget tree + loading/empty/ready UIitem_details_screen.dart: เลย์เอาต์ UI และรับ navigation paramsfavorites_store.dart: ตัวเก็บสเตตขั้นต่ำและเมทอดอัปเดตapp_routes.dart: routes และตัวช่วยการนำทางแบบ typedfavorites_screen.dart: อ่านสเตตและแสดง empty/list UIถ้าไฟล์ใดไฟล์หนึ่งกลายเป็น "ที่ที่ทุกอย่างเกิดขึ้น" ให้แยกมันก่อนเดินต่อ ไฟล์เล็ก ๆ ชื่อชัดช่วยให้การวนถัดไปเร็วและปลอดภัย
ถ้าเวิร์กโฟลว์ทำงานได้เฉพาะตอนคุณ "in the zone" มันจะพังเมื่อคุณเปลี่ยนหน้าจอหรือเพื่อนร่วมทีมแตะฟีเจอร์ ทำให้ลูปเป็นนิสัยโดยเขียนมันลงและวางการ์ดเรลรอบขนาดการเปลี่ยน
ใช้เทมเพลตทีมเดียวเพื่อให้การวนแต่ละครั้งเริ่มด้วยอินพุตเหมือนกันและผลิตเอาต์พุตชนิดเดียวกัน เก็บให้สั้นแต่เฉพาะเจาะจง:
นี้ลดโอกาสที่ผู้ช่วยจะประดิษฐ์แพตเทิร์นใหม่กลางทาง
เลือกคำนิยามของ "เล็ก" ที่บังคับใช้ได้ง่ายในการรีวิวโค้ด เช่น จำกัดจำนวนไฟล์ต่อการวน และแยก refactor UI ออกจากการเปลี่ยนพฤติกรรม
กฎชุดง่าย ๆ:
เพิ่มจุดตรวจเพื่อย้อนขั้นตอนที่ผิดได้อย่างรวดเร็ว อย่างน้อยใส่แท็กคอมมิตหรือเก็บจุดเช็คไว้ก่อน refactor ใหญ่ หากเวิร์กโฟลว์รองรับสแนปชอต/rollback ให้ใช้มันบ่อย ๆ
ถ้าคุณต้องการเวิร์กโฟลว์แบบแชทที่สร้างและปรับแต่งแอป Flutter แบบ end-to-end, Koder.ai มีโหมดการวางแผนที่ช่วยคุณรีวิวแผนและไฟล์ที่จะเปลี่ยนก่อนนำมาประยุกต์ใช้
ใช้ สเปก UI เล็กและทดสอบได้ ก่อนเสมอ เขียน 3–6 บรรทัดที่ครอบคลุม:
จากนั้นสร้างเฉพาะสไลซ์นั้น (มักเป็นหน้าจอหนึ่ง + 1–2 widgets).
แปลงเรื่องผู้ใช้เป็นสี่กลุ่ม:
ถ้าคุณอธิบายการยอมรับผลไม่ออกโดยเร็ว แปลว่าเรื่องยังคลุมเครือและต้องชัดกว่านี้ก่อนทำ UI diff ที่สะอาด
เริ่มด้วยการสร้าง โครงต้นไม้ของ widget เท่านั้น (ชื่อ + ลำดับชั้น + แต่ละส่วนแสดงอะไร) — ไม่มีโค้ดในขั้นแรก
จากนั้นขอ การแจกแจงหน้าที่ของคอมโพเนนต์ (widget ใดรับผิดชอบอะไร)
หลังจากนั้นค่อยสร้าง scaffold แบบ stateless ที่มีอินพุตชัดเจน (ค่ากับ callbacks) และทำการสไตลิงในรอบแยกต่างหาก
ยึดกฎแข็ง: หนึ่งความตั้งใจต่อการวนหนึ่งครั้ง
ถ้าคอมมิตเดียวเปลี่ยนเลย์เอาต์ สเตต และเส้นทางพร้อมกัน ผู้ตรวจจะไม่รู้ว่าอะไรเป็นสาเหตุของบั๊กและการย้อนกลับจะยุ่งยาก
เก็บให้ widgets “โง่”: ให้มัน เรนเดอร์สเตต ไม่ใช่ตัดสินกฎธุรกิจ
ทางปฏิบัติที่แนะนำ:
หลีกเลี่ยงการใส่การเรียก async ใน — มันจะทำให้เกิดการเรียกซ้ำเมื่อ rebuild
นิยามสเตตและการเปลี่ยนในภาษาธรรมดาก่อนเขียนโค้ด
ตัวอย่างรูปแบบ common:
จากนั้นระบุอีเวนต์ที่ย้ายระหว่างสถานะเหล่านี้ (refresh, retry, submit, edit) โค้ดจะเปรียบเทียบกับกฎที่เขียนไว้ได้ง่ายขึ้น
เขียนแผนผังเล็ก ๆ สำหรับสตอรี่:
ล็อกลงด้วยว่าข้อมูลอะไรเดินทางระหว่างหน้าจอ (IDs, filters, draft data) เพื่อหลีกเลี่ยงการซ่อนคอนเท็กซ์ใน global state
เริ่มจากโครงสร้างโฟลเดอร์แบบ feature-first เพื่อให้การเปลี่ยนถูกจำกัด เช่น:
lib/features/<feature>/screens/lib/features/<feature>/widgets/lib/features/<feature>/state/lib/features/<feature>/routes.dartแล้วให้แต่ละการวนโฟกัสในโฟลเดอร์ของฟีเจอร์เดียว หลีกเลี่ยงการ refactor แทรกซ้อนนอกพื้นที่นั้น
กฎง่าย ๆ: ล็อกอินเทอร์เฟซสาธารณะก่อน แล้วจึงเปลี่ยนภายใน
ผู้ตรวจดูจะสนใจว่าอินพุต/เอาต์พุตยังคงอยู่ ถ้าเป็นเช่นนั้นแม้ layout จะเปลี่ยนก็ตรวจง่าย
ตรวจแบบรวดเร็วสองนาที:
ถ้าระบบรองรับสแนปชอต/การย้อนกลับ ให้ถ่ายสแนปชอตก่อน refactor หน้าที่ใหญ่
build()