เจาะลึก StatefulWidget ของ Flutter และวงจรการใช้งาน

คุณจะใช้ StatefulWidget's หากคุณกำลังจะใช้ Flutter คุณจะต้องใช้ StatefulWidget's เยอะมาก. ด้วยกัน. หนึ่งอยู่ด้านบนของอีกอัน มันหลีกเลี่ยงไม่ได้ เมื่อคุณเรียนรู้ Flutter มากขึ้น แอพของคุณจะซับซ้อนมากขึ้น...ด้วยวิดเจ็ตที่มากขึ้น วิดเจ็ต Stateful เพิ่มเติม วิดเจ็ตไร้สัญชาติไม่เคยเปลี่ยนแปลง วิดเจ็ตเก็บสถานะสามารถเปลี่ยนแปลงได้ตามการโต้ตอบของผู้ใช้หรือเหตุการณ์อื่นๆ 'สถานะ' ของวิดเจ็ตนี้แสดงโดยอ็อบเจ็กต์คลาสแยกต่างหากที่เรียกว่า เอ่อ... สถานะ วัตถุสถานะประกอบด้วยค่าที่สามารถเปลี่ยนแปลงได้ ออบเจ็กต์ State มี 'สถานะที่ไม่แน่นอน' ของวิดเจ็ตที่เกี่ยวข้อง — โดยจะเก็บค่าที่สามารถเปลี่ยนแปลงได้ตลอดเวลา เมื่อค่าเหล่านั้นมีการเปลี่ยนแปลง บ่อยครั้ง StatefulWidget ที่เกี่ยวข้องจะถูกสร้างขึ้นใหม่

เรียนรู้ตามตัวอย่าง

ในบทความหลายบทความของฉัน ฉันพยายามใช้ตัวอย่างที่สร้างขึ้นจากเว็บไซต์ Flutter ของ Google เพื่อถ่ายทอดแนวคิด วิดเจ็ตเฉพาะ ฯลฯ ในบทความนี้ ฉันจะใช้ 'แอปตัวอย่าง' ที่สร้างขึ้นสำหรับคุณทุกครั้งที่คุณสร้าง ' โปรเจ็กต์ Flutter ใหม่...' ด้านล่างนี้ ฉันได้แยกทั้ง StatefulWidget และออบเจ็กต์ State ที่พบในโค้ดที่สร้างขึ้นนั้น ความคิดเห็นและรหัสอื่น ๆ ถูกลบออกเพื่อความกระชับ

ฉันชอบภาพหน้าจอ คลิกเพื่อดูส่วนสำคัญ

และเช่นเคย ฉันชอบใช้ภาพหน้าจอมากกว่าส่วนสำคัญเพื่อแสดงแนวคิดมากกว่าแค่แสดงโค้ดในบทความของฉัน ฉันพบว่าพวกเขาทำงานด้วยตรงไปตรงมาง่ายกว่า อย่างไรก็ตาม คุณสามารถคลิกหรือแตะภาพหน้าจอเหล่านี้เพื่อดูโค้ดที่แสดงในส่วนสำคัญหรือใน Github น่าแปลกที่คุณควรอ่านบทความนี้เกี่ยวกับการพัฒนาอุปกรณ์เคลื่อนที่บนคอมพิวเตอร์มากกว่าบนโทรศัพท์ นอกจากนี้ เรายังเขียนโปรแกรมบนคอมพิวเตอร์ ไม่ใช่บนโทรศัพท์ สำหรับตอนนี้.

ไม่มีภาพเคลื่อนไหว ไม่มีโซเชียลมีเดีย

โปรดทราบว่าจะมีไฟล์ gif ในบทความนี้เพื่อสาธิตแง่มุมต่างๆ ของหัวข้อนี้ อย่างไรก็ตาม มีการกล่าวกันว่าการดูไฟล์ gif ดังกล่าวเป็นไปไม่ได้เมื่ออ่านบทความนี้บนแพลตฟอร์ม เช่น Instagram, Facebook ฯลฯ โปรดทราบเรื่องนี้และอาจอ่านบทความนี้บน medium.com

เอาล่ะ.

สร้างหน้าจอ

