paint-brush
Cách tạo Công cụ giao diện người dùng điều khiển máy chủ cho Fluttertừ tác giả@alphamikle
1,250 lượt đọc
1,250 lượt đọc

Cách tạo Công cụ giao diện người dùng điều khiển máy chủ cho Flutter

từ tác giả Mike Alfa30m2024/06/13
Read on Terminal Reader

dài quá đọc không nổi

Câu chuyện đằng sau việc tạo ra Nui - Server Driven UI engine cho Flutter, là một phần của dự án lớn hơn - Backendless CMS Nanc.
featured image - Cách tạo Công cụ giao diện người dùng điều khiển máy chủ cho Flutter
Mike Alfa HackerNoon profile picture
0-item
1-item

Xin chào!

Hôm nay tôi sẽ chỉ cho bạn cách tạo một công cụ siêu lừa đảo cho Giao diện người dùng do máy chủ điều khiển trong Flutter , một phần không thể thiếu của CMS siêu lừa đảo (đó là cách người tạo ra nó, tức là tôi, định vị nó). Tất nhiên, bạn có thể có quan điểm khác và tôi sẽ vui lòng thảo luận về vấn đề đó trong phần bình luận.


Bài viết này là bài đầu tiên trong số hai (đã là ba) bài trong loạt bài này. Trong phần này, chúng ta sẽ xem xét trực tiếp Nui và trong phần tiếp theo - Nui được tích hợp sâu như thế nào với Nanc CMS, và giữa bài viết này và bài viết tiếp theo sẽ có một bài khác với lượng thông tin khổng lồ về hiệu suất của Nui.


Trong bài viết này sẽ có rất nhiều điều thú vị về Server-Driven UI, khả năng của Nui (Nanc Server-Driven UI), lịch sử dự án, sở thích ích kỷ và Doctor Strange. Ồ vâng, cũng sẽ có các liên kết đến GitHub và pub.dev, vì vậy nếu bạn thích nó và không ngại dành 1-2 phút thời gian của mình - tôi sẽ rất vui khi có ngôi saolượt thích của bạn.


Mục lục

  1. giới thiệu
  2. Lý do phát triển
  3. Bằng chứng của khái niệm
  4. Cú pháp
  5. Trình chỉnh sửa IDE
  6. Hiệu suất
  7. Tạo thành phần và giao diện người dùng
  8. Sân chơi
  9. Tương tác và logic
  10. Truyền dữ liệu
  11. Tài liệu
  12. Các kế hoạch trong tương lai

Giới thiệu một chút

Tôi đã viết một bài về Nanc, nhưng kể từ đó, đã hơn một năm trôi qua và dự án đã tiến triển đáng kể về mặt khả năng và mức độ "hoàn thiện", và quan trọng nhất - nó đã được phát hành với tài liệu hoàn thiện và theo MIT giấy phép.

Vậy Nanc là gì?

Nó là một CMS có mục đích chung không kéo theo phần phụ trợ của nó. Đồng thời, nó không giống như React Admin, để tạo ra thứ gì đó, bạn cần phải viết rất nhiều mã.


Để bắt đầu sử dụng Nanc, chỉ cần:

  1. Mô tả cấu trúc dữ liệu mà bạn muốn quản lý thông qua CMS bằng Dart DSL
  2. Viết một lớp API triển khai giao tiếp giữa CMS và chương trình phụ trợ của bạn


Hơn nữa, việc đầu tiên có thể được thực hiện hoàn toàn thông qua giao diện của chính CMS - nghĩa là bạn có thể quản lý cấu trúc dữ liệu thông qua giao diện người dùng. Việc thứ hai có thể được bỏ qua nếu:

  1. Bạn đang sử dụng Firebase
  2. Hoặc bạn đang sử dụng Supabase
  3. Hoặc bạn muốn thử nghiệm và chạy Nanc mà không cần ràng buộc nó với một chương trình phụ trợ thực sự - với cơ sở dữ liệu cục bộ (hiện tại, vai trò này được thực hiện bởi tệp JSON hoặc LocalStorage)


Do đó, trong một số trường hợp, bạn sẽ không phải viết một dòng mã nào để có CMS để quản lý bất kỳ nội dung và dữ liệu nào của mình. Trong tương lai, giả sử số lượng các tình huống này sẽ tăng lên - cộng với GraphQL và RestAPI. Nếu bạn có ý tưởng về những điều khác thì bạn có thể triển khai SDK cho - Tôi sẽ rất vui khi đọc các đề xuất trong phần nhận xét.


Nanc hoạt động với các thực thể - hay còn gọi là mô hình, ở cấp độ lớp lưu trữ dữ liệu có thể được biểu diễn dưới dạng bảng (SQL) hoặc tài liệu (No-SQL). Mỗi thực thể có các trường - biểu thị các cột từ SQL hoặc các "trường" tương tự từ No-SQL.


Một trong những loại trường có thể được gọi là loại "Màn hình". Tức là toàn bộ bài viết này chỉ là nội dung của một trường trong CMS. Đồng thời, về mặt kiến trúc, nó trông như thế này - có một thư viện hoàn toàn riêng biệt ( thực tế là một số thư viện ), cùng triển khai Công cụ giao diện người dùng hướng máy chủ có tên là Nui. Chức năng này được tích hợp vào CMS, trên đó có rất nhiều tính năng bổ sung được triển khai.


Đến đây, tôi kết thúc phần giới thiệu dành riêng cho Nanc và bắt đầu câu chuyện về Núi.

Mọi việc đã bắt đầu thế nào

Tuyên bố miễn trừ trách nhiệm: Mọi sự trùng hợp đều là ngẫu nhiên. Câu chuyện này là hư cấu. Tôi đã mơ thấy nó.

Tôi đã làm việc ở một công ty lớn trên nhiều ứng dụng cùng một lúc. Chúng phần lớn giống nhau nhưng cũng có nhiều điểm khác biệt.

Nhưng thứ hoàn toàn giống nhau ở chúng là thứ mà tôi có thể gọi là công cụ bài viết . Nó bao gồm một số (5-10-15, tôi không nhớ chính xác nữa) hàng nghìn dòng mã khá nhàu nát đã xử lý JSON từ phần phụ trợ. Những JSON này cuối cùng đã phải chuyển thành giao diện người dùng, hay nói đúng hơn là thành một bài viết để đọc trong ứng dụng di động.


