Author: nguyenkhue2311.it

  • Animation trong Flutter: Các loại animation và tìm hiểu chi tiết về Implicit Animations

    Animation trong Flutter: Các loại animation và tìm hiểu chi tiết về Implicit Animations

    Animation là một khái niệm rất mạnh mẽ và quan trọng trong Flutter. Chúng ta không thể tưởng tượng bất kỳ ứng dụng di động nào không có hình ảnh động. Khi bạn chạm vào một nút hoặc chuyển từ trang này sang trang khác, tất cả đều là hình ảnh động. Ảnh động nâng cao trải nghiệm người dùng và làm cho các ứng dụng tương tác hơn.

    Các loại animation trong Flutter

    1. Implicit Animation: Implicit Animation là những animation dựa vào bạn để cung cấp giá trị cho thuộc tính mà bạn muốn tạo hoạt ảnh cho widget. Flutter sẽ điều khiển các quá trình chuyển đổi và vòng đời của hoạt ảnh cho bạn.
    2. Explicit Animation: Những animation này yêu cầu bạn kiểm soát đặc biệt vòng đời tổng thể của hoạt ảnh. Điều đó có nghĩa là bạn kiểm soát cách thức và thời điểm nó bắt đầu? làm thế nào nó chuyển đổi? và nó kết thúc như thế nào?
    3. Animation với CustomPainter: Animation với CustomPainter giống như vẽ trên canvas, nơi bạn yêu cầu công cụ Flutter vẽ thứ gì đó, cung cấp các bước cần thiết để vẽ nó trên canvas và kiểm soát quá trình vẽ.
    4. Sử dụng package bên ngoài và công cụ của bên thứ ba: Nếu những gì bạn muốn tạo có vẻ khó thực hiện với code hoặc nếu animation của bạn liên quan đến việc sử dụng SVG và vectơ thì bạn có thể sử dụng một số package bên ngoài và công cụ của bên thứ ba như Rive và Lottie để chúng.

    Implicit Animations

    Nếu animation của bạn chỉ làm một việc đó là hoạt ảnh chuyển từ giá trị này sang giá trị khác và là một widget thì bạn nên sử dụng các implicit animation. Implicit animations cung cấp cách dễ nhất và nhanh nhất để bắt đầu với hoạt ảnh trong Flutter.

    Hãy xem ví dụ về nó có thể trông như thế nào

    Hmmm, AnimatedContainer ở đó là gì? Đoán xem, nó là cái gì! Trong Flutter, chúng ta cũng được cung cấp một số implicit animation được đóng gói sẵn mà chúng ta có thể sử dụng. Chúng được gọi là các widget AnimatedFoo. Nơi foo đại diện cho thuộc tính bạn muốn tạo hoạt ảnh. Bất cứ khi nào bạn đặt một giá trị mới, họ sẽ xây dựng lại với quá trình chuyển đổi từ giá trị cũ sang giá trị mới. Bạn có thể đặt thời lượng cho hoạt ảnh và đường cong để quá trình chuyển đổi diễn ra. Ngoài ra còn có một số thông số khác mà bạn có thể sử dụng. Thật thú vị đúng không! Trong widget này chúng ta có

    • AnimatedOpacity: Bạn có thể sử dụng điều này để tạo hoạt ảnh cho độ mờ của đối tượng một cách ngầm định.
    • AnimatedPadding: Một cách hay để tạo hiệu ứng cho vùng đệm xung quanh bất kỳ tiện ích nào để tạo hiệu ứng lối vào mượt mà nếu trộn với độ mờ.
    • AnimatedList: Để tạo hiệu ứng chèn và xóa các mục khỏi danh sách.
    • AnimatedIcon: Đây là một widget khá tiện dụng nếu bạn đang tìm kiếm một số hình ảnh động nút đẹp mắt nhanh chóng để thêm vào như biểu tượng bánh hamburger thay đổi thành dấu chéo khi nhấp vào.

    “Làm việc với một số AnimatedFoo như AnimatedIcon này đòi hỏi sự hiểu biết về cách sử dụng AnimationControllers với tweens mà chúng ta sẽ nói đến trong Explicit Animations.”

    Bây giờ bạn đã hiểu về một số Implicit Animation tạo trước, điều đó thật tuyệt! Đây là một cách tốt để bắt đầu nhưng điều gì sẽ xảy ra nếu bạn không tìm thấy bất kỳ AnimatedFoo nào cho thuộc tính bạn muốn tạo hoạt ảnh?

    TweenAnimationBuilder

    Đối với điều này, chúng ta có TweenAnimationBuilder, đây là một cách để xây dựng các Implicit Animation tùy chỉnh của bạn với nhiều quyền kiểm soát hơn đối với chúng.

    Hãy xem chúng trong ví dụ sau

    Ở đây, chúng ta chuyển một đối tượng tween đến TweenanimationBuilder chứa các giá trị bắt đầu và kết thúc của animation. Những đối tượng tween ở đây là gì? Đối tượng Tween đại diện cho tập hợp các giá trị cho một thuộc tính mà bạn muốn tạo hoạt ảnh. Bất cứ khi nào giá trị tween thay đổi, trình tạo sẽ được gọi và tiện ích con bên dưới nó được xây dựng lại với giá trị mới. Khi sử dụng TweenAnimationBuilder, bạn không có quyền kiểm soát thời điểm hoạt ảnh bắt đầu, nó sẽ bắt đầu ngay sau khi widget được kích hoạt. Và một điều thú vị nữa là chúng ta không cần một widget trạng thái cho việc này. Tất cả những gì được quản lý bởi chính TweenAnimationBuilder.

    Đó là hầu hết những điều bạn cần biết khi bắt đầu với implicit animation trong Flutter. Cảm ơn vì đã đọc!

  • Final và Constant trong Dart – Cách xác định Hằng số trong Dart

    Final và Constant trong Dart – Cách xác định Hằng số trong Dart

    Xin chào!. Trong bài viết này, chúng ta sẽ tim hiểu về final và constant trong dart là gì. Cách xác định constant trong Dart. Câu hỏi quan trọng nhất là Tại sao chúng ta cần final và const và mục đích của những từ khóa này là gì?

    Final Constant của dart

    Nếu bạn không bao giờ muốn thay đổi một giá trị thì hãy sử dụng các từ khóa final và const.

    Có rất nhiều trường hợp mà chúng ta không muốn thay đổi giá trị của một biến. Ví dụ, giá trị của Pi.

    const PI = 3,14;

    Hoặc có thể bạn không muốn thay đổi tên trong toàn bộ chương trình.

    final name = ”Waheed”;

    Hằng số PI sẽ chứa giá trị 3,14 và final name sẽ có giá trị ‘Waheed’ trong toàn bộ mã. Bây giờ trong cả hai trường hợp, bạn sẽ không thể thay đổi giá trị của biến PI hoặc name. Câu hỏi phổ biến đó là Sự khác biệt giữa final và const là gì.

    Sự khác biệt giữa Final và Const

    • Một biến final chỉ có thể được đặt một lần và nó chỉ được khởi tạo khi được truy cập.
      • Ở đây mấu chốt ở đây là “nó chỉ được khởi tạo chỉ khi nó được truy cập” nghĩa là bạn chỉ sử dụng được biến final trong chương trình của mình khi giá trị được khởi tạo và vị trí bộ nhớ được cấp phát. Và nếu bạn không bao giờ sử dụng biến final, giá trị sẽ không bao giờ được khởi tạo trong chương trình của bạn.
    • Một biến const hoàn toàn là final nhưng nó là một hằng số compile time
      • Nó có nghĩa là tất cả các biến const cũng là final.
      • Nó là một hằng số compile time đã biên dịch có nghĩa là nó được khởi tạo trong quá trình biên dịch. Bất cứ khi nào bạn biên dịch chương trình, giá trị của PI sẽ được khởi tạo và bộ nhớ sẽ được cấp phát. Không quan trọng bạn có đang sử dụng biến này hay không.
    • Một biến instance có thể là final nhưng không thể là const
      • Nếu bạn muốn tạo một Hằng số ở class, hãy khai báo nó là static const chứ không chỉ mỗi const.

    Bây giờ đã đến lúc làm rõ các khái niệm bằng một ví dụ thực tế.

    Chúng tôi đã xác định một biến final name và sau đó chúng tôi đang cố gắng thay đổi giá trị của biến final name nhưng chúng tôi sẽ nhận được thông báo lỗi như thế này.

    ‘error‘name’, a final variable, can only be set once.‘ Or something like ‘ cannot assign to final variable name‘

    Có nghĩa là: ‘‘Error‘name’, một biến final, chỉ có thể được đặt một lần. ‘Hoặc một cái gì đó như‘ không thể gán cho biến final name ‘’

    Bạn có thể nhận thấy khi chưa xác định kiểu dữ liệu cho tên biến. Chà, Dart hiểu nó là một Chuỗi theo giá trị nghĩa đen của nó. Bạn có thể xác định rõ ràng nó là một String nhưng nó là tùy chọn.

    Chúng ta cũng hãy xem một ví dụ về const.

    Đây là cách chúng ta định nghĩa const trong Dart.

    Có một khái niệm rất quan trọng khác cần hiểu. Hãy nhớ rằng chúng tôi đã nghiên cứu rằng ‘Một biến instance có thể là biến final nhưng không thể là const.’ Hãy làm rõ khái niệm này với sự trợ giúp của một ví dụ. Để làm được điều đó, chúng ta phải tạo một Class. Nếu bạn không quen thuộc với class là gì, xin đừng lo lắng, chúng tôi sẽ sớm đề cập đến khái niệm class. Còn bây giờ chỉ cần tiếp tục theo dõi với chúng tôi.

    Chà, trình biên dịch sẽ cung cấp cho chúng ta một lỗi tại const weight. ‘Chỉ có thể khai báo các trường static là const‘.

    Vì vậy, nếu bạn xác định một const ở mức Class thì bạn cũng phải sử dụng từ khóa static. Chúng ta sẽ đề cập đến từ khóa static là gì nhưng hiện tại, hãy nhớ rằng chúng ta không thể khai báo một biến const ở cấp độ Class một cách đơn giản. Nó phải là static const.

    Đó là một hướng dẫn rất ngắn nhưng chi tiết về final và const của dart. Chúng tôi chắc chắn rằng bây giờ bạn sẽ không bao giờ quên các khái niệm final và const. Cảm ơn vì đã theo dõi.

  • Cách viết các ứng dụng mạnh mẽ mọi lúc, bằng cách sử dụng “Clean Architecture”

    Cách viết các ứng dụng mạnh mẽ mọi lúc, bằng cách sử dụng “Clean Architecture”

    Là nhà phát triển, chúng ta không thể tiếp tục sử dụng các thư viện và framework bên ngoài trong hệ thống của mình. Việc cộng đồng tạo ra những công cụ hữu ích và việc sử dụng chúng là điều đương nhiên. Tuy nhiên, mọi thứ đều có mặt trái của nó.

    Các nhóm và các cá nhân bất cẩn có thể rơi vào tình huống nguy hiểm bằng cách cấu trúc hệ thống của họ xung quanh các công cụ mà họ sử dụng. Các business logic có thể bị lẫn lộn với các chi tiết thực hiện. Điều này có thể dẫn đến một hệ thống khó mở rộng và bảo trì. Những gì nên thay đổi nhanh chóng trong GUI cuối cùng lại biến thành một cuộc truy tìm lỗi kéo dài hàng giờ. Nhưng câu chuyện không cần thiết phải như thế này.

    Kiến trúc phần mềm đề xuất các mô hình và quy tắc để xác định cấu trúc (classes, interfaces, và structs) trong một hệ thống và cách chúng liên quan với nhau. Các quy tắc này thúc đẩy khả năng tái sử dụng và sự tách biệt các mối quan tâm đối với các yếu tố này. Điều này giúp dễ dàng thay đổi các chi tiết triển khai như DBMS hoặc thư viện front-end. Trình tái cấu trúc và sửa lỗi ảnh hưởng đến càng ít bộ phận của hệ thống càng tốt. Và thêm các tính năng mới trở nên dễ dàng.

    Trong bài viết này, tôi sẽ giải thích một mô hình kiến trúc được đề xuất vào năm 2012 bởi Robert C. Martin, Uncle Bob. Ông là tác giả của những tác phẩm kinh điển như Clean Code và The Clean Coder…vv.

    Mô hình có được xây dựng dựa trên các khái niệm đơn giản:

    Chia thành phần của hệ thống thành các lớp với các vai trò riêng biệt và được xác định rõ ràng. Và hạn chế các mối quan hệ giữa các entities ở các tầng khác nhau. Không có gì mới trong việc tách ứng dụng của bạn thành các lớp. Nhưng tôi đã chọn cách tiếp cận này vì nó là cách đơn giản nhất để nắm bắt và thực hiện. Và nó làm cho các usecase thử nghiệm trở nên đơn giản.

    Chúng tôi chỉ cần đảm bảo Tương tác hoạt động bình thường. Đừng lo lắng nếu từ “Tương tác” có vẻ xa lạ với bạn, chúng ta sẽ tìm hiểu về chúng ngay sau đây.

    Từ trong ra ngoài, chúng ta sẽ khám phá từng lớp sâu hơn một chút. Chúng tôi sẽ sử dụng một ứng dụng mẫu khá quen thuộc với chúng ta: counter app. Không mất thời gian để hiểu, vì vậy chúng ta có thể tập trung vào chủ đề của bài viết này.

    Entities

    Entities trong sơ đồ là Business Rules. Các Entities bao gồm các Business Rules phổ biến cho một công ty. Chúng đại diện cho các entities cơ bản đối với lĩnh vực hoạt động của nó. Chúng là những thành phần có mức độ trừu tượng cao nhất.

    Trong ví dụ counter app của chúng tôi, có một Thực thể rất rõ ràng: chính là Counter.

    Use Cases

    Các trường hợp sử dụng được chỉ ra dưới dạng Application Business Rules. Chúng đại diện cho từng trường hợp sử dụng của một ứng dụng. Mỗi phần tử của lớp này cung cấp một giao diện cho lớp bên ngoài và hoạt động như một trung tâm giao tiếp với các phần khác của hệ thống. Chúng chịu trách nhiệm thực hiện hoàn chỉnh các usecase và thường được gọi là Tương tác.

    Trong ví dụ của chúng tôi, chúng tôi có một Trường hợp sử dụng để tăng hoặc giảm bộ đếm của chúng

    Lưu ý rằng factory function cho ChangeCounterInteractor nhận một tham số kiểu CounterGateway. Chúng ta sẽ thảo luận về sự tồn tại của loại hình này ở phần sau của bài viết. Nhưng chúng ta có thể nói rằng Gateways là thứ đứng giữa các usecase và layer tiếp theo.

    Interface Adapters

    Lớp này bao gồm ranh giới giữa các business rules của hệ thống và các công cụ cho phép hệ thống tương tác với các phần bên ngoài, như database và UI. Các phần tử trong lớp này hoạt động như các phần tử trung gian, nhận dữ liệu từ một lớp và chuyển nó sang lớp kia, điều chỉnh dữ liệu khi cần thiết.

    Trong ví dụ của chúng tôi, chúng tôi có một vài Interface Adapters. Một trong số đó là React component trình bày Bộ đếm và các điều khiển của nó để tăng và giảm:

    Lưu ý rằng component không sử dụng một thể hiện Counter để trình bày giá trị của nó mà thay vào đó là một instance của CounterViewData. Chúng tôi đã thực hiện thay đổi này để tách present logic khỏi business data. Một ví dụ về điều này là logic hiển thị của counter dựa trên view mode. Cách triển khai CounterViewData ở bên dưới:

    Một ví dụ khác về Interface Adapter sẽ là triển khai Redux của ứng dụng của chúng tôi. Các mô-đun chịu trách nhiệm về các yêu cầu tới máy chủ và việc sử dụng bộ nhớ cục bộ cũng sẽ nằm trong layer này.

    Frameworks and Drivers

    Các công cụ mà hệ thống của bạn sử dụng để giao tiếp với các phần bên ngoài tạo nên lớp ngoài cùng. Chúng tôi thường không viết mã trong lớp này, bao gồm các thư viện như React / Redux, browser API, v.v.

    The Dependency Rule

    Sự phân chia thành các lớp này có hai mục tiêu chính. Một trong số đó là làm rõ trách nhiệm của từng bộ phận trong hệ thống. Hai là đảm bảo vai trò của mỗi lớp độc lập với nhau nhất có thể. Để điều này xảy ra, có một quy tắc nêu rõ các yếu tố phải phụ thuộc vào nhau như thế nào:

    Một phần tử không được phụ thuộc vào bất kỳ phần tử nào thuộc một lớp bên ngoài lớp của nó.

    Ví dụ: một phần tử trong Use Cases layer không được có bất kỳ thông tin nào về bất kỳ lớp hoặc mô-đun nào liên quan đến GUI hoặc tính ổn định của dữ liệu. Tương tự như vậy, một Entity không thể biết các Use Cases nào sử dụng nó.

    Quy tắc này có thể đã đặt ra câu hỏi trong đầu bạn. Lấy ví dụ về một Use Case. Nó được kích hoạt do tương tác của người dùng với UI. Việc thực thi nó liên quan đến việc cập nhật trong một số bộ lưu trữ dữ liệu liên tục như cơ sở dữ liệu. Làm cách nào Interactor có thể thực hiện các lệnh gọi liên quan đến quy trình cập nhật mà không phụ thuộc vào Interface Adapter chịu trách nhiệm về tính ổn định của dữ liệu?

    Câu trả lời nằm trong một yếu tố mà chúng tôi đã đề cập trước đây: Gateways. Họ chịu trách nhiệm thiết lập giao diện mà các usecase cần để thực hiện công việc của họ. Sau khi họ đã thiết lập giao diện này, Interface Adapters có thể thực hiện các khía cạnh của chúng, như thể hiện trong sơ đồ ở trên. Chúng tôi có CounterGateway interface và triển khai cụ thể bằng Redux bên dưới:

    Có thể bạn không cần nó

    Tất nhiên, ứng dụng mẫu này hơi phức tạp đối với counter app tăng / giảm. Và tôi muốn nói rõ rằng bạn không cần tất cả những điều này cho một dự án nhỏ hoặc nguyên mẫu. Nhưng hãy tin tôi, khi ứng dụng của bạn lớn hơn, bạn sẽ muốn tối đa hóa khả năng tái sử dụng và khả năng bảo trì. Kiến trúc phần mềm tốt giúp cho các dự án có khả năng chống chọi với thời gian.

  • Flutter – Life cycle

    Flutter – Life cycle

    Statefulwidget cần sử dụng state object để xử lý tương tác của người dùng hoặc thay đổi dữ liệu nội bộ của nó trong một giai đoạn cụ thể, được thể hiện trong giao diện người dùng. Giai đoạn cụ thể này bao gồm toàn bộ quá trình từ loading đến unloading một component, cụ thể là vòng đời. widget trong fltter cũng có vòng đời, được biểu thị bằng state

    App là một widget đặc biệt. Ngoài việc xử lý các giai đoạn của view hiển thị (tức là vòng đời của view), bạn cũng cần phải xử lý các state (vòng đời của app) mà app trải qua từ đầu đến khi thoát.

    State Life cycle

    Vòng đời của state đề cập đến các giai đoạn của widget được liên kết, từ khởi tạo đến hiển thị, cập nhật, dừng, hủy, v.v. Vòng đời của state có thể được chia thành ba giai đoạn: create (chèn view vào cây), update (tồn tại trong cây view), destroy (xóa khỏi cây view).

    Thiết lập

    Khởi tạo state sẽ được thực hiện liên tiếp: phương thức khởi tạo(contructor) -> initstate -> dischangedependencies -> build, và sau đó kết xuất trang sẽ hoàn tất.

    Ý nghĩa của mỗi phương thức trong quá trình khởi tạo như sau:

    • Contructor là điểm bắt đầu của vòng đời state, và flutter sẽ tạo ra một state bằng cách gọi Createstate() của statefullwidget. Bạn có thể nhận dữ liệu cấu hình giao diện người dùng khởi tạo được chuyển bởi widget thông qua contructor. Các dữ liệu cấu hình này xác định hiệu ứng hiển thị ban đầu của widget.
    • Initstate, được gọi khi state object được chèn vào cây view. Hàm này sẽ chỉ được gọi một lần trong vòng đời state, vì vậy bạn có thể thực hiện một số công việc khởi tạo ở đây, chẳng hạn như đặt giá trị mặc định cho các biến trạng thái.
    • Didchangedependencies được sử dụng đặc biệt để xử lý các thay đổi phụ thuộc của state object. Nó sẽ được gọi bởi flutter sau khi initstate() kết thúc.
    • Build được sử dụng để xây dựng các view. Sau các bước trên, framework cho rằng state đó đã sẵn sàng, vì vậy nó gọi là Build. Trong hàm này, bạn có thể tạo một widget và trả lại nó theo dữ liệu cấu hình khởi tạo được truyền bởi widget cha và trạng thái hiện tại của state.

    Cập nhật

    Cập nhật state của widget được kích hoạt bởi ba phương pháp: setstate, didchangedependencies và didupdatewidget.

    Ý nghĩa của 3 phương thức trên:

    • Setstate: khi dữ liệu state thay đổi, hãy gọi phương thức này để nói với flutter: “dữ liệu ở đây đã thay đổi, vui lòng sử dụng dữ liệu cập nhật để xây dựng lại UI!”.
    • Didchangedependencies: sau khi sự phụ thuộc của state object thay đổi, flutter sẽ gọi lại phương thức này và sau đó kích hoạt Build. Một tình huống điển hình là khi ngôn ngữ hệ thống hoặc chủ đề ứng dụng thay đổi, hệ thống sẽ thông báo state để thực thi gọi lại phương thức didchangedependencies.
    • Didupdatewidget: khi cấu hình của một widget thay đổi, chẳng hạn như widget cha kích hoạt rebuild (nghĩa là khi state của widget cha thay đổi), hệ thống sẽ gọi hàm này.

    Sau khi ba phương thức được gọi, flutter sẽ destroy widget cũ và gọi phương thức Build để xây dựng lại widget.

    Hủy

    Việc phá hủy widget tương đối đơn giản. Ví dụ, khi một widget bị xóa hoặc một trang bị hủy, hệ thống sẽ gọi hai phương thức, deactivate và dispose, để xóa hoặc hủy widget đó.

    Cách thức gọi cụ thể như sau:

    • Khi state hiển thị của widget thay đổi, function deactivate được gọi và state tạm thời bị xóa khỏi cây view. Cần lưu ý rằng trong quá trình chuyển đổi trang, do vị trí của state object trong cây view đã thay đổi, nó cần được tạm thời loại bỏ và sau đó thêm lại để kích hoạt cấu tạo widget một lần nữa, vì vậy hàm này cũng sẽ được gọi.
    • Khi sate bị loại bỏ vĩnh viễn khỏi view, flutter gọi hàm dispose. Khi chúng ta đạt đến giai đoạn này, các widget sẽ bị phá hủy, vì vậy chúng ta có thể giải phóng tài nguyên, giải phóng bộ nhớ, v.v.

    Các phương thức này được tóm tắt trong bảng sau:

    Tên phương thứcchức năngThời gian gọiSố lần gọi
    Construction methodNhận dữ liệu cấu hình giao diện người dùng khởi tạo được chuyển bởi widgetKhi tạo state1
    initStateKết xuất khởi tạo liên quanKhi state được chèn vào cây view1
    didChangeDependenciesXử lý các thay đổi phụ thuộc hình thành stateSau initstate và khi sự phụ thuộc của đối state object thay đổi> = 1
    buildChế độ xem buildKhi state sẵn sàng để hiển thị dữ liệu> = 1
    setStateTái tạo lại viewKhi nào giao diện người dùng cần được làm mới> = 1
    didUpdateWidgetXử lý các thay đổi cấu hình widgetWidget cha setstate kích hoạt build lại widget con> = 1
    deactivateWidget đã bị xóaWidget không hiển thị> = 1
    disposeWidget bị hủyWidget bị xóa vĩnh viễn1

    Vòng đời của app

    Vòng đời của view xác định toàn bộ quá trình từ khi view loading đến khi building. Cơ chế gọi lại của nó có thể đảm bảo rằng chúng ta có thể chọn đúng thời điểm để làm đúng theo trạng thái của view. Vòng đời của một ứng dụng xác định toàn bộ quá trình từ lúc bắt đầu đến khi thoát.

    Trong quá trình phát triển của Android và IOS native, đôi khi cần thực hiện xử lý các sự kiện tương ứng với vòng đời của ứng dụng, chẳng hạn như ứng dụng đi vào foreground từ background, thoát từ foreground sang background hoặc thực hiện một số xử lý sau khi UI được vẽ hoàn tất.

    Trong bản phát triển native, bạn có thể lắng nghe vòng đời của ứng dụng và thực hiện các xử lý tương ứng bằng cách override activity, phương thức gọi lại vòng đời của viewcontroller hoặc đăng ký các thông báo liên quan của ứng dụng. Trong flutter, chúng ta có thể sử dụng lớp widgetsbindingobserver để thực hiện các yêu cầu tương tự.

    Các hàm gọi lại cụ thể trong widgetsbindingobserver như sau:

    Hãy thử điều này để chuyển đổi giữa các giai đoạn trước và sau và quan sát trạng thái của đầu ra ứng dụng từ bảng console. Bạn có thể thấy:

    • Từ background đến foreground, vòng đời của ứng dụng được in trên bảng console thay đổi như sau: appfecyclestate.paid -> appfecyclestate.inactive -> appfecyclestate.resumed;
    • Khi quay trở lại từ foreground trở lại background, vòng đời của ứng dụng được in trên console sẽ thay đổi thành: appfecyclestate.resumed -> appfecyclestate.inactive -> appfecyclestate.paused

    Frame draw callback

    Ngoài việc lắng nghe gọi lại vòng đời của ứng dụng để thực hiện xử lý tương ứng, đôi khi bạn cần thực hiện một số thao tác liên quan đến bảo mật hiển thị sau khi kết xuất thành phần.

    Widgetsbinding cung cấp hai cơ chế: single frame drawing callbackreal-time frame drawing callback để đáp ứng cho các nhu cầu khác nhau:

    • A single frame drawing callback được thực hiện thông qua addpostframecallback. Nó sẽ gọi lại sau khi frame hiện tại được vẽ và chỉ một lần. Nếu muốn lắng nghe lại, bạn cần thiết lập lại.
    • Real time frame drawing callback được thực hiện thông qua addpersistentframecallback. Chức năng này sẽ gọi lại sau mỗi bản vẽ khung hình, có thể được sử dụng như phát hiện FPS.
  • Cách Flutter render các Widget

    Cách Flutter render các Widget

    Lời mở đầu

    Xin chào!

    Gần đây, Flutter nổi lên và được Google PR như một xu thế của lập trình di động, trên con đường trở thành master fluter thì Widget chính là chìa khóa. Trong Flutter, mọi thứ đều là widget. Và vì vậy điều quan trọng là phải hiểu cách chúng hoạt động và cách Flutter hiển thị các Widget. Trong bài viết này chúng ta sẽ tìm hiểu về Element Tree, Widget Tree và Render Tree.

    Widget tree

    Đầu tiên là Widget tree. Widget tree được sử dụng để cấu hình Giao diện người dùng. Ở đó, bạn có thể định cấu hình các thuộc tính của Widget và định nghĩa nó sẽ hiển thị như thế nào. Tức là code mà bạn viết sẽ tạo lên widget tree.

    Element tree

    Cây thứ hai được gọi là Element tree. Element tree được sử dụng để quản lý và cập nhật mọi thứ. Element tree liên kết vào Widget tree, một phần tử trong Element tree là một thể hiện cụ thể của một widget. Điều này nghe có vẻ quen thuộc nếu bạn biết OOP (Lập trình hướng đối tượng) nơi bạn có các class và object trong đó các object chỉ là các thể hiện của một class.

    Render Tree

    Mảnh ghép còn thiếu cuối cùng là Render Object. Bên trong Render tree là các render object và về cơ bản đây là những gì bạn đang thấy trên màn hình. Bạn không thấy các widget mà bạn thấy các render object. Render object sẽ quan tâm đến kích thước, bố cục và bức tranh thực tế trên màn hình.

    Cách Flutter render các widget – 3 trạng thái

    Flutter trải qua 3 giai đoạn khi hiển thị các widget ra màn hình. Như chúng ta đã biết, đó không phải là những widget mà chúng ta thấy trên màn hình. Đó là các render object. Nhưng làm thế nào Flutter có thể xử lý điều này?

    1. Cấu hình

    Như tôi đã đề cập ở trên, widget tree chứa các widget và ở trạng thái này, tất cả là về cấu hình của các widget của chúng ta. Thông qua một API, bạn chỉ định các thuộc tính và giá trị cho các tiện ích con của mình mà cây tiện ích con sẽ nắm giữ.

    2. Vòng đời

    Tại đây, toàn bộ vòng đời của giao diện người dùng được quản lý. Cũng ở đây, nó xác định các thành phần hiện có trong hệ thống phân cấp giao diện người dùng của bạn. Bạn có thể hình dung trạng thái này giống như chất keo kết dính giữa trạng thái 1 và 3.

    3. Vẽ

    Ở đây Render tree phát huy tác dụng. Tất cả những điều liên quan về vẽ UI sẽ được thực hiện ở đây. Nó quan tâm đến các ràng buộc, phần con của các widget sẽ thực sự trông như thế nào, kích thước của chúng ra sao. Đây là nơi các render object được vẽ trên màn hình.

    Ok, nhưng tại sao lại là 3 cây?

    Bây giờ bạn có thể tự hỏi mình “OK Nhưng tại sao Flutter không chỉ sử dụng các widget và chỉ có một cây?”.

    Thực ra đây là một câu hỏi rất hay.

    Đó là về hiệu năng

    Ok, điều đầu tiên.

    Các widget là bất biến.

    Bạn không thể thay đổi các widget. Và về cơ bản đây là lý do tại sao một widget tree là không đủ. Hãy tưởng tượng bạn thay đổi một widget Text chẳng hạn. Bạn sẽ cần một widget hoàn toàn mới bởi vì bạn không thể thay đổi cùng một widget. Và nếu bạn đang code Flutter, bạn có thể biết UI có thể thay đổi thường xuyên như thế nào.

    Điều này có nghĩa là khi một widget cần thay đổi, đối tượng widget bên trong widget tree sẽ được thay thế hoàn toàn và không thể cập nhật được.

    Và tại đây 2 cây còn lại phát huy tác dụng.

    Cả Element và Render Tree đều được cập nhật bất cứ khi nào có thể thay vì được tạo hoàn toàn mới. Và đây là một sự cải thiện hiệu suất lớn!

    Cập nhật hay không cập nhật?

    Và làm thế nào Flutter quyết định xem Element và Render Object có thể được cập nhật hay không?

    Hãy tưởng tượng bạn có một Widget Text, nơi bạn chỉ cần thay đổi nội dụng văn bản. Lần thứ hai bạn thay đổi nó, Phương thức CanUpdate của Widget sẽ kiểm tra hai thứ và nếu một trong số chúng là đúng, thì Render Object và Element Object sẽ được cập nhật thay vì được tạo ra các đối tượng hoàn toàn mới.

    • RuntimeType (kiểu widget như Text, Column, Container, v.v.) có còn giống nhau không?
    • Key có giống nhau không?

    Trong ví dụ của chúng ta, runtimeType vẫn là Text và vì vậy nó đúng và Đối tượng phần tử sẽ gọi Phương thức updateRenderObject để đảm bảo Render Object được cập nhật thay vì được tạo lại.

    Key là một thuộc tính mà bạn có thể cấp cho hầu hết mọi widget con và giúp Flutter xác định một widget con. Khá hữu ích cho mục đích này.

    Và bạn có thể kiểm tra hành vi này khá dễ dàng. Khi bạn chạy một Ứng dụng Flutter bên trong IDE của mình và mở Flutter DevTools, bạn sẽ thấy chế độ xem sau:

    Ở đây tôi đã chọn một widget Text và bên trong hộp màu đỏ, bạn sẽ thấy Render Object. Và Render Object này có ID bắt đầu bằng ký hiệu #. Khi bạn thay đổi nội dung văn bản cho Widget Text này, ID sẽ KHÔNG thay đổi.

    Tóm lại

    Flutter sẽ cố gắng sử dụng lại càng nhiều tài nguyên (các object trong cây) càng tốt trong khi cố gắng render các đối tượng mới càng ít càng tốt.

    Kết luận

    Flutter khá thông minh trong cách hiển thị các widget!

    Bài viết này đã cung cấp và đi sâu hơn vào đặc tính và cách render UI của Flutter. Tôi hy vọng bây giờ bạn đã có thể hiểu rõ hơn về những gì xảy ra đằng sau quá trình render các Widget. Mong là bạn sẽ thích bài viết này.‌‌