เมื่อคุณรันโค้ดที่สร้างขึ้น คุณจะได้รับแอปตัวนับที่ดี เมื่อคุณเห็นหน้าจอแบบเดียวกับในภาพหน้าจอด้านบน คุณจะรู้ว่าฟังก์ชัน build() สำหรับวัตถุ State _MyHomePageSate ได้ทำงานแล้ว และวิดเจ็ต Scaffold ถูกส่งกลับ ภายในวิดเจ็ต Scaffold นั้น มีวิดเจ็ตอื่นๆ อีกมากมาย ฉันนับแปดนอกมือ พวกมันทำงานหมดแล้วและตอนนี้ก็แสดงบนหน้าจอ สังเกตลูกศรสีแดงด้านล่าง คุณจะเห็นว่าเมื่อแสดงแล้ว วิดเจ็ตหนึ่งตัวจะถูกวางพร้อมกับตัวจัดการเหตุการณ์เพื่อทำบางสิ่งหากถูกกดและเมื่อใด

ตั้งค่าให้สร้างใหม่

คุณรู้หรือไม่ว่าฟังก์ชัน setState() ของ State object ทำหน้าที่อะไร? มันทำสิ่งต่างๆ มากมาย แต่สิ่งหนึ่งที่พิเศษที่เราสนใจก็คือ มันทำให้ฟังก์ชัน build() ของออบเจ็กต์ State ทำงานด้วยเช่นกัน ด้วยเหตุนี้ ในกรณีนี้ ฟังก์ชันที่ไม่ระบุชื่อจะรันและเพิ่มตัวแปรอินสแตนซ์ของออบเจ็กต์สถานะที่เรียกว่า _counter จากนั้นกรอบงาน Flutter จะได้รับแจ้งให้เรียกใช้ฟังก์ชัน build() ของวัตถุ State อีกครั้ง โดยแสดงค่าที่อัปเดตของตัวแปรอินสแตนซ์อีกครั้ง ทุกครั้งที่มีการเรียกใช้ฟังก์ชัน setState() ของวัตถุ State ฟังก์ชัน build() ของวัตถุนั้นจะถูกเรียกใช้หลังจากนั้นไม่นาน

นับสิ่งที่คุณสร้าง

ดังนั้น หากคุณกดปุ่มสีน้ำเงินลอยนั้น คุณจะเห็นหมายเลข 1 บนหน้าจอ ฟังก์ชัน build() ของ State object ถูกเรียกอีกครั้ง — แสดงคุณสมบัติของฟิลด์หรือตัวแปรอินสแตนซ์ _counter โดยมีค่าจำนวนเต็มใหม่เป็น 1 หน้าจอที่คุณเห็นตรงหน้าคือ 'สร้างใหม่' อย่างแท้จริงจากเนื้อหาในฟังก์ชัน build() ของวัตถุ State เข้าใจจนถึงตอนนี้?

ลองมาดูเรื่องนี้ให้ละเอียดยิ่งขึ้น ขณะนี้มีฟังก์ชัน print() จำนวนหนึ่งที่นำมาใช้กับโค้ด เพื่อเน้นย้ำว่าแต่ละคลาสถูกสร้างอินสแตนซ์เมื่อใด (เช่น เมื่อแต่ละวิดเจ็ตถูกสร้างขึ้น) ฉันได้กำหนด Constructor ไว้อย่างชัดเจนสำหรับทั้งอ็อบเจ็กต์ StatefulWidget และออบเจ็กต์ State โดยจัดให้มีฟังก์ชัน print() ในแต่ละอัน มีคำสั่งพิมพ์วางไว้ในตำแหน่งอื่นที่น่าสนใจเพื่อสาธิต 'วงจรชีวิต' ของ StatefulWidget และออบเจ็กต์สถานะ คลิกที่ภาพหน้าจอด้านล่างเพื่อรับสำเนาของคุณเอง จากนั้นคุณสามารถติดตามได้เลย

จะเกิดอะไรขึ้นกับสตาร์ทอัพ

ดังนั้น ด้วยฟังก์ชัน print() เหล่านั้น เราจึงสามารถเห็นได้ทันทีว่าจะเกิดอะไรขึ้นเมื่อแอปตัวนับเริ่มทำงานครั้งแรก ตามที่คาดไว้ เมื่อดูที่หน้าจอคอนโซลด้านล่าง คลาสทั้งสองที่ประกอบเป็นอ็อบเจ็กต์ StatefulWidget และ State จะถูกสร้างขึ้น จากนั้นฟังก์ชัน build() ของอ็อบเจ็กต์ State จะถูกเรียกเพื่อแสดงหน้าจอผลลัพธ์ ค่อนข้างตรงไปตรงมาจนถึงตอนนี้

กดปุ่ม. ดูว่าเกิดอะไรขึ้น