Các bài viết được tạo và chỉnh sửa bằng bảng quản trị và quá trình thêm các yếu tố mới rất, vô cùng, cực kỳ vất vả và lâu dài. Nhìn thấy điều kinh dị này, tôi quyết định đề xuất cách tối ưu hóa đầu tiên - thương xót những người quản lý nội dung kém và triển khai cho họ chức năng xem trước bài viết trong thời gian thực ngay trên trình duyệt, trong bảng quản trị.


Nói và xong. Sau một thời gian, một phần nhỏ của ứng dụng đã xuất hiện trong bảng quản trị, giúp người quản lý nội dung tiết kiệm rất nhiều thời gian xem trước các thay đổi. Nếu trước đó, họ phải tạo một liên kết sâu và sau đó, với mỗi thay đổi, hãy mở bản dựng dành cho nhà phát triển, theo liên kết này, đợi tải xuống rồi lặp lại mọi thứ, thì bây giờ họ có thể chỉ cần tạo bài viết và xem chúng ngay lập tức.


Nhưng suy nghĩ của tôi không dừng lại ở đó - tôi quá khó chịu với công cụ này và các nhà phát triển khác vì có thể xác định xem họ có cần thêm thứ gì đó vào nó hay chỉ dọn dẹp chuồng ngựa Augean .


Nếu là vế sau thì nhà phát triển luôn có tâm trạng vui vẻ trong các cuộc họp—mặc dù mùi... máy ảnh không thể ghi lại được mùi đó.

Nếu là trường hợp trước, nhà phát triển thường xuyên bị ốm, sống qua động đất, máy tính bị hỏng, đau đầu, va chạm với thiên thạch, trầm cảm ở giai đoạn cuối hoặc thờ ơ quá mức.


Việc mở rộng chức năng của công cụ cũng yêu cầu thêm nhiều trường mới vào bảng quản trị để người quản lý nội dung có thể sử dụng các tính năng mới.


Nhìn vào tất cả những điều này, tôi chợt nảy ra một ý nghĩ khó tin: tại sao không tạo ra một giải pháp chung cho vấn đề này? Một giải pháp ngăn chúng tôi liên tục điều chỉnh và mở rộng bảng quản trị cũng như ứng dụng cho từng thành phần mới. Một giải pháp có thể giải quyết vấn đề một lần và mãi mãi! Và đây là...

Kế hoạch nhỏ tham lam lén lút

Tôi nghĩ - "Tôi có thể giải quyết vấn đề này. Tôi có thể cứu công ty hàng chục, nếu không muốn nói là hàng trăm nghìn; nhưng ý tưởng này có thể quá giá trị nên công ty chỉ đem nó đi làm quà tặng ."


Khi nói đến quà tặng, ý tôi là tỷ lệ giá trị tiềm năng của công ty khác với những gì công ty sẽ trả cho tôi dưới dạng lương theo mức độ lớn. Nó giống như việc bạn đến làm việc tại một công ty khởi nghiệp ở giai đoạn đầu nhưng với mức lương thấp hơn mức lương bạn được trả ở một công ty lớn nào đó và không có cổ phần trong công ty. Và sau đó công ty khởi nghiệp trở thành một con kỳ lân, và họ nói với bạn - "Ồ, anh bạn, chúng tôi đã trả lương cho bạn." Và họ sẽ đúng!


Tôi thích sự tương tự, nhưng tôi thường được bảo rằng chúng không phải là điểm mạnh của tôi. Giống như bạn là một con cá thích bơi lội trong đại dương, nhưng bạn lại là một con cá nước ngọt.


Và sau đó - tôi quyết định thực hiện một bằng chứng về khái niệm (POC), trong thời gian rảnh rỗi của mình, để không làm hỏng việc khi đưa ra một số ý tưởng mà thậm chí có thể không thực hiện được.

Bằng chứng của khái niệm

Kế hoạch ban đầu là sử dụng một thư viện làm sẵn hiện có để hiển thị đánh dấu nhưng mở rộng khả năng của nó để nó có thể hiển thị không chỉ các phần tử tiêu chuẩn từ danh sách đánh dấu mà còn cả thứ gì đó phức tạp hơn nhiều. Các bài viết không chỉ có văn bản với hình ảnh. Ngoài ra còn có thiết kế hình ảnh đẹp mắt, trình phát âm thanh tích hợp và nhiều tính năng khác.


Tôi đã dành 40 giờ, tính từ tối thứ Sáu đến sáng thứ Hai, để kiểm tra giả thuyết này - thư viện này có khả năng mở rộng như thế nào đối với các tính năng mới, mọi thứ nói chung hoạt động tốt như thế nào và quan trọng nhất - liệu giải pháp này có thể lật đổ cỗ máy khét tiếng khỏi ngai vàng hay không. Giả thuyết đã được xác nhận - sau khi phân tách thư viện đến tận xương và vá một chút, có thể đăng ký bất kỳ thành phần UI nào theo từ khóa hoặc cấu trúc cú pháp đặc biệt, tất cả điều này có thể dễ dàng mở rộng và quan trọng nhất - nó thực sự có thể thay thế công cụ bài viết . Tôi đến đâu đó trong 15 giờ. 25 còn lại tôi dành để hoàn thiện POC.


Ý tưởng không chỉ là thay thế động cơ này bằng động cơ khác - không. Ý tưởng là thay thế toàn bộ quá trình! Bảng quản trị không chỉ cho phép bạn tạo bài viết mà còn quản lý nội dung hiển thị trong ứng dụng. Ý tưởng ban đầu là tạo ra một sự thay thế hoàn chỉnh không bị ràng buộc với một dự án cụ thể mà cho phép quản lý nó. Quan trọng nhất - sự thay thế này cũng phải cung cấp một trình chỉnh sửa thuận tiện cho chính những bài viết này để chúng có thể được tạo và xem ngay kết quả.


Đối với POC, tôi nghĩ chỉ cần làm một biên tập viên là đủ. Nó trông giống như thế này:

Trình chỉnh sửa giao diện người dùng

Sau 40 giờ, tôi đã có một trình soạn thảo mã hoạt động bao gồm một hỗn hợp hỗn loạn của đánh dấu và một loạt các thẻ XML tùy chỉnh (ví dụ: <container> ), một bản xem trước hiển thị giao diện người dùng từ mã này trong thời gian thực và cũng là bản xem trước lớn nhất bọng mắt mà thế giới này từng thấy. Cũng cần lưu ý rằng "trình soạn thảo mã" đã sử dụng là một thư viện khác có khả năng đánh dấu cú pháp, nhưng vấn đề là nó có thể làm nổi bật phần đánh dấu, nó cũng có thể làm nổi bật XML, nhưng phần đánh dấu của một hodgepodge liên tục bị hỏng. Vì vậy, trong 40 giờ, bạn có thể thêm một vài thứ nữa để mã hóa khỉ cho chimera để làm nổi bật cả hai trong một chai. Đã đến lúc hỏi - chuyện gì xảy ra tiếp theo?


