1. Giới thiệu về kiểm thử giao diện và kiểm thử tích hợp

Trong Flutter, phần lớn giao diện ứng dụng được xây dựng từ Widget. Một dòng chữ, nút bấm, ô nhập liệu, danh sách sản phẩm hay toàn bộ màn hình đều có thể được tổ chức thông qua những Widget kết hợp với nhau.

Khi phát triển ứng dụng, việc kiểm tra các hàm xử lý logic bằng Unit Testing là cần thiết. Tuy nhiên, một hàm hoạt động chính xác không đồng nghĩa với việc người dùng có thể sử dụng chức năng đó thành công trên giao diện.

Ví dụ, ứng dụng có một hàm tính tổng tiền hoàn toàn chính xác. Nhưng khi người dùng nhấn nút tăng số lượng trong giỏ hàng, giao diện không cập nhật. Trong trường hợp này, lỗi có thể không nằm ở hàm tính tiền mà nằm ở cách Widget quản lý trạng thái hoặc cách các thành phần giao tiếp với nhau.

Tương tự, màn hình đăng nhập có thể hiển thị đẹp và các ô nhập liệu hoạt động bình thường, nhưng sau khi nhấn nút đăng nhập, ứng dụng không chuyển sang trang chủ. Lỗi có thể nằm ở API, State Management hoặc Navigation.

Vì vậy, ngoài Unit Testing, Flutter còn hỗ trợ Widget Testing và Integration Testing.

Widget Testing là phương pháp kiểm tra giao diện và hành vi của một Widget hoặc một nhóm Widget trong môi trường kiểm thử do Flutter cung cấp.

Integration Testing là phương pháp kiểm tra sự phối hợp giữa nhiều thành phần của ứng dụng, thường thông qua một quy trình sử dụng tương đối hoàn chỉnh trên thiết bị hoặc trình giả lập.

Hai phương pháp này giúp kiểm tra những vấn đề mà Unit Testing đơn thuần khó phát hiện.

2. Bản chất của Widget Testing

Widget Testing tập trung vào câu hỏi: Khi Widget được hiển thị và người dùng tương tác với nó, giao diện có phản ứng đúng theo yêu cầu hay không?

Ví dụ, một Widget hiển thị số lượng sản phẩm và có hai nút tăng, giảm.

Yêu cầu của chức năng:

  • Ban đầu số lượng bằng 1.
  • Nhấn nút cộng thì số lượng tăng lên.
  • Nhấn nút trừ thì số lượng giảm xuống.
  • Không cho phép số lượng nhỏ hơn 1.

Widget Test có thể tự động kiểm tra những hành vi này.

Điểm đặc biệt là Flutter có thể tạo một môi trường kiểm thử Widget mà không cần chạy toàn bộ ứng dụng trên điện thoại thật.

Trong môi trường đó, bài test có thể xây dựng cây Widget, mô phỏng thao tác và kiểm tra những gì được render.

Tuy nhiên, Widget Test không hoàn toàn giống việc chạy ứng dụng trên thiết bị thật. Một số hành vi phụ thuộc vào hệ điều hành, plugin native hoặc phần cứng có thể không được mô phỏng đầy đủ.

Do đó, Widget Testing rất hữu ích nhưng vẫn cần kết hợp với Integration Testing khi muốn kiểm tra những luồng lớn hơn.

3. Widget Testing khác Unit Testing như thế nào?

Unit Testing chủ yếu kiểm tra logic của một đơn vị chương trình.

Ví dụ, hàm:

int increaseQuantity(int quantity) {
  return quantity + 1;
}

Unit Test có thể xác nhận rằng khi truyền 1, hàm trả về 2.

Nhưng bài Unit Test đó không kiểm tra liệu người dùng nhấn nút trên màn hình có thực sự gọi hàm hay không.

Widget Test sẽ kiểm tra toàn bộ hành vi ở phạm vi giao diện: nút có tồn tại, người dùng có thể nhấn nút và văn bản có thay đổi từ 1 thành 2 hay không.

Có thể hiểu đơn giản:

Unit Test kiểm tra logic bên trong. Widget Test kiểm tra cách giao diện sử dụng và thể hiện logic đó.

Một ứng dụng có thể vượt qua tất cả Unit Test nhưng vẫn có lỗi giao diện. Ngược lại, giao diện có thể hiển thị đúng ở một số tình huống nhưng logic bên trong vẫn có lỗi mà Widget Test chưa bao phủ.

