Month: April 2022

  • [FLUTTER] Tổng quan về ListView

    Nếu bạn muốn có một giải pháp để hiển thị một danh sách các Widget và thậm chí có thể cuộn được theo chiều ngang hay dọc thì ListView chính là một lựa chọn vô cùng hiệu quả.

    Dưới đây là một số cách để xây dựng một ListView:

    Sử dụng List<Widget> cho thuộc tính children

    Đây là cách xây dựng dựng mặc định của một ListView. Bằng cách xây dựng từng Widget cụ thể và đặt trong children của ListView, các Widget sẽ được hiển thị lần lượt theo trên giao diện người dùng.

    Cách xây dựng này phù hợp với việc hiển thị một số lượng ít các Widget vì việc xây dựng một List yêu cầu cần phải làm việc với tất cả các thành phần con có thể được hiển thị kể cả khi các Widget chưa hiển thị lên màn hình.

    Sử dụng ListView.builder

    Đây là cách xây dựng ListView được áp dụng cho trường hợp cần hiển thị một lượng lớn (hay vô hạn) các Widget con vì builder chỉ được gọi cho những Widget thực sự được hiển thị lên màn hình.

    Giả sử ta có một mảng dữ liệu 100 phần tử từ 1 đến 100
    Ta có thể thay đổi số lượng item được hiển thị qua thuộc tính itemCount

    Sử dụng ListView.separated

    Đây là cách xây dựng ListView được áp dụng cho trường hợp khi cần hiển thị một số lượng lớn các Widget con và các Widget dùng để ngăn cách giữa các Widget đó vì builder chỉ được gọi cho những Widget thực sự được hiển thị lên màn hình.

    Giả sử ta có một mảng dữ liệu 100 phần tử từ 1 đến 100
    Ta có thể thêm Widget dùng để tách các Widget con qua separatorBuilder

    Sử dụng ListView.custom

    Đây là cách xây dựng ListView giúp bạn có thể tùy chỉnh nhiều hơn cho các model con. Ví dụ: một model con tùy chỉnh có thể kiểm soát thuật toán được sử dụng để ước tính kích thước của các mô hình con không thực sự hiển thị.

    Giả sử ta có một mảng dữ liệu 100 phần tử từ 1 đến 100
    Sử dụng SliverChildBuilderDelegate để xây dựng Widget con

    Một số thuộc tính thường được sử dụng

    padding

    ListView có khoảng cách với Widget cha. Ví dụ: padding: const EdgeInsets.all(8).

    scrollDirection

    Thuộc tính scrollDirection xác định hướng cuộn của ListView, mặc định của ListView sẽ là vertical. Ví dụ: scrollDirection: Axis.horizontal.

    reverse

    ListView sẽ được hiển thị ngược chiều, đây là thuộc tính có kiểu bool, mặc định là false.

    physics

    Thuộc tính physics giúp bạn cài đặt ListView được cuộn như thế nào. Ví dụ: physics: const NeverScrollableScrollPhysics(),

    Tài liệu tham khảo

    ListView class trong Flutter: ListView class – widgets library – Dart API (flutter.dev)

  • SỬ DỤNG INPUT DECORATION CHO FLUTTER TEXTFIELD – PHẦN 3

    Phần này sẽ nói về các kiểu border, enabled và combined effects bên trong Input Decoration của TextField Widget.

    Border

    None

    Underline

    Tạo đường viền gạch dưới cho TextField. Ta có thể sử dụng thuộc tính borderRadius bên trong thuộc tính UnderlineInputBorder này.

    Outline with border radius

    Tạo một đường viền hình chữ nhật được bo các góc cho TextField và thuộc tính borderRadius bên trong OutlineInputBorder có chức năng thay đổi độ bo tròn viền của hình chữ nhật.

    Specific border behavior

    Border là một trường hợp chung nhưng bạn cũng có thể sửa đổi điều đó cho các tình huống cụ thể thiết lập focusedBorder, enabledBorder, disabledBorder, errorBorder và focusedBorder.

    Trường hợp enabledBorder.

    Trường hợp focusedBorder.

    Filled with color

    Thuộc tính filled giúp lấp đầy vùng trang trí bằng fillColor nếu filled có giá trị là true.

    Filled with color and no underline

    Thuộc tính BorderSide.none dùng để xóa hết Border khỏi TextField.

    Ta có thể sử dụng “decoration:null” hoặc InputDecoration.collapsed để loại bỏ underline mặc định của TextField.

    Hover color

    Thuộc tính này sẽ thay đổi màu của TextField khi mà người dùng di chuyển chuột hoặc con trỏ qua TextField.

    Enabled

    Nếu là false thì helperText, errorText và counterText sẽ không được hiển thị và độ mờ của các thành phần hình ảnh còn sẽ lại sẽ giảm.

    Thuộc tính này mặc định là true.

    True

    False

    Combined effects

    None

    Dense

    Giúp cho TextField sử dụng ít khoảng trắng hơn nếu là true.

    Thuộc tính isDense có giá trị mặc định là false.

    Content padding

    Là phần khoảng cách để nội dung không bị dí sát vào viền của TextField.

    Nếu isCollapsed là True thì thuộc tính contentPadding sẽ mặc định là zero (EdgeInsets.zero).

    Nếu thuộc tính isOutline là false và thuộc tính filled là true thì thuộc tính contentPadding sẽ có giá trị là EdgeInsets.fromLTRB(12, 8, 12, 8) khi isDense là true và có giá trị là EdgeInsets.fromLTRB(12, 8, 12, 8) khi isDense là false.

    Nếu isOutline và filled là false thì contentPadding sẽ có giá trị là EdgeInsets.fromLTRB(0, 8, 0, 8) khi isDense là true và có giá trị là EdgeInsets.fromLTRB(0, 12, 0, 12) khi isDense là false.

    Tác giả: DangDH9

  • SỬ DỤNG INPUT DECORATION CHO FLUTTER TEXTFIELD – PHẦN 2

    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.

    False

    True

  • SỬ DỤNG INPUT DECORATION CHO FLUTTER TEXTFIELD – PHẦN 1

    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ự.

  • Get data from server flutter

    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

    https://pub.dev/packages/http

    Step2 : Cần 1 link apidemo mà mình lấy link này

    https://jsonplaceholder.typicode.com/posts

    Step3 :

    Tạo file mới post_model.dart trong lớp model 

    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

  • Get data from server flutter

    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> 

  • KIẾN THỨC CƠ BẢN VỀ DART(PHẦN 2)

    KIẾN THỨC CƠ BẢN VỀ DART(PHẦN 2)

    Tiếp tục phần 1, sang phần 2 này ta sẽ tìm hiểu về các toán tử có trong Dart.

    Operators

    Dart có tất cả các toán tử thông thường mà bạn quen thuộc từ các ngôn ngữ khác như C, Swift và Kotlin.

    Một số ví dụ về các toán tử của Dart bao gồm:

    • Toán tử số học
    • Toán tử so sánh
    • Toán tử logic

    Lưu ý : Dart cũng cho phép nạp chồng toán tử , như trong C ++ và Kotlin, nhưng điều đó nằm ngoài phạm vi của hướng dẫn này. Để tìm hiểu thêm về chủ đề này, hãy truy cập trang operator overloading của Wikipedia .

    Tiếp theo, bạn sẽ xem xét từng toán tử này.

    Toán tử số học

    Các toán tử số học hoạt động giống như bạn mong đợi. Hãy thử chúng bằng cách thêm một loạt các thao tác vào DartPad của bạn:

    in ( 40 + 2 ); // 42 
    
    print ( 44 - 2 ); // 42 
    
    print ( 21 * 2 ); // 
    
    42 print ( 84/2 ) ; // 42.0
    
    

    Đố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 ( !=):

    in ( 42 == 43 ); // 
    
    in sai ( 42 ! = 43 ); // true
    
    

    Toán tử so sánh

    Dart sử dụng các toán tử so sánh điển hình:

    • Nhỏ hơn (<)
    • Lớn hơn (>)
    • Lơn hơn hoặc bằng(>=)
    • Nhỏ hơn hoặc bằng(<=)

    Dưới đây là một số ví dụ:

    print ( 42 < 43 ); // true 
    print (42> = 43); // false
    
    

    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à ||

    print (( 41 < 42 ) && ( 42 < 43 )); // true 
    
    print (( 41 < 42 ) || ( 42 > 43 )); // true
    
    

    Toán tử phủ định là dấu chấm than , biến false thành true và true thành false.

    print (! ( 41 < 42 )); // false
    
    

    Xem tài liệu Dart để biết danh sách đầy đủ các toán tử được hỗ trợ .

    Strings

    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’t và 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.

    String? middleName = null;
    
    print(middleName); // null
    
    

    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ử Elvis trong 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.

    Kết thúc phần 2.

  • Cách Flutter render các Widget

    Cách Flutter render các Widget

    Lời mở đầu

    Xin chào!

    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.‌‌

  • Cách viết Unit Test trong Flutter

    [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ì?

    Unit Test là gì? Tìm hiểu chi tiết về Unit Test
    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?

    Spring Break

    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)

    Nguồn tham khảo:

    Tác giả: LamNT59

  • Flutter Unit test: Mock dependencies using Mockito

    1. Tại sao phải sử dụng Mockito.

    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:

    1. Thêm các package vào pubspec.yaml.
    2. Tạo function để kiểm tra.
    3. Tạo file test với mock http.Client.
    4. Viết các test cho mỗi điều kiện.
    5. 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à:

    1. 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.
    2. 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ề:

    1. Nếu http gọi thành công sẽ trả về Album()
    2. 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 đó.

    Cảm ơn các bạn đã đọc bài viết của mình.

    Source code: https://github.com/falcon12795/unit_test

    Tài liệu tham khảo: https://docs.flutter.dev/cookbook/testing/unit/mocking

    Author: DuongVT19