กดปุ่มสีน้ำเงินลอยหนึ่งครั้ง และคุณจะได้รับสิ่งต่อไปนี้บนหน้าจอคอนโซล ทำให้รู้สึก ดังที่คุณทราบ การเรียกใช้ฟังก์ชัน setState() ของวัตถุ State จะทำให้ฟังก์ชัน build() ของวัตถุ State ถูกเรียกอีกครั้ง ดูดี.

คุณค่อนข้างเข้าใจแนวคิดนี้ ทุกครั้งที่คุณกดปุ่ม คุณจะเห็นการเรียกฟังก์ชัน build() ของ State object อีกครั้ง โดยแสดงค่าในตัวแปรอินสแตนซ์ของ State object _counter ด้านล่างเป็นภาพหน้าจอคอนโซลเมื่อมีการกดปุ่มหกครั้ง ค่อนข้างง่าย

เริ่มต้นสถานะ

ก่อนที่เราจะดำเนินการต่อไป ลองย้อนกลับไปดูที่วัตถุสถานะนั้นก่อน ตามเนื้อผ้า ตรรกะส่วนใหญ่สำหรับแอปของคุณจะพบ เข้าถึง และเรียกใช้ในออบเจ็กต์สถานะของแอป คุณจะพบว่าตัวเองกำลังกำหนดตรรกะนี้ในคลาสที่ประกอบเป็นวัตถุ State ของคุณ ดังนั้น สิ่งธรรมดาในการเริ่มต้นตรรกะดังกล่าวจึงอยู่ในฟังก์ชัน initState() ของออบเจ็กต์ State มาสาธิตสิ่งนี้ตอนนี้โดยการแก้ไขแอปตัวนับ ol' ของเราและเริ่มต้นในกรณีนี้คือตัวแปรอินสแตนซ์ _counter ด้วยค่าจำนวนเต็ม 0 ภายใน initState( ) การทำงาน.

ด้วยตัวอย่างง่ายๆ ในการใช้งาน แต่ฉันอยากจะถ่ายทอดแนวทางปฏิบัติทั่วไปบางอย่างที่พบในแอปพลิเคชันที่ซับซ้อนมากขึ้น วิธีหนึ่งคือการจัดเตรียมค่าเริ่มต้นให้กับตัวแปรอินสแตนซ์ดังกล่าวในฟังก์ชัน initState() ฟังก์ชันนี้จะถูกเรียกเมื่อมีการสร้างออบเจ็กต์ State เป็นครั้งแรกเท่านั้น ดังนั้นแอปจะทำงานเหมือนเดิม โปรดทราบว่าคำสั่งพิมพ์จะอยู่ภายในฟังก์ชัน initState() เช่นกัน

ทุกอย่างมีพฤติกรรมเหมือนกัน อย่างไรก็ตาม ตอนนี้คุณเห็นบรรทัดเพิ่มเติมในหน้าจอคอนโซล อีกครั้ง คุณจะเห็นบรรทัดนี้เพียงครั้งเดียวเมื่อวัตถุ State ถูกสร้างอินสแตนซ์เป็นครั้งแรก

มันจะซับซ้อน

ตกลงเรากลับมากันดีกว่า จนถึงตอนนี้มันง่ายมาก มันเป็นแอปที่เรียบง่ายจริงๆ อย่างไรก็ตาม แอปของคุณจะไม่เรียบง่ายจนน่าขัน พวกเขาจะซับซ้อนมากขึ้น มาดูกันว่าฉันสามารถแนะนำความซับซ้อนดังกล่าวในขณะที่ยังคงใช้แนวคิดของแอปตัวนับแบบง่ายได้หรือไม่

สองหน้าจอ; สามเคาน์เตอร์

ตอนนี้ฉันจะให้แอปตัวนับเวอร์ชันอื่นแก่คุณ ในเวอร์ชันนี้มีตัวนับสามตัวในสองหน้าจอแยกกัน! สิ่งต่างๆ เปลี่ยนไปในเวอร์ชันนี้ ในแบบที่คุณจะเห็นในแอปที่ซับซ้อนมากขึ้น ก่อนอื่น เมื่อเทียบกับแอปตัวนับดั้งเดิม คุณจะเห็น StatefulWidgets กำลังเริ่มต้น StatefulWidgets อื่น ๆ