4. Các thành phần quan trọng của Widget Testing

4.1. testWidgets()

Trong Flutter, testWidgets() được sử dụng để định nghĩa một bài Widget Test.

Ví dụ:

testWidgets(
  'Kiểm tra màn hình đăng nhập',
  (WidgetTester tester) async {
    // Nội dung kiểm thử
  },
);

Tham số đầu tiên là tên bài test.

Tham số thứ hai là hàm thực hiện kiểm thử, nhận đối tượng WidgetTester.

4.2. WidgetTester

WidgetTester là đối tượng hỗ trợ xây dựng Widget và mô phỏng thao tác trong môi trường kiểm thử.

Thông qua WidgetTester, lập trình viên có thể đưa Widget vào bài test, nhấn nút, nhập văn bản, kéo hoặc cuộn giao diện và chờ các frame được xử lý.

Ví dụ:

await tester.tap(find.text('Đăng nhập'));

Lệnh này mô phỏng thao tác nhấn vào Widget chứa văn bản “Đăng nhập”.

4.3. pumpWidget()

pumpWidget() được sử dụng để đưa một Widget vào môi trường kiểm thử và xử lý frame ban đầu.

Ví dụ:

await tester.pumpWidget(
  const MaterialApp(
    home: Scaffold(
      body: Text('Xin chào Flutter'),
    ),
  ),
);

Sau khi thực hiện, bài test có thể tìm văn bản “Xin chào Flutter”.

MaterialApp và Scaffold thường được sử dụng để cung cấp môi trường giao diện Material phù hợp cho Widget cần kiểm tra.

4.4. Finder

Finder giúp tìm những Widget đang tồn tại trong cây Widget của bài test.

Ví dụ:

find.text('Đăng nhập');

Tìm Widget theo nội dung văn bản.

find.byType(TextField);

Tìm Widget theo kiểu.

find.byKey(const Key('login_button'));

Tìm Widget theo Key.

Finder rất quan trọng vì trước khi mô phỏng thao tác, bài test cần xác định đúng Widget mà người dùng sẽ tương tác.

4.5. Matcher

Matcher là công cụ giúp xác nhận điều kiện mong đợi.

Ví dụ:

expect(
  find.text('Đăng nhập'),
  findsOneWidget,
);

Bài test xác nhận có đúng một Widget hiển thị văn bản “Đăng nhập”.

Ngoài findsOneWidget, Flutter còn có những Matcher như findsNothing, findsWidgets hoặc findsNWidgets().

5. Ví dụ đầy đủ về Widget Testing

Giả sử xây dựng một Widget quản lý số lượng sản phẩm.

Tạo file lib/quantity_counter.dart:

import 'package:flutter/material.dart';

class QuantityCounter extends StatefulWidget {
  const QuantityCounter({super.key});

  @override
  State<QuantityCounter> createState() =>
      _QuantityCounterState();
}