Bản demo đầu tiên

Tiếp theo là bản demo. Tôi đã tập hợp một vài nhà quản lý cấp cao, giải thích cho họ tầm nhìn của tôi trong việc giải quyết vấn đề, thực tế là tôi đã xác nhận tầm nhìn này trong thực tế và chỉ ra những gì hiệu quả, cách thức cũng như những khả năng mà nó có.


Các chàng trai thích công việc. Và có một mong muốn sử dụng nó. Nhưng cũng có một lòng tham gặm nhấm. Lòng tham của tôi. Tôi không thể cứ giao nó cho công ty như thế được sao? Dĩ nhiên là không. Nhưng tôi cũng không có ý định đó. Bản demo là một phần của kế hoạch táo bạo, trong đó tôi đã gây sốc cho họ bằng kỹ năng của mình, họ đơn giản là không thể cưỡng lại và sẵn sàng đáp ứng mọi điều kiện, chỉ để sử dụng sự phát triển đáng kinh ngạc, độc quyền và tuyệt vời này. Tôi sẽ không tiết lộ tất cả các chi tiết của câu chuyện hư cấu (!) Này, mà tôi sẽ chỉ nói rằng tôi muốn có tiền. Tiền và một kỳ nghỉ. Một kỳ nghỉ có lương một tháng cũng như tiền. Bao nhiêu tiền không quan trọng, quan trọng là số tiền đó tương ứng với mức lương của tôi và con số 6 là bao nhiêu.

Nhưng tôi không phải là một kẻ liều lĩnh hoàn toàn liều lĩnh.


Dormammu, tôi đến để mặc cả. Và thỏa thuận như sau - Tôi làm việc trọn hai tuần ở chế độ của mình ( ngủ 4 giờ, làm việc 20 giờ ), hoàn thiện POC ở trạng thái "có thể được sử dụng cho mục đích ứng dụng của chúng tôi" và song song với điều này, tôi thực hiện một tính năng mới trong ứng dụng - toàn bộ màn hình, sử dụng siêu thứ này (ban đầu được phân bổ cho hai tuần này). Và vào cuối hai tuần, chúng tôi tổ chức một buổi demo khác. Chỉ lần này chúng tôi tập hợp nhiều người hơn, thậm chí cả ban lãnh đạo cao nhất của công ty, và nếu những gì họ thấy gây ấn tượng với họ và họ muốn sử dụng nó - thỏa thuận đã hoàn tất, tôi đạt được mong muốn của mình và công ty có được một siêu súng. Nếu họ không muốn bất cứ điều gì trong số này - tôi sẵn sàng chấp nhận sự thật rằng tôi đã làm việc không công trong hai tuần qua.


Pedra Furada (gần Urubici)

Chà, thật không may, chuyến đi đến Urubici mà tôi đã lên kế hoạch cho kỳ nghỉ kéo dài một tháng của mình, thật không may, đã không bao giờ xảy ra. Những người quản lý không dám đồng ý với sự táo bạo như vậy. Và tôi, hạ tầm mắt xuống đất, đi cắt màn hình mới theo “cách cổ điển”. Nhưng không có câu chuyện nào mà nhân vật chính, bị số phận đánh bại, lại không đứng dậy và cố gắng thuần hóa con thú của mình một lần nữa.


Mặc dù không... có vẻ như có: 1 , 2 , 3 , 4 , 5 .


Sau khi xem hết những bộ phim này, tôi quyết định rằng đây là một dấu hiệu ! Và điều đó thậm chí còn tốt hơn theo cách này - thật đáng tiếc khi bán một khu phát triển đầy hứa hẹn như vậy cho một số món quà ở đó ( tôi đang đùa ai vậy ??? ), và tôi sẽ tiếp tục phát triển dự án của mình hơn nữa. Và tôi tiếp tục. Nhưng không còn 40 giờ vào cuối tuần nữa, chỉ 15-20 giờ một tuần, với tốc độ tương đối bình tĩnh.

Mã hóa hay không mã hóa?

Phá vỡ bức tường thứ 4 không phải là một việc dễ dàng. Cũng giống như việc cố gắng nghĩ ra những tiêu đề thú vị sẽ khiến người đọc tiếp tục đọc và chờ xem câu chuyện với công ty sẽ kết thúc như thế nào. Tôi sẽ kết thúc câu chuyện ở bài viết thứ hai. Và bây giờ, có vẻ như đã đến lúc chuyển sang cách triển khai, khả năng chức năng và tất cả những thứ đó, theo lý thuyết, sẽ làm cho bài viết này trở nên kỹ thuật và HackerNoon tốt hơn!

Cú pháp

Điều đầu tiên chúng ta sẽ nói đến là cú pháp. Ý tưởng hodgepodge ban đầu phù hợp với POC, nhưng như thực tế đã cho thấy, việc giảm giá không đơn giản như vậy. Ngoài ra, việc kết hợp một số phần tử markdown gốc với các phần tử Flutter thuần túy không phải lúc nào cũng nhất quán.


Câu hỏi đầu tiên là - hình ảnh sẽ là ![Description](Link) hay <image> ?


Nếu cái đầu tiên - tôi phải chuyển một loạt thông số vào đâu?

Nếu cái thứ hai - tại sao chúng ta lại có cái thứ nhất?


Câu hỏi thứ hai là văn bản. Khả năng của Flutter để tạo kiểu cho văn bản là vô hạn. Khả năng giảm giá là “tương đối”. Có, bạn có thể đánh dấu văn bản bằng chữ in đậm hoặc in nghiêng và thậm chí còn có ý định sử dụng các cấu trúc ** / __ này để tạo kiểu. Sau đó, người ta đã nghĩ đến việc nhét các thẻ <color="red"> văn bản </color> vào giữa, nhưng đây là một đường cong và ngoằn ngoèo đến mức máu chảy ra từ mắt. Việc có được một loại HTML nào đó, với cú pháp cận biên của riêng nó, là điều không mong muốn chút nào. Thêm vào đó, ý tưởng là mã này có thể được viết ngay cả bởi những người quản lý không có kiến thức kỹ thuật.


Từng bước một, tôi loại bỏ một phần của chimera và có được một siêu đột biến. Nghĩa là, chúng tôi có một thư viện được vá để hiển thị đánh dấu, nhưng chứa đầy các thẻ tùy chỉnh và không hỗ trợ đánh dấu. Nghĩa là, như thể chúng ta có XML.