ด้านล่างนี้เป็นภาพหน้าจอของแอปตัวนับที่ซับซ้อนกว่านี้ แตะหรือคลิกบนภาพหน้าจอเพื่อรับสำเนาสำหรับตัวคุณเอง คุณสามารถเห็นวัตถุ State ดั้งเดิม _MyHomePageState ตอนนี้มีตัวนับของตัวเองแล้ว! นอกจากนี้ StatefulWidget ใหม่ที่เรียกว่า _FirstPage จะแสดงหน้าจอดั้งเดิมแล้ว!

นอกจากนี้ในโค้ดคุณจะพบ StatefulWidget 'หน้าที่สอง' ที่จะแสดงตัวนับแยกต่างหากบนหน้าจอแยกต่างหาก อีกครั้ง มันมีคำสั่งพิมพ์เต็มไปหมด เพื่อให้เราสามารถติดตาม 'ลำดับของเหตุการณ์' สำหรับวิดเจ็ตดังกล่าวได้

เมื่อดูที่หน้าจอคอนโซล คุณจะเห็นว่าคำสั่งพิมพ์เน้นลำดับที่สร้างวิดเจ็ตและออบเจ็กต์สถานะและลำดับที่เรียกใช้ฟังก์ชัน ตัวอย่างเช่น เมื่อหน้าจอหลักแสดงขึ้นเป็นครั้งแรก คุณจะเห็นออบเจ็กต์ State ที่เกี่ยวข้องเรียกใช้ฟังก์ชัน initState() ก่อนจึงจะเรียกใช้ฟังก์ชัน build() ในที่สุด เมื่อพูดถึงหน้าจอหลัก มันคือออบเจ็กต์สถานะ _FirstPageState พร้อมด้วยวิดเจ็ตในฟังก์ชัน build() ที่แสดงหน้าจอแรกหรือหน้าแรก

ปฏิบัติตาม 'เส้นลูกศร' ตามลำดับ และคุณจะเห็นตรรกะที่เกี่ยวข้องเมื่อแสดงหน้าจอแรก StatelessWidget และ StatefulWidget แต่ละรายการจะถูกสร้างขึ้นทีละรายการ และด้วย StatefulWidget แต่ละรายการ คุณจะเห็นว่าวัตถุ State ที่เกี่ยวข้องนั้นถูกสร้างขึ้นและเรียกใช้ฟังก์ชันต่างๆ ของมัน

ถึงวินาที

ในขณะนี้ ขณะที่ดูหน้าจอคอนโซล หากคุณแตะที่ปุ่ม หน้าที่สอง คุณจะเห็นว่ามันไปที่ StatefulWidget อันที่สามในแอปชื่อ SecondPage มันสร้างวิดเจ็ตนั้นขึ้นมา มันสร้างออบเจ็กต์สถานะที่มาพร้อมกับชื่อ _SecondPageState โดยจะผ่านฟังก์ชัน initState() ของออบเจ็กต์ State และสุดท้ายไปที่ฟังก์ชัน build() เพื่อแสดงหน้าจอที่สอง แต่อย่างที่คุณเห็นในคอนโซล มันยังไม่เสร็จ

โปรดข้ามย่อหน้าแรกด้านล่าง ตอนนี้มันไม่เกิดขึ้นแล้ว

แต่อย่าลืมสิ่งแรก

กรอบงาน Flutter เมื่อแสดงหน้าจอที่สอง แล้วเรียกวัตถุ State ของหน้าจอแรก _FirstPageState อีกครั้ง! คราวนี้จะเรียกใช้ฟังก์ชัน ปิดใช้งาน() ของวัตถุสถานะนั้น แน่นอนว่าอย่างที่คุณเห็นเรายังไม่เสร็จ จากนั้น Flutter จะเรียกใช้ฟังก์ชัน build() ของ State object แรกในแอป _MyHomePageState เพื่อสร้าง StatefulWidget ขึ้นมาใหม่ FirstPage คุณจะเห็นว่า Constructor ของมันถูกเรียกอีกครั้ง สุดท้ายนี้ มีการเรียกฟังก์ชัน build() อีกครั้งในออบเจ็กต์ State _FirstPageState ทีนี้ทำไม Flutter framework ถึงทำแบบนั้นทั้งหมดล่ะ?? เราจะไปที่นั้นอีกครั้ง ในตอนนี้ คุณสังเกตเห็นบางสิ่งที่ขาดหายไปในลำดับการโทรนั้นหรือไม่

