Flutter | เจาะลึก Enum ในภาษา Dart ครบ จบ พร้อมใช้งาน

Pimorn Senakat
3 min readSep 21, 2024

--

What is Enum?

Enum คือ ชนิดของตัวแปรที่เราสามารถประกาศชุดของข้อมูลที่เราต้องการได้ ซึ่งช่วยให้ตัวแปรนั้นเข้าใจง่ายและเห็นภาพชัดเจนมากขึ้น เช่น วันในสัปดาห์ หรือ ค่าที่เป็นไปได้ของหมวดหมู่ต่างๆ

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

Simple Enum

การประกาศ Enum เริ่มต้นด้วย keyword enum ตามด้วยชื่อและค่าของข้อมูลที่เราต้องการกำหนด โดยชื่อ Enum ขึ้นต้นด้วยตัวอักษรใหญ่ตามหลัก UpperCamelCase ส่วนค่าข้อมูลใน Enum ขึ้นต้นด้วยตัวอักษรเล็กตามหลัก camelCase เพื่อความสอดคล้องและอ่านง่ายในโค้ด

// ENUM DECLARATION
enum Fruit { banana, apple, orange }

// USING ENUM
// If condition
if(fruit == Fruit.banana) {
print('Monkey loves me!');
}

// Switch-case
switch (fruit) {
case Fruit.banana:
print('Yellow');
case Fruit.apple:
print('Red');
case Fruit.orange:
print('Orange');
}

Properties และ methods ที่สามารถใช้งานกับ Enum ใน Dart ช่วยให้การจัดการกับ Enum สะดวกยิ่งขึ้น

Enum Properties
.values ใช้เพื่อเข้าถึงค่าทั้งหมดที่กำหนดไว้ใน Enum
.index ใช้เพื่อแสดงค่า index ของ Enum โดยเริ่มจาก 0
.name ใช้เพื่อแสดงชื่อของ Enum

// Print();
Fruit.values; // [Fruit.banana, Fruit.apple, Fruit.orange]
Fruit.banana.index; // 0
Fruit.banana; // 'Fruit.banana'
Fruit.banana.toString(); // 'Fruit.banana'
Fruit.banana.name; // 'banana'

Enum Methods
.compareByIndex(value1, value2)
ใช้เพื่อเปรียบเทียบค่าของ Enum สองค่าตามตำแหน่ง index

// compareByIndex
Enum.compareByIndex(Fruit.banana, Fruit.apple); // -1
Enum.compareByIndex(Fruit.apple, Fruit.banana); // 1
Enum.compareByIndex(Fruit.banana, Fruit.banana); // 0

.compareByName(value1, value2)
ใช้เพื่อเปรียบเทียบค่าของ Enum สองค่าตามชื่อ


// compareByName
Enum.compareByName(Fruit.apple, Fruit.banana); // -1
Enum.compareByName(Fruit.banana, Fruit.apple); // 1
Enum.compareByName(Fruit.banana, Fruit.banana); // 0

Enum Sorting
การเรียงลำดับค่าของ Enum สามารถเพิ่มความสะดวกในการใช้งาน โดยเฉพาะเมื่อต้องการแสดงตัวเลือกให้กับ User

final fruits = [Fruit.orange, Fruit.banana];
fruits.sort(); // Error: not a subtype of type 'Comparable<dynamic>'

// SORTING BY COMPARE METHOD
// Pass compare methed to sort()
fruits.sort((a, b) => Enum.compareByName(a, b)); // OR
fruits.sort(Enum.compareByName);

// RESULT: Fruit.banana, Fruit.orange

// SORTING BY COMPARABLE INTERFACE
// Implement Comparable<T> interface
enum Fruit implements Comparable<Fruit> {
...
...

@override
int compareTo(Fruit other) {
return Enum.compareByName(this, other);
}
}

// Now you can sort without passing compare method
days.sort();

// RESULT: Fruit.banana, Fruit.orange

