- Sự khác nhau giữa `var`, `dynamic`, và `final` trong Dart là gì?
var cho phép Dart tự suy luận kiểu dữ liệu một lần lúc khai báo và không thể thay đổi kiểu sau đó. dynamic cho phép kiểu thay đổi trong runtime, bỏ qua kiểm tra kiểu tĩnh. final tạo biến không thể gán lại sau…
- Null safety trong Dart là gì và tại sao nó quan trọng?
Null safety nghĩa là các biến không thể chứa null trừ khi được đánh dấu nullable bằng ?. Điều này giúp phòng ngừa lỗi NullPointerException ở runtime bằng cách phát hiện lỗi ngay lúc compile. Với null safety, lập trình viên phải khai báo rõ…
- Toán tử null assertion (`!`) là gì và khi nào nên dùng?
Toán tử ! bảo Dart coi biến nullable là non-nullable mà không kiểm tra. Ví dụ: String name = nullableName! — điều này khẳng định nullableName không phải null. Chỉ dùng khi bạn chắc chắn 100% biến không null; nếu sai sẽ ném lỗi runtime.…
- Các toán tử null-aware trong Dart là gì? Nêu ba ví dụ.
Toán tử null-aware giúp xử lý giá trị nullable an toàn. ?. truy cập thuộc tính chỉ khi đối tượng khác null: person?.name. ?? cung cấp giá trị mặc định nếu bên trái là null: name ?? "Unknown". ??= chỉ gán nếu biến đang là…
- Giải thích sự khác nhau giữa `List`, `Set` và `Map` trong Dart.
List là collection có thứ tự và cho phép trùng lặp: [1, 2, 2, 3]. Set là collection không có thứ tự với các phần tử duy nhất: {1, 2, 3}. Map lưu trữ cặp key-value: {"name": "John", "age": 30}. Chọn List khi thứ tự…
- `Future` trong Dart là gì và khác `Stream` như thế nào?
Future là một giá trị sẽ có một lần trong tương lai (giống Promise của JS); Stream là chuỗi nhiều giá trị đến dần theo thời gian, như một băng chuyền. Trong Flutter, FutureBuilder dựng UI từ một Future, StreamBuilder dựng UI từ một Stream.…
- Cú pháp `async`/`await` trong Dart hoạt động như thế nào?
async đánh dấu hàm là bất đồng bộ; await tạm dừng thực thi cho đến khi Future hoàn thành. Điều này giúp code bất đồng bộ đọc dễ như code đồng bộ. await chỉ hoạt động trong hàm async.
- Type promotion trong Dart là gì?
Type promotion là khi Dart tự động thu hẹp kiểu của biến dựa trên luồng điều khiển. Nếu bạn có String? và kiểm tra if (name != null), trong khối đó Dart coi name là String non-nullable mà không cần !. Điều này xảy ra…
- Named parameters và positional parameters trong hàm Dart khác nhau như thế nào?
Positional parameters được truyền theo thứ tự: void greet(String name, int age) gọi là greet("John", 30). Named parameters dùng dấu ngoặc nhọn: void greet({String? name, int? age}) gọi là greet(name: "John", age: 30). Named parameters có thể tuỳ chọn; kết hợp từ khoá required để…
- Closure trong Dart là gì?
Closure là một hàm capture (giữ lại) các biến từ phạm vi bao quanh và có thể truy cập chúng ngay cả sau khi hàm ngoài đã kết thúc. Ví dụ: int makeAdder(int x) { return (int y) = x + y; } — hàm…
- Sự khác nhau giữa `extends`, `implements`, và `with` (mixin) trong Dart là gì?
extends kế thừa từ lớp cha, tái sử dụng code: class Dog extends Animal. implements coi lớp như một interface và buộc override tất cả method (không tái sử dụng code): class Dog implements Animal. with thêm hành vi mixin mà không cần kế thừa:…
- Sự khác nhau giữa `StatelessWidget` và `StatefulWidget` là gì?
StatelessWidget chỉ phụ thuộc vào tham số truyền vào (và các InheritedWidget nó đọc); StatefulWidget có thêm một object State sống lâu, giữ dữ liệu thay đổi được trong lúc widget còn trên cây. - StatelessWidget: build() chạy lại khi widget cha rebuild với tham…
- `BuildContext` là gì và tại sao nó quan trọng?
BuildContext là "địa chỉ" của widget trong cây — thực chất nó chính là Element của widget đó. Mọi thao tác "tìm ngược lên cây" đều đi qua context: Theme.of(context), MediaQuery.of(context), Navigator.of(context), ScaffoldMessenger.of(context). Vì of(context) tìm từ vị trí của context đi lên, dùng context…
- Giải thích widget tree, element tree và render tree trong Flutter.
Flutter tách UI thành ba cây với vai trò khác nhau: widget là bản mô tả, element là "phiên bản đang sống" giữ vị trí và state, render object lo layout và vẽ. - Widget tree: object bất biến, rẻ, được tạo lại mỗi lần…
- Mục đích của widget `Key` là gì và khi nào nên dùng?
Key giúp Flutter nhận ra "đây vẫn là widget cũ" khi vị trí của nó trong danh sách con thay đổi. Element so widget mới với cũ bằng Widget.canUpdate: cùng runtimeType và cùng key. Không có key, Flutter chỉ so theo kiểu + vị trí.…
- Sự khác nhau giữa `ValueKey`, `ObjectKey` và `UniqueKey` là gì?
ValueKey xác định widget bằng một giá trị cụ thể; hai widget cùng giá trị được coi là giống nhau. ObjectKey dùng identity (tham chiếu) của đối tượng; mỗi đối tượng duy nhất có key riêng. UniqueKey luôn tạo identity duy nhất, hữu ích khi…
- `const` constructor là gì và tại sao quan trọng cho hiệu năng Flutter?
const constructor tạo object ngay lúc compile, và hai biểu thức const giống hệt nhau trả về cùng một instance (canonicalization). Lợi ích trong Flutter đến từ chính điều đó: khi widget cha rebuild, element so widget mới với widget cũ. Nếu widget con là…
- Hệ thống layout của Flutter hoạt động thế nào: `Column`, `Row`, `Expanded` và `Flexible` dùng ra sao?
Row xếp widget theo chiều ngang; Column xếp theo chiều dọc. Mặc định chúng chiếm không gian tối thiểu. Expanded buộc con lấp đầy không gian còn lại đều nhau. Flexible cho phép con chiếm thêm không gian nhưng có thể nhỏ hơn nếu cần.…
- `setState()` là gì và tại sao gọi nó lại kích hoạt rebuild?
setState() báo cho framework biết state trong State vừa đổi: nó chạy callback đồng bộ, rồi đánh dấu element là dirty (markNeedsBuild). Flutter gọi lại build() ở frame kế tiếp chứ không rebuild ngay, và nhiều lần setState trong cùng frame chỉ gây một lần…
- Vòng đời của `StatefulWidget` là gì? Liệt kê các method quan trọng.
initState() — gọi một lần khi widget được thêm vào (khởi tạo controller, listener). didChangeDependencies() — gọi sau initState và khi dependency thay đổi. build() — gọi thường xuyên, trả về widget tree. didUpdateWidget() — gọi khi parent rebuild với thuộc tính khác. dispose() —…
- Tại sao `dispose()` quan trọng và nên dọn dẹp gì trong đó?
dispose() được gọi khi widget bị xoá vĩnh viễn khỏi cây. Cần dọn dẹp tài nguyên để tránh memory leak: đóng stream, dispose animation controller, huỷ đăng ký change notifier, huỷ timer. Không dispose đúng cách khiến app giữ tham chiếu đến object đã chết,…
- Mục đích của `RepaintBoundary` là gì và khi nào nên dùng?
RepaintBoundary cô lập một subtree để nó vẽ lại độc lập mà không ảnh hưởng toàn bộ cây. Dùng nó xung quanh widget vẽ lại thường xuyên (animation, progress indicator). Khi con của RepaintBoundary thay đổi, chỉ subtree đó mới vẽ lại. Lạm dụng tạo…
- State management là gì và tại sao quan trọng?
State management là cách xử lý dữ liệu thay đổi trong app (input người dùng, kết quả API). Quản lý state kém gây ra bug, memory leak và code khó bảo trì. Quản lý state tốt tách UI khỏi logic, giúp test dễ dàng và…
- Những vấn đề gì khi dùng `setState()` cho app phức tạp?
setState() rebuild toàn bộ cây con của widget đó, gây vấn đề hiệu năng với cây widget lớn. Logic nghiệp vụ trộn lẫn với UI làm code khó test. Không scale được — nhiều cập nhật state làm code khó theo dõi và khó debug.…
- Provider pattern là gì và hoạt động như thế nào?
Provider là thư viện state management dùng ChangeNotifier để thông báo listener khi state thay đổi. Widget lắng nghe qua Consumer hoặc context.watch<T() và chỉ rebuild khi dữ liệu chúng phụ thuộc thay đổi. Provider nhẹ và phù hợp cho app nhỏ đến vừa, nhưng…
- Riverpod là gì và khác Provider như thế nào?
Riverpod (kế thừa tinh thần của Provider, cùng tác giả) hoàn toàn type-safe, không phụ thuộc BuildContext, và dùng functional provider. Riverpod sinh code lúc compile-time giúp phòng ngừa nhiều bug. Riverpod hỗ trợ parameterized provider, test tốt hơn và auto-dispose. Dùng Riverpod cho project…
- Giải thích pattern BLoC và tại sao interviewer hay hỏi về nó.
BLoC (Business Logic Component) tách logic khỏi UI theo một chiều: UI gửi event vào, BLoC xử lý rồi phát ra state mới, UI chỉ vẽ theo state. Bộ package thường dùng là bloc + flutterbloc. Phía UI: BlocProvider cung cấp bloc xuống cây, BlocBuilder…
- So sánh Provider, Riverpod và BLoC: khi nào dùng cái nào?
Ba lựa chọn phổ biến nhất, khác nhau chủ yếu ở mức ràng buộc kiến trúc: Provider Riverpod BLoC / Cubit ------------ Bản chất Wrapper quanh InheritedWidget + ChangeNotifier Viết lại từ Provider, không phụ thuộc BuildContext Luồng event → state trên Stream Lỗi thiếu…
- `ChangeNotifier` là gì và khi nào dùng nó?
ChangeNotifier là class đơn giản thông báo listener khi state thay đổi thông qua notifyListeners(). extend ChangeNotifier để tạo đối tượng observable. Provider theo dõi ChangeNotifier và rebuild khi notifyListeners() được gọi. Nó nhẹ nhưng cần quản lý thông báo thủ công. Cho state đơn…
- `Selector` trong Provider là gì và tại sao dùng nó thay vì `Consumer`?
Consumer theo dõi toàn bộ provider và rebuild bất cứ khi nào nó thay đổi. Selector cho phép chỉ theo dõi một phần cụ thể của state: Selector<UserProvider, String(selector: (, user) = user.name, ...) chỉ rebuild khi name thay đổi, không phải khi age thay…
- `GetIt` là gì và liên quan đến state management như thế nào?
GetIt là service locator cho dependency injection. Bạn đăng ký singleton hoặc factory: getIt.registerSingleton<Repository(Repository()), rồi truy cập ở bất kỳ đâu: getIt<Repository(). Bản thân nó không phải state management mà giúp clean architecture bằng cách quản lý dependency. Thường dùng kết hợp BLoC hoặc Riverpod…
- Làm thế nào để test code state management (Provider/Riverpod/BLoC)?
Provider: Mock ChangeNotifier, dùng ProviderContainer để test. Riverpod: Dùng ProviderContainer, override provider bằng mock. BLoC: Test trực tiếp với bloctest, xác minh event → state transition. Không bao giờ phụ thuộc BuildContext trong code cần test; tách logic nghiệp vụ vào service class. State management…
- Sự khác nhau giữa `Navigator` cũ (v1) và các cách tiếp cận routing hiện đại là gì?
Navigator 1.0 là API imperative: bạn tự push/pop từng màn lên stack. Navigator 2.0 (Router API) là declarative: stack màn hình được suy ra từ state của app, thường là URL. gorouter là package của Flutter team bọc Router API cho dễ dùng, và là…
- GoRouter là gì và giải quyết vấn đề gì?
GoRouter là routing package được Flutter chính thức khuyến nghị. Cung cấp URL-based declarative routing với path/query parameter. Tự động xử lý deep linking, hỗ trợ nested navigation với ShellRoute, thêm redirect logic cho auth guard, và cho phép type-safe route với code generation. Thay…
- Làm thế nào để truyền dữ liệu giữa các màn hình trong Flutter?
Cách 1 (Navigator cũ): Truyền qua constructor: Navigator.push(context, MaterialPageRoute(builder: () = DetailPage(item: item))). Cách 2 (GoRouter): Dùng path parameter: route: "/detail/:id" và truy cập qua GoRouterState. Cách 3 (State Management): Lưu data trong provider/BLoC, truy cập từ bất kỳ màn hình nào. GoRouter với path…
- Deep linking là gì và GoRouter hỗ trợ nó như thế nào?
Deep linking là mở app tại màn hình cụ thể thông qua URL (như myapp://detail/123). Deep link đến từ push notification, web link hoặc system intent. GoRouter hỗ trợ deep linking tự động — định nghĩa route như path: GoRoute(path: '/detail/:id', ...) và GoRouter khớp…
- Làm thế nào để xử lý navigation state với GoRouter?
GoRouter cung cấp GoRouterState với location, parameter, extra data: String id = state.pathParameters['id']!. Với nested navigation (bottom tab có stack độc lập), dùng ShellRoute. Với navigation dựa trên auth, dùng redirect() để kiểm tra state auth trước khi build page: if (!isLoggedIn) return '/login'. Nhờ…
- `ShellRoute` trong GoRouter là gì và khi nào dùng?
ShellRoute bọc nhiều route trong một khung cha dùng chung (như bottom navigation bar). Route bên trong ShellRoute build trong context của parent đó, duy trì navigation stack độc lập cho mỗi tab. Đây là cách đúng để triển khai bottom tab navigation: mỗi tab…
- Làm thế nào để triển khai authentication guard với GoRouter?
Dùng callback redirect để kiểm tra state auth trước khi build route. Ví dụ: nếu user chưa đăng nhập và cố truy cập route được bảo vệ, redirect về /login. Kết hợp với Riverpod để redirect tự động phản ứng khi auth state thay đổi.…
- Những vấn đề gì xảy ra với Navigator 1.0 named routes khi app lớn?
Navigator 1.0 yêu cầu quản lý navigation state và back stack thủ công. Named route hoạt động nhưng xử lý deep linking kém — phải thêm nhiều boilerplate. Navigation phức tạp (nested stack, conditional routing) trở nên rối. Toàn bộ navigation tree là global, khó…
- Clean architecture là gì và tại sao quan trọng cho app lớn?
Clean architecture tách code thành các layer: Presentation (UI, widget), Domain (logic nghiệp vụ, entity, use case), Data (repository, data source). Mỗi layer độc lập và có thể test. Sự tách biệt này cho phép thay đổi UI framework, thay data source, và test logic…
- Repository pattern là gì và hoạt động như thế nào?
Repository là lớp abstraction giữa domain logic và data source. Thay vì UI gọi trực tiếp API, bạn gọi Repository.getUser(id) — method này ẩn đi việc data đến từ network, cache hay database. Interface repository được định nghĩa trong domain layer; implementation trong data layer.…
- Dependency injection là gì và cách triển khai trong Flutter?
Dependency injection là truyền dependency (service, repository) vào class thay vì tạo chúng bên trong. Thay vì class UserBloc { final repo = UserRepository(); }, hãy inject: class UserBloc { UserBloc(this.repo); final UserRepository repo; }. Nhờ vậy dễ test (mock repository) và dễ thay (đổi…
- Giải thích sự khác nhau giữa singleton và factory pattern trong Flutter.
Singleton tạo một instance cho toàn bộ app: getIt.registerSingleton<Repository(Repository()). Dùng cho tài nguyên chia sẻ (database, API client, repository). Factory tạo instance mới mỗi lần: getIt.registerFactory<UserBloc(() = UserBloc(repo)). Dùng cho BLoC (mỗi màn hình cần state độc lập). Dùng sai gây state leak (chia sẻ…
- MVVM architecture trong Flutter là gì và khác MVC như thế nào?
MVC (Model-View-Controller): Controller xử lý input và cập nhật Model, View hiển thị Model. MVVM (Model-View-ViewModel): ViewModel expose data và logic UI cần. UI bind vào ViewModel. ViewModel không biết về UI. MVVM testable hơn MVC vì ViewModel không có dependency vào UI framework. Flutter…
- Use-case layer trong clean architecture là gì và tại sao cần nó?
Use case đóng gói logic nghiệp vụ cho một tính năng: GetUserUseCase, LoginUseCase. Mỗi use case là một class với method call() nhận tham số và trả về kết quả. Use case nằm ở domain layer, không phụ thuộc framework, và rất dễ test. Use…
- Cách tổ chức folder/package trong app Flutter clean architecture như thế nào?
Cấu trúc phổ biến: lib/presentation/ (UI, widget, BLoC/ViewModel), lib/domain/ (entity, repository interface, use case), lib/data/ (API client, local DB, repository implementation), lib/config/ (cấu hình app, constant). Mỗi feature có thể có domain/data/presentation riêng. Điều này giúp code ngăn nắp, dễ tìm và dễ test.
- Sự khác nhau giữa entity và model trong Flutter architecture là gì?
Entity đại diện cho khái niệm domain (đối tượng nghiệp vụ cốt lõi): User(id, name, email). Entity là class Dart thuần, không phụ thuộc framework và dễ test, nằm ở domain layer. Model là biểu diễn API/database với serialization: UserModel.toJson(). Model nằm ở data layer.…
- Isolate trong Dart là gì và khi nào dùng chúng?
- Platform channel là gì và khi nào dùng chúng?
- Làm thế nào để xử lý lỗi và exception trong code Dart bất đồng bộ?
- `Future.wait()` là gì và khi nào dùng?
- Làm thế nào để profile và tối ưu hiệu năng app Flutter?
- Sự khác nhau giữa `ListView.builder` và `ListView` cho danh sách dài là gì?
- Làm thế nào để triển khai CI/CD cho app Flutter?
- Sự khác nhau giữa debug, profile và release build là gì?
- Làm thế nào để test app Flutter? Sự khác nhau giữa unit, widget và integration test là gì?
- `WidgetsFlutterBinding` là gì và khi nào dùng nó?
- Records trong Dart là gì và chúng giúp ích thế nào khi cần trả về nhiều giá trị từ một hàm?
Records là kiểu dữ liệu nhẹ, bất biến, cho phép nhóm nhiều giá trị lại mà không cần tạo class. Bạn có thể dùng dạng positional (theo vị trí) (String, int) hoặc tên trường ({String name, int age}). Để trả về từ hàm: (String, int)…
- Pattern matching trong Dart 3 hoạt động như thế nào? Cho ví dụ thực tế.
Pattern matching cho phép khớp giá trị và destructure cùng lúc. So với chuỗi if/else thì cách viết này declarative hơn hẳn.
- Switch expression trong Dart khác switch statement như thế nào và khi nào nên dùng?
Switch expression trả về giá trị trực tiếp và bắt buộc phải exhaustive (phủ hết mọi trường hợp). Cú pháp dùng =: String grade = score switch { 90 = 'A', 80 = 'B', = 'F' };. Switch statement thì không trả về giá trị,…
- Class modifier trong Dart 3 (`base`, `interface`, `final`) dùng để làm gì?
- Sealed class trong Dart là gì và tại sao nó hữu ích hơn abstract class trong một số trường hợp?
- Impeller là gì và tại sao Flutter chuyển sang dùng nó làm rendering engine mặc định?
Impeller là rendering engine mới của Flutter, thay thế Skia. Mặc định trên iOS từ Flutter 3.10, và trên Android (API 29+) từ Flutter 3.27. Điểm khác biệt chính: Impeller pre-compile toàn bộ shader lúc build, trong khi Skia compile shader lần đầu tiên khi…
- WebAssembly (WASM) cải thiện hiệu năng Flutter Web như thế nào?
Mặc định Flutter Web compile Dart sang JavaScript, phải qua parser và interpreter của trình duyệt. Với WASM, Dart compile ra native bytecode chạy trực tiếp trên VM của browser, bỏ qua parsing overhead. Build bằng flutter build web --wasm. Kết quả: tác vụ CPU-intensive…
- Tree-shaking trong Flutter release build là gì và tại sao nó giảm kích thước app?
Tree-shaking là quá trình tự động loại bỏ code, class, method không được dùng trong quá trình build. Flutter chỉ giữ lại code reachable từ main(). Ví dụ: nếu bạn import một package lớn nhưng chỉ dùng một hàm, chỉ hàm đó được đưa vào…
- Signals trong Flutter là gì và khi nào nên dùng thay vì Riverpod?
Signals là hệ thống reactive nguyên bản lấy cảm hứng từ SolidJS, cho phép cập nhật UI theo kiểu fine-grained—chỉ widget nào phụ thuộc vào signal đó mới rebuild, không phải cả cây widget. Khai báo: final count = signal(0);, dùng trong widget: Watch((context) =…
- Riverpod (2.x trở lên) khác Provider như thế nào về bộ nhớ và an toàn kiểu?
- Khi nào nên chọn BLoC thay vì Riverpod để quản lý state?
- Làm thế nào để implement offline persistence với Riverpod?
- Những điểm khác biệt chính khi build Flutter Web so với các framework web truyền thống là gì?
Flutter Web compile Dart ra JS (hoặc WASM) và render vào Canvas/HTML, không phải DOM thực. Lợi thế: tái sử dụng code với mobile, không cần học JS ecosystem. Nhược điểm: bundle size ~3MB+ (nặng hơn SPA thông thường), SEO khó vì nội dung trong…
- Những điểm cần chú ý khi xây dựng Flutter Desktop (Windows, macOS)?
Desktop yêu cầu xử lý keyboard, mouse, window management—không có trên mobile. Responsive layout quan trọng hơn vì màn hình lớn và DPI khác nhau. Truy cập native API phải qua platform channel (file system, menu bar, notifications). Packaging khác nhau theo platform: MSIX (Windows),…
- Dio khác gì với package `http` mặc định và khi nào nên dùng Dio?
Package http là HTTP client đơn giản, đủ dùng cho request cơ bản. Dio là HTTP client mạnh hơn với: interceptor (middleware để log, thêm token tự động, retry), request/response transformer, quản lý timeout chi tiết, upload file multipart, download file với progress tracking, và…
- Làm thế nào để tích hợp Firebase Authentication vào Flutter và những lỗi phổ biến là gì?
Dùng package firebaseauth: await FirebaseAuth.instance.signInWithEmailAndPassword(email: email, password: password). Lắng nghe trạng thái auth bằng authStateChanges() stream—đây là cách đúng để biết user đã đăng nhập chưa. Lưu ý phổ biến: (1) không lắng nghe authStateChanges() mà kiểm tra currentUser ngay lập tức—có thể null khi…
- Shorebird code push là gì và khi nào nên dùng thay vì update thông qua App Store?
- `InheritedWidget` là gì và nó truyền state xuống cây widget như thế nào? (nền tảng của Provider)
InheritedWidget là widget đặc biệt cho phép các widget con đọc dữ liệu từ tổ tiên mà không phải truyền tay qua từng cấp constructor. Provider, Theme, MediaQuery đều được dựng trên cơ chế này. Hình dung nó như một "trạm phát" đặt cao trên…
- Phân biệt `StreamBuilder` và `FutureBuilder`. Khi nào dùng cái nào?
Cả hai là widget "tự rebuild theo dữ liệu bất đồng bộ", khác nhau ở nguồn dữ liệu phát ra bao nhiêu giá trị: - FutureBuilder lắng nghe một Future — nguồn phát đúng một kết quả rồi kết thúc. Hợp với thao tác một-lần:…
- `Hero` widget là gì và shared-element animation hoạt động thế nào khi chuyển màn hình?
Hero tạo hiệu ứng một phần tử chung "bay" liền mạch giữa hai màn hình khi điều hướng — ví dụ ảnh thumbnail nhỏ ở danh sách phóng to thành ảnh lớn ở trang chi tiết. Người dùng cảm nhận đó là cùng một đối…
- `MediaQuery` là gì và dùng nó để làm layout responsive/adaptive ra sao?
MediaQuery cung cấp thông tin về môi trường hiển thị hiện tại: kích thước màn hình (size), hướng xoay (orientation), tỉ lệ pixel (devicePixelRatio), padding an toàn (notch, thanh trạng thái qua padding/viewPadding), độ phóng chữ (textScaler). Đây là nguồn dữ liệu để layout tự…
- Phân biệt implicit và explicit animation trong Flutter (`AnimationController` + `Tween`).
- `GestureDetector` là gì và "gesture arena" giải quyết xung đột cử chỉ như thế nào?