ใน Flutter นั้น Widget จะถูกสร้างขึ้นใหม่ตลอดเวลา อย่างสม่ำเสมอ. ทุกครั้งที่คุณทำให้ฟังก์ชัน build() ทำงาน คุณกำลังสร้างวิดเจ็ตที่พบในนั้นขึ้นมาใหม่ ทำความคุ้นเคยกับข้อเท็จจริงนั้น แต่คุณสังเกตเห็นสิ่งที่ไม่ได้ถูกเรียกในลำดับเหตุการณ์นั้นหรือไม่? ถูกตัอง! คุณไม่เห็นออบเจ็กต์ State 'อันแรก' ถูกสร้างขึ้นใหม่และหรือฟังก์ชัน initState() ถูกเรียกอีกครั้ง! ฉันได้วางพวกเขาไว้ในที่ที่พวกเขาจะไปตามลำดับด้านล่าง แต่จริงๆ แล้ว พวกเขาไม่ได้ถูกเรียก ออบเจ็กต์สถานะ _FirstPageState ถูกปล่อยให้อยู่ตามลำพังเพื่อเรียกใช้ฟังก์ชัน build() อีกครั้งเท่านั้น แล้วทำไมพวกเขาถึงไม่โทรมาล่ะ?

อย่างไรก็ตาม วัตถุ State แรกเรียกใช้ฟังก์ชัน build() แต่ยังไม่แสดงเนื่องจากกลไกการกำหนดเส้นทางของ Flutter ขณะนี้เรากำลังอยู่ใน "การซ้อนทับแบบแยกกันบนสแต็กของเส้นทาง" ดังนั้นจึงนำเสนอด้วย StatefulWidget "หน้าที่สอง" และเนื้อหาของฟังก์ชัน build() ของอ็อบเจ็กต์สถานะแทน

รัฐยังคงอยู่

ความจริงยังคงอยู่ที่ออบเจ็กต์สถานะ 'หน้าแรก' ยังคงอยู่แม้ว่า StatefulWidget จะถูกทำลายและสร้างใหม่อีกครั้งก็ตาม วัตถุของรัฐและที่สำคัญกว่านั้น เนื้อหาภายในวัตถุของรัฐจะถูกเก็บรักษาไว้ ในขณะที่เฟรมเวิร์กที่อยู่รอบๆ วัตถุนั้นถูกสร้างขึ้นใหม่ครั้งแล้วครั้งเล่า นั่นคือสิ่งที่ฉันต้องการให้คุณชื่นชมในเวลานี้ จากการออกแบบ ตัวสร้างของวัตถุ State และ initState() จะไม่ถูกเรียกใช้อีกครั้งในลำดับของเหตุการณ์ดังกล่าว ลองดูอีกตัวอย่างหนึ่งของเรื่องนี้

จำนวนชื่อเรื่อง

ตอนนี้ในแอปตัวนับนี้ คุณจะสังเกตเห็นว่ามีปุ่ม "ตัวนับหน้าแรก" ปรากฏใกล้กับด้านล่างของหน้าจอเมื่อคุณเปิดใช้งานครั้งแรก ทุกครั้งที่คลิกปุ่ม ตัวนับที่ปรากฏบนแถบชื่อเรื่องของแอปจะเพิ่มขึ้น สวยดูเรียบง่ายใช่มั้ย? อย่างไรก็ตาม เรามาดูกันว่ามีอะไรเกี่ยวข้องบ้าง

โปรดจำไว้ว่าในแอปนี้ เมื่อคุณคลิกที่ปุ่ม ฟังก์ชัน setState() จะถูกเรียก นั่นหมายความว่าฟังก์ชัน build() จะถูกเรียกหลังจากนั้นไม่นาน โปรดจำไว้ในขณะที่เราดำเนินการต่อ

สร้างบ้านของคุณ

คุณสามารถดูได้เมื่อกดปุ่ม 'ตัวนับหน้าแรก' วิธีสำรองข้อมูลที่กำหนดไว้ในออบเจ็กต์สถานะ _MyHomePageSate จะเพิ่มขึ้น นอกจากนี้ ยังมีการเรียก setState() ของออบเจ็กต์ State นั้น และคุณก็รู้ว่ามันหมายถึงอะไร

ดังนั้น เมื่อสำรองไปที่ออบเจ็กต์ State _MyHomePageSate ฟังก์ชัน build() ของมันก็จะถูกเรียกในไม่ช้าเพื่อถ่ายทอดค่า "ตัวนับ" ใหม่ในชื่อแอป ดูลูกศรสีแดงด้านล่าง

คอนโซลรู้

