ก่อนอื่นฉันอยากจะบอกว่าฉันดีใจที่ได้ช่วยคุณในเรื่องนี้เพราะฉันสามารถเข้าใจการดิ้นรนของคุณได้ - มีประโยชน์ในการหาคำตอบด้วยตัวเองเช่นกัน
อะไรCustomSingleChildLayout
จะชัดเจนหลังจากที่ฉันอธิบายCustomMultiChildLayout
ให้คุณทราบ
จุดประสงค์ของวิดเจ็ตนี้ช่วยให้คุณสามารถจัดโครงร่างเด็ก ๆ ที่คุณส่งไปยังวิดเจ็ตนี้ในฟังก์ชั่นเดียวเช่นตำแหน่งและขนาดของพวกเขาสามารถพึ่งพาซึ่งกันและกันซึ่งเป็นสิ่งที่คุณไม่สามารถทำได้โดยใช้เช่นStack
วิดเจ็ตที่สร้างไว้ล่วงหน้า
CustomMultiChildLayout(
children: [
// Widgets you want to layout in a customized manner
],
)
ตอนนี้มีอีกสองขั้นตอนที่คุณต้องทำก่อนที่จะเริ่มวางลูกของคุณ:
- เด็กที่คุณส่งผ่านไปยังทุก
children
ความต้องการที่จะเป็นและคุณผ่านเครื่องมือที่คุณต้องการจริงที่จะแสดงความเป็นเด็กไปว่าLayoutId
LayoutId
The id
จะระบุวิดเจ็ตของคุณโดยไม่ซ้ำกันทำให้พวกเขาสามารถเข้าถึงได้เมื่อจัดวาง:
CustomMultiChildLayout(
children: [
LayoutId(
id: 1, // The id can be anything, i.e. any Object, also an enum value.
child: Text('Widget one'), // This is the widget you actually want to show.
),
LayoutId(
id: 2, // You will need to refer to that id when laying out your children.
child: Text('Widget two'),
),
],
)
- คุณต้องสร้าง
MultiChildLayoutDelegate
คลาสย่อยที่จัดการส่วนเลย์เอาต์ เอกสารที่นี่ดูเหมือนจะซับซ้อนมาก
class YourLayoutDelegate extends MultiChildLayoutDelegate {
// You can pass any parameters to this class because you will instantiate your delegate
// in the build function where you place your CustomMultiChildLayout.
// I will use an Offset for this simple example.
YourLayoutDelegate({this.position});
final Offset position;
}
ตอนนี้การตั้งค่าทั้งหมดเสร็จสิ้นแล้วและคุณสามารถเริ่มนำเค้าโครงจริงไปใช้ มีสามวิธีที่คุณสามารถใช้เพื่อ:
hasChild
ซึ่งช่วยให้คุณตรวจสอบว่ามีการส่งรหัสเฉพาะ(จำได้LayoutId
ไหม) ไปที่หรือไม่children
นั่นคือถ้ามีรายการย่อยของรหัสนั้น
layoutChild
ซึ่งคุณต้องโทรหาทุกidเด็กทุกคนที่มีให้ครั้งเดียวและมันจะให้คุณSize
ของเด็กคนนั้น
positionChild
ซึ่งช่วยให้คุณเปลี่ยนตำแหน่งจากOffset(0, 0)
เป็นออฟเซ็ตใด ๆ ที่คุณระบุ
ฉันรู้สึกว่าแนวคิดควรมีความชัดเจนในตอนนี้ซึ่งเป็นเหตุผลที่ฉันจะอธิบายวิธีการนำผู้ได้รับมอบหมายมาเป็นตัวอย่างCustomMultiChildLayout
:
class YourLayoutDelegate extends MultiChildLayoutDelegate {
YourLayoutDelegate({this.position});
final Offset position;
@override
void performLayout(Size size) {
// `size` is the size of the `CustomMultiChildLayout` itself.
Size leadingSize = Size.zero; // If there is no widget with id `1`, the size will remain at zero.
// Remember that `1` here can be any **id** - you specify them using LayoutId.
if (hasChild(1)) {
leadingSize = layoutChild(
1, // The id once again.
BoxConstraints.loose(size), // This just says that the child cannot be bigger than the whole layout.
);
// No need to position this child if we want to have it at Offset(0, 0).
}
if (hasChild(2)) {
final secondSize = layoutChild(
2,
BoxConstraints(
// This is exactly the same as above, but this can be anything you specify.
// BoxConstraints.loose is a shortcut to this.
maxWidth: size.width,
maxHeight: size.height,
),
);
positionChild(
2,
Offset(
leadingSize.width, // This will place child 2 to the right of child 1.
size.height / 2 - secondSize.height / 2, // Centers the second child vertically.
),
);
}
}
}
สองตัวอย่างอื่น ๆ เป็นหนึ่งจาก (การเตรียมการตรวจสอบเอกสารขั้นตอนที่ 2 ) และโลกแห่งความจริงตัวอย่างเช่นผมเขียนกลับมาบางครั้งสำหรับfeature_discovery
แพคเกจ: MultiChildLayoutDelegate
การดำเนินงานและCustomMultiChildLayout
ในbuild
วิธีการ
ขั้นตอนสุดท้ายคือการเอาชนะshouldRelayout
วิธีซึ่งควบคุมง่าย ๆ ว่าperformLayout
ควรจะเรียกอีกครั้ง ณ จุดใดก็ตามในเวลาโดยเปรียบเทียบกับผู้แทนเก่า (เลือกที่คุณสามารถแทนที่getSize
) และเพิ่มผู้แทนลงในCustomMultiChildLayout
:
class YourLayoutDelegate extends MultiChildLayoutDelegate {
YourLayoutDelegate({this.position});
final Offset position;
@override
void performLayout(Size size) {
// ... (layout code from above)
}
@override
bool shouldRelayout(YourLayoutDelegate oldDelegate) {
return oldDelegate.position != position;
}
}
CustomMultiChildLayout(
delegate: YourLayoutDelegate(position: Offset.zero),
children: [
// ... (your children wrapped in LayoutId's)
],
)
การพิจารณา
ฉันใช้1
และ2
เป็นidในตัวอย่างนี้ แต่การใช้enum
อาจเป็นวิธีที่ดีที่สุดในการจัดการรหัสถ้าคุณมีรหัสเฉพาะ
คุณสามารถส่งผ่านListenable
ไปยังsuper
(เช่นsuper(relayout: animation)
) หากคุณต้องการเคลื่อนไหวกระบวนการจัดวางหรือเรียกใช้งานโดยอิงจากการฟังโดยทั่วไป
เอกสารอธิบายสิ่งที่ฉันอธิบายไว้ข้างต้นได้ดีมากและที่นี่คุณจะเห็นว่าทำไมฉันถึงบอกว่าCustomSingleChildLayout
จะชัดเจนมากหลังจากเข้าใจวิธีการCustomMultiChildLayout
ทำงาน:
CustomMultiChildLayoutเหมาะสมเมื่อมีความสัมพันธ์ที่ซับซ้อนระหว่างขนาดและการวางตำแหน่งของวิดเจ็ตหลายรายการ ในการควบคุมเค้าโครงของลูกคนเดียวCustomSingleChildLayoutเหมาะสมกว่า
นี่ก็หมายความว่าการใช้เป็นCustomSingleChildLayout
ไปตามหลักการเดียวกับที่ฉันอธิบายไว้ข้างต้น แต่ไม่มีรหัสใด ๆ เพราะมีเพียงเด็กคนเดียว
คุณต้องใช้SingleChildLayoutDelegate
แทนซึ่งมีวิธีการที่แตกต่างกันสำหรับการใช้เลย์เอาต์ (พวกเขาทั้งหมดมีพฤติกรรมเริ่มต้นดังนั้นพวกเขาในทางเทคนิคทั้งหมดเป็นทางเลือกที่จะแทนที่ ):
ทุกอย่างอื่นเหมือนกันหมด (จำไว้ว่าคุณไม่ต้องการLayoutId
และมีลูกเพียงคนเดียวแทนที่จะเป็นchildren
)
นี่คือสิ่งที่CustomMultiChildLayout
สร้างขึ้นบน
การใช้สิ่งนี้ต้องการความรู้ที่ลึกซึ้งยิ่งขึ้นเกี่ยวกับ Flutter และมีความซับซ้อนเพิ่มขึ้นอีกเล็กน้อย แต่มันเป็นตัวเลือกที่ดีกว่าถ้าคุณต้องการการปรับแต่งเพิ่มเติมเพราะมันอยู่ในระดับที่ต่ำกว่า สิ่งนี้มีข้อดีอย่างหนึ่งที่สำคัญเหนือกว่าCustomMultiChildLayout
(โดยทั่วไปมีการควบคุมมากกว่า):
CustomMultiChildLayout
ไม่สามารถปรับขนาดตัวเองตามลูกหลานของตน (ดูปัญหาเกี่ยวกับเอกสารที่ดีกว่าสำหรับการให้เหตุผล )
ฉันจะไม่อธิบายวิธีใช้MultiChildRenderObjectWidget
ที่นี่ด้วยเหตุผลที่ชัดเจน แต่หากคุณสนใจคุณสามารถตรวจสอบการส่งของฉันต่อความท้าทายนาฬิกา Flutterหลังจากวันที่ 20 มกราคม 2020 ซึ่งฉันใช้MultiChildRenderObjectWidget
อย่างกว้างขวาง - คุณสามารถอ่านบทความเกี่ยวกับเรื่องนี้ได้ ซึ่งควรอธิบายเล็กน้อยว่ามันทำงานอย่างไร
สำหรับตอนนี้คุณสามารถจำได้ว่าMultiChildRenderObjectWidget
เป็นสิ่งที่ทำให้CustomMultiChildLayout
เป็นไปได้และการใช้งานโดยตรงจะให้ประโยชน์ที่ดีบางอย่างแก่คุณเช่นไม่ต้องใช้LayoutId
และแทนที่จะสามารถเข้าถึงRenderObject
ข้อมูลผู้ปกครองของโดยตรง
สนุกกับความเป็นจริง
ฉันเขียนรหัสทั้งหมดในข้อความล้วน (ในฟิลด์ข้อความ StackOverflow) ดังนั้นหากมีข้อผิดพลาดโปรดชี้ให้ฉันและฉันจะแก้ไข