Tiếp tục với nội dung của phần 1, phần 2 sẽ nói về các thuộc tính counter, style, max lines, hintTextDirection, floating label behavior và thuộc tính isCollapsed trong Input Decoration.
Counter
Bạn có thể thực hiện thay đổi widget dựa trên số lượng ký tự đã được nhập. Nếu thuộc tính này không rỗng thì counterText sẽ bị bỏ qua.
Style
Thuộc tính này giúp thay đổi TextStyle cho các loại Text bên trong InputDecoration.
Ví dụ bên dưới hiển thị thay đổi TextStyle của hintStyle, nhưng nó cũng làm tương tự nhau để đặt TextStyle cho labelStyle, counterStyle, errorStyle, prefixStyle, suffixStyle và helperStyle.
Max lines
Nếu không có MaxLines thì mặc định sẽ là 1 dòng.
Dưới đây là ví dụ hintMaxLines cho hintText, nhưng còn có errorMaxLines và helperMaxlines đều sử dụng giống như hintMaxlines.
Hint text direction
Hint text direction có nhiệm vụ điều hướng văn bản cho hintText.
Left to right
Right to left
Floating label behavior
Never
Khi người dùng tương tác với TextField, nhãn mác sẽ không xuất hiện ở bên trên trường nhập liệu.
Auto
Nhãn mác của TextField sẽ được đẩy lên và xuất hiện ở bên trên trường nhập liệu khi ta tương tác với TextField để nhập liệu.
Always
Nhãn mác của TextField sẽ luôn luôn được xuất hiện ở bên trên trường nhập liệu.
Condensed
Thay đổi kích thước của TextField giúp cho giảm bớt khoảng trắng trong trường nhập.
TextField là một Widget nhập văn bản cho phép người dùng thu thập dữ liệu vào từ bàn phím mobile vào ứng dụng. Chúng ta có thể sử dụng Widget TextField trong việc xây dựng biểu mẫu, gửi tin nhắn, tạo trải nghiệm tìm kiếm và nhiều hơn nữa. Theo mặc định của Flutter, TextField được trang trí bằng một gạch dưới.
Dưới đây là một số cách để trang trí cho TextField.
None
Icon
Một icon để hiện thị phía trước trường nhập và bên ngoài vùng chứa của trang trí.
Kích thước và màu sắc của icon được định cấu hình tự động bằng cách sử dụng IconTheme.
Prefix icon
Một biểu tượng xuất hiện ở trước phần có thể chỉnh sửa của trường nhập văn bản, bên trong decoration’s container. PrefixIcon bị hạn chế với kích thước tối thiểu là 48px x 48px, nhưng có thể được mở rộng hơn thế.
Suffix icon
Một biểu tượng xuất hiện ở sau phần có thể chỉnh sửa của trường nhập văn bản, bên trong decoration’s container. SuffixIcon bị hạn chế với kích thước tối thiểu là 48px x 48px, nhưng có thể được mở rộng hơn thế.
Prefix text
Là tiền tố văn bản được chọn để đặt trên dòng trước đầu vào.
Hint text
Là văn bản dùng để gợi ý loại đầu vào nào mà trường nhập chấp nhận.
Suffix text
Là hậu tố văn bản được tùy chọn để đặt trên dòng sau phần nhập.
Label text
Văn bản mô tả trường nhập cho người dùng biết.
Khi trường nhập trống hoặc không được nhấn vào, nhãn sẽ hiển thị bên trên đầu trường nhập. Khi trường nhập được nhấn vào hoặc nhận bất cứ giá trị nào thì nhãn sẽ di chuyển lên trên theo chiều dọc liền kề trường nhập.
Khi người dùng chưa tương tác với TextField.
Khi người dùng tương tác với TextField.
Error text
Văn bản xuất hiện bên dưới InputDecorator.child và đường viền, văn bản thể hiện lỗi khi nhập liệu của người dùng.
Helper text
Văn bản cung cấp ngữ cảnh về giá trị của InputDecorator.child, chẳng hạn như cách giá trị sẽ được sử dụng như thế nào. Văn bản ở cùng vị trí với errorText.
Nếu một giá trị errorText không phải null thì helperText sẽ không được hiển thị.
Counter text
Văn bản được đặt bên phải phía bên dưới trường nhập dưới dạng số ký tự.
Trong hầu hết các app hiện nay việc lấy dữ liệu từ server hay data base là việc hết sức bình thường để app của bạn có thể cập nhập các data mới nhất cũng như tương tác của người dùng . Trong bài viết này mình sẽ call data từ server một cách đơn giản nhất để những ai chưa biết có thể call được 1 cách dễ dàng . flutter có hỗ trợ nhiều thư viện nhưng ở bài viết này mình sẽ sử dụng packet http . http là một thư viện Future-based sử dụng tính năng await và async. Nó cung cấp phương thức cấp cao và đơn giản để phát triển REST trên ứng dụng di động.
Một vài phương thức chính :
– read : gởi yêu cầu lên sever thông qua phương thức GET và trả về Future<String>
– get : gởi yêu cầu lên sever thông qua phương thức GET và trả về Future<Response>. Response là lớp giữ lại các thông tin phản hồi
– post : gởi yêu cầu lên sever thông qua phương thức POST bằng việc đưa giá trị lên sever và phản hồi Future<Response>
– put : gởi yêu cầu lên sever thông qua phương thức PUT và trả về phản hồi như Future<Response>
– head : gởi yêu cầu lên sever thông qua phương thức HEAD và trả về phản hồi như Future<Response>
– delete : gởi yêu cầu lên sever thông qua phương thức DELETE và trả về phản hồi như Future<Response>
Ở bài viết này sẽ chỉ tập trung vào phương thức GET và hiển thị data dưới dangj list view cơ bản .
Step 1 : Bạn cần add thư viện http ở pub.dev vào file pubspec.yaml
Viết factory constructor trong lớp model , Post.fromMap dùng để chuyển đổi dữ liệu map trong đối tượng Post . Thông thường, tệp JSON sẽ được chuyển đổi bên trong đối tượng Dart Map và sau đó chuyển đổi sang đối tượng liên qua (Post)
Step 3 : Bây giờ ta sẽ viết 2 phương thức – parsePost và fetchPost – trong lớp chính để lấy và tải thông tin sản phẩm từ web server(máy chủ) trong List<Post>
Step 4 : Ta có thể hiển thị data lên màn hình ở main.dart
Và đây là kết quả :
Hi vọng qua bài viết này các anh em có thể nắm được cách get data từ server một cách dễ dàng
Trong hầu hết các app hiện nay việc lấy dữ liệu từ server hay data base là việc hết sức bình thường để app của bạn có thể cập nhập các data mới nhất cũng như tương tác của người dùng . Trong bài viết này mình sẽ call data từ server một cách đơn giản nhất để những ai chưa biết có thể call được 1 cách dễ dàng . flutter có hỗ trợ nhiều thư viện nhưng ở bài viết này mình sẽ sử dụng packet http . http là một thư viện Future-based sử dụng tính năng await và async. Nó cung cấp phương thức cấp cao và đơn giản để phát triển REST trên ứng dụng di động.
Một vài phương thức chính :
– read : gởi yêu cầu lên sever thông qua phương thức GET và trả về Future<String>
– get : gởi yêu cầu lên sever thông qua phương thức GET và trả về Future<Response>. Response là lớp giữ lại các thông tin phản hồi
– post : gởi yêu cầu lên sever thông qua phương thức POST bằng việc đưa giá trị lên sever và phản hồi Future<Response>
– put : gởi yêu cầu lên sever thông qua phương thức PUT và trả về phản hồi như Future<Response>
– head : gởi yêu cầu lên sever thông qua phương thức HEAD và trả về phản hồi như Future<Response>
– delete : gởi yêu cầu lên sever thông qua phương thức DELETE và trả về phản hồi như Future<Response>
Đối với phép chia, ngay cả với số nguyên, Dart suy ra biến kết quả là a double. Đó là lý do tại sao bạn nhận được 42.0 thay vì 42 cho print câu lệnh cuối cùng.
So sánh bằng
Dart sử dụng toán tử double-equals ( ==) và not-equals ( !=):
Ngoài ra, nó cũng sử dụng các toán tử số học / phép gán ghép thông thường:
var value = 42.0;
value += 1; print(value); // 43.0
value -= 1; print(value); // 42.0
value *= 2; print(value); // 84.0
value /= 2; print(value); // 42.0
Toán tử số học / phép gán ghép thực hiện hai nhiệm vụ. +=thêm giá trị ở bên phải vào biến ở bên trái và sau đó gán kết quả cho biến.
Một dạng rút gọn của += 1 là ++:
value++;
print(value); // 43.0
Và Dart có toán tử modulo thông thường ( %) để trả về phần còn lại sau khi một số đã được chia cho một số khác:
print( 392 % 50 ); // 42
392 ÷ 50 = 7,84 nhưng 42 đó trong ngăn kết quả đến từ đâu? Nó dễ dàng hơn để xem với sự phân chia dài.
Toán tử logic
Dart sử dụng các toán tử logic giống như các ngôn ngữ khác, bao gồm và có kí hiệu là &&, OR có kí hiệu là ||
Loại chuỗi Dart là String. Các chuỗi được thể hiện trong Dart bằng cách sử dụng văn bản được bao quanh bởi dấu ngoặc kép đơn hoặc kép.
Bạn có thể sử dụng var và nhập suy luận hoặc String để tạo biến chuỗi, giống như các kiểu khác mà bạn đã thấy:
var firstName = 'Albert' ; String lastName = "Einstein" ;
Tương tự như các ngôn ngữ như Kotlin và Swift, bạn có thể nhúng giá trị của một biểu thức bên trong một chuỗi bằng cách sử dụng ký hiệu ký hiệu đô la: $ { biểu thức }.
Nếu biểu thức là một số nhận dạng, bạn có thể bỏ qua dấu {} . Thêm những điều sau:
var physicist = "$firstName $lastName likes the number ${84 / 2}";
print(physicist); // Albert Einstein
$firstName và $lastName được thay thế bằng các giá trị biến. Trả về kết quả được tính toán.
Escaping Strings
Các trình tự thoát được sử dụng trong Dart tương tự như các trình tự được sử dụng trong các ngôn ngữ giống C khác. Ví dụ, bạn sử dụng \n cho một dòng mới.
Nếu có các ký tự đặc biệt trong chuỗi, hãy sử dụng \ để thoát khỏi chúng:
var quote = 'If you can\'t explain it simply\nyou don\'t understand it well enough.';
print(quote);
// If you can't explain it simply
// you don't understand it well enough.
Ví dụ này sử dụng các dấu ngoặc kép, vì vậy nó cần một chuỗi thoát \', để nhúng các dấu nháy đơn cho can’tvà don’t vào chuỗi. Bạn sẽ không cần phải thoát khỏi dấu nháy đơn nếu thay vào đó bạn sử dụng dấu ngoặc kép.
Nếu bạn cần hiển thị trình tự thoát trong chuỗi, bạn có thể sử dụng raw strings , có tiền tố là r.
var rawString = r"If you can't explain it simply\nyou don't understand it well enough.";
print(rawString);
// If you can't explain it simply\nyou don't understand it well enough.
Ở đây, Dart được coi `\n`là văn bản bình thường vì chuỗi bắt đầu bằng r.
Nhấp vào RUN trong DartPad để xem tất cả các chuỗi của bạn trong bảng điều khiển.
Immutability
Dart sử dụng các từ khóa constvà finalcác giá trị không thay đổi.
Sử dụng constcho các giá trị đã biết tại thời điểm biên dịch. Sử dụng finalcho các giá trị không cần biết tại thời điểm biên dịch nhưng không thể gán lại sau khi được khởi tạo.Lưu ý : finalhoạt động giống như valtrong Kotlin hoặc lettrong Swift.
Bạn có thể sử dụng constvà finalthay cho varvà để phép suy luận kiểu xác định kiểu:
const speedOfLight = 299792458 ;
in (speedOfLight); // 299792458
Ở đây, Dart cho rằng speedOfLight là một biến kiểu int, như bạn có thể thấy trong bảng thông tin của DartPad.
final cho biết rằng một biến là không thể thay đổi , nghĩa là bạn không thể gán lại final các giá trị. Bạn có thể nêu rõ loại với một trong hai final hoặc const:
final planet = 'Jupiter';
// planet = 'Mars';
// error: planet chỉ có thể khởi tạo 1 lần
final String moon = 'Europa';
print('$planet has a moon, $moon');
// Jupiter has a moon, Europa
Nullability
Trước đây, nếu bạn không khởi tạo một biến, Dart đã cấp cho nó giá trị null, có nghĩa là không có gì được lưu trữ trong biến. Tuy nhiên, kể từ Dart 2.12, Dart kết hợp với các ngôn ngữ khác, như Swift và Kotlin, để không thể null theo mặc định.
Ngoài ra, Dart đảm bảo rằng kiểu không thể null sẽ không bao giờ chứa giá trị null. Điều này được gọi là sound null safety. .
Thông thường, nếu bạn muốn khai báo một biến, bạn phải khởi tạo nó:
String middleName = 'May';
print(middleName); // May
Tuy nhiên, không phải tất cả mọi người đều có tên đệm, vì vậy việc tạo middleName một giá trị vô hiệu là rất hợp lý. Để nói với Dart rằng bạn muốn cho phép giá trị null, hãy thêm ? vào sau kiểu giữ liểu.
Giá trị mặc định cho kiểu nullable là null, vì vậy bạn có thể đơn giản hóa biểu thức thành như sau:
String? middleName;
print(middleName); // null
Run nó và null in ra bảng điều khiển.
Toán tử Null-Aware
Dart có một số toán tử nhận biết null mà bạn có thể sử dụng khi làm việc với các giá trị null.
Toán tử dấu hỏi kép ?? giống như toán tử Elvistrong Kotlin: Nó trả về toán hạng bên trái nếu đối tượng không phải là null. Nếu không, nó trả về giá trị bên phải:
var name = middleName ?? 'none';
print(name); // none
Vì middleName là null, Dart chỉ định giá trị bên phải của 'none'.
Toán tử ?. bảo vệ bạn khỏi việc truy cập các thuộc tính của các đối tượng null. Nó trả về null nếu bản thân đối tượng là null. Nếu không, nó trả về giá trị của thuộc tính ở phía bên phải:
print(middleName?.length); // null
Ngày trước khi chưa có null safety, nếu bạn quên dấu chấm hỏi và viết middleName.length, ứng dụng của bạn sẽ gặp sự cố trong thời gian chạy nếu middleNametrống. Đó không còn là vấn đề nữa, vì Dart giờ đây sẽ cho bạn biết ngay lập tức khi nào bạn cần xử lý các giá trị null.
Gần đây, Flutter nổi lên và được Google PR như một xu thế của lập trình di động, trên con đường trở thành master fluter thì Widget chính là chìa khóa. Trong Flutter, mọi thứ đều là widget. Và vì vậy điều quan trọng là phải hiểu cách chúng hoạt động và cách Flutter hiển thị các Widget. Trong bài viết này chúng ta sẽ tìm hiểu về Element Tree, Widget Tree và Render Tree.
Widget tree
Đầu tiên là Widget tree. Widget tree được sử dụng để cấu hình Giao diện người dùng. Ở đó, bạn có thể định cấu hình các thuộc tính của Widget và định nghĩa nó sẽ hiển thị như thế nào. Tức là code mà bạn viết sẽ tạo lên widget tree.
Element tree
Cây thứ hai được gọi là Element tree. Element tree được sử dụng để quản lý và cập nhật mọi thứ. Element tree liên kết vào Widget tree, một phần tử trong Element tree là một thể hiện cụ thể của một widget. Điều này nghe có vẻ quen thuộc nếu bạn biết OOP (Lập trình hướng đối tượng) nơi bạn có các class và object trong đó các object chỉ là các thể hiện của một class.
Render Tree
Mảnh ghép còn thiếu cuối cùng là Render Object. Bên trong Render tree là các render object và về cơ bản đây là những gì bạn đang thấy trên màn hình. Bạn không thấy các widget mà bạn thấy các render object. Render object sẽ quan tâm đến kích thước, bố cục và bức tranh thực tế trên màn hình.
Cách Flutter render các widget – 3 trạng thái
Flutter trải qua 3 giai đoạn khi hiển thị các widget ra màn hình. Như chúng ta đã biết, đó không phải là những widget mà chúng ta thấy trên màn hình. Đó là các render object. Nhưng làm thế nào Flutter có thể xử lý điều này?
1. Cấu hình
Như tôi đã đề cập ở trên, widget tree chứa các widget và ở trạng thái này, tất cả là về cấu hình của các widget của chúng ta. Thông qua một API, bạn chỉ định các thuộc tính và giá trị cho các tiện ích con của mình mà cây tiện ích con sẽ nắm giữ.
2. Vòng đời
Tại đây, toàn bộ vòng đời của giao diện người dùng được quản lý. Cũng ở đây, nó xác định các thành phần hiện có trong hệ thống phân cấp giao diện người dùng của bạn. Bạn có thể hình dung trạng thái này giống như chất keo kết dính giữa trạng thái 1 và 3.
3. Vẽ
Ở đây Render tree phát huy tác dụng. Tất cả những điều liên quan về vẽ UI sẽ được thực hiện ở đây. Nó quan tâm đến các ràng buộc, phần con của các widget sẽ thực sự trông như thế nào, kích thước của chúng ra sao. Đây là nơi các render object được vẽ trên màn hình.
Ok, nhưng tại sao lại là 3 cây?
Bây giờ bạn có thể tự hỏi mình “OK Nhưng tại sao Flutter không chỉ sử dụng các widget và chỉ có một cây?”.
Thực ra đây là một câu hỏi rất hay.
Đó là về hiệu năng
Ok, điều đầu tiên.
Các widget là bất biến.
Bạn không thể thay đổi các widget. Và về cơ bản đây là lý do tại sao một widget tree là không đủ. Hãy tưởng tượng bạn thay đổi một widget Text chẳng hạn. Bạn sẽ cần một widget hoàn toàn mới bởi vì bạn không thể thay đổi cùng một widget. Và nếu bạn đang code Flutter, bạn có thể biết UI có thể thay đổi thường xuyên như thế nào.
Điều này có nghĩa là khi một widget cần thay đổi, đối tượng widget bên trong widget tree sẽ được thay thế hoàn toàn và không thể cập nhật được.
Và tại đây 2 cây còn lại phát huy tác dụng.
Cả Element và Render Tree đều được cập nhật bất cứ khi nào có thể thay vì được tạo hoàn toàn mới. Và đây là một sự cải thiện hiệu suất lớn!
Cập nhật hay không cập nhật?
Và làm thế nào Flutter quyết định xem Element và Render Object có thể được cập nhật hay không?
Hãy tưởng tượng bạn có một Widget Text, nơi bạn chỉ cần thay đổi nội dụng văn bản. Lần thứ hai bạn thay đổi nó, Phương thức CanUpdate của Widget sẽ kiểm tra hai thứ và nếu một trong số chúng là đúng, thì Render Object và Element Object sẽ được cập nhật thay vì được tạo ra các đối tượng hoàn toàn mới.
RuntimeType (kiểu widget như Text, Column, Container, v.v.) có còn giống nhau không?
Key có giống nhau không?
Trong ví dụ của chúng ta, runtimeType vẫn là Text và vì vậy nó đúng và Đối tượng phần tử sẽ gọi Phương thức updateRenderObject để đảm bảo Render Object được cập nhật thay vì được tạo lại.
Key là một thuộc tính mà bạn có thể cấp cho hầu hết mọi widget con và giúp Flutter xác định một widget con. Khá hữu ích cho mục đích này.
Và bạn có thể kiểm tra hành vi này khá dễ dàng. Khi bạn chạy một Ứng dụng Flutter bên trong IDE của mình và mở Flutter DevTools, bạn sẽ thấy chế độ xem sau:
Ở đây tôi đã chọn một widget Text và bên trong hộp màu đỏ, bạn sẽ thấy Render Object. Và Render Object này có ID bắt đầu bằng ký hiệu #. Khi bạn thay đổi nội dung văn bản cho Widget Text này, ID sẽ KHÔNG thay đổi.
Tóm lại
Flutter sẽ cố gắng sử dụng lại càng nhiều tài nguyên (các object trong cây) càng tốt trong khi cố gắng render các đối tượng mới càng ít càng tốt.
Kết luận
Flutter khá thông minh trong cách hiển thị các widget!
Bài viết này đã cung cấp và đi sâu hơn vào đặc tính và cách render UI của Flutter. Tôi hy vọng bây giờ bạn đã có thể hiểu rõ hơn về những gì xảy ra đằng sau quá trình render các Widget. Mong là bạn sẽ thích bài viết này.
[FLUTTER] Cách viết Unit Test trong Flutter (Phần 1)
Lời ngỏ
Unit Test là một phần rất quan trọng trong quá trình phát triển phần mềm, tuy nhiên nó thường xuyên bị lãng quên với một lập trình viên mới vào nghề hoặc chưa có nhiều kinh nghiệm. Mong rằng bài viết sẽ giúp bạn có cái nhìn trực quan hơn về Unit Test tron phát triển phầm mềm, đặc biệt là trong Flutter.
A. Đôi điều về Unit Test
1. Unit Test là gì?
1.1 Ảnh unit test
Unit Test là một loại kiểm thử phần mềm trong đó các đơn vị hay thành phần riêng lẻ của phần mềm được kiểm thử. Kiểm thử đơn vị được thực hiện trong quá trình phát triển ứng dụng. Mục tiêu của Kiểm thử đơn vị là cô lập một phần code và xác minh tính chính xác của đơn vị đó.
Nếu khái niệm trên vẫn còn khá khó hiểu thì hãy thử tách nghĩa từng từ ra một nhé:
Unit là một thành phần Phần mềm nhỏ nhất mà ta có thể kiểm tra được như các hàm (Function), thủ tục (Procedure), lớp (Class), hoặc các phương thức (Method).
Test thì là kiểm thử, kiểm tra tính chính xác của một cái gì đó.
Đến đây thì chắc các bạn cũng đã có cho mình một chút khái niệm về Unit Test rồi đúng không nhỉ.
Thường các lập trình viên khi nghe về một khái niệm mà trong đó có từ “Test” thì điều mọi người sẽ nghĩ đến ngay đó là “Test là công việc của Tester đâu phải việc của mình nên mình không cần quan tâm :v”.
Nhưng KHÔNG, các bạn đã nhầm to. Unit Test sẽ phải được viết bởi các lập trình viên, bởi chính những người viết ra những dòng code đó.
2. Vòng đời của Unit Test
Vòng đời của Unit Test gồm 3 giai đoạn:
Fail (trạng thái lỗi).
Ignore (tạm ngừng thực hiện).
Pass (trạng thái làm việc).
Ba giai đoạn này sẽ thay phiên nhau làm việc khi một Unit Test được chạy tự động.
3. Unit Test quan trọng không và khi nào thì cần viết Unit Test?
Unit Test là một phần không thể thiếu trong quá trình phát triển phần mềm. Unit Test đem lại cho chúng ta rất nhiều lợi ích:
Tạo ra một môi trường để kiểm tra bất kỳ đoạn code nào, duy trì sự ổn định của phần mềm. Unit Test giúp chúng ra kiểm tra những kết quả trả về mong muốn cũng như những ngoại lệ mong muốn.
Phát hiện các lỗi, các xử lý không hiệu quả trong code, các vấn đề về thiết kế.
Việc viết Unit Test có thể coi như việc tạo một người dùng đầu tiên cho ứng dụng, từ đó chúng ta có thể biết được những vấn đề mà trong quá trình sử dụng ứng dụng người dùng có thể gặp phải.
Giúp cho quá trình phát triển phần mềm trở nên nhanh hơn, số lượng test case khi được test cũng sẽ pass nhiều hơn. Điều này giúp cho các bộ phận khác như QA, Tester làm việc sẽ nhàn hơn. Và trên hết đối với những coder chúng ta, việc ít phải đối mặt với Tester cũng làm cho buổi làm việc “bớt sóng gió” hơn đúng không nào?
Note
Viết Unit Test càng sớm càng tốt trong giai đoạn viết code và xuyên suốt chu kỳ Phát triển phần mềm.
4. Như nào là một Unit Test có giá trị?
Muốn viết một Unit Test hiệu quả, đem lại nhiều lợi ích nhất cho bản thân cũng như dự án thì cần chú ý những điểm sau:
Unit Test chạy nhanh, sử dụng dữ liệu dễ hiểu, dễ đọc.
Hãy làm cho mỗi test độc lập với những phần khác. Mỗi test chỉ nên liên quan đến một hàm, thủ tục, … Điều này sẽ giúp chúng ta dễ dàng hơn trong quá trình quản lý unit test, cũng như đáp ứng được các thay đổi trong code.
Giả lập tất cả các dịch vụ và trạng thái bên ngoài. Ví dụ: Nếu bạn có làm việc với Database, thì KHÔNG nên sử dụng database thật của ứng dụng để viết Unit Test, bởi vì giá trị trong đó sẽ có thể thay đổi và ảnh hưởng đến các kết quả mong đợi của bạn. Thay vào đó hay tự vào cho mình một fake database, với dự liệu có sẵn và chỉ sử dụng các hàm, thủ tục để làm việc với nó.
Nên đặt tên các đơn vị kiểm thử rõ ràng và nhất quán với nhau để đảm bảo rằng test case dễ đọc. Để bất kỳ ai cũng có thể khởi chạy test case mà không gặp phải trở ngại.
Triển khai Unit Test bao quát hết tất cả các ngoại lệ, các test case.
B. Cách triển khai Unit Test trong Flutter (Còn tiếp)
Các unit tests có thể sẽ phụ thuộc vào các class có fetch data từ web và cơ sở dữ liệu. Điều này bất tiền do một vài lý do:
Việc fetch data từ web và cơ sở dữ liệu về sẽ chậm để thực hiện kiểm tra.
Việc kiểm tra có thể bắt đầu lỗi nếu kết quả từ web và cơ sở dữ liệu là kết quả không được mong đợi. Điều này gọi là “flaky test”.
Khó để kiểm tra tất cả các trường hợp thành công hay lỗi các hành động sử dụng web và cơ sở dữ liệu.
Vì vậy, chúng ta có thể sử dụng “mock” dependencies. Mocks cho phép mô phỏng web và cơ sở dữ liệu và trả về kết quả cụ thể tùy thuộc vào tình huống.
2. Các bước sử dụng Mockito package để kiểm tra.
Các bước bao gồm:
Thêm các package vào pubspec.yaml.
Tạo function để kiểm tra.
Tạo file test với mock http.Client.
Viết các test cho mỗi điều kiện.
Chạy kiểm tra.
2.1. Thêm các package dependencies vào pubspec.yaml.
Thêm các package dependencies: http, mockito, build_runner với version mới nhất ( tham khảo trên trang pub.dev) vào file pubspec.yaml.
2.2. Tạo function để kiểm tra.
Ở ví dụ này, chúng ta tạo một unit test fetchAlbum function để lấy dữ liệu từ internet. Để kiểm tra function này, chúng ta cần 2 thay đổi đó là:
Tham số của function phải có http.Client. Điều này cho phép cung cấp chính xác http.Client phụ thuộc trong trường hợp. Đối với Flutter và dự án web và cơ sở dữ liệu-side cung cấp http.IOClient. Đối với Browse apps, cung cấp http.BrowserClient. Đối với tests, cung cấp mock http.Client.
Sử dụng client để lấy dữ liệu từ internet điều này tốt hơn là phương pháp http.get() trong việc làm giả.
2.3. Tạo file test với mock http.Client.
Tạo file fetch_album_test.dart trong folder test.
Thêm annotation @GenerateMocks([http.Client]) để main function tạo ra MockClient class với mockito. MockClient class được tạo ra sẽ thể hiện http.Client class. Điều này cho phép chúng ta có thể thực hiện fetchAlbum function và trả lại các kết quả khác nhau cho mỗi trường hợp test trong MockClient.
Các mocks được sinh ra sẽ được để trong .
Sau đó, trong command chạy: flutter pub run build_runner build để sinh ra mocks.
2.4. Viết các test cho mỗi điều kiện.
Ở ví dụ này, fetchAlbum() function sẽ trả về:
Nếu http gọi thành công sẽ trả về Album()
Nếu lỗi thì sẽ trả về Exception.
2.5. Chạy file tests.
Chúng ta chỉ việc ấn Run trong file tests trong thư mục test: …/test/fetch_album_test.dart.
3. Tổng kết
Trên đây là một số chia sẻ về việc sử dụng Unit test: Mock dependencies using Mockito cơ bản giúp cho việc kiểm tra các function thao tác với web và cơ sở dữ liệu dễ dàng hơn. Mong rằng qua bài viết sẽ giúp ích cho các bạn phần nào đó.
Giới thiệu về kiến thức cơ bản của ngôn ngữ lập trình Dart, được sử dụng để phát triển với Flutter SDK dành cho thiết bị di động, web và hơn thế nữa.
Flutter là một bộ công cụ giao diện người dùng thú vị của Google cho phép bạn viết ứng dụng cho các nền tảng khác nhau bao gồm iOS, Android, web và hơn thế nữa, tất cả đều sử dụng một cơ sở mã. Flutter sử dụng ngôn ngữ Dart.
Nếu bạn chưa quen với Dart, hướng dẫn này sẽ giới thiệu cho bạn các khái niệm cơ bản của nó và cho bạn thấy nó tương tự như thế nào với các ngôn ngữ lập trình khác mà bạn có thể đã biết.
Trong suốt quá trình hướng dẫn này, bạn sẽ được giới thiệu về những điều cơ bản của Dart chẳng hạn như:
Variables, data types, and operators
Conditionals and loops
Collections
Functions
Khi bạn hoàn thành, bạn sẽ sẵn sàng đi sâu vào phát triển Flutter bằng cách sử dụng Dart.
Bắt Đầu
Để bắt đầu nhanh chóng, cách tốt nhất của bạn là sử dụng công cụ mã nguồn mở DartPad , cho phép bạn viết và kiểm tra mã Dart thông qua trình duyệt web:
DartPad được thiết lập giống như một IDE thông thường. Nó bao gồm các thành phần sau:
Khung trình soạn thảo : Nằm ở bên trái. Mã của bạn sẽ xuất hiện ở đây.
Nút RUN : Chạy mã trong trình chỉnh sửa.
Bảng điều khiển : Nằm ở phía trên bên phải, bảng này hiển thị đầu ra.
Bảng tài liệu : Nằm ở dưới cùng bên phải, bảng này hiển thị thông tin về mã.
Samples: Trình đơn thả xuống này hiển thị một số mã mẫu.
Nút Null Safety: Sử dụng nút này để chọn tham gia vào tính năng an toàn không có âm thanh mới của Dart.
Thông tin phiên bản : Ở phía dưới cùng bên phải, DartPad hiển thị phiên bản Flutter và Dart mà nó hiện đang sử dụng.
Nếu muốn, bạn có thể cài đặt Dart SDK cục bộ trên máy của mình. Một cách để làm như vậy là cài đặt Flutter SDK . Cài đặt Flutter cũng sẽ cài đặt Dart SDK.
Dart có nhiều điểm tương đồng với các ngôn ngữ khác như Java, C #, Swift và Kotlin. Một số tính năng của nó bao gồm:
Statically typed
Type inference
String expressions
Multi-paradigm including object-oriented and functional programming
Null safe
Dart được tối ưu hóa để phát triển các ứng dụng nhanh trên nhiều nền tảng.
Variables, Comments and Data Types
Điều đầu tiên bạn sẽ thêm vào main là một câu lệnh gán biến. Các biến giữ dữ liệu mà chương trình của bạn sẽ hoạt động.
Bạn có thể coi một biến như một hộp trong bộ nhớ máy tính của bạn chứa một giá trị. Mỗi hộp có một tên, đó là tên của biến. Để biểu thị một biến bằng Dart, hãy sử dụng var từ khóa.
Thêm một biến mới vào main:
var myAge = 35 ;
Mỗi câu lệnh Dart kết thúc bằng dấu chấm phẩy, giống như các câu lệnh trong C và Java. Trong đoạn mã trên, bạn đã tạo một biến myAge và đặt nó bằng 35 .
Bạn có thể sử dụng print Dart tích hợp sẵn để in biến vào bảng điều khiển. Thêm lệnh gọi đó sau biến:
in (myAge); // 35
Nhấp vào RUN trong DartPad để chạy mã. Bạn sẽ thấy giá trị của biến 35 được in trong bảng điều khiển.
Comments
Comments trong Dart giống như trong C và các ngôn ngữ khác. Văn bản sau // là một nhận xét một dòng, trong khi văn bản bên trong /* ... */ là một khối nhận xét nhiều dòng.
Đây là một ví dụ:
// Đây là một nhận xét một dòng.
in (myAge); // Đây cũng là một nhận xét một dòng.
/ *
Đây là một khối bình luận nhiều dòng. Điều này rất hữu ích cho những
bình luận dài kéo dài vài dòng.
* /
Data Types
Dart là statically typed , nghĩa là mỗi biến trong Dart có một kiểu mà bạn phải biết khi biên dịch mã. Loại biến không thể thay đổi khi bạn chạy chương trình. C, Java, Swift và Kotlin cũng có statically typed .
Điều này trái ngược với các ngôn ngữ như Python và JavaScript, được gõ động . Điều đó có nghĩa là các biến có thể chứa các loại dữ liệu khác nhau khi bạn chạy chương trình. Bạn không cần biết kiểu khi bạn biên dịch mã.
Nhấp vào myAge cửa sổ trình chỉnh sửa và tìm trong bảng Tài liệu . Bạn sẽ thấy Dart được suy ra là myAge kiểu int vì nó được khởi tạo với giá trị số nguyên 35 .
Nếu bạn không chỉ định rõ ràng một kiểu dữ liệu, Dart sẽ sử dụng suy luận kiểu để cố gắng xác định nó, giống như Swift và Kotlin.
Dart cũng sử dụng kiểu suy luận cho các kiểu khác int. Nhập một biến pi bằng 3,14:
var pi = 3,14 ; in (pi); // 3,14
Dart pi được cho là double bởi vì bạn đã sử dụng một giá trị dấu phẩy động để khởi tạo nó. Bạn có thể xác minh điều đó trong bảng thông tin Dart bằng cách nhấp vào pi.
Các kiểu dữ liệu cơ bản
Dart sử dụng các loại cơ bản sau:
int : Số nguyên
double : Số phức
bool : Booleans
String: Chuỗi ký tự
Dưới đây là một ví dụ về từng loại trong Dart:
int và double cả hai đều bắt nguồn từ một kiểu được đặt tên num. num sử dụng dynamic từ khóa để bắt chước cách nhập động trong Dart.
Thực hiện việc này bằng cách thay thế varbằng loại bạn muốn sử dụng:
int yourAge = 27 ;
in (yourAge); // 27
Từ khóa Dynamic
Nếu bạn sử dụng từ khóa dynamic từ khóa thay vì var, bạn sẽ nhận được một biến được nhập động một cách hiệu quả:
dynamic numberOfKittens;
Tại đây, bạn có thể đặt numberOfKittensthành một String dấu ngoặc kép. Bạn sẽ tìm hiểu thêm về String loại này sau trong hướng dẫn.
numberOfKittens = 'There are no kittens!';
print(numberOfKittens); // There are no kittens!
numberOfKittenscó một kiểu, vì Dart có kiểu gõ tĩnh. Nhưng kiểu dynamicđó, có nghĩa là bạn có thể gán các giá trị khác với các kiểu khác cho nó. Vì vậy, bạn có thể chỉ định một intgiá trị bên dưới câu lệnh in của mình.
Nhấp vào RUN để xem ba giá trị khác nhau numberOfKittens được in trong bảng điều khiển. Trong mỗi trường hợp, kiểu của numberOfKittens phần còn lại dynamic, mặc dù bản thân biến đó giữ các giá trị của các kiểu khác nhau.
Booleans
Kiểu bool chứa các giá trị của một trong hai true hoặc false.
An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code.
Ở đây chúng ta sẽ lấy nguyên định nghĩa mà Apple đưa ra, hiểu một các nôm na thì Enum là kiểu dữ để định nghĩa một nhóm có giá trị liên quan, từ đó giúp bạn làm việc an toàn với kiểu dữ liệu đó trong code của bạn.
2. Enum trong Enum
Như tiêu đề, mình sẽ đi thẳng vào việc sử dụng Enum trong Enum, các trường hợp sử dụng enum thông thường các bạn có thể tìm hiểu ở các blog hoặc bài viết khác.
Bài toán đưa ra: Khi bạn có một struct như sau:
ở đây chúng ta thấy có một enum để định về type của các PieChart. Câu hỏi đưa ra là khi bạn vẫn muốn tiếp tục sử dụng lại struct trên cho các object tương tự, và các object đó có type khác nhau thì hướng xử lí của bạn là như thế nào?
-> Một trong các cách mà mình nghĩ các bạn sẽ dùng tới là sử dụng lại enum, thêm case hoặc là tạo một struct và enum mới như hình:
Ở đây chúng ta quan sát giữa strcut ChartViewEntity với ChartViewEntityInWeek. Chúng ta thấy hai struct chỉ khác nhau về type( ở đây là khác nhau vê Enum). Và giữa 2 enum lại có mối quan hệ giống nhau.
Câu hỏi đặt ra, tại sao chúng ta không sử dụng lại cùng 1 struct mà sửa lại Enum, và trong Enum của chúng ta sẽ chưa các case là type cho ChartView trong từng trường hợp mong muốn. Và chúng ta giải quyết bài toán trên bằng cách xử lí lại như sau:
Ở đây mình đã tạo ra 3 enum, và enum PieChartType sẽ chứa 2 Enum còn lại ( đó là các Enum con phù hợp cho từng bài toán đặt ra riêng cho mỗi Object có các thuộc tính được trìu tượng qua struct ChartViewEntity). Vậy là bài toán của chúng ta đã được giải quyết, và chúng ta nhận ra rằng việc sử dụng Enum trong Enum có hiệu quả nhất định, giúp code của bạn gọn hơn, và trở nên tường minh hơn, đảm bảo sự an toàn khi bạn viết code.
Qua ví dụ trên mình đã đưa ra cách giải quyết bài toán đồng thời là ứng dụng của việc sử dụng Enum trong Enum. Vậy tạo sao chúng ta không bắt tay vào sử dụng cho các bài toán riêng của mình. Chúc các bạn thành công.
3. Tổng kết
Trên đây mà một số chia sẻ về việc sử dụng Enum trong Enum nói riêng, và Enum nói chung. Với Swift chúng ta có Enum giúp cho việc viết code trở nên rõ ràng và rành mạch hơn. Mong rằng qua bài viết sẽ giúp ích cho các bạn phần nào đó về việc sử dụng enum và tiến tới con đường coder chuyên nghiệp hơn.