Tự học Flutter 2023

New Flutter cơ bản

Làm việc với Assets trong Flutter

Một ứng dụng thường sẽ bao gồm cả mã code và assets (còn được gọi là resources). Các dạng nội dung assets phổ biến thường thấy là: hình ảnh (jpg, png,...), tệp json, audio, icon,... Trong đa số các trường hợp ứng dụng của bạn sẽ đụng đến assets. Bài viết này sẽ hướng dẫn các bạn cách để làm việc với chúng.
  • Assets là gì?
  • Thêm assets vào ứng dụng Flutter
  • Load và sử dụng assets trong ứng dụng Flutter
  • Asset là gì?

    Mỗi asset là một file được đóng gói đi cùng với ứng dụng Flutter, file này có thể truy cập được trong thời gian chạy.

    Nếu như bạn chưa hình dung ra được, thì nói cách khác là như thế này: trong khi viết ứng dụng, sẽ có lúc bạn cần sử dụng các file hình ảnh, âm thanh... một cách cục bộ - tức là có sẵn trong app mình luôn chứ không phải tải từ server về. Bằng cách nào đó bạn sẽ đưa những file này vào dự án và đóng gói vào trong ứng dụng để lúc cần thì load lên xài chứ không phải gọi lên server để tải về (Flutter cung cấp cho chúng ta cách để làm điều này, sẽ tìm hiểu ở những đoạn tiếp theo). Những file này chính là asset. Các định dạng asset tiêu biểu mà flutter hỗ trợ bao gồm: JSON, configuration files, icons, and images (JPEG, WebP, GIF, animated WebP/GIF, PNG, BMP, and WBMP)...

    Thêm assets vào ứng dụng Flutter

    1. Chuẩn bị assets

    Để thêm asset vào ứng dụng thì tất nhiên, việc đầu tiên là phải chuẩn bị asset trước. Tức là chuẩn bị tất cả các file mà bạn cần đóng gói đi cùng với ứng dụng. Lưu ý là bạn nên cân nhắc giữa việc sử dụng asset và việc lưu nó trên server, bởi nếu bạn đóng gói đi cùng ứng dụng thì sẽ tiện hơn truy cập offline, tuy nhiên kích thước ứng dụng sẽ tăng lên tùy theo kích thước asset của bạn.

    Để thuận tiện cho bạn tưởng tượng, giả sử chúng ta có 2 files cần thêm vào ứng dụng đó là: photo.pngtitle.txt. Chúng ta sẽ cần phải chuẩn bị hai file này trước.

    2. Tạo thư mục chứa

    Sau khi đã chuẩn bị xong asset, bạn sẽ cần tạo một thư mục để chứa những assets này bên trong thư mục dự án. Thường chúng ta sẽ tạo ra một thư mục ở thư mục gốc của dự án.

    Thư mục này bạn có thể đặt tên tùy ý. Đa số các bài viết khác trên mạng đều bảo bạn đặt tên nó là assets, điều này không sai tuy nhiên bạn nên lưu ý là tên gì cũng được miễn là hợp lý. resource cũng là một cái tên hợp lý, mình sẽ dùng cái tên này.

    Sau khi đã có thư mục chứa, bước tiếp theo là copy tất cả những file asset đã chuẩn bị vào đây. Ở đây, chúng ta sẽ copy hai file photo.pngtitle.txt đã chuẩn bị vào thư mục này. Sau khi hoàn thành bước này, bước tiếp theo chúng ta sẽ tiến hành khai báo.

    3. Khai báo trong file pubspec.yalm

    Để những file assets của chúng ta được nhận biết và đóng gói trong ứng dụng, chúng ta cần phải khai báo những file đó trong file pubspec.yalm.

    Bạn có thể tìm thấy file pubspec.yalm này ở thư mục gốc của ứng dụng. Mở nó lên và tìm đến dòng "To add assets to your application, add an assets section, like this".

    Ở đây đã có một đoạn mẫu khai báo asset rồi, việc bạn cần làm là uncomment nó (xóa dấu # đi) và chỉnh sửa lại. Trong ví dụ của chúng ta, chúng ta sẽ sửa lại như sau:

    
      # To add assets to your application, add an assets section, like this:
      assets:
        - resources/photo.png
        - resources/title.txt
    

    Một điểm bạn cần CHÚ Ý ở đây đó là những dòng thụt lề. Cái dòng assets: phải cách lề 2 khoảng trắng, và những đường dẫn khai báo của chúng ta sẽ phải cách lề 4 khoảng trắng. Cái này quan trọng, nếu bạn gõ sai thì khi load nó sẽ không tìm thấy file.

    Ở đây, trong phần assets chúng ta sẽ liệt đường dẫn tới các file mà chúng ta muốn đóng gói và sử dụng. Nhìn đoạn code ở trên, do chúng ta có 2 files cần thêm vào nên chúng ta sẽ liệt kê 2 gạch đầu dòng đường dẫn đến 2 files đó. Thứ tự không quan trọng nhé, tức là liệt kê file text.txt trước hay file photo.png trước đều được.

    Giả sử bạn có rất là nhiều files asset, thay vì liệt kê từng file, bạn có thể đặt tất cả chúng trong một thư mục và chỉ liệt kê đường dẫn đến thư mục đó thôi (kèm dấu / ở cuối đường dẫn). Như ví dụ trên bạn có thể sửa thành:

    
      # To add assets to your application, add an assets section, like this:
      assets:
        - resources/
    

    Lưu ý: khi bạn khai báo đường dẫn đến thư mục thì chỉ những file nằm ở thư mục đó mới được nhận biết. Nếu bên trong có thư mục con, thì những asset ở trong thư mục con đó sẽ không được nhận biết và khi bạn load lên sử dụng sẽ gặp lỗi asset not found

    Assets variant

    Đến bước này, là bạn đã hoàn thành việc thêm asset vào ứng dụng của mình. Tuy nhiên trước khi đi qua phần tiếp theo thì mình muốn nói sơ qua một chút về asset variant.

    Asset variant là gì?

    Variant nghĩa là biến thể, vậy asset variants là những biến thể của assets :D. Ví dụ cho dễ hiểu nè: Giả sử trong ứng dụng của bạn có màn hình nào đó hiển thị một bức ảnh, và bạn muốn rằng khi người dùng (điện thoại của người dùng) ở 2 chế độ tối (dark mode)/sáng (light mode) thì sẽ hiển thị hai hình tương ứng khác nhau. (giả sử ở dark mode thì load hình có màu tối để không gây chói mắt, và ở chế độ light thì load hình có màu sáng hơn)

    Để làm điều này bạn có 2 cách, cách thứ nhất đó là thêm 2 hình khác nhau, một hình cho chế độ tối, một hình cho chế độ sáng. Và trong ứng dụng bạn sẽ kiểm tra xem nếu dark mode đang bật thì load hình cho chế độ tối và ngược lại thì load hình cho chế độ sáng.

    Cách thứ hai đó là sử dụng asset variants. Flutter hỗ trợ assets variant bằng cách đặt những file assets (trùng tên) trong những thư mục khác nhau đồng cấp. Nhìn hình bên dưới cho dễ hiểu nhé:

    
      |--assets
          |--cover.png
          |--dark
              |--cover.png
          |--light
              |--cover.png
    

    Ở đây bạn có một file tên là cover.png ở ngoài cùng, sau đó có 2 thư mục là dark và light, trong mỗi thư mục này lại có file cover.png. Như vậy chúng ta đã tạo ra 2 biến thể của cover.png đó là dark và light, còn file cover.png được coi là asset chính. Khi bạn ở chế độ tối và bạn load cover.png nó sẽ sử dụng file trong thư mục dark, và ngược lại nó sẽ load file cover.png trong thư mục light.

    LƯU Ý TO BỰ CHẢNG: ví dụ chúng ta vừa xem xét ở trên (dark/light mode) chỉ là ví dụ. Hiện tại Flutter chưa hỗ trợ kiểu này đâu, bạn sẽ phải kiểm tra trong code và load hình tương ứng. Ví dụ: dark mode thì load cover-dark.png, và light mode thì load cover-light.png.

    Ví dụ trên dùng để cho bạn dễ hình dung thôi. Hiện tại flutter chỉ hỗ trợ asset variant cho việc chọn hình ảnh phù hợp với độ phân giải. Và họ cũng hứa rằng sẽ sớm hỗ trợ các loại biến thể khác sớm (bao gồm cả dark/light mode). Tuy nhiên, hứa thì lâu rồi nhưng tới giờ vẫn chưa làm.

    Về việc hỗ trợ variant cho việc chọn hình ảnh phù hợp độ phân giải, bạn hiểu nôm na là chúng ta sẽ có nhiều thư mục khác nhau, mỗi thư mục là một biến thể dành cho những thiết bị có độ phân giải khác nhau. Và khi chúng ta load asset, Flutter sẽ tự động chọn biến thể phù hợp cho thiết bị đang dùng (thiết bị có độ phân giải cao thì dùng biến thể dành cho độ phân giải cao, thiết bị có độ phân giải thấp thì sử dụng biến thể cho màn hình có độ phân giải thấp).

    
    .../my_icon.png (mdpi baseline)
    .../1.5x/my_icon.png (hdpi)
    .../2.0x/my_icon.png (xhdpi)
    .../3.0x/my_icon.png (xxhdpi)
    .../4.0x/my_icon.png (xxxhdpi)
    

    Tên của những thư mục naỳ đã được quy định rồi nhé, phải đúng tên thì flutter mới biết mà load cho đúng biến thể. Bạn có thể tham khảo thêm tại đây: Declaring resolution-aware image assets

    Load và sử dụng assets trong ứng dụng Flutter

    Load và sử dụng image assets

    Để load và hiển thị image asset, chúng ta đơn giản sử dụng Image.asset("đường dẫn đến asset"). Bên dưới là ví dụ để hiển thị cái file photo.png của chúng ta.

    Image.asset("resources/photo.png");

    Image.asset trả về một Image widget, nên là bạn có thể đặt dòng lệnh trên trong widget tree. Để tùy chỉnh hình ảnh (cao, rộng, màu sắc,...) bạn có thể tìm hiểu thêm các thuộc tính của Image widget tại đây.

    Load và sử dụng text assets

    Đối với việc load và sử dụng text asset, chúng ta có thể sử dụng rootBundle hoặc DefaultAssetBundle.

    Load và sử dụng text assets với DefaultAssetBundle

    Cú pháp như sau: DefaultAssetBundle.of(context).loadString("đường dẫn đến asset");

    Một điểm cần chú ý ở đây là lệnh trên trả về một Future, nói cách khác nó là một hàm bất đồng bộ, chúng ta không biết khi nào thì nó load xong. Vì vậy chúng ta có thể sử dụng FutureBuilder để hiển thị. Đoạn code bên dưới sẽ tải và hiển thị text asset trong widget tree.

    
    FutureBuilder(
      future: DefaultAssetBundle.of(context)
      .loadString("resources/title.txt"),
      initialData: "",
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (snapshot.hasData) {
          return Text(snapshot.data);
        }
        return const SizedBox();
      },
    )
                          

    Trong đoạn code trên, bởi vì việc load asset sẽ tồn tại khả năng lỗi (asset not found chẳng hạn) nên chúng ta cần handle trường hợp lỗi. Nếu snapshot.hasData có nghĩa là tải asset thành công, chúng ta sẽ hiển thị dòng text lên với Text widget. Còn ngược lại nếu có lỗi chúng ta sẽ không hiển thị gì cả (ở đây dùng SizedBox).

    Load và sử dụng text assets với rootBundle

    Để load và sử dụng text assets với rootBundle, cú pháp như sau: rootBundle.loadString("đường dẫn đến asset"). Bên dưới là đoạn code mẫu.

    import 'package:flutter/services.dart' show rootBundle;
    
    Future loadAsset() async {
      return await rootBundle.loadString("resources/title.txt");
    }

    Lưu ý rằng hàm này cũng trả về một Future nhé. Bạn có thể gọi hàm loadAsset ở trên ở bất kỳ nơi nào bạn cần.

    Sử dụng rootBundle hay DefaultAssetBundle?

    Sử dụng DefaultAssetBundle được khuyến khích bởi Flutter. Tuy nhiên cách này yêu cầu một context, nên là tùy ngữ cảnh mà bạn có thể chọn DefaultAssetBundle hoặc rootBundle.

    Load và sử dụng audio, pdf assets

    Việc load và sử dụng audio, pdf hoặc bất kỳ loại asset nào khác cũng khá là tương tự. Tùy thuộc vào package (lib) bạn sử dụng mà lệnh có thể khác nhau chút ít. Bên dưới là ví dụ để load audio và pdf.

    player!.setAsset('resources/music.mp3');
    SfPdfViewer.asset('resources/title.pdf');