Tôi ngồi xuống để suy nghĩ và thử nghiệm những cú pháp đơn giản khác. JSON là xỉ. Bắt một người viết JSON trong trình soạn thảo Flutter quanh co chính là khiến một kẻ điên cuồng muốn giết bạn. Và không chỉ có vậy, đối với tôi, có vẻ như JSON không phù hợp để một người nói chung gõ, đặc biệt là đối với giao diện người dùng - nó không ngừng phát triển về bên phải, một loạt các bắt buộc "" , không có nhận xét nào. YAML? Vâng, có lẽ. Nhưng mã cũng sẽ bò sang một bên. Có những liên kết thú vị, nhưng bạn không thể đạt được nhiều điều nếu chỉ có sự trợ giúp của chúng. TOML? Pf-ff.


Được rồi, cuối cùng thì tôi đã quyết định dựa trên XML. Đối với tôi, và dường như bây giờ, đây là một cú pháp khá "dày đặc", rất phù hợp với giao diện người dùng. Suy cho cùng, các nhà thiết kế bố cục HTML vẫn tồn tại và ở đây mọi thứ thậm chí còn đơn giản hơn trên web ( có thể là ).


Tiếp theo, câu hỏi được đặt ra - thật tuyệt nếu có khả năng đánh dấu/hoàn thành mã. Cũng như các cấu trúc logic, loại {{ user.name }} . Sau đó tôi bắt đầu thử nghiệm với Twig, Liquid, xem một số template engine khác mà tôi không nhớ nữa. Nhưng tôi lại gặp phải một vấn đề khác - hoàn toàn có thể thực hiện một phần những gì đã được lên kế hoạch trên một công cụ tiêu chuẩn, chẳng hạn như Twig, nhưng chắc chắn sẽ không hiệu quả nếu thực hiện mọi thứ. Và vâng, thật tốt khi sẽ có tính năng tự động hoàn thành và đánh dấu, nhưng chúng sẽ chỉ gây trở ngại nếu bạn đưa các tính năng mới của riêng mình lên trên cú pháp Twig tiêu chuẩn, điều này sẽ cần thiết cho Flutter. Kết quả là, với XML, mọi thứ diễn ra rất tốt, các thử nghiệm với Twig / Liquid không mang lại kết quả nổi bật nào và ở một số điểm nhất định, tôi thậm chí còn gặp phải tình trạng không thể triển khai một số tính năng. Vì vậy, sự lựa chọn vẫn thuộc về XML. Chúng ta sẽ nói nhiều hơn về các tính năng, nhưng hiện tại, hãy tập trung vào tính năng tự động hoàn thành và đánh dấu, những tính năng rất hấp dẫn trong Twig/Liquid.


IDE

Điều tiếp theo tôi muốn nói là Flutter có kiểu nhập văn bản không chuẩn. Chúng hoạt động tốt ở định dạng di động. Cũng tốt ở định dạng máy tính để bàn khi nói đến một thứ gì đó, chiều cao tối đa là 5-10 dòng. Nhưng khi nói đến một trình soạn thảo mã chính thức, nơi trình soạn thảo này được triển khai trong Flutter - bạn không thể nhìn vào nó mà không rơi nước mắt. Trong Trello , nơi tôi theo dõi tất cả các nhiệm vụ cũng như viết ghi chú và ý tưởng, có một "nhiệm vụ" như vậy:


Nhiệm vụ thay đổi trình soạn thảo mã UI


Trên thực tế, gần như ngay từ khi bắt đầu thực hiện dự án, tôi đã ấp ủ ý tưởng thay thế trình soạn thảo mã Nui bằng một thứ gì đó phù hợp hơn. Giả sử - nhúng chế độ xem web với phần Nguồn mở từ Mã VS. Nhưng cho đến nay, tay tôi vẫn chưa đạt được điều này, bên cạnh đó, tôi đã nghĩ đến một giải pháp khó khăn nhưng vẫn hiệu quả cho vấn đề về độ cong của trình soạn thảo này - thay vào đó hãy sử dụng môi trường phát triển của riêng bạn.


Điều này đạt được như sau - tạo một tệp có mã UI (XML), lý tưởng nhất là có phần mở rộng .html / .twig , mở cùng một tệp thông qua CMS - Web / Desktop / Local / Deployed - không thành vấn đề. Và mở cùng một tệp thông qua bất kỳ IDE nào, thậm chí thông qua phiên bản web của VS Code. Và thì đấy - bạn có thể chỉnh sửa tệp này trong công cụ yêu thích của mình và xem trước theo thời gian thực ngay trong trình duyệt hoặc bất kỳ đâu.


Đồng bộ hóa Nanc + IDE


Trong trường hợp như vậy, bạn thậm chí có thể thực hiện tự động hoàn thành chính thức. Trong VS Code, có khả năng triển khai nó thông qua các thẻ HTML tùy chỉnh. Tuy nhiên, tôi không sử dụng VS Code, lựa chọn của tôi là IntelliJ IDEA và đối với IDE này không còn giải pháp đơn giản nào như vậy nữa (à, ít nhất là không có, hoặc ít nhất là tôi không tìm thấy nó). Nhưng có một giải pháp tổng quát hơn sẽ hoạt động cả ở đó và ở đó - Định nghĩa Lược đồ XML (XSD). Tôi đã dành khoảng 3 buổi tối để tìm ra con quái vật này, nhưng thành công không bao giờ đến, và cuối cùng, tôi từ bỏ vấn đề này, để nó cho thời điểm tốt hơn.


Điều thú vị là cuối cùng, sau nhiều lần lặp lại thử nghiệm, cập nhật, chẳng hạn như công cụ chịu trách nhiệm chuyển đổi XML thành các widget, chúng tôi đã có được một giải pháp mà ngôn ngữ không đặc biệt quan trọng. Giống như một nhà cung cấp thông tin về cấu trúc giao diện người dùng của bạn, sự lựa chọn cuối cùng thuộc về XML, nhưng đồng thời, bạn có thể cung cấp JSON một cách an toàn và thậm chí cả dạng nhị phân - Protobuf được biên dịch. Và điều này đưa chúng ta đến chủ đề tiếp theo.


Hiệu suất

Trong câu này, kích thước của bài viết này sẽ là 3218 từ. Khi tôi bắt đầu viết phần này, để thực hiện mọi thứ một cách chất lượng - cần phải viết rất nhiều trường hợp thử nghiệm so sánh hiệu suất của Nui và Flutter thông thường. Vì tôi đã triển khai màn hình demo, được tạo hoàn toàn trên Nui:


Demo màn hình Nalmart


điều cần thiết là phải tạo ra sự khớp chính xác của màn hình một cách nguyên bản (tất nhiên là trong bối cảnh của Flutter). Kết quả là phải mất hơn 3 tuần, viết lại rất nhiều thứ giống nhau, cải tiến quy trình thử nghiệm và thu được ngày càng nhiều con số thú vị. Và riêng phần này đã vượt quá 3500 từ. Do đó, tôi nảy ra ý tưởng rằng sẽ rất hợp lý khi viết một bài báo riêng dành toàn bộ và hoàn toàn cho hiệu suất của Nui, như một trường hợp cụ thể, cũng như mức giá bổ sung mà bạn sẽ phải trả nếu quyết định sử dụng Giao diện người dùng hướng máy chủ như một cách tiếp cận.


Nhưng tôi sẽ tiết lộ một chút: có hai tình huống chính để đánh giá hiệu suất mà tôi đã xem xét - thời điểm hiển thị ban đầu . Điều quan trọng là nếu bạn quyết định triển khai toàn bộ màn hình trên Giao diện người dùng do máy chủ điều khiển và màn hình này sẽ mở ở đâu đó trong ứng dụng của bạn.


Vì vậy, nếu màn hình này rất nặng thì ngay cả màn hình Flutter gốc cũng sẽ mất nhiều thời gian để hiển thị, vì vậy khi chuyển sang màn hình như vậy, đặc biệt nếu quá trình chuyển đổi này đi kèm với hoạt ảnh, độ trễ sẽ xuất hiện. Kịch bản thứ hai là thời gian khung hình (FPS) với những thay đổi về giao diện người dùng động . Dữ liệu đã thay đổi - bạn cần vẽ lại một số thành phần. Câu hỏi đặt ra là điều này sẽ ảnh hưởng đến thời gian render đến mức nào, có ảnh hưởng nhiều đến mức khi cập nhật màn hình người dùng sẽ thấy độ trễ hay không. Và đây là một spoiler khác - trong hầu hết các trường hợp, bạn sẽ không thể biết rằng màn hình bạn nhìn thấy được triển khai hoàn toàn trên Nui. Nếu bạn nhúng tiện ích Nui vào màn hình Flutter gốc thông thường (giả sử một số khu vực trên màn hình sẽ thay đổi rất linh hoạt trong ứng dụng) - bạn được đảm bảo là không thể nhận ra điều này. Tất nhiên, có sự sụt giảm về hiệu suất. Nhưng chúng đến mức không ảnh hưởng đến FPS ngay cả ở tốc độ khung hình 120FPS - tức là thời gian của một khung hình hầu như sẽ không bao giờ vượt quá 8ms . Điều này đúng với kịch bản thứ hai. Đối với cái đầu tiên - tất cả phụ thuộc vào mức độ phức tạp của màn hình. Nhưng ngay cả ở đây, sự khác biệt sẽ không ảnh hưởng đến nhận thức và sẽ không biến ứng dụng của bạn thành chuẩn mực cho điện thoại thông minh của người dùng .


Dưới đây là ba bản ghi màn hình từ Pixel 7a (Tensor G2, tốc độ làm mới màn hình được đặt thành 90 khung hình (tối đa cho thiết bị này), tốc độ quay video là 60 khung hình mỗi giây (tối đa cho cài đặt ghi). Cứ sau 500ms, vị trí của các phần tử trong danh sách được chọn ngẫu nhiên, từ dữ liệu của 3 thẻ đầu tiên được tạo và sau 500 mili giây nữa, trạng thái đơn hàng sẽ được chuyển sang màn hình tiếp theo. Bạn có đoán được màn hình nào trong số này được triển khai hoàn toàn trên Nui không?


Tái bút Thời gian tải hình ảnh không phụ thuộc vào quá trình triển khai vì trên màn hình này, với bất kỳ quá trình triển khai nào, có rất nhiều hình ảnh Svg - tất cả các biểu tượng, cũng như logo thương hiệu. Tất cả Svg (cũng như hình ảnh thông thường) đều được lưu trữ trên GitHub, dưới dạng máy chủ lưu trữ, vì vậy chúng có thể tải khá chậm, điều này được quan sát thấy trong một số thử nghiệm.


YouTube:


Các thành phần có sẵn - Cách tạo giao diện người dùng

Khi tạo Nui, tôi tuân thủ khái niệm sau - cần phải tạo ra một công cụ mà trước hết, các nhà phát triển Flutter sẽ thấy nó dễ sử dụng như tạo các ứng dụng Flutter thông thường. Do đó, cách đặt tên cho tất cả các thành phần rất đơn giản - đặt tên chúng giống như cách chúng được đặt tên trong Flutter.


Điều tương tự cũng áp dụng cho các tham số widget - các tham số vô hướng, như String , int , double , enum , v.v., những tham số này không được tự cấu hình với tư cách là một tham số. Những loại tham số này trong Nui được gọi là đối số . Và đối với các tham số lớp phức tạp, như decoration trong tiện ích Container , được gọi là thuộc tính . Quy tắc này không tuyệt đối vì một số thuộc tính quá dài dòng nên tên của chúng đã được đơn giản hóa. Ngoài ra, đối với một số tiện ích, danh sách các tham số khả dụng đã được mở rộng. Ví dụ: để tạo một SizedBox hoặc Container hình vuông, bạn chỉ có thể chuyển một đối số tùy chỉnh size , thay vì hai width + height giống hệt nhau.


Tôi sẽ không cung cấp danh sách đầy đủ các tiện ích đã triển khai, vì có khá nhiều trong số chúng (hiện tại là 53). Nói tóm lại - về nguyên tắc, bạn có thể triển khai hầu hết mọi giao diện người dùng mà việc sử dụng Giao diện người dùng do máy chủ điều khiển làm phương pháp tiếp cận về nguyên tắc là hợp lý. Bao gồm các hiệu ứng cuộn phức tạp liên quan đến Slivers .


Các widget đã triển khai



Ngoài ra, liên quan đến các thành phần, cần lưu ý điểm vào hoặc tiện ích mà bạn sẽ phải chuyển mã XML đám mây vào đó. Hiện tại có hai tiện ích như vậy - NuiListWidgetNuiStackWidget .


Cái đầu tiên, theo thiết kế, nên được sử dụng nếu bạn cần triển khai toàn bộ màn hình. Dưới mui xe, nó là CustomScrollView chứa tất cả các tiện ích sẽ được phân tích cú pháp từ mã đánh dấu ban đầu. Hơn nữa, người ta có thể nói, việc phân tích cú pháp là "thông minh": vì nội dung của CustomScrollView phải là slivers , nên giải pháp khả thi là bọc từng tiện ích trong luồng trong SliverToBoxAdapter , nhưng điều này sẽ có tác động cực kỳ tiêu cực về hiệu suất. Do đó, các tiện ích được nhúng trong phần tử mẹ của chúng như sau - bắt đầu từ tiện ích đầu tiên, chúng ta đi xuống danh sách cho đến khi gặp một sliver thực sự . Ngay khi chúng tôi gặp một sliver - chúng tôi sẽ thêm tất cả các tiện ích trước đó vào SliverList và thêm nó vào CustomScrollView gốc. Do đó, hiệu suất hiển thị toàn bộ giao diện người dùng sẽ cao nhất có thể vì số lượng slivers sẽ ở mức tối thiểu. Tại sao có nhiều slivers trong CustomScrollView lại tệ? Câu trả lời là ở đây .


Tiện ích thứ hai - NuiStackWidget cũng có thể được sử dụng làm toàn màn hình - trong trường hợp này, điều đáng lưu ý là mọi thứ bạn tạo sẽ được nhúng vào Stack xếp theo cùng một thứ tự. Và cũng cần phải sử dụng rõ ràng slivers - nghĩa là, nếu bạn muốn có danh sách slivers - bạn sẽ phải thêm CustomScrollView và đã triển khai danh sách bên trong nó.


Kịch bản thứ hai là triển khai một tiện ích nhỏ có thể được nhúng vào các thành phần gốc. Giả sử - để tạo một thẻ sản phẩm có thể tùy chỉnh hoàn toàn theo sáng kiến của máy chủ. Có vẻ như một kịch bản rất thú vị trong đó bạn có thể triển khai tất cả các thành phần trong thư viện thành phần bằng Nui và sử dụng chúng như các tiện ích thông thường. Đồng thời, sẽ luôn có cơ hội thay đổi chúng hoàn toàn mà không cần cập nhật ứng dụng.


Điều đáng chú ý là NuiListWidget cũng có thể được sử dụng làm tiện ích cục bộ chứ không phải toàn bộ màn hình, nhưng đối với tiện ích này, bạn sẽ cần áp dụng các hạn chế thích hợp, chẳng hạn như đặt chiều cao rõ ràng cho tiện ích gốc.


Đây là giao diện của một counter app nếu nó được tạo bằng Flutter:

 import 'package:flutter/material.dart'; import 'package:nui/nui.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Nui App', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(title: 'Nui Demo App'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({ required this.title, super.key, }); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: Center( child: NuiStackWidget( renderers: const [], imageErrorBuilder: null, imageFrameBuilder: null, imageLoadingBuilder: null, binary: null, nodes: null, xmlContent: ''' <center> <column mainAxisSize="min"> <text size="18" align="center"> You have pushed the button\nthis many times: </text> <text size="32"> {{ page.counter }} </text> </column> </center> ''', pageData: { 'counter': _counter, }, ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: const Icon(Icons.add), ), ); } }


