Month: April 2022

  • Backdrop Filter Widget trong Flutter

    Tạo hiệu ứng làm mờ bằng tiện ích BackdropFilter trong ứng dụng Flutter của bạn

    Chúng ta sẽ xem cách triển khai chương trình demo của BackdropFilter và hướng dẫn bạn cách sử dụng tiện ích đó để tạo hiệu ứng mờ trong các ứng dụng Flutter của bạn.

    Backdrop Filter Widget Flutter đang sử dụng để thực hiện các tác động làm mờ trên ảnh, container và mọi tiện ích. Tiện ích Backdrop Filter được sử dụng với sự kết hợp của lớp ImageFilter . Nó áp dụng một bộ lọc trên tiện ích con hiện tại và làm cho tác động mờ bên dưới tiện ích con hiện tại.

    Làm mờ hình ảnh:

    Chúng ta sẽ tạo tác động làm mờ sẽ được áp dụng cho toàn bộ không gian của tiện ích con. Khi BackdropFilter áp dụng bộ lọc, phần lớn chúng ta cần sử dụng tiện ích Stack để thực thi. Tiện ích mà bộ lọc sẽ được áp dụng phải được đặt trước bộ lọc.

    Stack(
      fit: StackFit.expand,
      children: <Widget>[
        Image.asset("assets/devs.jpg",fit: BoxFit.contain,),
        Positioned.fill(
          child: Center(
            child: BackdropFilter(
              filter: ImageFilter.blur(
                sigmaX: 10.0,
                sigmaY: 10.0,
              ),
              child: Container(
                color: Colors.black.withOpacity(0.2),
    
              ),
            ),
          ),
        ),
      ],
    ),

    Vì bộ lọc sẽ bao phủ toàn bộ không gian gốc của nó, chúng ta cần bao bọc tiện ích con BackdropFilter làm phần tử con của Positioned.fill. Bạn cần chứa ImageFilter. Đối với tình huống này, bộ lọc thích hợp nhất có thể được sử dụng ImageFilter.blursigmaX và sigmaY kiểm soát độ lệch tiêu chuẩn phụ thuộc vào bộ lọc trên trục x và trục y riêng lẻ. Cả hai đều có ước tính mặc định là 0, có nghĩa là không có tác động nào được áp dụng. Để áp dụng bộ lọc trên trục x, hãy thay đổi ước tính sigmaX thành một số dương. Đối với trục y, sử dụng thuộc tính sigmaY . Con của BackdropFilter có thể là một Container có độ mờ che bóng dưới 1, bằng 0 là giá trị điển hình. Khi chúng tôi chạy ứng dụng, chúng tôi phải lấy đầu ra của màn hình như ảnh chụp màn hình bên dưới.

    Làm mờ văn bản:

    Ví dụ Blur này là cách làm cho bộ lọc được áp dụng cho một không gian cụ thể của hình ảnh. Thay vì Positioned. fill , sử dụng hàm tạo mặc định của tiện ích con Positioned  mà bạn có thể đặt phân tách từ trên cùng, trái, dưới và phải. Trong mọi trường hợp, điều đó là không đầy đủ. Như tôi đã soạn ở trên, Flutter sẽ áp dụng bộ lọc cho tất cả các khoảng trống bên trong clip của tiện ích con của nó. Theo đó, để áp dụng kênh trên một lãnh thổ cụ thể, bạn cần bao bọc chúng BackdropFilter dưới dạng con của bất kỳ Clip widget, nào , như ClipRect, ClipRRect, ClipOval, ClipPath hoặc CustomClipper.

    Stack(
      fit: StackFit.expand,
      children: <Widget>[
        Image.asset("assets/devs.jpg",fit: BoxFit.contain,),
        Positioned(
          top: 250,
          left: 0,
          right: 0,
          child: Center(
            child: ClipRRect(
              borderRadius: BorderRadius.circular(24),
              child: BackdropFilter(
                filter: ImageFilter.blur(
                  sigmaX: 10.0,
                  sigmaY: 10.0,
                ),
                child: Container(
                  padding: EdgeInsets.all(24),
                  color: Colors.white.withOpacity(0.5),
                  child: Text(
                    "Flutter Dev's",
                    style: TextStyle(
                      fontSize: 28,
                      fontWeight: FontWeight.bold,
                      color: Colors.white,
                    ),
                  ),
                ),
              ),
            ),
          ),
        ),
      ],
    ),

    Chúng ta sẽ thêm văn bản trên một vùng chứa với độ mờ màu và bán kính đường viền sẽ là hình tròn do ClipRRect(). Khi ta chạy ứng dụng, ta phải lấy đầu ra của màn hình như ảnh chụp màn hình bên dưới.

    Làm mờ hình ảnh & văn bản:

    Trong hiệu ứng mờ này, tất cả mọi thứ sẽ giống nhau. Chúng tôi sẽ thêm Stack () , bên trong chúng tôi sẽ thêm một widget Positioned với trên cùng, bên trái và bên phải. Chúng tôi sẽ áp dụng BackdropFilter() và thuộc tính con của nó, chúng tôi sẽ thêm một container có cùng văn bản và màu sắc với độ mờ. Chúng tôi sẽ xóa ClipRRect. Tất cả các vật dụng sẽ bị mờ.

    Stack(
      fit: StackFit.expand,
      children: <Widget>[
        Image.asset("assets/devs.jpg",fit: BoxFit.contain,),
        Positioned(
          top: 250,
          left: 0,
          right: 0,
          child: Center(
            child: BackdropFilter(
              filter: ImageFilter.blur(
                sigmaX: 10.0,
                sigmaY: 10.0,
              ),
              child: Container(
                padding: EdgeInsets.all(24),
                color: Colors.white.withOpacity(0.5),
                child: Text(
                  "Flutter Dev's",
                  style: TextStyle(
                    fontSize: 28,
                    fontWeight: FontWeight.bold,
                    color: Colors.white,
                  ),
                ),
              ),
            ),
          ),
        ),
      ],
    ),

    Khi chạy ứng dụng, ta sẽ thấy màn hình như ảnh chụp màn hình bên dưới.

    Kết luận:

    Trong bài viết này, tôi đã giải thích cấu trúc cơ bản của widget BackdropFilter trong Flutter, bạn có thể sửa đổi mã này theo sự lựa chọn của bạn.

  • Mô hình Agile

    Mô hình Agile

    Có rất nhiều mô hình có thể áp dụng trong quá trình phát triển phần mềm, tuy nhiên Agile là một trong những mô hình đang được ứng dụng rất nhiều và phổ biến trong các công ty.

    1. Mô hình Agile là gì?

    Agile là một phương pháp phát triển phần mềm linh hoạt để làm sao đưa sản phẩm đến tay người dùng càng nhanh càng tốt.

    Tuyên ngôn Agile:

    Tuyên ngôn Agile hay còn gọi là Tuyên ngôn phát triển phần mềm linh hoạt, đánh giá cao:

    • Cá nhân và sự tương tác hơn là quy trình và công cụ.
    • Phần mềm chạy tốt hơn là tài liệu đầy đủ.
    • Cộng tác với khách hàng hơn là đàm phán hợp đồng.
    • Phản hồi với các thay đổi hơn là bám sát kế hoạch.

    Bên cạnh đó, các nhà phát triển còn nhấn mạnh hơn mười hai nguyên lý phía sau Tuyên ngôn Agile, để giúp các nhà phát triển vận dụng trong thực tiễn:

    • Ưu tiên cao nhất của chúng tôi là thỏa mãn khách hàng thông qua việc chuyển giao sớm và liên tục các phần mềm có giá trị.
    • Chào đón việc thay đổi yêu cầu, thậm chí rất muộn trong quá trình phát triển. Các quy trình linh hoạt tận dụng sự thay đổi cho các lợi thế cạnh tranh của khách hàng.
    • Thường xuyên chuyển giao phần mềm chạy tốt tới khách hàng. Từ vài tuần đến vài tháng, ưu tiên cho các khoảng thời gian ngắn hơn.
    • Nhà kinh doanh và nhà phát triển phải làm việc cùng nhau hàng ngày trong suốt dự án.
    • Xây dựng các dự án xung quanh những cá nhân có động lực. Cung cấp cho họ môi trường và sự hỗ trợ cần thiết, và tin tưởng họ để hoàn thành công việc.
    • Phương pháp hiệu quả nhất để truyền đạt thông tin tới nhóm phát triển và trong nội bộ nhóm phát triển là hội thoại trực tiếp.
    • Phần mềm chạy tốt là thước đo chính của tiến độ.
    • Các quy trình linh hoạt thúc đẩy phát triển bền vững. Các nhà tài trợ, nhà phát triển, và người dùng có thể duy trì một nhịp độ liên tục không giới hạn.
    • Liên tục quan tâm đến các kĩ thuật và thiết kế tốt để gia tăng sự linh hoạt.
    • Sự đơn giản – nghệ thuật tối đa hóa lượng công việc chưa xong – là căn bản.
    • Các kiến trúc tốt nhất, yêu cầu tốt nhất, và thiết kế tốt nhất sẽ được làm ra bởi các nhóm tự tổ chức.
    • Đội sản xuất sẽ thường xuyên suy nghĩ về việc làm sao để trở nên hiệu quả hơn. Sau đó họ sẽ điều chỉnh và thay đổi các hành vi của mình cho phù hợp.

    Có thể thấy mô hình Agile đặt khách hàng làm chủ đạo và tích cực trao đổi để tìm kiếm được sự hoàn thiện cho phần mềm.

    2. Đặc trưng của mô hình Agile

    Tính lặp

    Dự án sẽ được thực hiện trong các phân đoạn lặp đi lặp lại. Các phân đoạn (được gọi là Iteration hoặc Sprint) này thường có khung thời gian ngắn (từ 1 – 4 tuần).

    Trong mỗi phân đoạn này, nhóm phát triển thực hiện đầy đủ các công việc cần thiết như lập kế hoạch, phân tích yêu cầu, thiết kế, triển khai, kiểm thử (với các mức độ khác nhau) để cho ra các phần nhỏ của sản phẩm.

    Scrum Framework] Sprint là gì? - Scrum Viet

    Phân rã mục tiêu thành các phần nhỏ với quá trình lập kế hoạch đơn giản và gọn nhẹ nhất có thể

    Tính tăng trưởng và tiến hóa

    Cuối các phân đoạn (Sprint) nhóm phát triển thường cho ra các sản phẩm đầy đủ, có khả năng chạy tốt và được kiểm thử cẩn thận có thể sử dụng ngay.

    Đưa đến cho khách hàng góc nhìn chi tiết về tiến độ sản phẩm, từ đó có được sự tin tưởng từ khách hàng và có những thay đổi ngay lập tức với những yêu cầu của khách hàng.

    Tính thích nghi

    Các thay đổi trong quá trình phát triển có thể được đáp ứng một cách phù hợp, bởi vì các quy trình đã được phân chia rất rõ ràng và chi tiết.

    Nhóm tự tổ chức và liên chức năng

    Các nhóm này tự phân công công việc mà không dựa trên mô tả cứng về chức danh hay làm việc trên một phân cấp rõ ràng của tổ chức.

    Quản lý tiến trình thực nghiệm

    Các nhóm Agile đưa ra các quyết định dựa trên các dữ liệu thực tiễn thay vì lý thuyết hoặc các giả định. Từ đó giúp giảm quá trình phát triển và nâng cao năng suất lao động

    Giao tiếp trực diện

    Agile khuyến khích việc giao tiếp trực tiếp giữa nhóm phát triển với khách hàng, giữa các thành viên trong nhóm phát triển. Từ đó cùng nhau phát triển các chức năng một cách hiệu quả nhất

    Phát triển dựa trên giá trị

    Ưu tiên việc chạy tốt của sản phẩm, giao tiếp với khách hàng để biết yêu cầu nào được ưu tiên cao hơn, mang lại giá trị sớm nhất cho dự án.

    3. Ưu điểm của mô hình Agile

    • Thực hiện thay đổi dễ dàng: Bởi vì dự án được chia thành các phần nhỏ, riêng biệt, không phụ thuộc lẫn nhau, nên những thay đổi được thực hiện rất dễ dàng, ở bất kỳ giai đoạn nào của dự án.
    • Không cần phải nắm mọi thông tin ngay từ đầu: Phù hợp với những dự án chưa xác định được mục tiêu cuối cùng rõ ràng, vì việc này không quá cần thiết trong giai đoạn đầu.
    • Bàn giao nhanh hơn: Việc chia nhỏ dự án cho phép đội ngũ có thể tiến hành kiểm tra theo từng phần, xác định và sửa chữa vấn đề nhanh hơn, nhờ đó việc bàn giao công việc sẽ nhất quán và thành công hơn.
    • Chú ý đến phản hồi của khách hàng và người dùng: Cả khách hàng và người dùng cuối đều có cơ hội để đóng góp các ý kiến và phản hồi, từ đó họ sẽ có ảnh hưởng một cách mạnh mẽ và tích cực tới sản phẩm cuối cùng.
    • Cải tiến liên tục: Agile khuyến khích thành viên trong đội ngũ làm việc và khách hàng cung cấp phản hồi của mình, khi đó các giai đoạn khác nhau của sản phẩm cuối có thể được kiểm tra và cải thiện lại nhiều lần nếu cần.

    4. Nhược điểm của mô hình Agile

    • Khó lên kế hoạch dự án: Khá là khó để xác định rõ ràng thời gian bàn giao sản phẩm cuối cùng, vì dự án được chia nhỏ thành các phần khác nhau và mỗi phần lại có thời gian bàn giao riêng biệt.
    • Bắt buộc phải hướng dẫn và đào tạo chi tiết: Phương pháp Agile phức tạp hơn nhiều so với phương pháp truyền thống. Họ sẽ cần phải trải qua đào tạo, hướng dẫn thì mới có thể nắm được phương pháp một cách rõ ràng, đặc biệt là thời gian đầu.
    • Ít tài liệu hướng dẫn: Vì Agile thay đổi rất nhiều nên các tài liệu thích hợp cũng thường bị bỏ qua, vì không xác định rõ được kỳ vọng và thành phẩm ngay từ đầu. Mặc dù tài liệu không phải là yếu tố quan trọng nhất, nhưng chúng vẫn rất cần thiết.
    • Bắt buộc phải hợp tác để dự án thành công: Điều này đòi hỏi một sự cam kết về thời gian từ cả hai bên trong suốt thời gian của dự án mà các cấu trúc quản lý dự án khác không luôn yêu cầu. Phải có sự tham gia tích cực của người dùng và tiếp tục cộng tác để nó hoạt động.
    • Chi phí cao: Chi phí thực hiện theo phương pháp Agile thường hơn một chút so với các phương pháp phát triển khác.

    5. Áp dụng

    Để áp dụng thành công mô hình Agile trong tổ chức, chúng ta cần:

    • Thứ nhất, các thành viên phối hợp, giao tiếp hiệu quả trong nội bộ. Kỹ năng giao tiếp tốt giúp nhóm làm việc thấu hiểu khách hàng, hợp tác tốt với nhau đảm bảo chất lượng và tốc độ. 
    • Thứ hai, tính tự chủ của mỗi thành viên phải được đảm bảo để các nhóm tự quản lý có thể vận hành một cách chủ động, trơn tru thay vì chỉ tuân thủ theo chỉ dẫn cấp trên như trong các mô hình truyền thống. 
    • Thứ ba, các hoạt động được module hóa thông qua những nhóm liên chức năng. Những nhóm này có khả năng làm việc với tốc độ và chất lượng cao, với khách hàng là trung tâm

    Tham khảo:

    Author: LamNT59

  • JSON vs YAML: A Dive Into 2 Popular Data Serialization Languages

    Bất kỳ người nào quan tâm đến lập trình và công nghệ đều biết JSON là gì. YAML không phổ biến như JSON, nhưng nó cũng là một ngôn ngữ tuần tự hóa dữ liệu phổ biến và tuyệt vời. Ví dụ, bất kỳ người nào đã sử dụng Docker chắc chắn biết YAML là gì.

    Trước hết, hãy xem tuần tự hóa dữ liệu nghĩa là gì. Theo Devopedia, tuần tự hóa dữ liệu là quá trình chuyển đổi các đối tượng dữ liệu có trong cấu trúc dữ liệu phức tạp thành một luồng byte cho các mục đích lưu trữ, truyền và phân phối trên các thiết bị vật lý. Vì vậy, JSON và YAML đều là một cách lưu trữ các đối tượng và cấu trúc dữ liệu trong tệp.

    JSON là gì?

    Mặc dù hầu hết chúng ta đều biết JSON là gì, nhưng chúng ta hãy giới thiệu nhanh. JSON là tên viết tắt của JavaScript Object Notation. JSON dựa trên một tập con của tiêu chuẩn ngôn ngữ lập trình JavaScript ECMA-262 3rd Edition-December 1999. JSON được sử dụng rộng rãi với JavaScript nhưng vì nó không phụ thuộc vào ngôn ngữ nên nó có thể được sử dụng với bất kỳ ngôn ngữ lập trình nào.

    JSON có một định dạng tiêu chuẩn để lưu trữ dữ liệu. Nó lưu trữ dữ liệu trong các cặp khóa / giá trị. Các bản ghi được phân tách bằng dấu phẩy và cả tên trường và chuỗi đều được đặt trong dấu ngoặc kép.

    YAML là gì?

    YAML là tên viết tắt của YAML Ain’t Markup Language. Và YAML Ain’t Markup Language là tên viết tắt của YAML Ain’t Markup Language Ain’t Markup Language. Khá tuyệt, phải không? Để làm cho mọi thứ thú vị hơn, trang web chính thức của YAML cũng được hiển thị ở định dạng YAML.

    YAML sử dụng ba dấu gạch ngang (—) để biểu thị phần bắt đầu của tài liệu và ba dấu chấm (…) để biểu thị phần cuối của tài liệu. Không giống như JSON, YAML sử dụng thụt lề giống như trong Python để hiển thị các cấp trong dữ liệu. Các cặp khóa / giá trị được phân tách bằng dấu hai chấm và danh sách bắt đầu bằng dấu gạch ngang trong YAML. Và các tệp YAML cũng được viết với phần mở rộng YML ở một số nơi và cả .YAML và .YML đều có nghĩa là cùng một loại tệp.

    Pros and Cons: JSON vs. YAML

    Về mặt lý thuyết, cả JSON và YAML đều sẽ thực hiện cùng một nhiệm vụ, tức là cung cấp định dạng trao đổi dữ liệu có thể đọc được của người dùng.

    Khả năng đọc và tính linh hoạt

    Mục tiêu thiết kế của JSON là càng đơn giản càng tốt và có thể sử dụng được trên toàn cầu. Điều này đã làm giảm khả năng đọc của dữ liệu ở một mức độ nào đó. Ngược lại, mục tiêu thiết kế của YAML là cung cấp một định dạng tốt mà con người có thể đọc được và cung cấp hỗ trợ cho việc tuần tự hóa các cấu trúc dữ liệu gốc tùy ý. Điều này đã làm tăng khả năng đọc của các tệp YAML, nhưng nó đã làm cho việc phân tích cú pháp và tạo tệp hơi phức tạp. Chúng ta có thể thấy rõ điều này trong trang web chính thức của YAML, nơi nó hiển thị nội dung ở định dạng YAML: Bất kỳ ai truy cập trang web cũng có thể dễ dàng đọc được. Mặt khác, nếu nó được hiển thị ở định dạng JSON, trang web sẽ vô dụng.

    Người ta nói rằng YAML là một tập siêu định dạng JSON. Điều này có nghĩa đơn giản là chúng ta có thể phân tích cú pháp JSON bằng trình phân tích cú pháp YAML. Tuy nhiên, trong các tình huống thực tế, việc phân tích cú pháp này có thể gây ra vấn đề, nhưng về mặt lý thuyết thì hoàn toàn có thể.

    Hiệu suất tuần hoàn

    Trong cuộc thi tuần tự hóa dữ liệu, JSON là người chiến thắng vì khả năng phân tích cú pháp dữ liệu được tuần tự hóa JSON một cách nhanh chóng và dễ dàng với thiết kế đơn giản hơn. Và điều này đã làm cho JSON phổ biến hơn trong số các nhà phát triển, dẫn đến ngày càng có nhiều hỗ trợ gốc hơn và điều này đã cải thiện hiệu suất một lần nữa. Do đó JSON đã trở thành định dạng trao đổi dữ liệu được sử dụng rộng rãi nhất cho các ứng dụng web và dịch vụ web.

    Sự hỗ trợ

    Đối với bất kỳ ngôn ngữ lập trình nào, chúng ta có thể dễ dàng tìm thấy một thư viện JSON được tích hợp với ngôn ngữ này do tính phổ biến, dễ thực hiện và đơn giản của nó. Trang web chính thức của JSON liệt kê nhiều ngôn ngữ với nhiều thư viện hỗ trợ cho JSON. YAML cũng được hỗ trợ rộng rãi và nhiều thư viện để tích hợp nó với nhiều ngôn ngữ khác nhau, nhưng không nhiều như JSON. Bạn có thể lấy danh sách các thư viện và ngôn ngữ hỗ trợ YAML tại đây.

    Khả năng bình luận

    Cho đến nay, chúng ta đã thảo luận về những ưu điểm của JSON so với YAML. Nhưng một số tính năng quan trọng đáng kể của YAML không được tìm thấy với JSON. YAML hỗ trợ nhận xét mà JSON không hỗ trợ. Chúng ta có thể nhận xét ở bất kỳ đâu trong tài liệu bằng ký tự # đơn giản. Điều này đã được chứng minh là có lợi khi viết các tệp cấu hình nơi một nhà phát triển có thể dễ dàng mô tả cấu hình bằng cách sử dụng các nhận xét. Do đó, định dạng YAML được sử dụng trong nhiều ngăn xếp công nghệ như ElasticSearch và Docker để lưu trữ thông tin cấu hình.

    Khả năng sử dụng các cấu trúc phúc tạp

    Một tính năng khác mà YAML cung cấp là khả năng tham chiếu đến các đối tượng dữ liệu khác. Với tham chiếu này, có thể ghi dữ liệu đệ quy trong tệp YAML.

    Đối với điều này, chúng tôi có thể xác định neo trong tệp YAML bằng cách sử dụng & và tham chiếu đến chúng sau này bằng bí danh, *. Đây là một tính năng rất quan trọng trong YAML mà JSON không cung cấp. Trong JSON, không thể tuần tự hóa các cấu trúc phức tạp với các tham chiếu đối tượng. Nhưng tính năng trên trong YAML giải quyết được vấn đề đó. Nhưng một nhược điểm của điều này là khả năng lặp lại vô hạn trong một số bộ chuyển đổi.

    Vì vậy, từ tất cả những điểm này, chúng ta có thể thấy rằng cả JSON và YAML đều có điểm mạnh và điểm yếu của họ. Một nhà phát triển giỏi phải có thể xác định những điều này và sử dụng đúng định dạng ở đúng vị trí.

    Ví dụ:

    Yaml file:

    Json file:

    Tổng kết:

    Trên đây là 1 số chia sẻ về JSON vs YAML mong có thể giúp đỡ mọi người.

    Author: DuongVT19

  • SỬ DỤNG RETROFIT TRONG FLUTTER

    Trong bài viết này tôi sẽ giới thiệu về retrofit trong Flutter và cách sử dụng retrofit Flutter.

    Retrofit là gì ?

    Trong Android, Retrofit là một HTTP client type-safe cho Android & Java, giúp dễ dàng kết nối đến một dịch vụ REST API trên web bằng cách chuyển đổi API thành Java Interface.

    Theo khái niệm chung thì Retrofit là một thư viện dùng để gọi Rest API bằng cách gửi các dynamic header, parameter, print request và response theo cách bảo mật ( secured way).

    Cách sử dụng Retrofit trong Flutter

    Hãy tạo một project mới để sử dụng retrofit Flutter !

    + Tích hợp thư viện
    Trong file : pubspec.yaml, thêm các thư viện package : retrofit, dio, logger, retrofit_generator, build_runner, json_annotation, json_serializable ; khai báo các thư viện như hình dưới :

    + Tạo các file

    + Tạo package : services, trong package tạo file : post_api_service.dart có nội dụng như bên dưới :

    bạn thấy báo rất nhiều lỗi ?
    Đừng quá lo lắng, chạy dòng lệnh trong mục terminal : flutter pub run build_runner build
    Trong quá trình chạy bạn thấy báo lỗi : Missing “part ‘post_api_service.g.dart’;”. ?

    Thêm khai báo : part ‘post_api_service.g.dart và tiếp tục chạy tiếp dòng lệnh trên : flutter pub run build_runner build
    Khi đó file post_api_service.g.dart sẽ được tạo ra

    Nói thêm về: Chuyển đổi JSON format sang Dart file

    Có hai cách dùng đển chuyển Json format sang dart file. Những source trong mục này , chỉ để dùng diễn giải không sử dụng trong project.

    Cách 1: Manual Serialization

    Đây là các mà chúng ta vẫn hay làm khi sử dụng http thuần với bloc, sử dụng các hàm ví dụ như fromJson, dưới đây là ví dụ :

    Tạo file post.dart , có nội dung như sau :

    Cách 2 – Automatic Serialization

    Trước khi để sử dụng cách này thì bạn cần khai báo các thư viện như json_annotation và json_serializable như ở trên.

    Bạn thấy một số lỗi ?
    Tiếp tuc chạy dòng lệnh trong mục terminal : flutter pub run build_runner build
    Trong quá trình chạy bạn thấy báo lỗi : Missing “part ‘post.g.dart’;”.

    Thêm khai báo đó: part ‘post.g.dart’; và tiếp tục chạy tiếp dòng lệnh trên : flutter pub run build_runner build

    Nhận xét : Phương pháp automatic serialization dùng cho các dự án trung bình và lớn

    Xây dựng UI
    Trong file main.dart, bạn có thể dùng đoạn code bên dưới :

    Dưới đây là kết quả :

    Kết luận : Retrofit là khái niệm ban đầu được dùng cho Android, tuy nhiên khi dùng trên Flutter framework, nó cũng phát huy tác dụng trên iOS. Sử dụng retrofit có rất nhiều ưu việt, nó phù hợp với các dự án trung bình và lớn bởi tính tiện dụng của nó.

    Cảm ơn các bạn đã đọc bài viết về Retrofit Flutter của tôi !

  • Firebase dùng trong Flutter (Phần 2)

    Cloud Firestore

    Cloud Firestore là gì?

    Firestore là một cơ sở dữ liệu đám mây NoSQL linh hoạt, có thể mở rộng để lưu trữ và đồng bộ dữ liệu. Nó giữ cho dữ liệu của bạn được đồng bộ hóa trên các ứng dụng khách thông qua trình nghe thời gian thực và cung cấp hỗ trợ ngoại tuyến để bạn có thể tạo các ứng dụng đáp ứng hoạt động bất kể độ trễ mạng hoặc kết nối Internet.

    Thêm package depedentcy vào ứng dựng

    • Từ root của ứng dụng, chạy đoạn command dưới đây:

    flutter pub add cloud_firestore

    • Sau khi hoàn tất, hãy xây dựng lại ứng dụng Flutter của bạn.

    flutter run

    • Gọi thư viện vào trong Dart code:

    import ‘package:cloud_firestore/cloud_firestore.dart’;

    Khởi tạo:

                    FirebaseFirestore firestore = FirebaseFirestore.instance;

    Document & Query Snapshots

    Khi thực hiện một truy vấn, Firestore trả về một QuerySnapshot hoặc một DocumentSnapshot.

    a. QuerySnapshot

    QuerySnapshot được trả về từ truy vấn bộ sưu tập và cho phép bạn kiểm tra bộ sưu tập, chẳng hạn như có bao nhiêu tài liệu tồn tại bên trong nó, cấp quyền truy cập vào các tài liệu trong bộ sưu tập, xem bất kỳ thay đổi nào kể từ truy vấn cuối cùng và hơn thế nữa.

    b. DocumentSnapshot

    DocumentSnapshot được trả về từ một truy vấn hoặc bằng cách truy cập trực tiếp vào tài liệu. Ngay cả khi không có tài liệu nào tồn tại trong cơ sở dữ liệu, ảnh chụp nhanh sẽ luôn được trả về.

    c. Một số Query.

    Filter: Dựa theo method where() để lọc dữ liệu theo trường.

    Limiting: Để giới hạn dự liệu trả về sử dụng method limit().

    Ordering: Sự dụng method oderby().

    Start & End Cursors: Để bắt đầu và / hoặc kết thúc một truy vấn tại một điểm cụ thể trong một tập hợp, bạn có thể chuyển một giá trị cho các phương thức startAt, endAt, startAfter hoặc endBefore.

    Updating documents:

    Đôi khi bạn có thể muốn cập nhật một tài liệu, thay vì thay thế tất cả dữ liệu. Phương thức đặt ở trên thay thế mọi dữ liệu hiện có trên DocumentReference nhất định. Nếu bạn muốn cập nhật tài liệu thay thế, hãy sử dụng phương pháp cập nhật:

    Removing Data

    Collections & Documents

    Firestore lưu trữ dữ liệu trong ” documents”, được chứa trong ” collections”. Tài liệu cũng có thể chứa các bộ sưu tập lồng nhau. Ví dụ: mỗi người dùng của chúng tôi sẽ có ” documents” của riêng họ được lưu trữ bên trong collection “users”. Phương pháp thu thập cho phép chúng ta tham chiếu một tập hợp trong mã của chúng ta.

    Ví dụ:

    Đọc dữ liệu.

    Để đọc một bộ sưu tập hoặc tài liệu một lần, hãy gọi phương thức Query.get hoặc DocumentReference.get. Trong ví dụ dưới đây, FutureBuilder được sử dụng để giúp quản lý trạng thái của yêu cầu:

    Cloud Storage

    Cloud Storage là gì?

    Cloud Storage được thiết kế để giúp bạn nhanh chóng, dễ dàng lưu trữ và phục vụ nội dung do người dùng tạo, chẳng hạn như ảnh và video.

    Thêm package depedentcy vào ứng dựng

    • Từ root của ứng dụng, chạy đoạn command dưới đây:

    flutter pub add flutter_storage

    • Sau khi hoàn tất, hãy xây dựng lại ứng dụng Flutter của bạn.

    flutter run

    • Gọi thư viện vào trong Dart code:

    import ‘package:cloud_firestore/ firebase_storage.dart;

    Upload file lên

    Upload và lấy ảnh:

    Delete a File

    Tổng kết:

    Trên đây là 1 số chia sẻ về Firebase dùng trong Flutter mong có thể giúp đỡ mọi người.

    Author: DuongVT19

  • 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!

  • Shared Preferences trong Flutter

    [FLUTTER] Shared Preferences trong Flutter

    Để lưu trữ các dữ liệu ở local trong ứng dụng Flutter, ngoài cách lưu bằng sqlite, chúng ta còn thể lưu dữ liệu vào Shared Preferences

    1. Sơ lược về Shared Preferences trong Flutter

    • Dùng để lưu những tập dữ liệu nhỏ dưới dạng key-value
    • Các loại dữ liệu có thể lưu như là int, double, bool, String and List<String>
    • Các dữ liệu được lưu lại trong một file .xml và được lưu vào trong bộ nhớ đệm của máy
    • Các dữ liệu chúng ta có thể dùng để lưu như là các thông số về Settings, token,, …

    2. Cách sử dụng

    • Thêm thư viện vào trong file pubspect.yaml:
    shared_preferences: ^2.0.13

    Vì các hàm xử lý lưu dữ liệu trong shared_preferences đều là các hàm Future, nên chúng ta cần dùng await để gọi:

    • Hàm lưu dữ liệu
    // Obtain shared preferences.
    final prefs = await SharedPreferences.getInstance();
    
    // Save an integer value to 'counter' key. 
    await prefs.setInt('counter', 10);
    // Save an boolean value to 'repeat' key. 
    await prefs.setBool('repeat', true);
    // Save an double value to 'decimal' key. 
    await prefs.setDouble('decimal', 1.5);
    // Save an String value to 'action' key. 
    await prefs.setString('action', 'Start');
    // Save an list of strings to 'items' key. 
    await prefs.setStringList('items', <String>['Earth', 'Moon', 'Sun']);
    • Hàm đọc dữ liệu
    // Try reading data from the 'counter' key. If it doesn't exist, returns null.
    final int? counter = prefs.getInt('counter');
    // Try reading data from the 'repeat' key. If it doesn't exist, returns null.
    final bool? repeat = prefs.getBool('repeat');
    // Try reading data from the 'decimal' key. If it doesn't exist, returns null.
    final double? decimal = prefs.getDouble('decimal');
    // Try reading data from the 'action' key. If it doesn't exist, returns null.
    final String? action = prefs.getString('action');
    // Try reading data from the 'items' key. If it doesn't exist, returns null.
    final List<String>? items = prefs.getStringList('items');
    • Nếu chúng ta muốn xóa bỏ dữ liệu đã được lưu
    // Remove data for the 'counter' key. 
    final success = await prefs.remove('counter');

    Tài liệu tham khảo: shared_preferences | Flutter Package (pub.dev)

    Author: LamNT59

  • Firebase dùng trong Flutter (Phần 1):

    1. Tổng quan.

    Firebase là một nền tảng để phát triển ứng dụng di động và trang web, bao gồm các API đơn giản và mạnh mẽ mà không cần backend hay server.

    Lợi ích của Firebase là gì? Firebase còn giúp các lập trình viên rút ngắn thời gian triển khai và mở rộng quy mô của ứng dụng mà họ đang phát triển. Firebase là dịch vụ cơ sở dữ liệu hoạt động trên nền tảng đám mây – cloud.

    Kèm theo đó là hệ thống máy chủ cực kỳ mạnh mẽ của Google. Chức năng chính là giúp người dùng lập trình ứng dụng bằng cách đơn giản hóa các thao tác với cơ sở dữ liệu.

    Cụ thể là những giao diện lập trình ứng dụng API đơn giản. Mục đích nhằm tăng số lượng người dùng và thu lại nhiều lợi nhuận hơn.

    Đặc biệt, còn là dịch vụ đa năng và bảo mật cực tốt. Firebase hỗ trợ cả hai nền tảng Android và IOS. Và Flutter framework cũng hỗ trợ đa nền tảng đặc biệt là phát triển ứng dựng mobile cho cả Android và IOS từ một source code. Vì vậy, tôi muốn trình bày việc sử dụng Firebase trong Flutter.

    2. Firebase Authentication.

    2.1. Thêm Firebase Authentication vào ứng dựng.

    1. Từ root của ứng dụng, chạy đoạn command dưới đây:

    flutter pub add firebase_auth

    • Sau khi hoàn tất, hãy xây dựng lại ứng dụng Flutter của bạn.

    flutter run

    • Gọi thư viện vào trong Dart code:

    import ‘package:firebase_auth/firebase_auth.dart’;

    2.2. Kiểm tra các trạng thái hiện tại.

    Firebase Auth cung cấp nhiều phương pháp và tiện ích cho phép bạn tích hợp xác thực an toàn vào ứng dụng Flutter mới hoặc hiện có của mình. Trong nhiều trường hợp, bạn sẽ cần biết về trạng thái xác thực của người dùng, chẳng hạn như họ đã đăng nhập hay đã đăng xuất.

    Có 3 phương thức để lắng nghe các trạng thái thay đổi xác thực:

    a. authStateChanges()

    Các sự kiện sẽ kích hoạt khi xảy ra những điều sau:

    • Ngay sau khi người nghe đã được đăng ký.
    • Khi người dùng đã đăng nhập.
    • Khi người dùng hiện tại đã đăng xuất.

    b. idTokenChanges()

    Các sự kiện sẽ kích hoạt khi xảy ra những điều sau:

    • Ngay sau khi người nghe đã được đăng ký.
    • Khi người dùng đã đăng nhập.
    • Khi người dùng hiện tại đã đăng xuất.
    • Khi có sự thay đổi trong mã thông báo của người dùng hiện tại.

    c. userChanges()

    Các sự kiện sẽ kích hoạt khi xảy ra những điều sau:

    • Ngay sau khi người nghe đã được đăng ký.
    • Khi người dùng đã đăng nhập.
    • Khi người dùng hiện tại đã đăng xuất.
    • Khi có sự thay đổi trong mã thông báo của người dùng hiện tại.

    Chú ý: idTokenChanges(), userChanges() & authStateChanges() sẽ không kích hoạt nếu bạn cập nhật Hồ sơ người dùng bằng SDK quản trị Firebase. Bạn sẽ phải buộc tải lại bằng cách sử dụng FirebaseAuth.instance.currentUser.reload() để truy xuất hồ sơ Người dùng mới nhất.

    idTokenChanges(), userChanges() & authStateChanges() cũng sẽ không kích hoạt nếu bạn tắt hoặc xóa Người dùng bằng SDK quản trị Firebase hoặc bảng điều khiển Firebase. Bạn sẽ phải buộc tải lại bằng FirebaseAuth.instance.currentUser.reload(), điều này sẽ gây ra trường hợp người dùng bị vô hiệu hóa hoặc không tìm thấy ngoại lệ mà bạn có thể bắt và xử lý trong code ứng dụng của mình.

    2.3. Quản lý Users trong Firebase.

    a. Tạo tài khoản.

    Sử dụng phương thức createUserWithEmailAndPassword():

    b. Đăng nhập với email.

    Sử dụng phương thức: signInWithEmailAndPassword():

    c. Lấy thông tin user hiện tại.

    d. Lấy thông tin được cung cấp cụ thể của người dùng.

    e. Cập nhật thông tin người dùng.

    Sử dụng phương thức update().

    f. Xóa người dùng hiện tại.

    Sử dụng phương thức delete()

    Trên đây là những kiến thức cơ bản về Firebase ứng dụng trong Flutter, ở phần tiếp theo tôi sẽ trình biết tiếp về ứng Firestore và Sotorage.

  • [Flutter]Tạo Widget với Android Native bằng Platform Views

    [Flutter]Tạo Widget với Android Native bằng Platform Views

    Một ví dụ về tạo 2 widget được triển khai dưới Android Native

    Dưới Native

    Đầu tiên, chúng ta sẽ tạo 2 class FirstWidget.kt và SecondWidget.kt:

    • FirstWidget.kt : Sử dụng file .xml
    
    import android.content.Context
    import android.view.LayoutInflater
    import android.view.View
    import io.flutter.plugin.platform.PlatformView
    
    internal class FirstWidget(context: Context, id: Int, creationParams: Map<String?, Any?>?) : PlatformView {
        private val view: View
    
        override fun getView(): View {
            return view
        }
    
        init {
            view = LayoutInflater.from(context).inflate(R.layout.first_widget, null)
        }
    
        override fun dispose() {
        }
    }
    • first_widget.xml
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context=".MainActivity"
        tools:showIn="@layout/first_widget">
    
        <TextView
            android:id="@+id/first_widget_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="FirstWidget from Android!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    • SecondWidget.kt: 1 cách khác để tạo view
    import android.content.Context
    import android.graphics.Color
    import android.view.View
    import android.widget.TextView
    import io.flutter.plugin.platform.PlatformView
    
    internal class SecondWidget(context: Context, id: Int, creationParams: Map<String?, Any?>?) : PlatformView {
        private val textView = TextView(context)
    
        override fun getView(): View {
            return textView
        }
    
        override fun dispose() {}
    
        init {
            textView.textSize = 20f
            textView.setBackgroundColor(Color.rgb(255, 255, 255))
            textView.text = "Rendered on a native Android view (id: $id)"
        }
    }

    Tạo 2 class FirstWidgetFactory.kt và SecondWidgetFactory.kt:

    • FirstWidgetFactory.kt
    import android.content.Context
    import io.flutter.plugin.common.StandardMessageCodec
    import io.flutter.plugin.platform.PlatformView
    import io.flutter.plugin.platform.PlatformViewFactory
    
    class FirstWidgetFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
        override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
            val creationParams = args as Map<String?, Any?>?
            return FirstWidget(context, viewId, creationParams)
        }
    }
    • SecondWidgetFactory.kt
    import android.content.Context
    import io.flutter.plugin.common.StandardMessageCodec
    import io.flutter.plugin.platform.PlatformView
    import io.flutter.plugin.platform.PlatformViewFactory
    
    class SecondWidgetFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
        override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
            val creationParams = args as Map<String?, Any?>?
            return SecondWidget(context, viewId, creationParams)
        }
    }

    Tại MainActivity.kt:

    import io.flutter.embedding.android.FlutterActivity
    import io.flutter.embedding.engine.FlutterEngine
    import io.flutter.plugins.GeneratedPluginRegistrant
    
    class MainActivity : FlutterActivity() {
    
        override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
            GeneratedPluginRegistrant.registerWith(flutterEngine)
            flutterEngine
                    .platformViewsController
                    .registry
                    .registerViewFactory("com.example.flutter_app_demo1.FirstWidgetPlugin", FirstWidgetFactory())
            flutterEngine
                    .platformViewsController
                    .registry
                    .registerViewFactory("com.example.flutter_app_demo1.SecondWidgetPlugin", SecondWidgetFactory())
        }
    
    }

    Về phần Dart

    Tạo 2 file first_widget.dart và second_widget.dart:

    • first_widget.dart
    import 'package:flutter/foundation.dart';
    import 'package:flutter/gestures.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    import 'package:flutter/services.dart';
    
    
    class FirstWidget extends StatefulWidget {
      const FirstWidget({
        Key? key,
      }) : super(key: key);
    
    
      @override
      State<StatefulWidget> createState() => _FirstWidgetState();
    }
    
    class _FirstWidgetState extends State<FirstWidget> {
       String viewType = 'com.example.flutter_app_demo1.FirstWidgetPlugin';
       Map<String, dynamic> creationParams = <String, dynamic>{};
      @override
      Widget build(BuildContext context) {
        if (defaultTargetPlatform == TargetPlatform.android) {
          return SizedBox(
            width: 200,
            child: PlatformViewLink(
              viewType: viewType,
              surfaceFactory:
                  (BuildContext context, PlatformViewController controller) {
                return AndroidViewSurface(
                  controller: controller as AndroidViewController,
                  gestureRecognizers:  const <Factory<OneSequenceGestureRecognizer>>{},
                  hitTestBehavior: PlatformViewHitTestBehavior.opaque,
                );
              },
              onCreatePlatformView: (PlatformViewCreationParams params) {
                return PlatformViewsService.initSurfaceAndroidView(
                  id: params.id,
                  viewType: viewType,
                  layoutDirection: TextDirection.ltr,
                  creationParams: creationParams,
                  creationParamsCodec: const StandardMessageCodec(),
                  onFocus: () {
                    params.onFocusChanged(true);
                  },
                )
                  ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
                  ..create();
              },
            ),
          );
        }
        return const Text('iOS platform version is not implemented yet.');
      }
    
    }
    • second_widget.dart
    import 'package:flutter/foundation.dart';
    import 'package:flutter/gestures.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    import 'package:flutter/services.dart';
    
    class SecondWidget extends StatefulWidget {
      const SecondWidget({
        Key? key,
      }) : super(key: key);
    
      @override
      State<StatefulWidget> createState() => _SecondWidgetState();
    }
    
    class _SecondWidgetState extends State<SecondWidget> {
      String viewType = "com.example.flutter_app_demo1.SecondWidgetPlugin";
      Map<String, dynamic> creationParams = <String, dynamic>{};
      @override
      Widget build(BuildContext context) {
        if (defaultTargetPlatform == TargetPlatform.android) {
          return PlatformViewLink(
            viewType: viewType,
            surfaceFactory:
                (BuildContext context, PlatformViewController controller) {
              return AndroidViewSurface(
                controller: controller as AndroidViewController,
                gestureRecognizers:  const <Factory<OneSequenceGestureRecognizer>>{},
                hitTestBehavior: PlatformViewHitTestBehavior.opaque,
              );
            },
            onCreatePlatformView: (PlatformViewCreationParams params) {
              return PlatformViewsService.initSurfaceAndroidView(
                id: params.id,
                viewType: viewType,
                layoutDirection: TextDirection.ltr,
                creationParams: creationParams,
                creationParamsCodec: const StandardMessageCodec(),
                onFocus: () {
                  params.onFocusChanged(true);
                },
              )
                ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
                ..create();
            },
          );
        }
        return const Text('iOS platform version is not implemented yet.');
      }
    
    }
    

    Sau đó gọi lại 2 Widget vừa rồi tại main.dart

    @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Flutter PlatformView Example'),
          ),
          body: Column(
            mainAxisSize: MainAxisSize.min,
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: const <Widget>[
              Card(
                child: SizedBox(
                  height: 200,
                  child: FirstWidget(),
                ),
              ),
              Card(
                child: SizedBox(
                  height: 200,
                  child: SecondWidget(),
                ),
              ),
            ],
          ),
        );
      }

    PlatformView

    PlatformView là một tính năng Flutter cần thực hiện để hiển thị Native-UIs toàn diện trên Android View / UIKitView.

    Nếu bạn muốn biết thêm thông tin về PlatformView, hãy xem tài liệu chính thức:

  • [FLUTTER] MỘT SỐ WIDGET HỮU ÍCH TRONG FLUTTER (PHẦN CUỐI)

    [FLUTTER] MỘT SỐ WIDGET HỮU ÍCH TRONG FLUTTER (PHẦN CUỐI)

    Ở phần cuối này mình sẽ giới thiệu cho anh em nốt những widget hay ho mà mình tìm hiểu được.
    Anh em có thể đọc lại 2 phần trước ở đây nhé:

    Phần 1
    Phần 2

    Bắt đầu luôn nào!!!

    1. RichText

    Có lúc nào anh em muốn viết một dòng chữ nhưng các phần trong đó lại có định dạng khác nhau? Ví dụ có thể kể ra là một phần của đoạn văn bản có thể click vào như ảnh dưới đây:

    Anh em có thể nghĩ đến việc sử dụng Row với 2 Text con bên trong, nhưng có vẻ hơi cồng kềnh nhỉ? Và đó chính là lúc RichText phát huy tác dụng của mình. Thay vì viết như này:

            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text("Not a user?"),
                TextButton(
                  onPressed: () {},
                  child: const Text(
                    "Sign up",
                    style: TextStyle(decoration: TextDecoration.underline),
                  ),
                )
              ],
            ),

    Chúng ta có thể viết như này để dễ hiểu hơn (về mặt logic, code thì cũng same nhau à :D):

            RichText(
              text: TextSpan(
                text: "Not a user? ",
                style: const TextStyle(color: Colors.black),
                children: <TextSpan>[
                  TextSpan(
                      text: "Signup",
                      style: const TextStyle(
                          color: Colors.blue, decoration: TextDecoration.underline),
                      recognizer: TapGestureRecognizer()..onTap = () {})
                ],
              ),
            ),

    RichText sẽ nhận vào một text là InlineSpan, ở đây ta sẽ sử dụng TextSpan. Ở trong TextSpan lại có các children là InlineSpan, cho nên ta có thể nối nhiều đoạn text lại với nhau, mỗi đoạn có thể có các style riêng biệt.

    2. SafeArea

    Các thiết bị di động ngày nay đã khác xưa rất nhiều, không chỉ đơn giản là một màn hình hình chữ nhật nữa. Chúng ta có màn hình “tai thỏ”, “nốt ruồi” rồi thậm chí là “giọt nước”. Các thiết kế phức tạp trên khiến người lập trình cũng phải để ý khi tạo ra các ứng dụng mới.

    Và để tránh nội dung của ứng dụng bị che khuất bởi các vị trí “nhạy cảm” kia, ở Flutter chúng ta có SafeArea.

    So sánh khi không sử dụng / sử dụng SafeArea

    Ở ảnh trên ta thấy, khi sử dụng SafeArea, AppBar sẽ không đè lên phần status bar của máy nữa (AppBar màu xanh dương), rất phù hợp khi thiết bị có những “nốt ruồi” hay “tai thỏ”.

    thông thường ta sẽ wrap cả Scaffold ở trong widget SafeArea để đảm bảo không bị mất nội dung.

    3. SelectableText

    Chắc mình sẽ không cần phải nói nhiều về widget này vì cái tên đã thể hiện quá rõ tác dụng của nó rồi. 😀 😀

    Khi anh em muốn cho phép người dùng chọn được đoạn văn bản thì có thể sử dụng widget này.

    SelectableText

    À mà anh em có thể kết hợp widget này với RichText ở bên trên bằng cách sử dụng SelectableText.rich() nhé, thật tiện lợi phải không ^^

    Tổng kết

    Vậy là qua 3 phần mình đã giới thiệu đến anh em một số widget mà mình cảm thấy hữu ích trong quá trình làm việc với Flutter. Do kiến thức còn hạn chế, khả năng viết bài cũng non nớt nên nếu có lỗi sai thì mong anh em hãy comment góp ý để mọi người có thể nâng cao kiến thức của mình nhé. Cảm ơn anh em đã theo dõi 😀 😀