Lập trình di động với Flutter là môn học xây dựng ứng dụng đa nền tảng Android và iOS bằng Flutter và ngôn ngữ Dart. Ngân hàng câu hỏi trắc nghiệm PRM393 có giải thích bao quát trọn bộ chương trình: cài đặt Flutter SDK, cấu trúc project, hot reload và DevTools, nền tảng Dart (kiểu dữ liệu, null safety, class, collection, Map, generics, mixin, package và import), lập trình bất đồng bộ với Future, async/await, Stream, StreamController và StreamBuilder, dựng giao diện bằng widget tree, Scaffold, ListView, ThemeData, bố cục Row/Column, GridView, Stack, LayoutBuilder, MediaQuery và responsive UI, điều hướng với Navigator 1.0/2.0, named route, deep link và guarded route, form với TextEditingController và validation, gọi REST API bằng http, parse JSON, FutureBuilder, phân trang và caching, lưu trữ cục bộ bằng SharedPreferences, SQLite (sqflite) và Hive, xác thực bằng token và refresh token, quản lý state với setState, Provider, BLoC và Cubit, animation với Tween cùng AnimatedSwitcher, tối ưu hiệu năng bằng RepaintBoundary và thu hẹp phạm vi rebuild, kiểm thử unit/widget/integration, quản lý mã nguồn bằng Git và build - release ứng dụng. Tài liệu ôn thi FE dành cho sinh viên FPT University.
StreamController.broadcast() cho phép nhiều người dùng có thể đăng ký nhận dữ liệu từ cùng một luồng dữ liệu. Điều này có nghĩa là nhiều widget hoặc ứng dụng có thể nhận dữ liệu từ cùng một nguồn và cập nhật trạng thái của mình dựa trên dữ liệu đó. Điều này giúp tạo ra các ứng dụng phức tạp hơn với nhiều tính năng và chức năng khác nhau. Ví dụ, bạn có thể có một ứng dụng có nhiều trang khác nhau, và mỗi trang có thể nhận dữ liệu từ cùng một nguồn và cập nhật trạng thái của mình dựa trên dữ liệu đó.
B`microtask` uses the microtask queue; `Future((){})` usually schedules on the event queue
CNever run
DBoth go to the GPU
Lời giải
Khi so sánh `Future(() => print(1))` và `Future.microtask(() => print(2))`, chúng ta cần hiểu về các loại hàng đợi trong Dart. Hàng đợi microtask được sử dụng để thực hiện các tác vụ nhỏ, chẳng hạn như xử lý sự kiện, trong khi hàng đợi sự kiện được sử dụng để thực hiện các tác vụ lớn hơn, chẳng hạn như I/O hoặc giao tiếp với người dùng. Khi tạo một Future với hàm không có tham số, nó thường được sắp xếp vào hàng đợi sự kiện. Ngược lại, khi sử dụng `Future.microtask`, Future sẽ được sắp xếp vào hàng đợi microtask. Điều này có nghĩa là `Future.microtask(() => print(2))` sẽ được thực hiện trước `Future(() => print(1))` vì hàng đợi microtask có ưu tiên cao hơn. Vì vậy, câu trả lời đúng là `microtask` sử dụng hàng đợi microtask, trong khi `Future((){})` thường sắp xếp vào hàng đợi sự kiện.
Một Future đại diện cho một giá trị bất đồng bộ duy nhất, trong khi một Stream cung cấp một chuỗi sự kiện bất đồng bộ. Future thường được sử dụng để chờ đợi một kết quả cuối cùng, trong khi Stream được sử dụng để xử lý một dòng dữ liệu liên tục. Ví dụ, bạn có thể sử dụng Future để chờ đợi một kết quả từ một yêu cầu HTTP, trong khi Stream có thể được sử dụng để xử lý một dòng dữ liệu từ một thiết bị cảm biến. Tóm lại, Future đại diện cho một giá trị bất đồng bộ duy nhất, trong khi Stream đại diện cho một chuỗi sự kiện bất đồng bộ.
Trong quá trình viết unit tests, AAA là một mô hình phổ biến được sử dụng để tổ chức các bước kiểm tra. AAA là viết tắt của Arrange, Act và Assert. Arrange là bước chuẩn bị dữ liệu và môi trường cần thiết cho việc kiểm tra. Act là bước thực hiện hành động hoặc phương thức cần được kiểm tra. Assert là bước kiểm tra kết quả của hành động hoặc phương thức đã được thực hiện. Mô hình AAA giúp các nhà phát triển viết unit tests rõ ràng, dễ hiểu và hiệu quả hơn.
Sau khi sử dụng `await` trong async form submit, trạng thái `State` nên làm gì? Trạng thái `State` cần phải kiểm tra xem component có được mount hay chưa trước khi thực hiện cập nhật trạng thái. Điều này rất quan trọng vì nếu component chưa được mount, việc cập nhật trạng thái sẽ không được thực hiện. Điều này có thể xảy ra khi component đang trong quá trình render hoặc đang được hủy bỏ. Vì vậy, việc kiểm tra `mounted` trước khi cập nhật trạng thái là một bước quan trọng để đảm bảo rằng trạng thái được cập nhật một cách chính xác và an toàn.
CDrain all microtasks before taking the next event from the event queue
DOnly one queue exists
Lời giải
Sau khi chạy mã đồng bộ, trình duyệt sẽ thực hiện các tác vụ microtask và các sự kiện theo một thứ tự nhất định. Trong đó, trình duyệt sẽ ưu tiên chạy hết các tác vụ microtask trước khi xử lý các sự kiện. Điều này có nghĩa là trình duyệt sẽ không xử lý các sự kiện cho đến khi tất cả các tác vụ microtask đã được hoàn thành. Sau đó, trình duyệt mới sẽ lấy các sự kiện tiếp theo từ hàng đợi sự kiện để xử lý. Vì vậy, câu trả lời đúng là "Drain all microtasks before taking the next event from the event queue".
Sự xác thực bất đồng bộ (như kiểm tra khả dụng địa chỉ email) có thể được thực hiện sau khi xác thực địa phương của biểu mẫu vượt qua. Khi người dùng nhập thông tin vào biểu mẫu, hệ thống sẽ thực hiện xác thực địa phương để đảm bảo rằng dữ liệu nhập vào hợp lệ. Nếu dữ liệu hợp lệ, hệ thống sẽ thực hiện xác thực bất đồng bộ để kiểm tra tính khả dụng của dữ liệu, chẳng hạn như kiểm tra xem địa chỉ email đã được sử dụng hay chưa. Điều này giúp đảm bảo rằng dữ liệu nhập vào là chính xác và hợp lệ.
CSafe (light async); you can cache the instance to optimize
DNot async
Lời giải
Khi gọi `SharedPreferences.getInstance()` nhiều lần, có thể gây ra sự lãng phí tài nguyên và ảnh hưởng đến hiệu suất ứng dụng. Tuy nhiên, trong thực tế, Android đã được tối ưu hóa để xử lý trường hợp này một cách an toàn. Khi bạn gọi `SharedPreferences.getInstance()` nhiều lần, Android sẽ tự động trả về cùng một đối tượng `SharedPreferences` đã được tạo trước đó. Điều này được thực hiện thông qua cơ chế caching, giúp tối ưu hóa hiệu suất ứng dụng. Vì vậy, bạn có thể gọi `SharedPreferences.getInstance()` nhiều lần mà không cần lo lắng về hiệu suất hoặc sự ổn định của ứng dụng.
Dart sử dụng async programming thông qua Future & async/await. Future là một đối tượng đại diện cho một giá trị có thể chưa được tính toán, trong khi async/await là một cách viết code đơn giản hơn để xử lý các Future. Async/await cho phép bạn viết code giống như đang chạy tuần tự, nhưng thực tế nó vẫn đang chạy song song. Điều này giúp code trở nên dễ đọc và dễ viết hơn. Ví dụ, bạn có thể sử dụng async/await để gọi một API và chờ đợi kết quả trước khi tiếp tục chạy code tiếp theo.
Dart's Event Loop hoạt động theo nguyên tắc ưu tiên xử lý các microtask trước các callback liên quan đến I/O hoặc timer. Điều này có nghĩa là bất kỳ tác vụ nào được đánh dấu là microtask sẽ được thực hiện trước khi các tác vụ khác như đọc/ghi file, xử lý mạng hoặc các tác vụ liên quan đến thời gian. Điều này giúp đảm bảo rằng các tác vụ quan trọng được xử lý một cách kịp thời và hiệu quả. Ví dụ, khi bạn thực hiện một yêu cầu HTTP, tác vụ đọc dữ liệu từ mạng sẽ được thực hiện sau khi tác vụ xử lý dữ liệu đã được đánh dấu là microtask.
Khi sử dụng Factory constructor, chúng ta thường muốn tạo ra một lớp có khả năng tạo ra các đối tượng mới mà không cần phải tạo ra một lớp mới. Điều này đặc biệt hữu ích khi chúng ta muốn tạo ra các đối tượng có cùng thuộc tính và hành vi nhưng có các giá trị khác nhau. Ví dụ, chúng ta có thể sử dụng Factory constructor để tạo ra các đối tượng người dùng với các thông tin khác nhau như tên, tuổi, địa chỉ v.v. Ngoài ra, Factory constructor cũng giúp chúng ta giảm thiểu việc tạo ra các đối tượng không cần thiết và tăng hiệu suất của chương trình. Tuy nhiên, Factory constructor không phải lúc nào cũng được sử dụng, mà chỉ được sử dụng khi chúng ta cần tạo ra các đối tượng mới mà không cần phải tạo ra một lớp mới.
CAdd the keyword async after the function signature
DUse await in front of the function name
Lời giải
Để đánh dấu một hàm là bất đồng bộ trong Dart, bạn cần thêm từ khóa async sau phần khai báo hàm. Điều này cho phép hàm thực hiện các tác vụ khác nhau trong thời gian thực thi, giúp tăng hiệu suất và khả năng xử lý của chương trình. Ví dụ, bạn có thể khai báo một hàm async như sau:
Lời giải
async void main(){...}
hoặc
Lời giải
async Function(){...}
. Khi sử dụng hàm async, bạn cần sử dụng từ khóa await để chờ đợi kết quả trả về từ hàm đó. Điều này giúp đảm bảo rằng chương trình thực thi các tác vụ một cách tuần tự và tránh được các lỗi do thực thi không đồng bộ.
A`await push` returns a `Future`; child calls `Navigator.pop(context, value)`
BOnly global `setState`
CCannot return data
DWeb only
Lời giải
Khi bạn muốn nhận kết quả từ màn hình con sau khi sử dụng `Navigator.push`, bạn cần sử dụng cách thức sau. Đầu tiên, bạn cần sử dụng `await push` để chờ đợi màn hình con được hiển thị. Sau đó, màn hình con sẽ gọi hàm `Navigator.pop(context, value)` để trả về kết quả. Trong hàm này, `context` là tham số cần thiết để xác định màn hình cần được quay lại, còn `value` là kết quả mà màn hình con muốn trả về. Khi màn hình cha nhận được kết quả này, bạn có thể sử dụng nó để cập nhật trạng thái ứng dụng.
ABroadcast allows many listeners; single-subscription is usually one sequential listener
BSingle-subscription allows more listeners
CNo difference
DBroadcast cannot be used with `StreamBuilder`
Lời giải
Một luồng phát sóng (broadcast) khác với một luồng đăng ký đơn (single-subscription) ở điểm số lượng người nghe (listener). Luồng phát sóng cho phép nhiều người nghe cùng lúc, trong khi luồng đăng ký đơn chỉ cho phép một người nghe duy nhất. Điều này có nghĩa là khi bạn sử dụng luồng phát sóng, nhiều phần tử trong ứng dụng của bạn có thể lắng nghe và phản ứng với các sự kiện được phát ra từ luồng. Ngược lại, khi bạn sử dụng luồng đăng ký đơn, chỉ có một phần tử duy nhất có thể lắng nghe và phản ứng với các sự kiện được phát ra từ luồng. Điều này giúp giảm thiểu sự phức tạp và tăng cường hiệu suất của ứng dụng.
Câu hỏi này yêu cầu bạn xác định các loại dữ liệu cơ bản trong Dart được sử dụng để biểu diễn số nguyên và số thập phân. Các loại dữ liệu này được sử dụng để lưu trữ và xử lý các giá trị số trong chương trình. Trong Dart, các loại dữ liệu cơ bản được sử dụng để biểu diễn số nguyên là int, còn để biểu diễn số thập phân là double. Int được sử dụng để biểu diễn các số nguyên không dấu, trong khi double được sử dụng để biểu diễn các số thập phân có dấu. Ví dụ, bạn có thể sử dụng int để lưu trữ số 10, trong khi double để lưu trữ số 10.5. Việc xác định đúng các loại dữ liệu cơ bản này là rất quan trọng trong việc viết chương trình hiệu quả và chính xác.
Trong Dart, 'Future.delayed()' được sử dụng để mô phỏng độ trễ mạng trong quá trình kiểm tra. Điều này cho phép bạn tạo ra các thử nghiệm giả lập các tình huống thực tế, chẳng hạn như mạng chậm hoặc bị gián đoạn. Khi sử dụng 'Future.delayed()', bạn có thể tạo ra các tương tác giả lập với các dịch vụ mạng, giúp bạn kiểm tra khả năng xử lý của ứng dụng trong các tình huống khác nhau. Điều này giúp bạn viết mã tốt hơn và đảm bảo ứng dụng của mình có thể hoạt động ổn định trong các tình huống mạng khác nhau.
AIt pauses the execution of the async function until the Future completes and returns a value.
BTo determine the user's authentication state and route them to either the Home or Login screen.
CIt allows for the persistence of simple key-value pairs (like tokens or settings) on the device.
DIt creates a Future that completes after a specified duration, often used to simulate network latency.
Lời giải
Trong Lab 10.3, mục đích chính của `SplashScreen` là xác định trạng thái xác thực của người dùng và hướng dẫn họ đến màn hình Home hoặc Login. SplashScreen thường được sử dụng như một màn hình khởi động, nơi nó kiểm tra trạng thái xác thực của người dùng và quyết định hướng dẫn họ đến màn hình phù hợp. Điều này giúp đảm bảo rằng người dùng được hướng dẫn đến màn hình phù hợp dựa trên trạng thái xác thực của họ.
ATo determine the user's authentication state and route them to either the Home or Login screen.
BIt allows for the persistence of simple key-value pairs (like tokens or settings) on the device.
CIt allows a single codebase to render UI using its own engine across different platforms.
DIt pauses the execution of the async function until the Future completes and returns a value.
Lời giải
Trong ngữ cảnh lập trình bất đồng bộ, từ khóa `await` thực hiện chức năng gì? Từ khóa `await` được sử dụng để tạm dừng việc thực thi hàm async cho đến khi Future hoàn thành và trả về giá trị. Điều này cho phép chúng ta viết mã dễ dàng hơn và giảm thiểu sự phức tạp trong việc quản lý các tác vụ bất đồng bộ. Khi sử dụng `await`, chúng ta có thể viết mã như thể chúng ta đang thực thi các tác vụ đồng bộ, nhưng thực tế thì mã này vẫn được thực thi bất đồng bộ. Điều này giúp tăng hiệu suất và giảm thiểu sự tắc nghẽn trong ứng dụng.
Trong vòng lặp sự kiện Dart, hàng đợi có ưu tiên cao hơn là hàng đợi microtask hay hàng đợi sự kiện. Hàng đợi microtask được ưu tiên cao hơn vì nó chứa các tác vụ cần được xử lý ngay lập tức, chẳng hạn như xử lý các sự kiện async/await. Điều này giúp đảm bảo rằng các tác vụ quan trọng được xử lý trước khi đến lượt các tác vụ khác trong hàng đợi sự kiện. Vì vậy, nếu bạn đang phát triển ứng dụng Flutter và muốn đảm bảo rằng các tác vụ của mình được xử lý một cách kịp thời, hãy ưu tiên sử dụng hàng đợi microtask.