สร้างวิธีการออกแบบในลักษณะดังกล่าวว่ามันควรจะเป็น/ บริสุทธิ์ไม่มีผลข้างเคียง เนื่องจากปัจจัยภายนอกหลายอย่างสามารถทริกเกอร์การสร้างวิดเจ็ตใหม่เช่น:
- เส้นทางป๊อป / พุช
- การปรับขนาดหน้าจอมักเกิดจากลักษณะแป้นพิมพ์หรือการเปลี่ยนทิศทาง
- วิดเจ็ตหลักสร้างลูกใหม่
- InheritedWidget วิดเจ็ตขึ้นอยู่กับ
Class.of(context)
การเปลี่ยนแปลง( รูปแบบ)
ซึ่งหมายความว่าbuild
วิธีการที่ควรจะไม่ก่อให้เกิดการเรียกร้อง http หรือปรับเปลี่ยนใด ๆ ของรัฐ
เกี่ยวข้องกับคำถามนี้อย่างไร?
ปัญหาที่คุณกำลังเผชิญคือวิธีการสร้างของคุณมีผลข้างเคียง / ไม่บริสุทธิ์ทำให้การสร้างการโทรภายนอกเป็นเรื่องยุ่งยาก
แทนที่จะป้องกันการสร้างการโทรคุณควรทำให้วิธีการสร้างของคุณบริสุทธิ์เพื่อให้สามารถเรียกใช้ได้ตลอดเวลาโดยไม่มีผลกระทบ
ในกรณีของตัวอย่างของคุณที่คุณต้องการเปลี่ยนวิดเจ็ตของคุณให้เป็นStatefulWidget
แล้วดึงที่โทร HTTP ไปinitState
ของคุณState
:
class Example extends StatefulWidget {
@override
_ExampleState createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
Future<int> future;
@override
void initState() {
future = Future.value(42);
super.initState();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: future,
builder: (context, snapshot) {
// create some layout here
},
);
}
}
ฉันรู้เรื่องนี้แล้ว ฉันมาที่นี่เพราะต้องการเพิ่มประสิทธิภาพการสร้างใหม่จริงๆ
นอกจากนี้ยังเป็นไปได้ที่จะสร้างวิดเจ็ตที่สามารถสร้างใหม่โดยไม่ต้องบังคับให้ลูก ๆ สร้างด้วย
เมื่ออินสแตนซ์ของวิดเจ็ตยังคงเหมือนเดิม การกระพือปีกอย่างตั้งใจจะไม่สร้างเด็กขึ้นมาใหม่ หมายความว่าคุณสามารถแคชบางส่วนของแผนผังวิดเจ็ตของคุณเพื่อป้องกันการสร้างใหม่โดยไม่จำเป็น
วิธีที่ง่ายที่สุดคือใช้ตัวconst
สร้างโผ:
@override
Widget build(BuildContext context) {
return const DecoratedBox(
decoration: BoxDecoration(),
child: Text("Hello World"),
);
}
ด้วยconst
คีย์เวิร์ดนั้นอินสแตนซ์ของDecoratedBox
จะยังคงเหมือนเดิมแม้ว่าบิลด์จะถูกเรียกหลายร้อยครั้งก็ตาม
แต่คุณสามารถบรรลุผลลัพธ์เดียวกันได้ด้วยตนเอง:
@override
Widget build(BuildContext context) {
final subtree = MyWidget(
child: Text("Hello World")
);
return StreamBuilder<String>(
stream: stream,
initialData: "Foo",
builder: (context, snapshot) {
return Column(
children: <Widget>[
Text(snapshot.data),
subtree,
],
);
},
);
}
ในตัวอย่างนี้เมื่อ StreamBuilder ได้รับแจ้งค่าใหม่subtree
จะไม่สร้างใหม่แม้ว่า StreamBuilder / Column จะทำก็ตาม มันเกิดขึ้นเนื่องจากการปิดตัวอย่างMyWidget
ไม่เปลี่ยนแปลง
รูปแบบนี้ถูกใช้มากในแอนิเมชั่น การใช้งานทั่วไปคือAnimatedBuilder
และการเปลี่ยนทั้งหมดเช่นAlignTransition
.
นอกจากนี้คุณยังสามารถจัดเก็บsubtree
ลงในช่องของชั้นเรียนของคุณได้แม้ว่าจะแนะนำให้ใช้น้อยกว่าก็ตามเนื่องจากมันทำลายคุณสมบัติการโหลดซ้ำ