Và đây là một ví dụ khác, chỉ hoàn toàn trên Núi (bao gồm cả logic):

 import 'package:flutter/material.dart'; import 'package:nui/nui.dart'; void main() { runApp(const MyApp()); } final DataStorage globalDataStorage = DataStorage(data: {'counter': 0}); final EventHandler counterHandler = EventHandler( test: (BuildContext context, Event event) => event.event == 'increment', handler: (BuildContext context, Event event) => globalDataStorage.updateValue( 'counter', (globalDataStorage.getTypedValue<int>(query: 'counter') ?? 0) + 1, ), ); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return DataStorageProvider( dataStorage: globalDataStorage, child: EventDelegate( handlers: [ counterHandler, ], child: MaterialApp( title: 'Nui App', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(title: 'Nui Counter'), ), ), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({ required this.title, super.key, }); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: Center( child: NuiStackWidget( renderers: const [], imageErrorBuilder: null, imageFrameBuilder: null, imageLoadingBuilder: null, binary: null, nodes: null, xmlContent: ''' <center> <column mainAxisSize="min"> <text size="18" align="center"> You have pushed the button\nthis many times: </text> <dataBuilder buildWhen="counter"> <text size="32"> {{ data.counter }} </text> </dataBuilder> </column> </center> <positioned right="16" bottom="16"> <physicalModel elevation="8" shadowColor="FF000000" clip="antiAliasWithSaveLayer"> <prop:borderRadius all="16"/> <material type="button" color="EBDEFF"> <prop:borderRadius all="16"/> <inkWell onPressed="increment"> <prop:borderRadius all="16"/> <tooltip text="Increment"> <sizedBox size="56"> <center> <icon icon="mdi_plus" color="21103E"/> </center> </sizedBox> </tooltip> </inkWell> </material> </physicalModel> </positioned> ''', pageData: {}, ), ), ); } }


Mã UI riêng biệt để có điểm nhấn:

 <center> <column mainAxisSize="min"> <text size="18" align="center"> You have pushed the button\nthis many times: </text> <dataBuilder buildWhen="counter"> <text size="32"> {{ data.counter }} </text> </dataBuilder> </column> </center> <positioned right="16" bottom="16"> <physicalModel elevation="8" shadowColor="black" clip="antiAliasWithSaveLayer"> <prop:borderRadius all="16"/> <material type="button" color="EBDEFF"> <prop:borderRadius all="16"/> <inkWell onPressed="increment"> <prop:borderRadius all="16"/> <tooltip text="Increment"> <sizedBox size="56"> <center> <icon icon="mdi_plus" color="21103E"/> </center> </sizedBox> </tooltip> </inkWell> </material> </physicalModel> </positioned> 

Ứng dụng Nui Counter với logic Nui


Ngoài ra còn có tài liệu tương tác và toàn diện hiển thị thông tin chi tiết về các đối số và thuộc tính mà mỗi tiện ích có, cũng như tất cả các giá trị có thể có của chúng. Đối với mỗi thuộc tính, cũng có thể có cả đối số và các thuộc tính khác, cũng có tài liệu minh họa đầy đủ tất cả các giá trị có sẵn. Ngoài ra, mỗi thành phần còn chứa một ví dụ tương tác trong đó bạn có thể xem cách triển khai tiện ích này trực tiếp và sử dụng nó bằng cách thay đổi nó theo ý muốn.

Sân chơi Nanc

