Claude Code के लिए Flutter UI इटरेशन: एक व्यावहारिक लूप जो यूज़र स्टोरियों को विजेट ट्री, स्टेट और नेविगेशन में बदलने का तरीका बताता है, ताकि बदलाव मॉड्यूलर और रिव्यू करने में आसान रहें।

तेज़ Flutter UI काम अक्सर अच्छी शुरुआत करता है। आप लेआउट को थोड़ा बदलते हैं, एक बटन जोड़ते हैं, एक फील्ड घुमाते हैं — और स्क्रीन जल्दी बेहतर दिखने लगती है। समस्या तब आती है जब कुछ राउंड के बाद तेज़ी परिवर्तनों के ढेर में बदल जाती है जिसे कोई रिव्यू करना नहीं चाहता।
टीम अक्सर वही गलतियाँ करती हैं:
एक बड़ा कारण है “एक बड़ा प्रॉम्प्ट” अप्रोच: पूरी फ़ीचर का वर्णन करें, सभी स्क्रीन मांगें, और बड़ा आउटपुट स्वीकार करें। असिस्टेंट मदद करने की कोशिश करता है, पर एक साथ बहुत सारे हिस्सों को छू लेता है। इससे बदलाव गड़बड़, रिव्यू के लिए कठिन और मर्ज करने में जोखिम भरे हो जाते हैं।
एक दोहराने योग्य लूप स्पष्टता लाकर ब्लास्ट रेंज को सीमित करता है। “फ़ीचर बनाओ” की बजाय, इसे बार-बार करें: एक यूज़र स्टोरी चुनें, इसका सबसे छोटा UI स्लाइस जनरेट करें जो उसे साबित करे, केवल उसी स्लाइस के लिए आवश्यक स्टेट जोड़ें, फिर एक पथ के लिए नेविगेशन वायर करें। प्रत्येक पास छोटा रहता है, रिव्यू आसान होता है, और गलतियाँ वापस करना सरल।
लक्ष्य यहाँ एक व्यावहारिक वर्कफ़्लो देना है जो यूज़र स्टोरियों को स्क्रीन, स्टेट हैंडलिंग, और नेविगेशन फ्लोज़ में बदल दे बिना नियंत्रण खोए। अच्छे तरीके से किया जाए तो आप मॉड्यूलर UI टुकड़े, छोटे डिफ़्स, और आवश्यकताओं बदलते समय कम आश्चर्य पाएंगे।
यूज़र स्टोरीज़ इंसानों के लिए लिखी जाती हैं, विजेट ट्री के लिए नहीं। कुछ भी जनरेट करने से पहले, स्टोरी को छोटे UI स्पेक में बदलें जो दृश्य व्यवहार बताता हो। “डन” टेस्टेबल होना चाहिए: उपयोगकर्ता क्या देख सकता है, क्या टैप कर सकता है, और क्या कन्फर्म कर सकता है — न कि डिज़ाइन “कितना मॉडर्न लगता है।”
एक सरल तरीका स्कोप को ठोस रखने का है स्टोरी को चार भागों में बाँटना:
अगर स्टोरी अभी भी धुंधली लगती है, इन सवालों का सीधे-सादे भाषा में जवाब दें:
शुरू में सीमाएँ जोड़ें क्योंकि वे हर लेआउट चुनाव को मार्गदर्शित करती हैं: थीम के बेसिक्स (रंग, स्पेसिंग, टाइपोग्राफी), रिस्पॉन्सिवनेस (पहले फ़ोन पोर्ट्रेट, फिर टैबलेट), और एक्सेसिबिलिटी न्यूनतम जैसे टैप टार्गेट साइज, पठनीय टेक्स्ट स्केलिंग, और आइकन के लिए अर्थपूर्ण लेबल।
अंत में, तय करें क्या स्थिर है और क्या लचीला ताकि आप कोडबेस को बार-बार बदलने से बचा सकें। स्थिर आइटम वे हैं जिनपर दूसरे फीचर निर्भर करते हैं, जैसे रूट नाम, डेटा मॉडल, और मौजूद APIs। लचीले आइटम सुरक्षित होते हैं पर इटरेट करने के लिए—जैसे लेआउट स्ट्रक्चर, माइक्रोकॉपी, और ठीक-ठीक विजेट कॉम्पोज़िशन।
उदाहरण: “एक यूज़र के रूप में, मैं डिटेल स्क्रीन से आइटम को Favorites में सेव कर सकता/सकती हूँ।” एक बनायाबिल UI स्पेक हो सकता है:
इतना ही काफी है बनाने, रिव्यू करने और बिना अनुमानों के इटरेट करने के लिए।
छोटे डिफ़्स का मतलब धीमी काम नहीं है। इसका मतलब हर UI बदलाव को रिव्यू करना आसान, वापस करना आसान और टूटना मुश्किल बनाना है। सबसे सरल नियम: प्रति इटरेशन एक स्क्रीन या एक इंटरैक्शन।
शुरू होने से पहले एक तंग स्लाइस चुनें। “Orders स्क्रीन में खाली स्टेट जोड़ें” एक अच्छा स्लाइस है। “पूरा Orders फ्लो दोबारा बनाओ” नहीं। लक्ष्य ऐसा डिफ़ रखें जिसे टीममेट एक मिनट में समझ सके।
एक स्थिर फ़ोल्डर संरचना भी बदलावों को सीमित रखने में मदद करती है। एक सरल, फीचर-फर्स्ट लेआउट रोकता है कि आप विजेट्स और रूट्स को ऐप में बिखेर दें:
lib/
features/
orders/
screens/
widgets/
state/
routes.dart
विजेट्स को छोटा और कंपोज़्ड रखें। जब किसी विजेट के पास स्पष्ट इनपुट्स और आउटपुट्स हों, तो आप लेआउट बदल सकते हैं बिना स्टेट लॉजिक को छुए, और स्टेट बदल सकते हैं बिना UI को फिर से लिखे। ऐसे विजेट्स पसंद करें जो साधारण मान और कॉलबैक लेते हों, न कि ग्लोबल स्टेट।
एक रिव्यू योग्य लूप:
एक कड़ा नियम सेट करें: हर बदलाव को रिवर्ट या आइसोलेट करना आसान होना चाहिए। इटरेट करते समय ड्राइव-बाय रिफैक्टर्स से बचें। अगर किसी अनदेखे समस्या पर ध्यान गया है, उसे लिख कर अलग कमिट में फिक्स करें।
अगर आपका टूल स्नैपशॉट्स और रोलबैक सपोर्ट करता है, तो हर स्लाइस को एक स्नैपशॉट बिंदु मानें। कुछ vibe-coding प्लेटफ़ॉर्म जैसे Koder.ai स्नैपशॉट्स और रोलबैक शामिल करते हैं, जो大胆 UI परिवर्तन के दौरान एक्सपेरिमेंटेशन को सुरक्षित बना सकते हैं।
एक और आदत जो शुरूआती इटरेशन को शांत रखती है: साझा विजेट्स को संपादित करने से ज़्यादा नए विजेट जोड़ना पसंद करें। साझा कम्पोनेंट्स वही जगह हैं जहाँ छोटे बदलाव बड़े डिफ़्स में बदल जाते हैं।
तेज़ UI काम तब सुरक्षित रहता है जब आप सोचने को टाइपिंग से अलग रख दें। कोड जनरेट करने से पहले एक स्पष्ट विजेट ट्री प्लान प्राप्त करें।
केवल विजेट ट्री का आउटलाइन मांगें। आप विजेट नाम, हायारकी, और हर भाग क्या दिखाता है यही चाहते हैं। अभी कोई कोड नहीं। यहाँ आप गायब स्टेट्स, खाली स्क्रीन और अजीब लेआउट चुनाव पकड़ सकते हैं जब सब कुछ सस्ता बदलने योग्य हो।
कम्पोनेंट ब्रेकडाउन माँगें और जिम्मेदारियाँ लिखवाएँ। हर विजेट को केंद्रित रखें: एक विजेट हेडर रेंडर करे, दूसरा लिस्ट, तीसरा खाली/एरर UI। अगर बाद में किसी चीज़ को स्टेट चाहिए होगा, उसे नोट करें पर अभी लागू न करें।
स्क्रीन स्कैफ़ोल्ड और stateless विजेट्स जनरेट करें। एक सिंगल स्क्रीन फाइल से शुरू करें जिसमें प्लेसहोल्डर कंटेंट और स्पष्ट TODOs हों। इनपुट्स एक्सप्लिसिट रखें (कन्स्ट्रक्टर पैरामीटर्स) ताकि बाद में असली स्टेट प्लग करना आसान हो।
स्टाइलिंग और लेआउट डिटेल्स के लिए अलग पास करें: स्पेसिंग, टाइपोग्राफी, थीमिंग, और रिस्पॉन्सिव बिहेवियर। स्टाइलिंग को अपने ही अलग डिफ़ में रखें ताकि रिव्यू सरल रहें।
सीमाएँ शुरुआत में रखें ताकि असिस्टेंट ऐसा UI न बना दे जिसे आप शिप न कर सकें:
कॉनक्रेट उदाहरण: यूज़र स्टोरी है “As a user, I can review my saved items and remove one.” विजेट ट्री माँगें जिसमें एक ऐप बार, आइटम रोज़ वाली लिस्ट, और एक खाली स्टेट हो। फिर SavedItemsScreen, SavedItemTile, EmptySavedItems जैसे ब्रेकडाउन माँगें। उसके बाद स्टेटलेस स्कैफ़ोल्ड फेक डेटा के साथ जनरेट करें, और आखिर में स्टाइल जोड़ें (डिवाइडर, पैडिंग, स्पष्ट रिमूव बटन) अलग पास में।
UI इटरेशन तब टूटता है जब हर विजेट निर्णय लेने लगता है। विजेट ट्री को “डम्ब” रखें: यह स्टेट पढ़े और रेंडर करे, बिजनेस नियम न करे।
पहले स्टेट्स को साधारण शब्दों में नाम दें। ज़्यादातर फीचर्स को सिर्फ “लोडिंग” और “डन” से अधिक चाहिए:
फिर उन ईवेंट्स को list करें जो स्टेट बदल सकते हैं: टैप्स, फॉर्म सबमिट, पुल-टू-रिफ्रेश, बैक, रीट्राई, और “यूज़र ने फील्ड एडिट किया।” पहले यह करने से बाद में अनुमान लगाने की ज़रूरत नहीं रहती।
फीचर के लिए एक स्टेट अप्रोच चुनें और उस पर टिके रहें। लक्ष्य “सबसे अच्छा पैटर्न” नहीं है, बल्कि निरंतर डिफ़्स हैं।
छोटी स्क्रीन के लिए एक साधारण कंट्रोलर (जैसे ChangeNotifier या ValueNotifier) अक्सर काफी होता है। लॉजिक एक जगह रखें:
कोड डालने से पहले plain English में स्टेट ट्रांज़िशन लिखें। उदाहरण लॉगिन स्क्रीन:
“जब यूज़र Sign in टैप करे: Loading सेट करें। अगर ईमेल अवैध है: Partial input में रहें और इनलाइन मैसेज दिखाएँ। अगर पासवर्ड गलत है: Error सेट करें और Retry सक्षम करें। अगर सफल: Success सेट करें और Home पर नेविगेट करें।”
फिर वही सेंटेंस मिलती-जुलती मिनिमल Dart कोड में जनरेट करें। रिव्यू सरल रहते हैं क्योंकि आप डिफ़ को नियमों से तुलना कर सकते हैं।
वैलिडेशन को स्पष्ट बनाएं। निर्णय लें कि जब इनपुट्स अवैध हों तो क्या होगा:
जब ये जवाब लिखे होते हैं, आपकी UI साफ़ रहती है और स्टेट कोड छोटा रहता है।
अच्छा नेविगेशन एक छोटे नक्शे के रूप में शुरू करें, न कि रूट्स के ढेर के रूप में। हर यूज़र स्टोरी के लिए चार पल लिखें: यूज़र कहाँ से इन्ट्री करता है, सबसे संभावित अगला कदम क्या है, वे कैसे कैंसिल करते हैं, और “बैक” का मतलब क्या है (पिछली स्क्रीन पर जाएँ या सुरक्षित होम स्टेट पर)।
एक सरल रूट मैप उन सवालों का जवाब दे जो अक्सर रीवर्क का कारण बनते हैं:
फिर स्क्रीन के बीच पास होने वाले पैरामीटर तय करें। स्पष्ट रहें: IDs (productId, orderId), फिल्टर्स (डेट रेंज, स्टेटस), और ड्राफ्ट डाटा (आंशिक भरा फॉर्म)। अगर आप इससे चूकोगे, तो आप स्टेट ग्लोबल सिंगलटन में छिपा देंगे या स्क्रीन्स को फिर से बनायेंगे ताकि वे संदर्भ “ढूँढ” सकें।
डीप लिंक का महत्व होता है भले आप उन्हें पहले दिन शिप न करें। तय करें कि जब यूज़र बीच के फ्लो में लैंड करे तो क्या होगा: क्या आप गायब डाटा लोड कर पाएंगे, या सुरक्षित एंट्री स्क्रीन पर रीडायरेक्ट करेंगे?
यह भी तय करें कि कौन सी स्क्रीन रिज़ल्ट लौटाएंगी। उदाहरण: “Select Address” स्क्रीन एक addressId लौटाती है, और चेकआउट स्क्रीन बिना फुल रिफ्रेश के अपडेट हो जाती है। रिटर्न शेप छोटा और टाइप्ड रखें ताकि बदलाव रिव्यू में सरल रहें।
कोड करने से पहले एज केस निकालें: अनसेव्ड बदलाव (कन्फर्म डायलॉग दिखाएँ), ऑथ आवश्यक (लॉगिन के बाद पाज़ और रीज़्यूम), और गायब/डिलीटेड डाटा (एरर दिखाएँ और बाहर निकलने का स्पष्ट रास्ता)।
जब आप तेज़ी से इटरेट करते हैं, असली जोखिम “गलत UI” नहीं है—यह अनरिव्यूएबल UI है। अगर टीममेट यह नहीं बता सकता कि क्या बदला, क्यों बदला, और क्या स्थिर रहा, तो हर अगला इटरेशन धीमा होगा।
एक नियम जो मदद करता है: इंटरफेस पहले लॉक करें, फिर अंदरूनी हिस्से बदलने दें। सार्वजनिक विजेट प्रॉप्स (इनपुट्स), छोटे UI मॉडल्स, और रूट आर्ग्युमेंट्स को स्थिर करें। एक बार ये नामांकित और टाइप किए गए, आप विजेट ट्री को फिर भी बिना बाकी ऐप तोड़े बदल सकते हैं।
कोड जनरेट करने से पहले एक डिफ़-फ्रेंडली योजना माँगें। आप ऐसी योजना चाहेंगे जो बताए कि कौन सी फाइलें बदलेगी और कौन सी अप्रभावित रहेंगी। इससे रिव्यू फोकस्ड रहते हैं और आकस्मिक रिफैक्टर से व्यवहार बदलने से बचता है।
डिफ़्स को छोटा रखने वाले पैटर्न:
मान लीजिए यूज़र स्टोरी है “As a shopper, I can edit my shipping address from checkout.” पहले रूट आर्ग्स लॉक करें: CheckoutArgs(cartId, shippingAddressId) स्थिर रहें। फिर स्क्रीन के अंदर इटरेट करें। जब लेआउट सेट हो जाए, तो उसे AddressForm, AddressSummary, और SaveBar में विभाजित करें।
अगर स्टेट हैंडलिंग बदलती है (उदाहरण के लिए, वैलिडेशन विजेट से CheckoutController में चली जाती है), तो रिव्यू पठनीय रहता है: UI फाइलें ज्यादातर रेंडरिंग बदलती हैं, जबकि कंट्रोलर में लॉजिक चेंज एक जगह दिखेगा।
सबसे तेज़ तरीका धीमा पड़ने का है असिस्टेंट से एक बार में सब कुछ बदलवाना। अगर एक कमिट लेआउट, स्टेट, और नेविगेशन को एक साथ छूता है, तो रिव्यूअर को नहीं पता चलेगा कि किस वजह से बग आया, और रोलबैक उलझ जाएगा।
एक सुरक्षित आदत है प्रति इटरेशन एक इरादा: विजेट ट्री बनाएं, फिर स्टेट वायर करें, फिर नेविगेशन कनेक्ट करें।
एक आम समस्या है जनरेटेड कोड को हर स्क्रीन पर नया पैटर्न इंट्रोड्यूस करने देना। अगर एक पेज Provider उपयोग करता है, अगला setState, और तीसरा कस्टम कंट्रोलर क्लास—ऐसा होने पर ऐप जल्दी असंगत बन जाता है। छोटे पैटर्न चुनें और उन पर रोक लगाएँ।
एक और गलती है async काम को सीधे build() में रखना। डेमो में यह ठीक लग सकता है, पर यह रिबिल्ड्स पर बार-बार कॉल करता है, फ्लिकर और ट्रैक करने में कठिन बग बनाता है। कॉल को initState(), व्यू मॉडल, या समर्पित कंट्रोलर में ले जाएँ, और build() को सिर्फ रेंडरिंग पर रखें।
नेमिंग एक शांत जाल है। Widget1, data2, या temp जैसे नाम भविष्य के रिफैक्टर को दर्दनाक बना देते हैं। स्पष्ट नाम असिस्टेंट को भी बेहतर फॉलो-अप चेंजेस देने में मदद करते हैं क्योंकि इरादा साफ़ होता है।
रोकथाम के गार्डरेइल्स:
build() के अंदर नेटवर्क या डेटाबेस कॉल न रखेंएक क्लासिक विज़ुअल-बग फिक्स है और भी Container, Padding, Align, और SizedBox जोड़ना जब तक यह सही न दिखे। कुछ पास के बाद ट्री पढ़ने लायक नहीं रहता।
अगर कोई बटन गलत अलाइन है, पहले रैपर्स हटाकर देखें, एक सिंगल पेरेंट लेआउट विजेट का उपयोग करें, या एक छोटा विजेट निकालें जिसके अपने कंस्ट्रेन्ट हों।
उदाहरण: एक चेकआउट स्क्रीन जहां लोडिंग के समय कुल कीमत स्किप करती है। एक असिस्टेंट शायद कीमत रो को “स्थिर” करने के लिए और विजेट्स रैप कर देगा। एक साफ़ समाधान है लोडिंग प्लेसहोल्डर से जगह आरक्षित करना जबकि रो स्ट्रक्चर को अपरिवर्तित रखना।
कमिट करने से पहले दो मिनट का पास करें जो यूज़र वैल्यू चेक करे और आपको आश्चर्यजनक रिग्रेशन से बचाए। लक्ष्य पूर्णता नहीं है—यह सुनिश्चित करना है कि यह इटरेशन रिव्यू करने योग्य, टेस्ट करने योग्य और वापस करने योग्य हो।
यूज़र स्टोरी एक बार पढ़ें, फिर रनिंग ऐप (या कम से कम एक साधारण विजेट टेस्ट) के खिलाफ इन आइटम्स को वेरीफाई करें:
एक वास्तविकता जाँच: अगर आपने एक नई Order details स्क्रीन जोड़ी है, तो आपको (1) लिस्ट से उसे खोलना चाहिए, (2) लोडिंग स्पिनर दिखना चाहिए, (3) एक एरर सिमुलेट कर पाना चाहिए, (4) खाली ऑर्डर दिखना चाहिए, और (5) बिना अजीब कूद के बैक दबाने पर लिस्ट पर वापस लौटना चाहिए।
अगर आपका वर्कफ़्लो स्नैपशॉट्स और रोलबैक सपोर्ट करता है, तो बड़े UI बदलाव से पहले स्नैपशॉट लें। कुछ प्लेटफ़ॉर्म जैसे Koder.ai यह सपोर्ट करते हैं, और यह आपको मेन ब्रांच को जोखिम में डाले बिना तेज़ी से इटरेट करने में मदद कर सकता है।
यूज़र स्टोरी: “As a shopper, I can browse items, open a details page, save an item to favorites, and later view my favorites.” लक्ष्य है शब्दों से स्क्रीन तक पहुँचना तीन छोटे, रिव्यू योग्य कदमों में।
इटरेशन 1: सिर्फ ब्राउज़ लिस्ट स्क्रीन पर ध्यान दें। एक ऐसा विजेट ट्री बनाएं जो रेंडर करने के लिए पूरा हो पर असली डाटा से जुड़ा न हो: Scaffold के साथ AppBar, placeholder रोज़ वाली ListView, और लोडिंग व खाली स्टेट के लिए स्पष्ट UI। स्टेट सरल रखें: लोडिंग (CircularProgressIndicator दिखाएँ), खाली (छोटी संदेश और शायद Try again बटन), और रेडी (लिस्ट दिखाएँ)।
इटरेशन 2: डिटेल्स स्क्रीन और नेविगेशन जोड़ें। स्पष्ट रखें: onTap एक रूट पुश करे और छोटा पैरामीटर ऑब्जेक्ट पास करे (उदा.: item id, title)। डिटेल्स पेज को अभी के लिए रीड-ओनली रखें जिसमें एक टाइटल, विवरण प्लेसहोल्डर, और Favorite एक्शन बटन हो। मकसद स्टोरी से मेल खाना है: list -> details -> back, बिना एक्स्ट्रा फ्लो के।
इटरेशन 3: फेवरेइट्स स्टेट अपडेट और UI फीडबैक जोड़ें। एक सिंगल सोर्स ऑफ़ ट्रूथ जोड़ें (भले ही वह अभी इन-मेमोरी ही हो), और इसे दोनों स्क्रीन में वायर करें। Favorite टैप करने पर आइकन तुरंत अपडेट हो और छोटा कन्फर्मेशन दिखे (जैसे SnackBar)। फिर एक Favorites स्क्रीन जोड़ें जो उसी स्टेट को पढ़े और खाली/लिस्ट UI संभाले।
रिव्यूएबल डिफ़ आमतौर पर ऐसा दिखता है:
browse_list_screen.dart: विजेट ट्री प्लस लोडिंग/खाली/रेडी UIitem_details_screen.dart: UI लेआउट और नेविगेशन पैरामीटर स्वीकार करता हैfavorites_store.dart: मिनिमल स्टेट होल्डर और अपडेट मेथडapp_routes.dart: रूट्स और टाइप्ड नेविगेशन हेल्पर्सfavorites_screen.dart: स्टेट पढ़ता है और खाली/लिस्ट UI दिखाता हैअगर कोई एक फाइल “हर चीज़ होने वाली जगह” बन जाए, तो आगे बढ़ने से पहले उसे विभाजित करें। छोटे नामी फाइलें अगला इटरेशन तेज़ और सुरक्षित रखती हैं।
अगर वर्कफ़्लो केवल तब काम करता है जब आप “ज़ोन में” हों, तो यह स्क्रीन बदलते ही या कोई टीममेट टच करते ही टूट जाएगा। लूप को आदत बनाएं—इसे लिख कर रखें और परिवर्तन आकार के बारे में गार्डरेइल्स लगाएँ।
एक टीम टेम्पलेट उपयोग करें ताकि हर इटरेशन एक ही इनपुट से शुरू हो और एक ही तरह का आउटपुट दे। इसे छोटा पर विशिष्ट रखें:
यह असिस्टेंट को बीच में नए पैटर्न बनाने की संभावना घटा देता है।
एक परिभाषा चुनें जो कोड रिव्यू में लागू करना आसान हो। उदाहरण के लिए, हर इटरेशन को सीमित फाइलों तक कॅप करें, और UI रिफैक्टर को बिहेवियर चेंज से अलग रखें।
सरल नियमों का सेट:
बुरे कदम को जल्दी वापस लेने के लिए चेकपॉइंट जोड़ें। कम से कम, टैग कमिट्स या लोकल चेकपॉइंट रखें। अगर आपका वर्कफ़्लो स्नैपशॉट्स और रोलबैक सपोर्ट करता है, तो उनका उपयोग आक्रामक रूप से करें।
यदि आप चाहते हैं कि एक चैट-आधारित वर्कफ़्लो अंत तक Flutter ऐप्स जनरेट और परिष्कृत करे, तो Koder.ai में एक प्लानिंग मोड है जो आपको कोड लागू करने से पहले योजना और अपेक्षित फाइल चेंजेस रिव्यू करने में मदद करता है।
Use a small, testable UI spec first. Write 3–6 lines that cover:
Then build only that slice (often one screen + 1–2 widgets).
Convert the story into four buckets:
If you can’t describe the acceptance check quickly, the story is still too fuzzy for a clean UI diff.
Start by generating only a widget tree outline (names + hierarchy + what each part shows). No code.
Then request a component responsibility breakdown (what each widget owns).
Only after that, generate the stateless scaffold with explicit inputs (values + callbacks), and do styling in a separate pass.
Treat it as a hard rule: one intent per iteration.
If a single commit changes layout, state, and routes together, reviewers won’t know what caused a bug, and rollback gets messy.
Keep widgets “dumb”: they should render state, not decide business rules.
A practical default:
Avoid putting async calls in build()—it leads to repeated calls on rebuild.
Define states and transitions in plain English before coding.
Example pattern:
Then list events that move between them (refresh, retry, submit, edit). Code becomes easier to compare against the written rules.
Write a tiny “flow map” for the story:
Also lock down what travels between screens (IDs, filters, draft data) so you don’t end up hiding context in globals.
Default to feature-first folders so changes stay contained. For example:
lib/features/<feature>/screens/lib/features/<feature>/widgets/lib/features/<feature>/state/lib/features/<feature>/routes.dartThen keep each iteration focused on one feature folder and avoid drive-by refactors elsewhere.
A simple rule: stabilize interfaces, not internals.
Reviewers care most that inputs/outputs stayed stable even if the layout moved around.
Do a two-minute pass:
If your workflow supports it (for example snapshots/rollback), take a snapshot before a bigger layout refactor so you can revert safely.