อีกครั้ง เมื่อกดปุ่ม 'ตัวนับหน้าแรก' คุณจะเห็นย้อนกลับไปในหน้าแรก โดยมีการเรียกฟังก์ชัน build() ของออบเจกต์ State (ดูภาพหน้าจอด้านบน) และอัปเดตแถบชื่อเรื่อง สำหรับแอป แน่นอน เนื่องจากฟังก์ชัน _MyHomePageState build() ถูกรัน ดังนั้น StatefulWidget FirstPage จึงถูกสร้างขึ้นใหม่ (เรียกว่า Constructor) และ build< ของอ็อบเจ็กต์ State /strong>() ฟังก์ชันถูกเรียกอีกครั้ง อย่างไรก็ตาม ขอย้ำอีกครั้งว่าออบเจ็กต์ State เองไม่ได้ถูกสร้างขึ้นใหม่ มิฉะนั้นจะเหลืออยู่คนเดียว ดูวิธีการทำงานนี้?

รอสักครู่!

มาลองตัวนับบนหน้าจอที่สองกัน คุณสามารถดูลำดับเหตุการณ์ที่พาเราไปที่ภาพหน้าจอที่เราเห็นด้านล่าง แอปเริ่มต้นขึ้น และปุ่มที่มีข้อความว่า 'หน้าที่สอง' ถูกกดอยู่ หน้าที่สองปรากฏขึ้นบนหน้าจอ (ผ่านคลาสเนวิเกเตอร์) และปุ่มสีน้ำเงินลอยของหน้านั้นถูกกดปุ่มหนึ่งครั้ง สุดท้าย คุณจะเห็นว่าออบเจ็กต์ State _SecondPageState มีการเรียกใช้ฟังก์ชัน build() เพื่อ "วาดใหม่" หน้าจอเพื่อแสดงหมายเลข 1

ดูค่อนข้างตรงไปตรงมาใช่ไหม? ทุกครั้งที่กดปุ่ม ออบเจ็กต์ State _SecondPageState จะมีฟังก์ชัน setState() และ build() ตามลำดับ

กลับไปก่อน

เอาล่ะ กลับไปที่หน้าจอแรกกันดีกว่า เนื่องจากเราใช้คลาส Navigator เพื่อเปิดและนำเสนอหน้าจอที่สอง เราจึงสามารถใช้ Navigator อีกครั้งเพื่อถอยกลับ 'route stack of widgets' และกลับสู่หน้าจอแรกโดยใช้คำสั่ง “Navigator.pop (บริบท);” ตอนนี้ดูที่หน้าจอคอนโซลด้านล่าง

คนแรกไป

ดูว่าเกิดอะไรขึ้นเมื่อแอปกลับมาที่หน้าจอแรก! แอปจะสร้าง StatefulWidget 'หน้าแรก' ขึ้นมาใหม่อีกครั้ง ขั้นแรก ให้เรียกฟังก์ชัน deactivate() จากออบเจ็กต์ State ของหน้าแรกอีกครั้ง จากนั้นจึงเรียก _HomePageState build() (ดังนั้นเพื่อสร้าง StatefulWidget 'หน้าแรก' อีกครั้ง ). คุณจะเห็นว่าเป็นเพราะตัวสร้างของมันจึงยิง สุดท้าย StatefulWidget's State object (ปล่อยทิ้งไว้โดยไม่ถูกแตะต้อง) จะเรียกใช้ฟังก์ชัน build() อีกครั้ง โว้ว! เกิดขึ้นมากมายที่นั่นใช่มั้ย? แต่นั่นไม่ใช่ทั้งหมด!

ป๊อปไปรัฐ

เมื่อวิดเจ็ต 'เพจที่สอง' ถูกลบออกจากสแต็กการกำหนดเส้นทาง วิดเจ็ตจะถูกล้างออกจากหน่วยความจำ - และออบเจ็กต์สถานะก็เช่นกัน คุณสามารถเห็นการเรียกฟังก์ชัน ปิดใช้งาน() ของออบเจ็กต์สถานะ 'หน้าสอง' (ดูด้านล่าง) เช่นเดียวกับฟังก์ชันอื่น สิ่งหนึ่งที่อาจใหม่สำหรับคุณ ฟังก์ชัน กำจัด() นั่นกำลังบอกคุณว่าออบเจ็กต์ State ได้รับการเผยแพร่ออกจากหน่วยความจำแล้ว และเช่นเดียวกับ StatefulWidget ก็คือตัวเลือกสำหรับการรวบรวมขยะของแพลตฟอร์ม โปรดทราบว่า 'ทรัพยากรจำนวนมาก' ใดๆ เช่น ไฟล์ที่เปิดอยู่และหรือสตรีมแบบเปิดควรถูกปิดอย่างชัดเจนในฟังก์ชัน ปิดใช้งาน() หรือในฟังก์ชัน กำจัด() (บางคนบอกว่าเป็นการดีที่สุดที่จะใช้ ปิดใช้งาน() เทียบกับ กำจัด())

