ถ้า iOS Developer อยากเขียน Flutter ต้องทำยังไง?

Amorn Apichattanakul
KBTG Life
Published in
13 min readOct 5, 2021

Flutter เป็น UI Framework ที่ไว้สำหรับเขียนครั้งเดียวเพื่อลงหลายๆ แพลตฟอร์ม แต่คำกล่าวที่ว่า “เขียนครั้งเดียว” แล้วลงได้หมดนั้นอาจจะเว่อร์ไปสักหน่อย เพราะสุดท้ายก็ต้องมาปรับให้เหมาะกับแต่ละแพลตฟอร์มอีกรอบอยู่ดี เช่น เราเขียนเพื่อเรียก GPS บนแอปมือถือ แต่เราจะไม่สามารถใช้ฟังก์ชันนี้บน Windows หรือ macOS ได้ ฉะนั้นแล้วโค้ดจะไม่ได้เขียนแค่ครั้งเดียวแล้วนะครับ อย่างไรก็ดีการที่แชร์กันได้อย่างน้อยกว่า 90% นั้นนับว่ายอมเยี่ยมแล้ว

ตัวผมเองเริ่มต้นเขียน iOS ตั้งแต่สมัย iPhone 3GS และในช่วง 1.8 ปีที่ผ่านมาได้เรียนรู้เกี่ยวกับ Flutter เพิ่มเติมเพราะมีโปรเจคที่ต้องใช้ ซึ่ง Dart เป็นภาษาและ Framework ที่ผมชอบ อ่านง่าย ทั้งยังโชคดีที่ Flutter Team ทำ Tutorial ไว้ดีมาก ช่วยให้ผมสามารถเริ่มต้นง่ายๆ

ตอนแรกที่ได้เริ่มเขียน Dart ก็รู้สึกว่าภาษาแปลกดี มีทั้งอันที่ชอบและไม่ค่อยชอบ ย้อนไปตอนเริ่มเขียนครั้งแรก ผมพยายามหาว่ามีใครไหมนะที่มาจากสาย iOS เหมือนกันแล้วทดเหมือนโน้ตข้อสอบว่า Swift ใช้แบบนี้ Dart ใช้ยังไง พอลองหาแล้วปรากฏว่าไม่เจอเลย จึงต้องมาเรียนรู้เองระดับหนึ่งเหมือนกัน ทั้งเรื่อง Syntax ต่างๆ ผ่านไปสักพักก็คิดว่าเราเนี่ยแหละที่ควรต้องเขียนขึ้นมา เพราะเราเองรู้ทั้ง 2 ภาษา ดังนั้นน่าจะมีประโยชน์ต่อเพื่อนๆ iOS เหมือนกัน หลักๆ ผมนำมาจาก Doc ของ Swift แล้วแปลเป็น Dart นะครับ มาเริ่มด้วยเรื่องพื้นฐานสุดๆ อย่าง Variable กัน

การประกาศตัวแปร Constants and Variables

Swift

let name = “Swift”var lastname = “Apple”

Dart

final name = "Dart";
const nickName = "Flutter";
var lastname = "Google";

สำหรับตัวแปรที่เปลี่ยนค่าไม่ได้ Dart จะใช้คำว่า Final ในขณะที่ Swift ใช้ Let ส่วนตัวแปรที่เปลี่ยนค่าได้ ทั้งคู่จะใช้คำว่า Var เหมือนกัน

ในภาษา Dart จะมีประกาศตัวแปรแบบ Immutable Objects อยู่ 2 แบบ คือ final กับ const 2 แบบนี้ต่างกันยังไง?

  • Final คุณไม่รู้ว่าค่าสุดท้ายจะเป็นค่าอะไรขณะที่ Compile เพราะค่าจะถูกใส่เมื่อ Run Time ยกตัวอย่างเช่น การดึงข้อมูลจาก API
  • Const คุณรู้ว่าคืออะไรตั้งแต่ตอน Compile เช่น API Path หรือชื่อตัวแปรที่จะใช้

Note: อย่าลืมนะครับ Dart ต้องการ Semicolon ตอนท้าย ไม่เหมือนกับ Swift

การประกาศชื่อ

Swift

private var name: String = "Swift"
let isLoggedIn = false

Dart

String _name = "Swift"; // It's mean private 
var _name = "Swift"; // has the same meaning as above but you can omit declare String
final isLoggedIn = false;