Enum extensions method สำหรับ Enum.values
.byName(String name) ใช้เพื่อค้นหาค่าใน Enum ตามชื่อที่กำหนด
.asNameMap() สร้าง map จากชื่อของค่าใน Enum

Fruit.values.byName('apple'); // Fruit.apple

// WARNING: The method .byName will throw exception,
// if the name is not found
Fruit.values.byName('grape'); // Exception!!

เนื่องจากการใช้ Enum.values.byName สามารถทำให้เกิด Exception ได้ ถ้าค่าที่เราส่งเข้าไปไม่ถูกต้อง เพื่อหลีกเลี่ยงการเกิด exception เมื่อเรียกใช้ .byName เราควรสร้าง extension method ที่สามารถจัดการกับ null ได้มาครอบไว้อีกชั้น แล้วเรียกใช้แทน .byName

extension EnumByNameNullable<T extends Enum> on Iterable<T> {
T? byNameOrNull(String name) {
try {
return byName(name);
} catch(_) {
return null;
}
}
}

Fruit.values.byNameOrNull('melon'); // null

Override toString()
สามารถ override toString() ของ Enum เพื่อปรับการแสดงข้อความได้

// BEFORE: override toString()
// Print();
Fruit.values; // [Fruit.banana, Fruit.apple, Fruit.orange]
Fruit.banana; // 'Fruit.banana'
Fruit.banana.toString(); // 'Fruit.banana'
Fruit.banana.name; // 'banana'


enum Fruit {
...
...

@override
String toString() {
return name.toUpperCase();
}
}

// AFTER: override toString()
// Print();
Fruit.values; // [BANANA, APPLE, ORANGE]
Fruit.banana; // 'BANANA'
Fruit.banana.toString(); // 'BANANA'
Fruit.banana.name; // 'banana'

จากตัวอย่างข้างบนจะเห็นว่าการ override toString() ไม่ได้ทำให้ค่า name property เปลี่ยนแปลงไปด้วย

Enhanced Enum

ตั้งแต่ Dart 2.17 เราสามารถประกาศ Enum ที่มี property, method และ constructor รวมถึง override name property ได้เช่นกัน

โดยมีเงื่อนไขดังนี้

  • property ต้องเป็น final เท่านั้น
  • constructors ต้องเป็น const เท่านั้น
enum Fruit {
banana('B', 60),
apple('A', 65),
orange('O', 50);

const Fruit(this.name, this.calory);

final String name
final int calory;

String get code => '$name-$calory';

int mixWith(List<Fruit> fruits) {
return fruits.fold(0, (sum, it) => sum + it.calory);
}
}

// Access Enum property
Fruit.banana.name // 'B'
Fruit.banana.calory // 60

// Access getter and method
Fruit.banana.code; // B-60
Fruit.apple.mixWith([Fruit.banana]); // 125 calory!!

// WARNING: The method Enum.values.byName
// NOT uses override name property

Fruit.apple.name; // 'A'
Fruit.values.byName('A'); // Exception!
Furit.values.byName('apple'); // Fruit.apple

Fruit.values.asNameMap(); // { banana: Fruit.banana, .., }
Fruit.values.asNameMap()['banana']; // Fruit.banana
Fruit.values.asNameMap()['B']; // null

Converting Values to Enum

สามารถเรียกผ่าน .values เพื่อค้นหาค่า Enum จากชื่อ หรือสร้าง static method เพื่อช่วยในการค้นหา Enum จากค่าที่กำหนดได้

Fruit.values.byName('apple'); // Fruit.apple

// OR sta
enum Fruit {
banana('B', 60),
apple('A', 65),
orange('O', 50);
..
..

static Fruit? fromCalory(int cal) {
for(final value in values) {
if(value.calory == cal) {
return value;
}
}

return null;
}
}

Fruit.fromCalory(60); // Fruit.banana
Fruit.fromCalory(20); // null

--

--

Pimorn Senakat
Pimorn Senakat

Written by Pimorn Senakat

Mobile Developer | Flutter Evangelist

No responses yet