ดังนั้น หากคุณกลับไปที่หน้าสอง คุณจะพบว่าการนับกลับเป็นศูนย์ StatefulWidget และออบเจ็กต์ State ประกอบได้ถูกสร้างขึ้นอีกครั้งเมื่อคุณกลับไปที่เพจที่สองผ่านคลาส Navigator

วางใจที่บ้าน

เราสามารถทำสิ่งนี้ต่อไปได้ จะเกิดอะไรขึ้น เช่น หากตัวนับโฮมเพจบนแถบชื่อเรื่องกลับมาที่หน้าจอแรกเพิ่มขึ้นในขณะที่เราอยู่ที่หน้าสอง มาดูกัน. ด้านล่างนี้เป็นลำดับเมื่อกด 'ตัวนับหน้าแรก' บนหน้าที่ 2 สามครั้งติดต่อกัน จากนั้นเราก็กลับมาที่หน้าจอแรก เราจะเห็นได้ว่าทุกครั้งที่กดปุ่มนั้น StatefulWidget ของหน้าจอแรกจะถูกสร้างขึ้นใหม่ แน่นอนว่า นั่นเป็นเพราะออบเจ็กต์สถานะของหน้าแรกกำลังเรียกใช้ฟังก์ชัน build() (เพื่ออัปเดตแถบหัวเรื่อง) เป็นผลให้ StatefulWidget ของหน้าจอแรกถูกสร้างขึ้นใหม่ และฟังก์ชัน build() ของออบเจ็กต์ที่เชื่อมโยงก็ถูกเรียกเช่นกัน ในที่สุด เมื่อเรากลับไปที่หน้าจอแรก เราจะเห็นว่าแถบชื่อเรื่องแสดงขึ้นโดยมีค่าตัวนับเป็น 3

รัฐที่สำคัญ

คุณสังเกตเห็นปุ่มเพิ่มเติมนั้นบนหน้าจอที่สองหรือไม่? ปุ่ม 'รหัสใหม่' นั้นเหรอ? คุณรู้ไหมว่ามันทำอะไร? ฉันจะบอกคุณ มันสร้างวัตถุสถานะของหน้าจอแรกขึ้นมาใหม่! หน้าจอแรก ไม่ใช่หน้าจอที่สอง ฉันเพิ่งบอกคุณเสร็จแล้วว่า State object ของหน้าจอแรกถูกทิ้งไว้ตามลำพัง และตอนนี้ฉันกำลังบอกคุณเรื่องนี้ คุณเชื่อได้ไหม?

ตอนนี้ทำไมคุณถึงต้องการ 'ล้าง' วัตถุสถานะของหน้าจอ? เพราะและฉันสามารถพูดสิ่งนี้จากประสบการณ์ คุณอาจต้องการรีเซ็ต 'ค่าที่ไม่แน่นอน' ในวัตถุสถานะด้วยเหตุผลใดก็ตาม - และ Flutter ยอมให้ทำสิ่งนี้ได้อย่างง่ายดาย

เมื่อคุณกดปุ่ม 'คีย์ใหม่' คุณจะเห็นด้านล่างในโค้ดว่าสิ่งที่เกิดขึ้นคือตัวแปรส่วนตัวได้รับการกำหนดค่าคีย์เฉพาะใหม่ การทำเช่นนั้นมีผลที่เรากำลังมองหา เมื่อกลับมาที่หน้าจอแรก ตัวนับจะกลับเป็นศูนย์ นั่นเป็นเพราะวัตถุสถานะได้ถูกสร้างขึ้นใหม่

ตัวแปรนั้นเป็นตัวแปรระดับสูง — กำหนดไว้ภายในไฟล์ dart และไม่ได้อยู่ในคลาสใดคลาสหนึ่งโดยเฉพาะ ตอนนี้ไม่มีเหตุผลพิเศษในการทำเช่นนี้ ในความเป็นจริง มันสามารถถูกกำหนดให้เป็นตัวแปรอินสแตนซ์ในออบเจ็กต์สถานะ _MyHomePageState ได้อย่างง่ายดายพอๆ กัน อย่างไรก็ตาม โปรดทราบว่าจะมีการระบุไว้ในไฟล์ในเวลาคอมไพล์ จากนั้นจึงกำหนดให้กับ StatefulWidget _FirstPage ด้านล่างนี้ คุณจะเห็นลำดับที่เกิดขึ้นหลังจากกดปุ่ม "คีย์ใหม่"