ใน Swift เราสามารถเติมคำว่า private , internal , public , protected , fileprivate ในหน้า Class, Function หรือ Variable ได้

ใน Dart การประกาศแบบ Private จะใช้ _นำหน้าชื่อที่จะใช้ ถ้าไม่มีจะนับว่าเป็น Public เราสามารถประกาศบอก Type ได้เหมือนกับ Swift เจาะจงไปเลยว่าเป็น String หรือ Int

Tuples

Swift

let http404Error = (404, “Not Found”)

ส่วน Dart ไม่มี Tuples จะใช้ Object แทน

Optionals

Swift

var serverResponseCode: Int? = 404

Dart ก่อน 2.12 จะยังไม่มีเรื่อง Optional ซึ่งจะใช้กับ Flutter 2.0 เป็นต้นไป ส่วน Dart 2.12 จะประกาศเหมือนด้านล่างครับ

Int? serverResponseCode = 404;

If Statements and Forced Unwrapping

Swift

var name: String? = "Swift"
if let unwrapName = name {
print(unwrapName)
} else {
print("name is null")
}// Or you can use Guard with let
var name: String? = "Swift"
guard let unwrapName = name else {
print("name is null")
return
}
print(unwrapName)

Dart ไม่มีการ Unwrap Optional จึงใช้วิธีเช็คว่าค่าเป็น Null รึเปล่า ก่อนที่จะนำไปใช้แบบ Java

String? name = "Dart";
if (name != null) {
print(name);
} else {
print("name is null");
}

Error Handling

Swift

