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ì CustomSingleChildLayout
sẽ là hiển nhiên sau khi tôi giải thích CustomMultiChildLayout
cho 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 Stack
tiệ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
children
cần phải là một LayoutId
và bạn vượt qua tiện ích mà bạn thực sự muốn hiển thị khi còn nhỏ LayoutId
. Ý id
chí 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
MultiChildLayoutDelegate
lớ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ớ LayoutId
khô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_discovery
gói: MultiChildLayoutDelegate
triển khai và CustomMultiChildLayout
trong build
phương thức .
Bước cuối cùng là ghi đè shouldRelayout
phương thức , điều khiển đơn giản liệu có performLayout
nê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 1
và 2
làm id trong ví dụ này, nhưng sử dụng an enum
có 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 đó CustomSingleChildLayout
sẽ rất rõ ràng sau khi hiểu cách thức CustomMultiChildLayout
hoạ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 CustomSingleChildLayout
tuâ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 SingleChildLayoutDelegate
thay 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 LayoutId
và 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 MultiChildRenderObjectWidget
rộ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 đó MultiChildRenderObjectWidget
là những gì CustomMultiChildLayout
có 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 LayoutId
và thay vào đó có thể truy cập RenderObject
trự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.