Trước hết, tôi muốn nói rằng tôi rất vui khi được giúp bạn điều này vì tôi có thể hiểu được sự đấu tranh của bạn - cũng có những lợi ích để tự mình tìm ra nó (tài liệu này thật tuyệt vời).
Điều gì CustomSingleChildLayoutsẽ là hiển nhiên sau khi tôi giải thích CustomMultiChildLayoutcho bạn.
Điểm của tiện ích này là cho phép bạn bố trí những đứa trẻ bạn chuyển đến tiện ích này trong một chức năng duy nhất, tức là vị trí và kích thước của chúng có thể phụ thuộc lẫn nhau, đây là điều bạn không thể đạt được bằng cách sử dụng Stacktiện ích dựng sẵn .
CustomMultiChildLayout(
children: [
// Widgets you want to layout in a customized manner
],
)
Bây giờ, có hai bước nữa bạn cần thực hiện trước khi bạn có thể bắt đầu đẻ con:
- Mỗi đứa trẻ bạn vượt qua
childrencần phải là một LayoutIdvà bạn vượt qua tiện ích mà bạn thực sự muốn hiển thị khi còn nhỏ LayoutId. Ý idchí sẽ xác định duy nhất các vật dụng của bạn, làm cho chúng có thể truy cập được khi đặt chúng ra:
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'),
),
],
)
- Bạn cần tạo một
MultiChildLayoutDelegatelớp con xử lý phần bố trí. Các tài liệu ở đây dường như là rất công phu.
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;
}
Bây giờ, tất cả các thiết lập đã được thực hiện và bạn có thể bắt đầu thực hiện bố trí thực tế. Có ba phương pháp bạn có thể sử dụng cho việc đó:
hasChild, cho phép bạn kiểm tra xem một id cụ thể (nhớ LayoutIdkhông?) đã được chuyển đến children, tức là nếu có con của id đó.
layoutChild, mà bạn cần gọi cho mọi id , mọi đứa trẻ, được cung cấp chính xác một lần và nó sẽ cung cấp cho bạn Sizeđứa trẻ đó.
positionChild, cho phép bạn thay đổi vị trí từ Offset(0, 0)bất kỳ phần bù nào bạn chỉ định.
Bây giờ tôi cảm thấy khái niệm này khá rõ ràng, đó là lý do tại sao tôi sẽ minh họa cách triển khai một đại biểu cho ví dụ 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.
),
);
}
}
}
Hai ví dụ khác là một ví dụ từ tài liệu (kiểm tra bước chuẩn bị 2 ) và một ví dụ trong thế giới thực mà tôi đã viết một thời gian trước cho feature_discoverygói: MultiChildLayoutDelegatetriển khai và CustomMultiChildLayouttrong buildphương thức .
Bước cuối cùng là ghi đè shouldRelayoutphương thức , điều khiển đơn giản liệu có performLayoutnên gọi lại tại bất kỳ thời điểm cụ thể nào hay không bằng cách so sánh với một đại biểu cũ, (tùy chọn bạn cũng có thể ghi đè getSize) và thêm đại biểu vào 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)
],
)
Cân nhắc
Tôi đã sử dụng 1và 2làm id trong ví dụ này, nhưng sử dụng an enumcó lẽ là cách tốt nhất để xử lý id nếu bạn có id cụ thể.
Bạn có thể vượt qua một Listenableđến super(ví dụ super(relayout: animation)) nếu bạn muốn animate quá trình bố trí hoặc kích hoạt nó dựa trên một listenable nói chung.
Tài liệu giải thích những gì tôi mô tả ở trên thực sự tốt và ở đây bạn cũng sẽ thấy lý do tại sao tôi nói điều đó CustomSingleChildLayoutsẽ rất rõ ràng sau khi hiểu cách thức CustomMultiChildLayouthoạt động:
CustomMultiChildLayout phù hợp khi có mối quan hệ phức tạp giữa kích thước và vị trí của nhiều widget. Để kiểm soát bố cục của một đứa trẻ, CustomSingleChildLayout thích hợp hơn.
Điều này cũng có nghĩa là việc sử dụng CustomSingleChildLayouttuân theo các nguyên tắc tương tự mà tôi đã mô tả ở trên, nhưng không có bất kỳ id nào vì chỉ có một đứa trẻ duy nhất.
Bạn cần sử dụng SingleChildLayoutDelegatethay thế, trong đó có các phương thức khác nhau để thực hiện bố cục (tất cả chúng đều có hành vi mặc định, vì vậy về mặt kỹ thuật, tất cả đều là tùy chọn để ghi đè ):
Mọi thứ khác đều giống hệt nhau (hãy nhớ rằng bạn không cần LayoutIdvà chỉ có một đứa con duy nhất children).
Đây là những gì CustomMultiChildLayoutđược xây dựng trên.
Sử dụng điều này đòi hỏi kiến thức sâu hơn về Flutter và lại phức tạp hơn một chút, nhưng nó là lựa chọn tốt hơn nếu bạn muốn tùy chỉnh nhiều hơn vì nó thậm chí còn ở cấp độ thấp hơn. Điều này có một lợi thế lớn hơn CustomMultiChildLayout(nói chung, có nhiều quyền kiểm soát hơn):
CustomMultiChildLayout không thể tự kích thước dựa trên con cái của nó (xem vấn đề liên quan đến tài liệu tốt hơn cho lý do ).
Tôi sẽ không giải thích cách sử dụng MultiChildRenderObjectWidgetở đây vì những lý do rõ ràng, nhưng nếu bạn quan tâm, bạn có thể kiểm tra bài nộp của tôi cho thử thách Flutter Clock sau ngày 20 tháng 1 năm 2020, trong đó tôi sử dụng MultiChildRenderObjectWidgetrộng rãi - bạn cũng có thể đọc một bài viết về điều này , mà sẽ giải thích một chút về cách thức hoạt động của tất cả.
Bây giờ bạn có thể nhớ rằng đó MultiChildRenderObjectWidgetlà những gì CustomMultiChildLayoutcó thể và sử dụng trực tiếp sẽ mang lại cho bạn một số lợi ích tốt như không phải sử dụng LayoutIdvà thay vào đó có thể truy cập RenderObjecttrực tiếp vào dữ liệu gốc của phụ huynh.
Sự thật thú vị
Tôi đã viết tất cả mã bằng văn bản thuần túy (trong trường văn bản StackOverflow), vì vậy nếu có lỗi, vui lòng chỉ ra cho tôi và tôi sẽ sửa chúng.