Nui được tích hợp rất chặt chẽ vào Nanc CMS. Bạn không cần phải sử dụng Nanc để sử dụng Nui, nhưng sử dụng Nanc có thể mang lại cho bạn những lợi thế, cụ thể là - cùng một tài liệu tương tác, cũng như Playground, nơi bạn có thể xem kết quả của bố cục trong thời gian thực, chơi với dữ liệu sẽ được sử dụng trong đó. Hơn nữa, không cần thiết phải tạo bản dựng CMS cục bộ của riêng bạn, bạn hoàn toàn có thể quản lý bằng bản demo đã xuất bản, trong đó bạn có thể làm mọi thứ bạn cần.


Bạn có thể thực hiện việc này bằng cách nhấp vào liên kết , sau đó nhấp vào trường Page Interface / Screen . Màn hình đã mở có thể được sử dụng làm Sân chơi và bằng cách nhấp vào nút Đồng bộ hóa , bạn có thể đồng bộ hóa Nanc với IDE của mình thông qua tệp có nguồn và tất cả tài liệu đều có sẵn bằng cách nhấp vào nút Trợ giúp .


Tái bút Những sự phức tạp này tồn tại bởi vì tôi chưa bao giờ có thời gian để tạo một trang riêng biệt rõ ràng với tài liệu về các thành phần trong Nanc, cũng như không thể chèn liên kết trực tiếp đến trang này.


Tương tác và logic

Sẽ là quá vô nghĩa nếu tạo một trình ánh xạ thông thường từ XML đến các widget. Tất nhiên, điều này cũng có thể hữu ích, nhưng sẽ có ít trường hợp sử dụng hơn. Không giống nhau - các thành phần và màn hình tương tác hoàn toàn mà bạn có thể tương tác, bạn có thể cập nhật chi tiết (nghĩa là không phải tất cả cùng một lúc - mà ở những phần cần cập nhật). Ngoài ra, giao diện người dùng này cần dữ liệu. Điều này, có tính đến sự hiện diện của chữ S trong cụm từ Giao diện người dùng do máy chủ điều khiển, có thể được thay thế trực tiếp vào bố cục trên máy chủ, nhưng bạn cũng có thể làm điều đó đẹp hơn. Và không được kéo một phần mới của bố cục từ phần phụ trợ cho mỗi thay đổi trong giao diện người dùng (Nui không phải là cỗ máy thời gian mang đến những phương pháp hay nhất về jQuery).


Hãy bắt đầu với logic: các biến và biểu thức được tính toán có thể được thay thế vào bố cục. Giả sử một tiện ích được xác định là <container color="{{ page.background }}"> sẽ trích xuất màu của nó trực tiếp từ dữ liệu được truyền đến "ngữ cảnh gốc" được lưu trữ trong biến background . Và <aspectRatio ratio="{{ 3 / 4}}"> sẽ đặt giá trị tỷ lệ khung hình tương ứng cho con cháu của nó. Có các hàm dựng sẵn, so sánh và nhiều chức năng khác có thể được sử dụng để xây dựng giao diện người dùng với một số logic.


Điểm thứ hai là tạo khuôn mẫu . Bạn có thể xác định trực tiếp tiện ích của riêng mình trong mã giao diện người dùng bằng cách sử dụng thẻ <template id="your_component_name"/> . Đồng thời, tất cả các thành phần bên trong của mẫu này sẽ có quyền truy cập vào các đối số được truyền cho mẫu này, điều này sẽ cho phép tham số hóa linh hoạt các thành phần tùy chỉnh và sau đó sử dụng lại chúng bằng cách sử dụng thẻ <component id="your_component_name"/> . Bên trong các mẫu, bạn có thể chuyển không chỉ các thuộc tính mà còn cả các thẻ/widget khác, điều này giúp bạn có thể tạo các thành phần có thể tái sử dụng ở bất kỳ mức độ phức tạp nào.


Điểm thứ ba - "vòng lặp". Trong Nui, có thẻ <for> tích hợp cho phép bạn sử dụng các lần lặp để hiển thị cùng một (hoặc nhiều) thành phần nhiều lần. Điều này thuận tiện khi có một tập hợp dữ liệu mà từ đó bạn cần tạo danh sách/hàng/cột tiện ích.


Thứ tư - hiển thị có điều kiện. Ở cấp độ bố cục, thẻ <show> được triển khai (có ý tưởng gọi nó là <if> ), cho phép bạn vẽ các thành phần lồng nhau hoặc hoàn toàn không nhúng chúng vào cây trong các điều kiện khác nhau.


Điểm năm - hành động. Một số thành phần mà người dùng có thể tương tác có thể gửi sự kiện . Mà bạn hoàn toàn có thể điều khiển theo ý muốn. Giả sử <inkWell onPressed="something"> - với tuyên bố như vậy, tiện ích này sẽ có thể nhấp được và ứng dụng của bạn, hay đúng hơn là một số EventHandler , sẽ có thể xử lý sự kiện này và thực hiện điều gì đó. Ý tưởng là mọi thứ liên quan đến logic nên được triển khai trực tiếp trong ứng dụng, nhưng bạn có thể triển khai bất cứ thứ gì. Tạo một số trình xử lý chung có thể xử lý các nhóm hành động, như "đi tới màn hình"/"phương thức gọi"/"gửi sự kiện phân tích". Cũng có kế hoạch triển khai mã động, nhưng ở đây có nhiều sắc thái. Đối với Dart, có nhiều cách để thực thi mã tùy ý nhưng điều này ảnh hưởng đến hiệu suất và bên cạnh đó khả năng tương tác của mã này với mã ứng dụng khó có thể đạt 100%. Nghĩa là, bằng cách tạo logic trong mã động này, bạn sẽ liên tục gặp phải một số hạn chế. Vì vậy, cơ chế này cần phải được nghiên cứu rất kỹ lưỡng để thực sự có thể áp dụng và hữu ích.


Điểm thứ sáu là cập nhật giao diện người dùng cục bộ. Điều này có thể thực hiện được nhờ thẻ <dataBuilder> . Thẻ này (Khối dưới mui xe) có thể "nhìn" vào một trường cụ thể và khi thay đổi, nó sẽ vẽ lại cây con của nó.


Dữ liệu

Ban đầu, tôi đi theo con đường hai kho lưu trữ dữ liệu - "bối cảnh gốc" đã đề cập ở trên. Cũng như "dữ liệu" - dữ liệu có thể được xác định trực tiếp trong giao diện người dùng bằng cách sử dụng thẻ <data> . Thành thật mà nói, bây giờ tôi không thể nhớ lập luận tại sao cần phải triển khai hai cách lưu trữ và truyền dữ liệu sang giao diện người dùng, nhưng bằng cách nào đó tôi không thể chỉ trích gay gắt bản thân vì một quyết định như vậy.