class _QuantityCounterState
    extends State<QuantityCounter> {
  int quantity = 1;

  void increase() {
    setState(() {
      quantity++;
    });
  }

  void decrease() {
    if (quantity > 1) {
      setState(() {
        quantity--;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Text(
          '$quantity',
          key: const Key('quantity_text'),
        ),
        ElevatedButton(
          key: const Key('increase_button'),
          onPressed: increase,
          child: const Text('Tăng'),
        ),
        ElevatedButton(
          key: const Key('decrease_button'),
          onPressed: decrease,
          child: const Text('Giảm'),
        ),
      ],
    );
  }
}

Widget này có trạng thái quantity, ban đầu bằng 1.

Khi người dùng nhấn nút “Tăng”, phương thức increase() gọi setState() để thay đổi giá trị.

Khi nhấn “Giảm”, phương thức decrease() chỉ cho phép giảm nếu số lượng lớn hơn 1.

Tiếp theo, tạo file test/quantity_counter_test.dart:

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/quantity_counter.dart';

void main() {
  testWidgets(
    'Nhấn tăng làm số lượng thay đổi',
    (WidgetTester tester) async {
      await tester.pumpWidget(
        const MaterialApp(
          home: Scaffold(
            body: QuantityCounter(),
          ),
        ),
      );

      expect(find.text('1'), findsOneWidget);

      await tester.tap(
        find.byKey(const Key('increase_button')),
      );

      await tester.pump();

      expect(find.text('2'), findsOneWidget);
    },
  );

  testWidgets(
    'Số lượng không nhỏ hơn 1',
    (WidgetTester tester) async {
      await tester.pumpWidget(
        const MaterialApp(
          home: Scaffold(
            body: QuantityCounter(),
          ),
        ),
      );

      await tester.tap(
        find.byKey(const Key('decrease_button')),
      );

      await tester.pump();

      expect(find.text('1'), findsOneWidget);
    },
  );
}

Trong ví dụ này, my_app là tên package minh họa.

Bài test thứ nhất kiểm tra nút tăng.

Bài test thứ hai kiểm tra giới hạn số lượng tối thiểu.

Nhờ đó, lập trình viên không chỉ kiểm tra giao diện có hiển thị mà còn kiểm tra được quy tắc tương tác.

6. Kiểm thử biểu mẫu bằng Widget Test

Một ứng dụng Flutter thường có những biểu mẫu như đăng nhập, đăng ký, cập nhật thông tin cá nhân hoặc đặt hàng.

Những biểu mẫu này có thể chứa nhiều quy tắc validation.

Ví dụ, màn hình đăng nhập yêu cầu:

  • Email không được để trống.
  • Email phải có định dạng phù hợp.
  • Mật khẩu không được để trống.
  • Khi nhập sai dữ liệu, giao diện hiển thị thông báo.

Widget Testing có thể kiểm tra các hành vi này.

Ví dụ:

await tester.enterText(
  find.byKey(const Key('email_field')),
  '[email protected]',
);

await tester.tap(
  find.byKey(const Key('login_button')),
);

await tester.pump();

Đoạn code trên mô phỏng việc nhập email rồi nhấn đăng nhập.

Sau đó, bài test có thể kiểm tra thông báo validation hoặc trạng thái giao diện tương ứng.

Điểm quan trọng là khi kiểm tra một biểu mẫu, không nên chỉ kiểm tra người dùng có thể nhập văn bản. Cần kiểm tra cả những điều kiện không hợp lệ và phản hồi của giao diện.

7. pump(), pumpAndSettle() và quá trình cập nhật giao diện

Flutter xây dựng và cập nhật giao diện theo cơ chế frame.

Khi một Widget thay đổi trạng thái, giao diện không nhất thiết được cập nhật ngay tại cùng một thời điểm mà bài test vừa gọi phương thức.

Vì vậy, WidgetTester cung cấp những phương thức hỗ trợ xử lý frame.

pump() thực hiện một lần xử lý frame.

Ví dụ:

await tester.tap(find.text('Tăng'));
await tester.pump();

pumpAndSettle() tiếp tục xử lý các frame cho đến khi không còn frame mới được lên lịch hoặc quá thời gian cho phép.

Ví dụ:

await tester.tap(find.text('Tiếp tục'));
await tester.pumpAndSettle();

Phương thức này thường hữu ích khi giao diện có animation hoặc điều hướng.

Tuy nhiên, nếu giao diện có animation chạy liên tục, pumpAndSettle() có thể không hoàn thành.

Vì vậy, lập trình viên cần hiểu hành vi của Widget để lựa chọn cách chờ phù hợp, thay vì sử dụng pumpAndSettle() cho mọi tình huống.

8. Integration Testing là gì?

Integration Testing là phương pháp kiểm tra sự phối hợp giữa nhiều thành phần trong ứng dụng.

Trong một dự án Flutter thực tế, một thao tác của người dùng có thể đi qua nhiều lớp xử lý.

Ví dụ, khi đăng nhập:

Người dùng nhập tài khoản → Widget nhận dữ liệu → Controller hoặc State Management xử lý → Repository gọi API → Backend xác thực → Ứng dụng cập nhật trạng thái → Điều hướng đến trang chủ.

Mỗi thành phần có thể hoạt động đúng khi được kiểm tra riêng, nhưng khi kết hợp lại vẫn có khả năng xảy ra lỗi.

Ví dụ, API trả về dữ liệu thành công nhưng State Management không cập nhật, khiến giao diện vẫn hiển thị loading.

Integration Testing giúp kiểm tra những tình huống liên quan đến nhiều thành phần như vậy.

Trong Flutter, Integration Test thường được chạy trên thiết bị thật, trình giả lập hoặc môi trường chạy ứng dụng được hỗ trợ.

9. Integration Testing và End-to-End Testing

Hai thuật ngữ Integration Testing và End-to-End Testing thường được sử dụng gần nhau nhưng không hoàn toàn đồng nghĩa.

Integration Testing tập trung kiểm tra sự phối hợp giữa các thành phần.

End-to-End Testing, thường viết tắt là E2E Testing, tập trung kiểm tra một quy trình sử dụng từ đầu đến cuối theo góc nhìn người dùng hoặc hệ thống.

Ví dụ, kiểm tra màn hình đăng nhập phối hợp với Authentication Repository là một dạng kiểm thử tích hợp.

Trong khi đó, kiểm tra người dùng mở ứng dụng, đăng nhập, chọn sản phẩm, thêm vào giỏ hàng và hoàn tất đặt hàng là một luồng E2E.

Trong Flutter, package integration_test có thể được sử dụng để xây dựng những bài kiểm thử thuộc cả hai phạm vi, tùy cách thiết kế.

10. Cài đặt Integration Testing

Flutter cung cấp package integration_test.

Để thêm vào dự án:

flutter pub add --dev integration_test --sdk=flutter

Thư mục dự án có thể được tổ chức như sau:

lib/
  main.dart
  screens/
  services/
  repositories/

test/
  unit/
  widget/

integration_test/
  login_flow_test.dart
  checkout_flow_test.dart

Đây là cấu trúc tham khảo, không phải quy định bắt buộc.

Thông thường, Unit Test và Widget Test được đặt trong thư mục test, còn Integration Test được đặt trong integration_test.

11. Ví dụ Integration Test kiểm tra quy trình đăng nhập

Giả sử ứng dụng có:

  • Ô nhập email với Key email_field.
  • Ô nhập mật khẩu với Key password_field.
  • Nút đăng nhập với Key login_button.
  • Sau khi đăng nhập thành công, màn hình hiển thị “Trang chủ”.

Một bài Integration Test có thể được viết như sau:

import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;

void main() {
  IntegrationTestWidgetsFlutterBinding
      .ensureInitialized();

  testWidgets(
    'Người dùng đăng nhập thành công',
    (WidgetTester tester) async {
      app.main();

      await tester.pumpAndSettle();

      await tester.enterText(
        find.byKey(const Key('email_field')),
        '[email protected]',
      );

      await tester.enterText(
        find.byKey(const Key('password_field')),
        'TestPassword123',
      );

      await tester.tap(
        find.byKey(const Key('login_button')),
      );

      await tester.pumpAndSettle();

      expect(
        find.text('Trang chủ'),
        findsOneWidget,
      );
    },
  );
}

Đây là ví dụ minh họa, không phải bài test có thể chạy ngay trên mọi dự án.

Ứng dụng thực tế cần có những Widget tương ứng, tài khoản kiểm thử hợp lệ và môi trường Backend phù hợp.

Nếu quá trình khởi tạo ứng dụng hoặc đăng nhập có những tác vụ bất đồng bộ đặc biệt, bài test cũng cần có cơ chế chờ phù hợp.

Đặc biệt, không nên sử dụng tài khoản thật của khách hàng hoặc kết nối với Backend production chỉ để kiểm thử tự động.

12. Test Environment và Test Data

Một bài Integration Test thường phụ thuộc vào trạng thái của ứng dụng và dữ liệu.

Ví dụ, bài test đặt hàng cần có sản phẩm còn hàng, tài khoản hợp lệ và địa chỉ giao hàng.

Nếu sản phẩm đã bị xóa hoặc hết hàng, bài test có thể thất bại dù code Flutter không thay đổi.

Vì vậy, nhóm phát triển cần quản lý Test Environment và Test Data.

Test Environment là môi trường được chuẩn bị để thực hiện kiểm thử.

Test Data là dữ liệu được tạo hoặc lựa chọn để phục vụ những trường hợp kiểm thử.

Ví dụ, một hệ thống có thể tạo tài khoản kiểm thử, sản phẩm thử nghiệm và đơn hàng mẫu trong Backend staging.

Trước khi chạy test, hệ thống chuẩn bị dữ liệu cần thiết.

Sau khi test kết thúc, hệ thống có thể dọn dẹp hoặc đặt lại trạng thái dữ liệu.

Nhờ đó, kết quả kiểm thử ổn định hơn và ít phụ thuộc vào những thay đổi bên ngoài.

13. Flaky Test là gì?

Flaky Test là bài kiểm thử có kết quả không ổn định: cùng một code nhưng đôi khi thành công, đôi khi thất bại.

Ví dụ, một Integration Test kiểm tra màn hình đăng nhập.

Khi mạng nhanh, bài test thành công.

Khi Backend phản hồi chậm, bài test thất bại vì không chờ đủ thời gian.

Nguyên nhân Flaky Test có thể bao gồm:

  • Phụ thuộc vào kết nối mạng.
  • Dữ liệu kiểm thử thay đổi.
  • Thời gian chờ không phù hợp.
  • Animation chưa hoàn tất.
  • Các bài test ảnh hưởng lẫn nhau.
  • Thiết bị hoặc trình giả lập có hiệu năng không ổn định.

Để hạn chế Flaky Test, nên sử dụng điều kiện chờ dựa trên trạng thái thực tế, chuẩn bị dữ liệu nhất quán và hạn chế phụ thuộc vào dịch vụ bên ngoài không cần thiết.

Không nên giải quyết mọi Flaky Test chỉ bằng cách tăng thời gian chờ, vì điều đó có thể làm bài test chậm hơn mà không xử lý được nguyên nhân.

14. Golden Testing là gì?

Golden Testing là phương pháp kiểm thử giao diện bằng cách so sánh hình ảnh Widget được render với hình ảnh chuẩn đã được lưu trước đó.

Ví dụ, nhóm phát triển có một thẻ sản phẩm với hình ảnh, tên, giá và nút mua hàng.

Sau khi chỉnh sửa code, Golden Test có thể giúp phát hiện sự khác biệt ngoài mong đợi về giao diện.

Ví dụ:

await expectLater(
  find.byType(ProductCard),
  matchesGoldenFile('goldens/product_card.png'),
);

Đây là ví dụ minh họa, yêu cầu dự án phải có Widget ProductCard, file golden chuẩn và môi trường kiểm thử phù hợp.

Golden Test hữu ích trong việc phát hiện thay đổi về hình ảnh, bố cục và một số thuộc tính giao diện.

Tuy nhiên, kết quả có thể phụ thuộc vào font chữ, hệ điều hành và môi trường render.

Vì vậy, nhóm phát triển cần duy trì môi trường tạo và kiểm tra golden ổn định.

15. Test Pyramid và chiến lược kiểm thử

Test Pyramid là một mô hình tư duy giúp tổ chức các cấp độ kiểm thử.

Thông thường, phần đáy gồm nhiều Unit Test vì chúng chạy nhanh và ít phụ thuộc vào môi trường.

Phần giữa gồm những bài kiểm thử thành phần hoặc Widget Test.

Phần trên gồm những bài Integration Test hoặc E2E Test kiểm tra các luồng quan trọng.

Ví dụ, với ứng dụng bán hàng:

Unit Test kiểm tra tính tiền, giảm giá, validation và xử lý dữ liệu.

Widget Test kiểm tra trang đăng nhập, thẻ sản phẩm, giỏ hàng và các thông báo lỗi.

Integration Test kiểm tra đăng nhập, đặt hàng và xem lịch sử đơn hàng.

Không có một tỷ lệ cố định áp dụng cho mọi dự án. Số lượng và phạm vi bài test phải phụ thuộc vào mức độ phức tạp, rủi ro và yêu cầu của hệ thống.

16. Kiểm thử kết hợp với CI/CD

Trong dự án thực tế, các bài kiểm thử có thể được chạy tự động khi lập trình viên cập nhật code.

Ví dụ, GitHub Actions có thể thực hiện:

flutter analyze
flutter test

Lệnh flutter test thông thường chạy những bài test trong thư mục test.

Integration Test có thể cần môi trường thiết bị hoặc trình giả lập riêng và được cấu hình thành một bước khác trong pipeline.

Nhờ CI/CD, nhóm phát triển có thể phát hiện lỗi trước khi hợp nhất code hoặc phát hành ứng dụng.

Tuy nhiên, pipeline chỉ kiểm tra những điều kiện mà nhóm đã thiết lập. Nếu bài test thiếu những tình huống quan trọng, lỗi vẫn có thể xuất hiện sau khi phát hành.

17. Những sai lầm thường gặp

Sai lầm đầu tiên là chỉ kiểm tra Widget có tồn tại mà không kiểm tra hành vi tương tác.

Sai lầm thứ hai là sử dụng Finder không rõ ràng, khiến bài test thao tác nhầm Widget.

Sai lầm thứ ba là phụ thuộc vào dữ liệu production.

Sai lầm thứ tư là viết một Integration Test quá dài, kiểm tra nhiều chức năng không liên quan.

Sai lầm thứ năm là sử dụng thời gian chờ cố định thay vì chờ trạng thái phù hợp.

Sai lầm thứ sáu là không kiểm tra các trường hợp lỗi như đăng nhập thất bại, mất mạng hoặc dữ liệu không hợp lệ.

Sai lầm cuối cùng là cho rằng Widget Test và Integration Test có thể thay thế hoàn toàn Unit Test.

18. Kết luận

Widget Testing và Integration Testing là hai phương pháp kiểm thử quan trọng trong Flutter.

Widget Testing giúp kiểm tra giao diện và phản ứng của Widget trước thao tác người dùng. Những công cụ như testWidgets(), WidgetTester, Finder, pumpWidget() và pumpAndSettle() hỗ trợ thực hiện công việc này.

Integration Testing giúp kiểm tra sự phối hợp giữa các thành phần và những quy trình sử dụng lớn hơn trong ứng dụng.

Khi kết hợp Unit Test, Widget Test và Integration Test, nhóm phát triển có thể kiểm tra phần mềm ở nhiều cấp độ, từ logic nhỏ đến giao diện và luồng nghiệp vụ.

Điều quan trọng là phải xác định mục tiêu của từng bài test, chuẩn bị dữ liệu phù hợp và hạn chế những yếu tố khiến kết quả không ổn định.

19. Tài liệu tham khảo

Bài viết khác

BÀI 5: TÌM HIỂU CHUYÊN SÂU VỀ LANGUAGE SWITCHING TRONG FLUTTER

1. Language Switching là gì? Trong một ứng dụng hỗ trợ nhiều ngôn ngữ, việc có sẵn các bản dịch chưa đủ để tạo ra trải nghiệm đa ngôn ngữ hoàn chỉnh. Ứng dụng còn cần xác định ngôn ngữ nào sẽ được sử dụng và cho phép thay đổi ngôn ngữ khi cần thiết. […]

TÌM HIỂU CHUYÊN SÂU VỀ UTC & LOCAL TIME CONVERSION TRONG FLUTTER

1. UTC & Local Time Conversion là gì? Trong một ứng dụng Flutter, thời gian xuất hiện ở rất nhiều chức năng như ngày tạo tài khoản, thời gian đặt hàng, lịch sử giao dịch, lịch hẹn, thời gian gửi thông báo và thời hạn của chương trình khuyến mãi. Khi ứng dụng chỉ chạy […]

TÌM HIỂU CHUYÊN SÂU VỀ DATE / NUMBER / CURRENCY FORMATTING TRONG FLUTTER

1. Date / Number / Currency Formatting là gì? Trong một ứng dụng Flutter, dữ liệu thường được lưu trữ và xử lý dưới dạng số, ngày giờ hoặc các kiểu dữ liệu có cấu trúc. Tuy nhiên, dữ liệu dùng để tính toán không nhất thiết giống với cách dữ liệu được hiển thị […]

TÌM HIỂU CHUYÊN SÂU VỀ LOCALIZATION (l10n) TRONG FLUTTER

1. Localization (l10n) là gì? Khi một ứng dụng được sử dụng ở nhiều quốc gia, sự khác biệt không chỉ nằm ở ngôn ngữ mà còn ở cách người dùng đọc ngày tháng, số, tiền tệ, đơn vị đo lường và các thông tin trên giao diện. Ví dụ, một ứng dụng bán hàng […]

TÌM HIỂU CHUYÊN SÂU VỀ INTERNATIONALIZATION (i18n) TRONG FLUTTER

1. Internationalization (i18n) là gì? Trong quá trình phát triển ứng dụng, lập trình viên thường xây dựng giao diện bằng một ngôn ngữ ban đầu, chẳng hạn như tiếng Việt. Khi ứng dụng được sử dụng bởi người dùng ở nhiều quốc gia, việc chỉ hỗ trợ một ngôn ngữ có thể khiến người […]

TÌM HIỂU CHUYÊN SÂU VỀ APP RELEASE & DEPLOYMENT TRONG FLUTTER

1. App Release & Deployment là gì? Trong quá trình phát triển ứng dụng Flutter, sau khi hoàn thành các chức năng, lập trình viên cần đưa ứng dụng đến người dùng thực tế. Ví dụ, một nhóm đã xây dựng xong ứng dụng bán hàng với những chức năng như đăng nhập, xem sản […]

Leave a Reply

Your email address will not be published. Required fields are marked *