Cách sử dụng CustomMultiChildLayout & CustomSingleChildLayout trong Flutter


10

Ai đó có kinh nghiệm trong việc sử dụng CustomSingleChildLayoutCustomMultiChildLayoutcác lớp có thể giải thích chi tiết (với các ví dụ) về cách sử dụng chúng không.

Tôi chưa quen với Flutter và đang cố gắng hiểu cách sử dụng chúng. Tuy nhiên, tài liệu này là khủng khiếp và không rõ ràng. Tôi đã cố gắng truy quét internet để lấy ví dụ, nhưng không có tài liệu nào khác.

Tôi sẽ biết ơn mãi mãi nếu bạn có thể giúp đỡ.

Cảm ơn bạn!


Bạn có thể vui lòng thêm vào những gì bạn muốn sử dụng chúng cho?
João Soares

@ JoãoSoares Đừng lo lắng về điều này vì tôi đang viết một câu trả lời mở rộng.
creativecreatorormaybenot

Kỳ vọng cao rồi!
João Soares

@ JoãoSoares Xong, cho tôi biết nếu bạn có bất kỳ sửa chữa.
creativecreatorormaybenot

@creativecreatorormaybenot Điều đó rất khó xảy ra. Tôi đã thấy câu trả lời của bạn trước đây. Câu trả lời tuyệt vời, như mọi khi.
João Soares

Câu trả lời:


20

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.

CustomMultiChildLayout

Đ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:

  1. 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'),
    ),
  ],
)
  1. 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 khaiCustomMultiChildLayouttrong 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 12là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.

CustomSingleChildLayout

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).


MultiChildRenderObjectWidget

Đâ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.


1
Ồ Câu trả lời tuyệt vời. Nên LayoutIdlà duy nhất trên toàn cầu hoặc địa phương? trên toàn cầu tức là các vật dụng anh chị em cùng loại CustomMultiChildLayoutđòi hỏi phải có sự khác biệt LayoutIdtrong con cái của họ.
om-ha

1
@ om-ha Ở địa phương - id sẽ được lưu trữ trong dữ liệu gốc của LayoutId, có nghĩa là dữ liệu này, id, sẽ chỉ được truy cập bởi cha mẹ trực tiếp :)
creativecreatorormaybenot

2
Ôi! Bạn là vị cứu tinh của tôi! ĐÂY LÀ NHỮNG GÌ TÔI ĐANG NÓI VỀ! đây là cách tài liệu rung nên !! Không có cách nào tôi có thể tìm ra cách các lớp này làm việc chỉ với tài liệu bình thường. CẢM ƠN BẠN RẤT NHIỀU!
Walter M

1
Câu trả lời tuyệt vời thực sự. CustomMultiChildLayoutrất hữu ích trong các tình huống khi bạn cần phải nhóm tự động thay đổi kích thước nhiều widget văn bản trong một Columncủa Row's, cho một ví dụ.
om-ha
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.