คุณสามารถดูได้ในภาพหน้าจอด้านบนเมื่อคุณคลิกที่ปุ่ม "คีย์ใหม่" StatefulWidget ของหน้าจอแรก หน้าแรก จะถูกสร้างขึ้นใหม่ตามปกติ อย่างไรก็ตาม มีบางอย่างเปลี่ยนแปลงไป วัตถุสถานะ _FirstPageState ก็ถูกสร้างขึ้นใหม่เช่นกัน ตอนนี้ลำดับที่เน้นด้วยลูกศรสีแดงนั้นดูน่าสับสนเล็กน้อย แต่เราจะอธิบายต่อไป เริ่มจากลูกศรสีแดงอันแรก เราจะเห็น 'อินสแตนซ์ดั้งเดิม' ของออบเจ็กต์ State ที่เรียกใช้ฟังก์ชัน ปิดใช้งาน() ลูกศรสามลูกถัดไปแสดงถึง 'อินสแตนซ์ใหม่' ของวัตถุสถานะที่ถูกสร้างขึ้นและเรียกใช้ฟังก์ชัน build() ลูกศรสุดท้ายชี้กลับไปยัง 'ตัวอย่างดั้งเดิม' ของวัตถุ State ในขณะที่มันเรียกใช้ฟังก์ชัน ทิ้ง() และเตรียมตัวเองเพื่อนำไปรีไซเคิล

โปรดทราบว่าฟังก์ชัน dispose() อาจปรากฏขึ้นเมื่อใดก็ได้หลังจากการเรียกใช้ฟังก์ชัน deactivate() - หลังจากนั้นหรือหลังจากนั้นมาก นั่นเป็นเหตุผลที่ผู้คนแนะนำให้คุณล้างทรัพยากร "ข้อจำกัดด้านเวลา" ในฟังก์ชัน ปิดใช้งาน() เนื่องจากจะมีการเรียกอย่างสม่ำเสมอและคาดการณ์ได้เมื่อออบเจ็กต์สถานะถูกแทนที่ด้วยอินสแตนซ์ใหม่

ดังนั้น เมื่อกดปุ่ม 'หน้าแรก' เพื่อกลับไปยังหน้าจอแรก เราจะรู้ว่า StatefulWidget ของหน้าจอแรกจะถูกสร้างขึ้นใหม่เสมอ อย่างไรก็ตาม ในครั้งนี้ ได้มีการกำหนด 'คีย์เฉพาะ' ใหม่ การทำเช่นนี้จะทำให้กรอบงานกำจัดออบเจ็กต์สถานะดั้งเดิมเพื่อสร้างออบเจ็กต์ใหม่ ดังนั้นคุณไปได้แล้ว นอกจากการทดสอบวิดเจ็ตแล้ว ยังมีอีกเหตุผลหนึ่งที่ต้องกำหนดคีย์ให้กับวิดเจ็ต แน่นอนว่าทีม Flutter มีวิดีโอเกี่ยวกับ "เมื่อใดจึงควรใช้ Keys" และน่าจะให้ข้อมูลเชิงลึกเพิ่มเติม

รับวงจรชีวิต

โปรดทราบว่าบทความฟรี "Flutter App Lifecycle" เป็นบทความอ่านที่ยอดเยี่ยม โดยจะแนะนำคลาส WidgetsBindingObserver ที่คุณจะใช้เพื่อใช้งานตัวจัดการเหตุการณ์ 'วงจรชีวิต' เพิ่มเติม พวกคุณที่มีพื้นหลัง Android จะรู้จักตัวจัดการเหตุการณ์เหล่านี้เหมือนกับที่พบในคลาสกิจกรรม ด้านล่างนี้คือภาพหน้าจอของตัวจัดการเหตุการณ์การเรียกกลับบางส่วนที่พร้อมใช้งานสำหรับคุณ

นั่นก็เพียงพอแล้วสำหรับตอนนี้ บางสิ่งที่ควรคำนึงถึงเมื่อทำงานกับ StatefulWidgets มากมายของคุณ — และรัฐของพวกเขา

ไชโย

→ เรื่องอื่น ๆ โดย Greg Perry