Get in touch
or send us a question?
CONTACT

Hướng dẫn custom RenderObject của riêng bạn

  • Thông thường, để tạo widget trong Flutter, mình phải thông qua composition (tree lồng nhau), nghĩa là kết hợp một số widget cơ bản tạo thành một widget phức tạp hơn.
  • Khi “khám phá” source code Flutter, bạn sẽ phát hiện ra rằng phần lớn các widget không sử dụng composition hay CustomPaint. Thay vào đó, chúng sử dụng RenderObject hoặc các subclass khác nhau, đặc biệt là RenderBox. Tạo một widget tùy chỉnh theo cách này sẽ giúp bạn vẽ và định kích thước cho widget linh hoạt nhưng cũng rất phức tạp

Tổng quan

Tạo một custom render object bao gồm các khía cạnh sau:

  • Widget: là giao diện hiển thị để show với thế giới bên ngoài, nó bao gồm cả các thuộc tính mà bạn muốn khi sử dụng. Bạn sử dụng các thuộc tính này để tạo hoặc cập nhật render object.
  • Children: render object hoặc widget có thể có 0, một hoặc nhiều children. Số lượng children sẽ ảnh hưởng đến kích thước và layout của render object. Trong bài viết này, bạn sẽ tạo một render object không có children. Điều này sẽ giúp đơn giản hóa một số bước.
  • Layout: Trong Flutter, sự ràng buộc (constraints go down) giảm xuống, vì vậy bạn được cấp chiều rộng và chiều dài max và min từ parent và sau đó sẽ “báo cáo” lại kích thước mà bạn muốn có khi sử dụng. Bạn sẽ wrap nội dung của mình hay cố gắng mở rộng hết mức kích thước mà parent cho phép? Một phần của việc tạo render object là cung cấp thông tin về kích thước bên trong (intrinsic size). Vì widget bạn tạo ra trong bài này sẽ không có bất kỳ children nào, bạn không cần lo về việc “hỏi” các widget con  muốn độ lớn như thế nào hoặc xác định vị trí hiển thị của chúng. Tuy nhiên, bạn sẽ cần quy định kích thước mà bạn cần để paint nội dung của mình.
  • Painting: Bước này tương tự như những gì bạn sẽ làm với widget CustomPaint. Bạn sẽ nhận được một canvas để bạn có thể vẽ những thứ mong muốn lên đó.
  • Hit testing: bạn cho Flutter biết những thứ mà bạn sẽ xử lý đối với các sự kiện touch. Trong bài viết này, bạn sẽ thêm gesture detector để giúp bạn xử lý các sự kiện touch.
  • Semantics: bổ sung thông tin về text widget của bạn. Bạn sẽ không thấy text này, nhưng Flutter có thể sử dụng để giúp người dùng khiếm thị. Nếu bạn không biết bất kỳ ai bị mù, thì bạn nên bỏ qua bước này.

Preview Widget

Khởi tạo Widget

Bước đầu tiên là tạo một widget cho phép bạn truyền các thuộc tính mong muốn vào render object của bạn. Hãy bắt đầu với ba tùy chọn sau:

  • progress bar color.
  • thumb color.
  • thumb size.

Hiểu các phần chính của widget

class ProgressBar extends LeafRenderObjectWidget {
  @override
  RenderProgressBar createRenderObject(...) {}
  @override
  void updateRenderObject(...) {}
  @override
  void debugFillProperties(...) {}
}

Ghi chú:

  • Widget của bạn sẽ extend LeafRenderObjectWidget vì nó không có bất kỳ children nào. Nếu bạn đang tạo một render object với một child, bạn sẽ sử dụng SingleChildRenderObjectWidgetvà đối với nhiều children, bạn sẽ sử dụng MultiChildRenderObjectWidget.
  • Flutter framework (chỉ các element) sẽ gọi createRenderObject khi nó muốn tạo render object liên kết với widget này. Vì chúng ta đặt tên widget là ProgressBar, nên theo thói quen, chúng ta đặt tiền tố này bằng “Render” khi đặt tên cho render object. Đó là lý do tại sao bạn có giá trị trả về là RenderProgressBar. Lưu ý rằng bạn chưa tạo class này. Đó là class render object mà bạn tạo sau.
  • Chi phí để tạo các widget không tốn kém là bao nhưng sẽ rất tốn kém nếu tạo lại các render object mỗi khi có bản cập nhật mới. Vì vậy, khi một thuộc tính của widget thay đổi, hệ thống sẽ gọi updateRenderObject, nơi bạn chỉ cần cập nhật các thuộc tính chung của render object của mình mà không cần tạo lại toàn bộ object.
  • Phương thức debugFillProperties cung cấp thông tin về các thuộc tính của class trong quá trình debugging, nhưng nó không cần thiết lắm đối với mục đích của bài viết này.

Tạo một tệp mới có tên là progress_bar.dart. Thêm code dưới đây vào. Đây chỉ là một phiên bản đầy đủ hơn những gì bạn đã thấy ở trên.

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

class ProgressBar extends LeafRenderObjectWidget {
  const ProgressBar({
    Key? key,
    required this.barColor,
    required this.thumbColor,
    this.thumbSize = 20.0,
  }) : super(key: key);
  
  final Color barColor;
  final Color thumbColor;
  final double thumbSize;

  @override
  RenderProgressBar createRenderObject(BuildContext context) {
    return RenderProgressBar(
      barColor: barColor,
      thumbColor: thumbColor,
      thumbSize: thumbSize,
    );
  }

  @override
  void updateRenderObject(
      BuildContext context, RenderProgressBar renderObject) {
    renderObject
      ..barColor = barColor
      ..thumbColor = thumbColor
      ..thumbSize = thumbSize;
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(ColorProperty('barColor', barColor));
    properties.add(ColorProperty('thumbColor', thumbColor));
    properties.add(DoubleProperty('thumbSize', thumbSize));
  }
}

Bây giờ bỏ qua các lỗi về RenderProgressBar . Bạn vẫn chưa tạo ra nó.
Trong phiên bản ProgressBar đã điền đầy đủ thông tin này, bạn có thể thấy các thuộc tính barColorthumbColor và thumbSize được sử dụng theo những cách sau:

  • Đầu tiên là mình sẽ gọi hàm khởi tạo.
  • Tạo một instance mới của RenderProgressBar
  • Cập nhật instance hiện có của RenderProgressBar
  • Cung cấp debug information

Bây giờ bạn đã tạo ra widget ProgressBar, đã đến lúc tạo class RenderProgressBar.

Khởi tạo Render object …. <Phần tiếp>