Ví dụ.

Để minh họa, hãy xem xét ứng dụng đơn giản sau.

Ứng dụng có hai màn hình riêng biệt: danh mục và giỏ hàng (được đại diện bởi các tiện ích MyCatalog và MyCart, tương ứng). Nó có thể là một ứng dụng mua sắm, nhưng bạn có thể tưởng tượng cấu trúc tương tự trong một ứng dụng mạng xã hội đơn giản (thay thế danh mục cho “tường” và giỏ hàng cho “yêu thích”).

Màn hình danh mục bao gồm thanh ứng dụng tùy chỉnh (MyAppBar) và dạng xem cuộn của nhiều mục danh sách (MyListItems).

Đây là ứng dụng được hình dung dưới dạng cây tiện ích con.

Vì vậy, chúng ta có ít nhất 5 lớp con của Widget. Nhiều người trong số họ cần quyền truy cập vào trạng thái “thuộc về” ở nơi khác. Ví dụ: mỗi MyListItem cần có khả năng tự thêm vào giỏ hàng. Nó cũng có thể muốn xem liệu mặt hàng được hiển thị hiện đã có trong giỏ hàng hay chưa.

Điều này đưa chúng ta đến câu hỏi đầu tiên: chúng ta nên đặt trạng thái hiện tại của giỏ hàng ở đâu?

Đưa trạng thái lên

Trong Flutter, bạn nên giữ trạng thái ở trên các widget sử dụng nó.

Tại sao? Trong các khung công tác khai báo như Flutter, nếu bạn muốn thay đổi giao diện người dùng, bạn phải xây dựng lại nó. Không có cách nào dễ dàng để có MyCart.updateWith (somethingNew). Nói cách khác, thật khó để thay đổi thứ bậc một tiện ích từ bên ngoài, bằng cách gọi một phương thức trên đó. Và ngay cả khi bạn có thể làm cho điều này thành công, bạn sẽ chiến đấu với khuôn khổ thay vì để nó giúp bạn.

Ngay cả khi code trên hoạt động, bạn sẽ phải xử lý những điều sau trong tiện ích MyCart:

Bạn sẽ cần phải xem xét trạng thái hiện tại của giao diện người dùng và áp dụng dữ liệu mới cho nó. Thật khó để tránh lỗi theo cách này.

Trong Flutter, bạn tạo một widget mới mỗi khi nội dung của nó thay đổi. Thay vì MyCart.updateWith (somethingNew) (một cuộc gọi phương thức), bạn sử dụng MyCart (nội dung) (một phương thức khởi tạo). Vì bạn chỉ có thể tạo các tiện ích con mới trong các phương pháp tạo của cha mẹ chúng, nên nếu bạn muốn thay đổi nội dung, tiện ích con đó phải ở chế độ gốc của MyCart trở lên.

Bây giờ MyCart chỉ có một đường dẫn mã để xây dựng bất kỳ phiên bản giao diện người dùng nào.

Trong ví dụ của chúng ta, nội dung cần phải tồn tại trong MyApp. Bất cứ khi nào nó thay đổi, nó sẽ xây dựng lại MyCart từ bên trên (sẽ nói thêm về điều đó sau). Do đó, MyCart không cần phải lo lắng về vòng đời — nó chỉ khai báo những gì sẽ hiển thị cho bất kỳ nội dung nhất định nào. Khi điều đó thay đổi, tiện ích MyCart cũ sẽ biến mất và được thay thế hoàn toàn bằng tiện ích mới.

Đây là ý của chúng ta khi nói rằng widget là bất biến. Chúng không thay đổi — chúng được thay thế.

Bây giờ chúng ta đã biết nơi đặt trạng thái của giỏ hàng, hãy xem cách truy cập vào nó.

Truy cập trạng thái

Khi người dùng nhấp vào một trong các mặt hàng trong danh mục, mặt hàng đó sẽ được thêm vào giỏ hàng. Nhưng kể từ khi giỏ hàng nằm trên MyListItem, chúng ta làm điều đó như thế nào?

Một tùy chọn đơn giản là cung cấp một lệnh gọi lại mà MyListItem có thể gọi khi nó được nhấp vào. Các hàm của Dart là các đối tượng hạng nhất, vì vậy bạn có thể chuyển chúng theo bất kỳ cách nào bạn muốn. Vì vậy, bên trong MyCatalog, bạn có thể xác định những điều sau:

Điều này hoạt động tốt, nhưng đối với một trạng thái ứng dụng mà bạn cần sửa đổi từ nhiều nơi khác nhau, bạn sẽ phải chuyển qua nhiều lần gọi lại — điều này sẽ cũ đi khá nhanh.