func testAge(age: Int) throws {
if age < 0 {
throw "Age can't be negative"
}
// do something}do { try testAge(age: -2)} catch {// an error was thrown}

Dart

void testAge(int age) { // void is optional, no need to declare
if (age < 0) {
throw "Age can't be negative";
}
// do something
}
try {
testAge(-2);
} catch(e) {
print(e.message);
}

Ternary Conditional Operator

Swift

let contentHeight = 40let hasHeader = truelet rowHeight = contentHeight + (hasHeader ? 50 : 20)

Dart

final contentHeight = 40;final hasHeader = true;final rowHeight = contentHeight + (hasHeader ? 50 : 20);

Strings and Characters

อันนี้ใช้เหมือนกันเลยครับ

Swift

let someString = "Some string literal value"let quotation = """The White Rabbit put on his spectacles. “Where shall I begin,please your Majesty?” he asked.“Begin at the beginning,” the King said gravely, “and go ontill you come to the end; then stop.”"""

Dart

const someString = "Some string literal value";const quotation = """The White Rabbit put on his spectacles. “Where shall I begin,please your Majesty?” he asked.“Begin at the beginning,” the King said gravely, “and go ontill you come to the end; then stop.”""";

String Interpolation

Swift

let name = "Swift"
let version = 5.4
let people = People(name: "Apple, age: 4)
print("my name is \(name) version: \(version) age: \(people.age)")

Dart

final name = "Flutter";
final version = 2.0;
final people = People("Apple", 4);
print("my name is $name version: ${version} age: ${people.age}");
// For simple variable you can omit {}

Arrays

Swift

// Create Empty Array
var someInts = [Int]()
someInts.append(5)
var shoppingList: [String] = [“Eggs”, “Milk”]
print(shoppingList[0]) // Print "Eggs"
print("The shopping list contains \(shoppingList.count) items.")
// Prints "The shopping list contains 2 items."
shoppingList.append("Fish")
for item in shoppingList {
print(item)
}
// Prints
// Eggs
// Milk
// Fish
var peopleList: [People] = [People(name: "Amorn"), People(name: "Swift"), People(name: "Apple")]
for people in peopleList {
print(people.name)
}
// Prints
// Amorn
// Swift
// Apple

Dart

// Create Empty Array
var someInts = <int>[];
someInts.add(5);
var shoppingList = ["Eggs", "Milk"];
print(shoppingList[0]); // Print "Eggs"
print("The shopping list contains ${shoppingList.length} items.") ;
// Prints "The shopping list contains 2 items."
shoppingList.add("Fish")for (String item in shoppingList) {
print(item);
}
// Prints
// Eggs
// Milk
// Fish
List<People> peopleList = [People("Amorn"), People("Swift"), People("Apple")];for (People people in peopleList) {
print(people.name);
}
// Prints
// Amorn
// Swift
// Apple

Dictionaries or map

Swift

var fruitDictionary: [String: Int] = [“Apple”: 30, “Banana”: 50, "Carrot": 70, "Durian": 100]// Access variable in key
let durianPrice = fruitPrice["Durian"]
for (fruitName, fruitPrice) in fruitPrice {
print("\(fruitName): \(fruitPrice)")
}
// Prints
// Apple: 30
// Banana: 50
// Carrot: 70
// Durian: 100

Dart

Map<String, int> fruitDictionary = {"Apple": 30, "Banana": 50, "Carrot": 70, "Durian": 100};// Access variable in key
final durianPrice = fruitPrice["Durian"];
for (MapEntry e in fruitDictionary.entries) {
print("${e.key}: ${e.value}")
}
// Prints
// Apple: 30
// Banana: 50
// Carrot: 70
// Durian: 100

For-In Loops

Swift

for index in 0…5 {
print('hello \(index)')
}

Dart

for (int i = 0; i < 5; i++) {
print('hello ${i}');
}

Conditional Statements

Swift

var temperatureInFahrenheit = 30if temperatureInFahrenheit <= 32 {print(“It’s very cold. Consider wearing a scarf.”)} else if temperatureInFahrenheit > 32 && temperatureInFahrenheit < 72 {
print(“It’s cozy, I like it.”)
} else {
print("too hot!!!")
}

Dart

var temperatureInFahrenheit = 30;if (temperatureInFahrenheit <= 32) {print(“It’s very cold. Consider wearing a scarf.”);} else if (temperatureInFahrenheit > 32 && temperatureInFahrenheit < 72) {
print(“It’s cozy, I like it.”);
} else {
print("too hot!!!");
}

Switch

Swift

let someCharacter: Character = “z”switch someCharacter {   case “a”:   print(“The first letter of the alphabet”)   case “z”:   print(“The last letter of the alphabet”)   default:   print(“Some other character”)
}
enum Language {
case english
case thai
case spanish
}let language: Language = .english
switch language {
case .english:
print("I speak English")
case .thai:
print("I speak Thai")
case .spanish:
print("I speak Spanish")
}

Dart

final someCharacter = "z";switch (someCharacter) {case "a":
print("The first letter of the alphabet");
break;
case "z":
print("The last letter of the alphabet");
break;
default:
print("Some other character");
break;
}
enum Language {
english,
thai,
spanish
}
final Language language = Language.english;switch language {
case Language.english:
print("I speak English");
break;
case Language.thai:
print("I speak Thai");
break;
case Language.spanish:
print("I speak Spanish");
break;
}

Functions

Swift

// Function Parameters and Return Values
func greet(person: String) -> String {
let greeting = “Hello, “ + person + “!”
return greeting
}
// Functions With Multiple Parameters
func greet(person: String, alreadyGreeted: Bool) -> String {
if alreadyGreeted { return greetAgain(person: person) } else { return greet(person: person) }}// Functions Without Return Valuesfunc greet(person: String) { print(“Hello, \(person)!”)}greet(person: “Dave”)// Prints “Hello, Dave!”// Default Parameter Values
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
// If you omit the second argument when calling this function, then
// the value of parameterWithDefault is 12 inside the function body.}someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6)
// parameterWithDefault is 6
someFunction(parameterWithoutDefault: 4)
// parameterWithDefault is 12

Dart

// Function Parameters and Return Values
// Dart has single line return function
String greet(person: String) => "Hello, $person !";orString greet(String person) {
final greeting = "Hello, $person !";
return greeting;
}
// Functions With Multiple Parameters
String greet(String person, bool alreadyGreeted) {
if (alreadyGreeted) { return greetAgain(person); } else { return greet(person); }}// Functions Without Return Valuesvoid greet(String person) { print("Hello, $person!");}greet("Dave"); // Dart doesn't have Argument Labels// Prints “Hello, Dave!”// Default Parameter Values
void someFunction(int parameterWithoutDefault, {int parameterWithDefault = 12}) {
}someFunction(3, parameterWithDefault: 6);
// parameterWithDefault is 6
someFunction(4);
// parameterWithDefault is 12

Closures

Swift

let numbers = [0, 1, 2, 3, 4]
let reversedNumbers = numbers.sorted(by: { $0 > $1 } )
// reversedNames is equal to [4, 3, 2, 1 , 0]

Dart

final numbers = [0, 1, 2, 3, 4];
numbers.sort((a, b) => b.compareTo(a));
// numbers is equal to [4, 3, 2, 1 , 0]

Enumerations

Swift

enum CompassPoint: String {   case north   case south   case east   case west}// Associated Values
enum Barcode {
case upc(Int, Int, Int, Int) case qrCode(String)}// Iterating over Enumeration Cases
enum Beverage: CaseIterable {
case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print(“\(numberOfChoices) beverages available”)
for beverage in Beverage.allCases {
print(beverage)
}
// coffee
// tea
// juice

Dart ไม่มี Enum แบบซับซ้อนเหมือน Swift จะมีแค่ Enum พื้นฐานไว้ใช้สำหรับ Switch Case เท่านั้น

Structures and Classes

Swift

จะมีคอนเซ็ปต์ Value Types คือ Struct และ Reference Type คือ Class

struct Resolution {var width = 0var height = 0}class VideoMode {var resolution = Resolution()let interlaced: Boollet frameRate: Doublelet name: Stringinit(name: String, frameRate: Double, interlaced: Bool) {
self.name = name
self.frameRate = frameRate
self.interlaced = interlaced
}
}

Dart

จะไม่มีคอนเซ็ปต์ส่วนนี้ ทุกอย่างคือ Reference Types ทั้งหมด

class Resolution {var width = 0;var height = 0;}class VideoMode {
Resolution resolution = Resolution();
final bool interlaced; final Double frameRate; final String name;// Automatic initializers in constructors
// if you don't have require, it will have value as null, if you forget to add value
VideoMode({
@required this.interlaced,
@required this.frameRate, @required this.name,});}

Computed Properties

Swift

struct Rect {   var origin = Point()   var size = Size()   var center: Point {   get {      let centerX = origin.x + (size.width / 2)      let centerY = origin.y + (size.height / 2)      return Point(x: centerX, y: centerY)    }   set(newCenter) {      origin.x = newCenter.x — (size.width / 2)      origin.y = newCenter.y — (size.height / 2)   }// Read only Properties
var isSizeZero: Bool {
return size.width == 0.0 && size.height == 0.0
}
}}

Dart

class Rect {   var origin = Point();   var size = Size();

int get center {
final centerX = origin.x + (size.width / 2); final centerY = origin.y + (size.height / 2); return Point(centerX, centerY);
}
void set center(Point newCenter) {

origin.x = newCenter.x — (size.width / 2);
origin.y = newCenter.y — (size.height / 2); }
// Single line return will use =>
bool get isSizeZero => (size.width == 0.0 && size.height == 0.0);}

และมาถึงอันสุดท้าย ที่ทำผมงงตาแตกตอนเจอครั้งแรก

Cascades

คือการที่เราไม่ต้องเขียนตัวแปรซ้ำๆ เราสามารถย่อให้สั้นลงได้ ซึ่ง Swift จะไม่มี Cascades

Swift

let people = People()
people.tag = 1
people.name = "Apple"
people.lastname = "Swift"
people.age = 20
people.isGraduated = fasle

Dart

People()
..tag = 1
..name = "Apple"
..lastname = "Swift"
..age = 20
..isGraduated = false

ผมคิดว่าน่าจะครอบคลุมเบสิกที่ใช้ทั่วไปได้ระดับหนึ่งนะครับ สำหรับคนที่จะลอง Dart แบบที่ยากกว่านี้จะไม่ได้มีเขียนไว้ครับ เพราะเขียนได้อีกยาวออกมาเป็นหนังสือเลย 😅 ส่วนต่อไปจะเกี่ยวกับเรื่อง UI

User Interface

ในบทความนี้จะเทียบระหว่าง UIKit กับ Dart ซึ่งส่วนของ SwiftUI นั้นผมไม่ได้เขียนครอบคลุมเพราะว่ายังไม่ค่อยคล่องเรื่อง SwiftUI นัก ไว้มีประสบการณ์มากกว่านี้จะนำมาเล่านะครับ

UIKit เป็น Imperative UI ในขณะที่ Dart เป็น Declarative UI ต่างกันยังไง ลองไปอ่านดูได้ครับ ถ้าให้เล่าง่ายๆ Declarative UI จะคล้ายกับ HTML Style ครับผม

ใน Swift UIKit หลักๆ เราจะรู้จักตามนี้

  1. Storyboard
  2. Nib Files
  3. ViewController
  4. UIView

UI ใน Dart ทั้งหมดจะทำด้วยโค้ดเท่านั้น ไม่มี Storyboard หรือไฟล์ UI เหมือนกับของ Swift แต่ยังดีที่ Flutter มี Hot Reload ทำให้ง่ายต่อการทดลองวาง Layout แม้ว่าจะมองไม่เห็น UI ก็ตาม

ทุกอย่างใน Dart จะใช้ Widget ไม่ว่าจะปุ่ม รูป หรือแม้กระทั่งการเว้นช่อง Spacing, Padding, Margin หรือ Layout เช่น Row, Column ก็เป็น Widget ทั้งสิ้น แปลกดีใช่มั้ยครับ ViewController กับ UIView ก็จะนับเป็น Widget เช่นกัน

ใน UIKit คนเขียนสามารถอัพเดต UI ด้วยการใส่ค่าใหม่ๆ ลงไป UI ของ Swift ก็จะถูกอัพเดต ในขณะที่คอนเซ็ปต์ Dart นั้น UI จะไม่สามารถถูกใส่ค่าใหม่ได้ ทุกอย่างเป็น Immutable ซึ่งการจะอัพเดต UI ได้ต้อง Rebuild ทั้ง UI ใหม่หมดเลย โดยจะมีคอนเซ็ปต์ Stateful กับ Stateless Widgets

  • StatelessWidget Widget ที่ไม่สามารถทำการเปลี่ยนค่าได้ โดย UI พื้นฐานทั่วไปของ Dart จะเป็น Stateless ทั้งหมด เช่น ข้อความ ปุ่ม รูป เหมาะสำหรับการทำ Hardcore UI
  • StatefulWidget Widget ที่สามารถอัพเดต UI ได้ตามเงื่อนไข ตัวแปร หรือค่าต่างๆ ที่ได้มาจาก API

พอฟังแล้ว ทำไมเราไม่ให้ทุกอย่างเป็น StatefulWidget หมดเลยล่ะ มีด้วยเหรอ UI ที่ห้ามอัพเดต แล้วจะเขียนแอปกันยังไง คอนเซ็ปต์นี้หลักๆ ทำไว้เพื่อ Performance ครับ สามารถอ่านเพิ่มได้ที่ลิงก์ด้านล่างนี้

ส่วนที่ว่าห้ามอัพเดต ทำให้ Dart มีคอนเซ็ปต์ Build ครับ คือทำลายของเก่าทิ้งแล้วสร้างใหม่หมดเลย ดู UI ตัวใหม่ ไม่ได้อัพเดตของเก่าแต่อย่างใด

NavigationController

Dart

เท่าที่ผมเจอมา Root ของแอปยังไงก็ใช้ MaterialApp กับ Scaffold คู่กัน เหมือนกับคนที่ทำแอป iOS ยังไงก็ใช้ NavigationController ไว้ตั้งแต่ต้นเลย

MaterialAppScaffold
  • MaterialApp เป็นการบอกว่าแอปเราจะใช้ Material Design เป็น Google Design
  • Scaffold เหมือนกับ NavigationController ซึ่ง Widget ตัวนี้จะมีพวก AppBar, BottomBar และ Components อื่นๆ ที่จำเป็นมาให้

ถ้าอยากจะใช้ iOS Design จริงๆ สามารถเลือกใช้ CupertinoApp แทน แต่ผมว่าอย่าเลยครับ เพราะ MaterialApp ดีกว่า บางตัวใน CupertinoApp ทำไม่ได้ด้วยซ้ำ ซึ่งผมเข้าใจว่าเขาน่าจะโฟกัสตรงปรับ MaterialApp มากกว่า ก็ Product ของ Google เขาเองน่ะ

MaterialApp(   
home: Scaffold(
appBar: AppBar(
title: Text('Home'),
),
)
)

เมื่อทำตามวิธีข้างต้น จะได้ผลตามด้านล่างครับ

สำหรับคนที่อยากอ่านรายละเอียดเพิ่มเติมครับ

UITabBarController

Dart

UITableView & UICollectionView

Dart

มีแค่ Widget คือ ListView

ถ้าเราจะใช้ข้อมูลเยอะๆ จำเป็นต้องใช้ ListView.builder แทนครับ แต่ใน Swift สามารถใช้ TableView กับข้อมูลเยอะๆ ได้เลย

TableViewCell Or CollectionViewCell

Dart

ใช้ ListTiles หรือจะใช้ Custom Widget ทำ UI แทนได้ครับ

UILabel

Dart

UIButton

Dart

มีปุ่มหลากหลายดีไซน์เลยครับ ลองเลือกจากด้านล่างได้เลย

  • Button แบบมีเงา
  • Button แบบ Flat Design
  • Floating Button แบบ Android
  • Button แบบมีไอคอน
  • Button แบบมีคลื่นตอนคลิก
  • Button แบบมีข้อความ
  • Button แบบมีแต่เส้น Outline

หรือถ้าอยากทำปุ่มเอง สามารถนำ TapGesture ไปคลุมไว้ที่ Widget ไหนก็ได้ครับ เท่านั้นก็จะทำงานเหมือนปุ่มแล้ว

UIImageView

Dart

จะมีหลายแบบใน Widget เดียวครับ ไม่ว่าจะเป็นรูปไอคอนหรือรูปโหลดจากเน็ตเวิร์ค ใส่อันเดียวกันได้หมดเลย

Threading & Asynchronicity

Dart

Dart เป็นภาษา Single Thread แปลว่าไม่สามารถสั่งให้ทำงานเบื้องหลังได้ครับ จะเหมือนกับ node.js

loadData() async {
String dataURL = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(dataURL);
print("This message will show after API is finished");
setState(() {
widgets = jsonDecode(response.body);
});
}

จากโค้ดด้านบน Await จะเป็นคำสั่งที่บอกว่า “ทำบรรทัดนี้ให้เสร็จก่อนที่จะไปบรรทัดต่อไป รอนานแค่ไหน ผมก็จะรอ” และหลังจากนั้นผมจะนำค่าที่ได้ไปใส่ในตัวแปร แล้ว Rebuild ทั้งหน้าใหม่ด้วยคำสั่ง setState()

แต่ถ้าอยากได้ Callback คล้ายๆ iOS เราสามารถเปลี่ยน Await ให้เป็น Then ได้แบบด้านล่าง

loadData() async {
String dataURL = "https://jsonplaceholder.typicode.com/posts";
http.get(dataURL).then((response) {
setState(() {
widgets = jsonDecode(response.body);
});
});
print("This message will show without waiting for network");
}

ที่ต่างกันคือ Dart จะทำงานบรรทัดต่อไปเลยไม่รอ และถ้าทำงานเสร็จเมื่อไหร่ จะกระโดดไปทำงานใน Block ทันที ในขณะที่แบบแรกคือจะรอจนกว่าจะเสร็จ จึงจะทำงานบรรทัดต่อไปครับ สามารถอ่านรายละเอียดเพิ่มเติมได้ที่นี่

ViewLifeCycle

ผมเจอบทความที่อธิบายได้ละเอียดที่นี่

UIView
awakeFromNib
UIViewController
viewDidLoad
viewWillAppear
viewDidAppear
viewWillDisappear
viewDiddisappear

Dart

Widget
initState() - จะถูกเรียกครั้งแรกครั้งเดียว เวลาเข้ามาที่หน้า UI จะคล้้ายๆกับ viewDidLoad ไม่คล้าย init นะครับ ผมว่าเราไม่มี nib file
build() - จะเป็น function ที่บอกว่า UI หน้านี้จะ build อะไรบ้าง จะคล้ายๆกับ draw(_:) เพราะว่าคำสั่งนี้ build จะถูกเรียกตลอดเวลา เวลามีการเปลี่ยน UIsetState() - สั่งให้ Widget เรียก build() ใหม่ จะคล้ายๆกับ setNeedLayout ใน Swiftdispose() - เหมือน deinit ครับ ไว้ clear memory ที่ไม่จำเป็นจะใช้แล้วเช่นพวก subscriber

อันนี้จะเป็น 4 ฟังก์ชันหลักใน Widget UI ที่เราใช้กันบ่อยๆ ยังมีฟังก์ชันอื่นๆ อีกนะครับ แต่ส่วนใหญ่จะไม่ได้ใช้เท่าไหร่

มาถึงสิ่งสุดท้ายที่สำคัญมาก ซึ่ง Swift ไม่มี แต่ Flutter ใช้เยอะมาก

BuildContext

จริงๆ แล้วผมเองยังแอบงงๆ กับตัวนี้เหมือนกันครับ แต่จะพยายามอธิบายให้ดีที่สุด 😢 มันคือ Object ที่เป็น Reference Widget นั้นๆ ซึ่งสามารถส่งค่าผ่านไป Object อื่นๆ ได้ โดยทำงานเหมือนเป็น Reference ให้

Architecture

Swift

iOS Developers ส่วนใหญ่จะใช้ MVC, MVVM, Clean Swift ไม่ก็ VIPER กัน

Dart

จะมีคอนเซ็ปต์คล้ายๆ กัน แต่ส่วนใหญ่จะใช้ BLoC (Business Logic Component) คล้ายๆ MVVM แหละครับ คอนเซ็ปต์เดียวกัน ชื่อต่างกัน ก็คือหลักการที่เราเอา Business Logic ออกจาก View เพื่อที่จะได้ทำการเทสได้ แต่ Flutter Developer จะชอบใช้คู่กับ StreamBuilder

เป็น Widget ไว้สำหรับ Binding View เข้ากับ Data โดยจะเป็น Observable Pattern เหมือนกับใน Swift ที่ถ้ามีการอัพเดต Data จะทำการอัพเดต UI ให้โดยอัตโนมัติ ซึ่งใน Swift จะเป็น Optional ครับ คนส่วนใหญ่ที่ผมเจอจะไม่ได้ใช้กัน ในขณะที่ Flutter Developer จะใช้ Pattern นี้กันเกือบทุกคนและเกือบทุกที่ครับ ประหนึ่ง SwiftUI Developers ที่จะใช้ Combine Framework กัน

Dependency Manager

Swift

Cocoapods, Carthage, Swift Package Manager

Dart

Pub

ถ้าจะหา Lib ใดๆ สามารถดูได้ที่ https://cocoapods.org/

Must-Have Libs

Swift

Alamofire
KingFisher
SnapKit
Lottie
Hero

อันนี้แค่ตัวอย่างนะครับ แต่ละคนน่าจะใช้ไม่เหมือนกัน

Dart

ใช้ Libs หมด เพราะทุกอย่างใน Flutter ต้องการ Libs ทั้งหมด ด้วยความที่ Flutter เป็นแค่ UI Framework ไม่ได้มี Functionality อื่นๆ ดังนั้นไม่ว่าจะเป็นกล้อง GPS รูปถ่าย หรือแม้กระทั่งเปิดเว็บไซต์ยังต้องใช้ Libs เลย 😄 ถึงจะไม่ได้รวมเข้าไปใน Flutter Framework ให้ แต่ทาง Flutter Team ได้ทำ Libs พื้นฐานทุกตัวให้หมดแล้ว ไม่ต้องกังวลไปอย่างใดครับ

สำหรับบทความนี้น่าจะถูกอัพเดตเรื่อยๆ นะครับ เผื่อผมยังหลุดหรือพลาดอะไรไป จะได้นำมาเสริมให้ครับผม สำหรับชาว iOS ทั้งหลาย หวังว่าบทความนี้จะช่วย iOS ที่อยากลองเขียน Dart ได้นะครับ และสุดท้ายนี้… ถึงคุณจะเขียน Dart ยังไง ทักษะ iOS ก็ยังจำเป็นต้องมีในการทำ Flutter ครับ

สำหรับชาวเทคคนไหนที่สนใจเรื่องราวดีๆแบบนี้ หรืออยากเรียนรู้เกี่ยวกับ Product ใหม่ๆ ของ KBTG สามารถติดตามรายละเอียดกันได้ที่เว็บไซต์ www.kbtg.tech

--

--

KBTG Life
KBTG Life

Published in KBTG Life

At KBTG, we never cease to develop and innovate financial technologies on top of our “Customer First” mindset. We are the driving force behind KBank’s success as well as their navigator exploring the digital world beyond Thailand.

Amorn Apichattanakul
Amorn Apichattanakul

Written by Amorn Apichattanakul

Google Developer Expert for Flutter & Dart | Senior Flutter/iOS Software Engineer @ KBTG