Chúng hoạt động như sau - "ngữ cảnh gốc" là một đối tượng thuộc loại Map<String, dynamic> , được truyền trực tiếp đến các tiện ích NuiListWidget / NuiStackWidget . Có thể truy cập vào dữ liệu này bằng page tiền tố:

 <someWidget value="{{ page.your.field }}"/>

Bạn có thể tham chiếu đến bất kỳ thứ gì, ở bất kỳ độ sâu nào, bao gồm cả mảng - {{ page.some.array.0.users.35.age }} . Nếu không có khóa/giá trị đó, bạn sẽ nhận được null . Danh sách có thể được lặp lại bằng cách sử dụng <for> .


Cách thứ hai - "dữ liệu" là kho lưu trữ dữ liệu toàn cầu. Trong thực tế, đây là một Bloc nhất định nằm trên cây cao hơn NuiListWidget / NuiStackWidget . Đồng thời, không có gì ngăn cản việc tổ chức việc sử dụng chúng theo kiểu cục bộ, chuyển phiên bản DataStorage của riêng bạn thông qua DataStorageProvider .


Đồng thời, phương thức đầu tiên không có tính phản hồi - tức là khi dữ liệu trong page thay đổi, sẽ không có giao diện người dùng nào tự cập nhật. Vì trên thực tế, đây chỉ là đối số của StatelessWidget của bạn. Ví dụ: nếu nguồn dữ liệu cho page là Khối của riêng bạn, Khối này sẽ cung cấp một tập hợp các giá trị cho Nui...Widget - thì, giống như với StatelessWidget thông thường, nó sẽ được vẽ lại hoàn toàn bằng dữ liệu mới.


Cách thứ hai để làm việc với dữ liệu là phản ứng. Nếu bạn thay đổi dữ liệu trong DataStorage bằng cách sử dụng API của lớp này - phương thức updateValue thì phương thức này sẽ gọi phương thức emit của lớp Bloc và nếu có trình xử lý hoạt động của dữ liệu này trong giao diện người dùng của bạn - thẻ <dataBuilder> thì nội dung của chúng sẽ được thay đổi tương ứng nhưng phần còn lại của giao diện người dùng sẽ không bị ảnh hưởng.


Do đó, chúng tôi nhận được hai nguồn dữ liệu tiềm năng - một page rất đơn giản và một data phản hồi. Ngoại trừ logic cập nhật dữ liệu trong các nguồn này và phản ứng của UI đối với những cập nhật này, không có sự khác biệt nào giữa chúng.

Tài liệu

Tôi cố tình không mô tả tất cả các sắc thái và khía cạnh của công việc, vì nó sẽ trở thành bản sao của tài liệu đã có sẵn. Vì vậy, nếu bạn muốn thử hoặc chỉ muốn tìm hiểu thêm - vui lòng chào mừng bạn đến đây. Nếu bất kỳ khía cạnh nào của công việc không rõ ràng hoặc tài liệu không đề cập đến điều gì đó, thì tôi sẽ rất vui khi nhận được thông báo của bạn chỉ ra vấn đề:


Tôi sẽ liệt kê ngắn gọn một số tính năng không được đề cập trong bài viết này nhưng có sẵn cho bạn:

  • Tạo thẻ/thành phần của riêng bạn, với khả năng tạo tài liệu tương tác giống hệt nhau cho chúng, giống như đối số và thuộc tính của chúng với bản xem trước trực tiếp. Ví dụ: đây là cách triển khai thành phần hiển thị hình ảnh SVG . Không có ích gì khi đẩy nó vào lõi của động cơ, bởi vì không phải ai cũng cần nó, mà là một phần mở rộng có sẵn để sử dụng bằng cách chỉ chuyển một biến - dễ dàng và đơn giản. Trực tiếp -một ví dụ về thực hiện .


  • Một thư viện biểu tượng tích hợp khổng lồ có thể được mở rộng bằng cách thêm biểu tượng của riêng bạn (ở đây tôi thấy không nhất quán và bị "xô", logic là tạo ra càng nhiều biểu tượng càng tốt để sử dụng ngay lập tức và không cần phải cập nhật ứng dụng để sử dụng các biểu tượng mới). Có sẵn: Fluentui_system_icons , Material_design_icons_flutterremixicon . Bạn có thể xem tất cả các biểu tượng có sẵn bằng Nanc , Page Interface / Screen -> Icons

  • Phông chữ tùy chỉnh, bao gồm cả Phông chữ Google ngay lập tức


  • Chuyển đổi XML thành JSON/protobuf và sử dụng chúng làm "nguồn" cho giao diện người dùng


Tất cả điều này và nhiều hơn nữa có thể được nghiên cứu trong tài liệu .


Cái gì tiếp theo?

Điều chính là tìm ra khả năng thực thi mã động bằng logic. Đây là một tính năng rất thú vị cho phép bạn mở rộng khả năng của Nui một cách nghiêm túc. Ngoài ra, bạn có thể (và nên) thêm các widget hiếm khi được sử dụng nhưng đôi khi rất quan trọng còn lại từ thư viện Flutter tiêu chuẩn. Để làm chủ XSD, để tính năng tự động hoàn thành cho tất cả các thẻ xuất hiện trong IDE (có ý tưởng tạo lược đồ này trực tiếp từ tài liệu thẻ, sau đó sẽ dễ dàng tạo nó cho các tiện ích tùy chỉnh và nó sẽ luôn được cập nhật -date và cũng có ý tưởng tạo DSL được tạo trong Dart, sau đó có thể chuyển đổi thành XML / Json / Protobuf). Chà, và tối ưu hóa hiệu suất bổ sung - hiện tại nó không tệ, rất không tệ, nhưng nó thậm chí có thể tốt hơn, thậm chí gần hơn với Flutter bản địa.


Đó là tất cả những gì tôi có. Trong bài viết tiếp theo, tôi sẽ kể rất chi tiết về hiệu suất của Nui, cách tôi tạo các trường hợp thử nghiệm, tôi đã trải qua bao nhiêu lần cào trong quá trình này và những con số nào có thể đạt được trong những tình huống nào.


Nếu bạn quan tâm đến việc dùng thử Nui hoặc tìm hiểu rõ hơn về nó - vui lòng đến bàn tài liệu . Ngoài ra, nếu nó không khó, hãy đánh dấu sao trên GitHub và một lượt thích trên pub.dev - nó không khó đối với bạn, nhưng đối với tôi, một người chèo thuyền cô đơn trên chiếc thuyền khổng lồ này - nó vô cùng hữu ích.