May mắn thay, Flutter có các cơ chế để các widget cung cấp dữ liệu và dịch vụ cho con cháu của chúng (nói cách khác, không chỉ con cái của chúng mà bất kỳ widget nào bên dưới chúng). Như bạn mong đợi từ Flutter, nơi mọi thứ đều là Widget, các cơ chế này chỉ là những loại widget đặc biệt — InheritedWidget, InheritedNotifier, InheritedModel, v.v. Chúng ta sẽ không đề cập đến những điều đó ở đây, vì chúng hơi thấp so với những gì chúng ta đang cố gắng thực hiện.

Thay vào đó, chúng ta sẽ sử dụng một gói hoạt động với các widget cấp thấp nhưng dễ sử dụng. Nó được gọi là nhà cung cấp.

Trước khi làm việc với nhà cung cấp, đừng quên thêm phần phụ thuộc vào nhà cung cấp đó vào pubspec.yaml của bạn.

Bây giờ bạn có import ‘package:provider/provider.dart’; và bắt đầu xây dựng.

Với provider, bạn không cần phải lo lắng về các cuộc gọi lại hoặc InheritedWidgets. Nhưng bạn cần hiểu 3 khái niệm:

  • ChangeNotifier
  • ChangeNotifierProvider
  • Consumer

ChangeNotifier

ChangeNotifier là một lớp đơn giản có trong Flutter SDK cung cấp thông báo thay đổi cho người nghe của nó. Nói cách khác, nếu thứ gì đó là ChangeNotifier, bạn có thể đăng ký các thay đổi của nó. (Đây là một dạng có thể quan sát được, dành cho những người quen thuộc với thuật ngữ này.)

Trong provider, ChangeNotifier là một cách để đóng gói trạng thái ứng dụng của bạn. Đối với các ứng dụng rất đơn giản, bạn có thể sử dụng với một ChangeNotifier duy nhất. Trong những cái phức tạp, bạn sẽ có một số mô hình và do đó là một số ChangeNotifier. (Bạn hoàn toàn không cần sử dụng ChangeNotifier với provider, nhưng đó là một lớp dễ làm việc.)

Trong ví dụ về ứng dụng mua sắm của chúng ta, chúng ta muốn quản lý trạng thái của giỏ hàng trong ChangeNotifier. Chúng ta tạo một lớp mới để mở rộng nó, giống như vậy

Code duy nhất dành riêng cho ChangeNotifier là lệnh gọi tới notificationListists (). Gọi phương thức này bất cứ khi nào mô hình thay đổi theo cách có thể thay đổi giao diện người dùng của ứng dụng của bạn. Mọi thứ khác trong CartModel là chính mô hình và logic của nó.

ChangeNotifierProvider

ChangeNotifierProvider là widget cung cấp một phiên bản của ChangeNotifier cho con cháu của nó. Nó đến từ provider package.

Chúng ta đã biết nơi đặt ChangeNotifierProvider: phía trên các widget cần truy cập nó. Trong trường hợp của CartModel, điều đó có nghĩa là ở đâu đó trên cả MyCart và MyCatalog.

Bạn không muốn đặt ChangeNotifierProvider cao hơn mức cần thiết (vì bạn không muốn làm giảm hiệu suất). Nhưng trong trường hợp của chúng ta, tiện ích duy nhất nằm trên cả MyCart và MyCatalog là MyApp.

Consumer

Bây giờ CartModel đã được cung cấp cho các widget trong ứng dụng của chúng ta thông qua khai báo ChangeNotifierProvider ở trên cùng, chúng ta có thể bắt đầu sử dụng nó.

Điều này được thực hiện thông qua tiện ích Consumer widget.

Chúng ta phải chỉ định loại mô hình mà chúng ta muốn truy cập. Trong trường hợp này, chúng ta muốn CartModel, vì vậy chúng ta viết Consumer <CartModel>. Nếu bạn không chỉ định chung chung (<CartModel>), provider sẽ không thể giúp bạn. Provider dựa trên các loại và nếu không có loại, nó không biết bạn muốn gì.

Đối số bắt buộc duy nhất của Consumer Widget là trình tạo. Builder là một hàm được gọi bất cứ khi nào ChangeNotifier thay đổi. (Nói cách khác, khi bạn gọi thông báo () trong mô hình của mình, tất cả các phương thức trình tạo của tất cả Consumer Widget tương ứng sẽ được gọi.)

Trình tạo được gọi với ba đối số. Điều đầu tiên là ngữ cảnh, mà bạn cũng nhận được trong mọi phương pháp xây dựng.

Đối số thứ hai của hàm trình tạo là phiên bản của ChangeNotifier. Đó là những gì chúng ta đã yêu cầu ngay từ đầu. Bạn có thể sử dụng dữ liệu trong mô hình để xác định giao diện người dùng sẽ trông như thế nào tại bất kỳ điểm nhất định nào.

Đối số thứ ba là con, ở đó để tối ưu hóa. Nếu bạn có một cây con tiện ích con lớn trong Consumer Widget không thay đổi khi mô hình thay đổi, bạn có thể tạo nó một lần và lấy nó thông qua trình tạo.

Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.