Day 8: Inheritance & Mixins
Share logic across classes without deep hierarchies using mixins and inheritance to build reusable, composable code like professional Flutter developers.
မင်္ဂလာပါ.. 👋
100 days of Flutter ရဲ့ Day 8 က ကြိုဆိုပါတယ်။ ဒီနေ့မှာတော့ dart langauge နဲ့ ပတ်သက်တာတွေကို ဆက်လက်ပြီး လေ့လာသွားပါမယ်။ ဒီနေ့ဆွေးနွေးသွားမယ့် ခေါင်းစဥ်တွေကတော့
- Inheritance
- Mixins
တွေပဲ ဖြစ်ပါတယ်။
Inheritance
ဒါကတော့ Object oriented programming (OOP) အကြောင်းပြောတဲ့အခါ မပါမဖြစ် ခေါင်းစဥ်တခုပါ။ Inheritance ဆိုတာကို family tree တခုလိုမျိုး မြင်ကြည့်လို့ရပါတယ်။ ဆွေစဥ်မျိုးဆက် အဆင့်ဆင့် အမွေဆက်ခံလာတာမျိုးပါ။ ကိုယ့်ဆီမှာ သပ်သပ်ထပ်လုပ်စရာ မလိုဘဲ ပါလာတဲ့ ဆံပင်အရောင်တို့၊ မျက်လုံးအရောင်တို့လို feature တွေပေါ့။
Programming မှာတော့ inheritance ဆိုတာ child class က parent class ကနေ properties တွေရော methods တွေပါ လက်ဆင့်ကမ်း ရသွားတာလို့ ပြောလို့ရပါတယ်။ ရလာတာတွေအပြင် ထပ်ပြီးတော့လဲ ထည့်တာတွေ၊ ပြင်တာတွေ လုပ်သွားလို့ရပါတယ်။ Inheritance ကိုတော့ Is-A relationship လို့ခေါ်ကြပါတယ်။ အခုလက်ရှိ class ကို တခြား class တခု inherit လုပ်လိုက်တဲ့အခါ parent class ဖြစ်သွားပါတယ်။

ဥပမာ code လေးနဲ့ တချက်ကြည့်ရအောင်ပါ။
class Device {
Device(this.brand, this.model);
String brand;
String model;
void powerOn() {
print('$brand $model is powering on...');
}
void powerOff() {
print('$brand $model is shutting down...');
}
}
class Smartphone extends Device {
Smartphone(super.brand, super.model, this.operatingSystem);
String operatingSystem;
void makeCall(String number) {
print('$brand is calling $number...');
}
}
void main() {
final myPhone = Smartphone('Samsung', 'Galaxy S24', 'Android');
myPhone.powerOn();
myPhone.powerOff();
myPhone.makeCall('123-4567');
}
// Output:
// Samsung Galaxy S24 is powering on...
// Samsung Galaxy S24 is shutting down...
// Samsung is calling 123-4567...ဒီမှာကြည့်မယ်ဆို Smartphone class က Device class ကို extends လုပ်ထားတာကို တွေ့ရမှာပါ။ extends ကိုတော့ inherit လုပ်တဲ့နေရာမှာ သုံးပါတယ်။ ဒီလို လုပ်လိုက်ခြင်းအားဖြင့် Device class ထဲမှာ ရှိတဲ့ properties တွေ၊ methods တွေအကုန်ကို Smartphone က သုံးလို့ရသွားပါတယ်။ myPhone.powerOn(), myPhone.powerOff() တွေကတော့ Device class ကနေ inherit လုပ်ပြီး ရရှိလာတာတွေပဲ ဖြစ်ပါတယ်။
တခုသတိထပ်ထားပြီး ကြည့်ရမှာကတော့ constructor တွေကနေ vlaue တွေ ဘယ်လို pass လုပ်ပေးထားလဲဆိုတာပါ။ မနေ့က class တွေအကြောင်းပြောတော့ this keyword ကို သုံးပြီးတော့ constructor ထဲကို ထည့်ပေးလိုက်တဲ့ value ကို class ရဲ့ properties အဖြစ် သတ်မှတ်ပါတယ်။ ဒီမှာတော့ super keyword ကို အသုံးပြုပြီးတော့ ဝင်လာတဲ့ value ကို parent class ရဲ့ constructor ကတဆင့် parent class properties အဖြစ် ထည့်သွားပါတယ်။ တကယ်လို့ parent class ကိုလဲ ပေးရမယ် အခုလက်ရှိ class အတွက်လဲ လိုအပ်တယ်ဆိုရင် super ရော this ရောကို အတူတွဲရေးလို့ ရပါတယ်။
Inherit လုပ်တာတွေအပြင် လက်ရှိ child class ဖြစ်တဲ့ Smartphone class ထဲမှာလဲ ထပ်ဖြည့်ချင်တဲ့ properties တွေ method တွေ ထပ်ဖြည့်လို့ရပါတယ်။
ဆက်ပြီးတော့ နောက်ထပ် inherited class တခု တည်ဆောက်ကြရအောင်ပါ။
class Smartwatch extends Device {
Smartwatch(super.brand, super.model, this.batteryPercent);
int batteryPercent;
@override
void powerOn() {
// super.powerOn();
if (batteryPercent > 10) {
print('$brand $model: Loading with battery $batteryPercent...');
} else {
print('Battery too low! Please charge!');
}
}
}
void main() {
final myWatch = Smartwatch('Apple', 'Watch Ultra', 85);
myWatch.powerOn();
}
// Output:
// Apple Watch Ultra: Loading with battery 85...ဒီမှာတော့ Smartwatch အတွက် class တခု ထပ်ဆောက်ပြီးတော့ Device class ကနေပဲ inherited လုပ်ထားတာ ဖြစ်ပါတယ်။ Smartwatch class ထဲက powerOn ဆိုတာကတော့ Device parent class မှာ ပါတဲ့ powerOn ကို override လုပ်ထားတာ တွေ့ရမှာပါ။ Parent class က method တွေကို တိုက်ရိုက်မသုံးချင်တဲ့အခါ၊ အသုံးမလိုတဲ့အခါ override လုပ်လိုက်လို့ရပါတယ်။ တကယ်လို့ parent class ရဲ့ method ကိုပါ ခေါ်ချင်တယ်ဆိုရင်တော့ super.powerOn() ဆိုပြီး သုံးလို့ရပါတယ်။ super keyword ကတော့ child class ထဲမှာ parent ကို reference လုပ်ချင်တဲ့အခါ အသုံးပြုပါတယ်။
အခုလိုမျိုး inheritance ကို သုံးချင်းအားဖြင့် လုပ်ဆောင်ပုံတူတဲ့ code တွေကို အခါခါ ရေးနေစရာ မလိုတော့ဘဲ base/parent class မှာ ကြေငြာလိုက်တာနဲ့ သူ့ကို inherit လုပ်တဲ့ class တိုင်းလိုလိုကလဲ အသုံးပြုလို့ ရသွားမှာ ဖြစ်ပါတယ်။ ဒါ့အပြင် value တွေ pass လုပ်ဖို့ လိုတဲ့အခါ base/parent class ကို type အဖြစ်ကြေငြာလိုက်တာနဲ့ ကြိုက်တဲ့ derived/child class တွေကို pass လုပ်လို့ရသွားမှာပါ။ ဒါကိုတော့ polymorphism ပုံစံမျိုးစုံနဲ့ တည်ရှိလို့ရတာလို့ ခေါ်ပါတယ််။
// Base class
abstract class PaymentMethod {
void processPayment(double amount);
}
// Different payment types
class CreditCard extends PaymentMethod {
@override
void processPayment(double amount) {
print('💳 Processing \$$amount via Credit Card');
print(' Connecting to bank...');
print(' Payment authorized!');
}
}
class PayPal extends PaymentMethod {
@override
void processPayment(double amount) {
print('🅿️ Processing \$$amount via PayPal');
print(' Redirecting to PayPal...');
print(' Payment complete!');
}
}
class CryptoCurrency extends PaymentMethod {
@override
void processPayment(double amount) {
print('₿ Processing \$$amount via Bitcoin');
print(' Broadcasting transaction...');
print(' Waiting for confirmation...');
print(' Payment confirmed!');
}
}
// THE POWER OF POLYMORPHISM:
// This function accepts ANY PaymentMethod type!
void checkout(PaymentMethod payment, double total) {
print('\n🛒 Total: \$$total');
payment.processPayment(total); // Different behavior for each type!
print('✅ Order confirmed!\n');
}
void main() {
// All these are PaymentMethod types
final card = CreditCard();
final paypal = PayPal();
final crypto = CryptoCurrency();
// Same function, different behaviors!
checkout(card, 99.99);
checkout(paypal, 149.50);
checkout(crypto, 299.00);
}
// 🛒 Total: $99.99
// 💳 Processing $99.99 via Credit Card
// Connecting to bank...
// Payment authorized!
// ✅ Order confirmed!
//
// 🛒 Total: $149.5
// 🅿️ Processing $149.5 via PayPal
// Redirecting to PayPal...
// Payment complete!
// ✅ Order confirmed!
//
// 🛒 Total: $299.0
// ₿ Processing $299.0 via Bitcoin
// Broadcasting transaction...
// Waiting for confirmation...
// Payment confirmed!
// ✅ Order confirmed!အစဆုံး base class မှာ ဘာ implementation မှ မထည့်ချင်ဘဲ function ကိုပဲ ကြေငြာချင်တာမျိုးဆို abstract ဆိုပြီးတော့ သုံးပါတယ်။ သူ့ကိုတော့ concrete class တွေ ဆောက်လို့မရဘဲ extends လုပ်လို့ပဲ ရပါတယ်။ သူကနေ inherit လုပ်ထားတဲ့ class တွေကိုပဲ object တွေ အနေနဲ့ ဆောက်လို့ရမှာ ဖြစ်ပါတယ်။ ဒါကိုတော့ အပေါ်မှာ ပြထားသလိုမျိုး child class တွေမှာ ကွဲပြားတဲ့ implementation တွေ ရှိကိုရှိမှာ သေချာတဲ့အခါ base class မှာ implementation ကို သပ်သပ် ထည့်မရေးတော့ဘဲ abstract အနေနဲ့ ကြေငြာလိုက်တာမျိုး ဖြစ်ပါတယ်။
ဒီတော့ inherit ဟာ အသုံးတော်တော်လေး ဝင်ပေမယ့် သူ့ကို နေရာတိုင်းမှာသုံးရင် codebase ရဲ့ complexity က များလာမှာပဲ ဖြစ်ပါတယ်။ သူ့ရဲ့ limitation တွေကို ကြည့်ကြည့်ရအောင်ပါ။
Single Parent Limitation
Inheritance ကို သုံးမယ်ဆိုရင် class တခုကပဲ inherit လုပ်လို့ရပါတယ်။ ဆိုလိုတာက extends လုပ်တာက class တခုကိုပဲ လုပ်လို့ရတာပါ။ ဒီတော့ တခုထပ်ပိုတဲ့ class တွေက implementation တွေ လက်ခံလို့မရပါဘူး။
abstract class PaymentMethod {
void processPayment(double amount);
}
abstract class ExtraPromo {}
// Different payment types
class CreditCard extends PaymentMethod, ExtraPromo {
@override
void processPayment(double amount) {
print('💳 Processing \$$amount via Credit Card');
print(' Connecting to bank...');
print(' Payment authorized!');
}
}အပေါ်မှာ ပြထားသလိုမျိုး class ၂ခုကနေ inherit လုပ်တာမျိုး လုပ်လို့မရပါဘူး။
Fragile Base Class
နောက်တခုကတော့ inherit လုပ်တဲ့ အဆင့်တွေ များလာတဲ့အခါ base class ဒါမှမဟုတ် parent class တခုခုမှာ အပြောင်းအလဲ လုပ်လိုက်တာက ရှိသမျှ inherit လုပ်ထားတဲ့ class တွေကို သွားပြီးတော့ သက်ရောက်မှာပါ။
class Device {
void initialize() {
print('Initializing hardware...');
}
}
class SmartPhone extends Device {
@override
void initialize() {
super.initialize(); // Calls parent
print('Loading Android OS...');
}
}
class GamingPhone extends SmartPhone {
@override
void initialize() {
super.initialize(); // Calls parent's parent!
print('Activating gaming mode...');
}
}ဒီမှာ Device class ရဲ့ initialize method ထဲမှာ တခုခုပြောင်းလိုက်တာနဲ့ သူ့ကို inherit လုပ်ထားတဲ့ SmartPhone , GamingPhone class တွေအားလုံးမှာ သွားပြီး သက်ရောက်မှာပဲ ဖြစ်ပါတယ်။
Deep Hierarchies
နောက်ဆုံးတခုကတော့ inherit လုပ်တာတွေ များလာတဲ့အခါ တခုခု အလုပ်လုပ်တာကို သိချင်လို့ပဲ ဖြစ်ဖြစ်၊ error တက်လို့ debug လုပ်တာပဲ ဖြစ်ဖြစ် ရှိသမျှ parent class တွေ တခုပြီးတခု မီးခိုးကြွက်လျှောက် လိုက်ကြည့်ရတော့မှာပါ။
Object
↓
Device
↓
PortableDevice
↓
SmartDevice
↓
Smartphone
↓
Android Phone
↓
GamingPhone
↓
5GGamingPhone
↓
FoldableGamingPhone ← You are here!
ဒီတော့ inheritance သုံးပြီဆို အပေါ်မှာ ပြထားတဲ့ အချက်တွေကို ဂရုစိုက်ပြီး သုံးသွားပေးဖို့ လိုတာပဲ ဖြစ်ပါတယ်။ Flutter app တွေမှာ inheritance ကို သုံးထားတဲ့နေရာတွေ အများကြီး ရှိပါတယ်။ UI တွေတည်ဆောက်တဲ့အခါ အခြေခံ component တွေဖြစ်တဲ့ StatelessWidget, StatefulWidget စသဖြင့် ဒါတွေကို inheritance ကို အခြေခံပြီး တည်ဆောက်ထားတာ ဖြစ်ပါတယ်။
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text('Hello World');
}
}Mixins
Dart မှာ class မှာ လုပ်ဆောင်တာတွေ ခဏခဏရေးတာမျိုး မဖြစ်ချင်ဘူး၊ inheritance လိုမျိုး ရှုပ်ထွေးတာတွေကိုလဲ ရှောင်ရှားချင်တယ်ဆိုရင်တော့ mixin ကို အသုံးပြုလို့ရပါတယ်။ Inheritance ကို Is-A relationship လို့ ပြောရင် mixin ကိုတော့ Has-A relationship လို့ ပြောလို့ ရပါတယ်။
mixin Camera {
int megapixels = 48;
void takePhoto() {
print('📸 Capturing $megapixels MP photo...');
}
void recordVideo() {
print('🎥 Recording 4K video...');
}
}
mixin GPS {
void getCurrentLocation() {
print('📍 Location: 16.8661°N, 96.1951°E (Yangon)');
}
void navigate(String destination) {
print('🗺️ Navigating to $destination...');
}
}
mixin MediaPlayer {
void playMusic(String song) {
print('🎵 Playing: $song');
}
void pauseMusic() {
print('⏸️ Music paused');
}
}
// Now compose a Smartphone with multiple capabilities!
class Smartphone extends Device with Camera, GPS, MediaPlayer {
Smartphone(String brand, String model) : super(brand, model);
}
void main() {
final myPhone = Smartphone('Google', 'Pixel 9');
// From Device (inheritance)
myPhone.powerOn();
// From Camera (mixin)
myPhone.takePhoto();
myPhone.recordVideo();
// From GPS (mixin)
myPhone.getCurrentLocation();
myPhone.navigate('Downtown');
// From MediaPlayer (mixin)
myPhone.playMusic('Bohemian Rhapsody');
myPhone.pauseMusic();
}
// Output:
// Google Pixel 9 is powering on...
// 📸 Capturing 48 MP photo...
// 🎥 Recording 4K video...
// 📍 Location: 16.8661°N, 96.1951°E (Yangon)
// 🗺️ Navigating to Downtown...
// 🎵 Playing: Bohemian Rhapsody
// ⏸️ Music pausedဒီအပေါ်မှာ ပြထားတာကို ကြည့်မယ်ဆို Camera, GPS, MediaPlayer စသဖြင့် capabilities တွေကို mixin တွေအနေနဲ့ သီးသန့်သတ်မှတ်ထားတာကို တွေ့ရမှာပါ။ Smartphone class ကြေငြာတဲ့အခါမှာတော့ ဒီ mixin တွေကို အသုံးပြုထားတာကိုလဲ တွေ့ရမှာပါ။ ဒီတော့ Smartphone class က capabilies တွေ အများကြီးကို အသုံးပြုနိုင်ပြီး inheritance တွေနဲ့လဲရှုပ်မနေတော့ပါဘူး။ ကြိုက်တဲ့ အခြား class တွေမှာလဲ ဒီလို mixin တွေကို တွဲပြီးတော့ လွယ်လွယ်ကူကူပဲ capabilities တွေကို သုံးလို့ရသွားမှာပဲ ဖြစ်ပါတယ်။
ဒီအပေါ်မှာ ပြထားတဲ့ ကြည့်မယ်ဆို mixin တွေက standalone လို့ပြောလို့ ရပါတယ်။ ဆိုလိုတာက သူ့ထဲမှာ ရှိတဲ့ parameter တွေကိုပဲ သုံးလို့ရနေမှာပါ။ သူ့ကို ဘယ် class မှာ တွဲသုံးမယ်မှန်း မသိတဲ့အတွက် တခြား parameter တွေကို သုံးမရနေပါဘူး။ သုံးလို့ရအောင် လုပ်တဲ့နည်း ရှိပါတယ်။ ဒါကတော့ mixin ကို သူနဲ့ တွဲသုံးမယ့် class ကို on keyword နဲ့ ပြောပေးလိုက်တာပဲဖြစ်ပါတယ်။
mixin Camera on Device {
int megapixels = 48;
void takePhoto() {
print('📸 Capturing $megapixels MP photo on $brand $model...');
}
void recordVideo() {
print('🎥 Recording 4K video on $brand $model...');
}
}
// 📸 Capturing 48 MP photo on Google Pixel 9...
// 🎥 Recording 4K video on Google Pixel 9...ဒါဆိုရင်တော့ mixin ကို ဘယ် class မှာ တွဲသုံးဖို့ရှိတယ်ဆိုတာ ပြောလိုက်တာဖြစ်ပြီး အဲ့ဒီ class ထဲမှာ ကြေငြာထားတဲ့ variable တွေကို access လုပ်လို့ ရသွားပါလိမ့်မယ်။
Flutter app တွေ ရေးတဲ့အခါမှာလဲ mixin တွေကို သုံးရတာမျိုးတွေ ရှိလာမှာပါ။ SingleTickerProviderStateMixin, RouteAware စသဖြင့် အသုံးများတဲ့ mixin တွေ ရှိပါတယ်။
class Device {
String brand;
bool isPoweredOn = false;
Device(this.brand);
void powerOn() {
isPoweredOn = true;
print('$brand powered on');
}
}
mixin Battery on Device {
int batteryLevel = 100;
void checkBattery() {
print('$brand battery: $batteryLevel%');
if (batteryLevel < 20 && isPoweredOn) {
print('⚠️ Low battery! Consider power saving mode.');
}
}
void consumeBattery(int amount) {
batteryLevel -= amount;
print('$brand: Battery reduced to $batteryLevel%');
if (batteryLevel <= 0) {
isPoweredOn = false;
print('💀 $brand: Battery dead! Powering off...');
}
}
}
mixin Camera on Device {
int megapixels = 48;
void takePhoto() {
if (isPoweredOn) {
print('📸 $brand: Photo captured! ($megapixels MP)');
} else {
print('❌ $brand is powered off!');
}
}
}
class Smartphone extends Device with Battery, Camera {
Smartphone(String brand) : super(brand);
}
void main() {
final phone = Smartphone('iPhone');
phone.powerOn();
phone.checkBattery(); // iPhone battery: 100%
phone.takePhoto(); // 📸 iPhone: Photo captured!
phone.consumeBattery(30); // Battery reduced to 70%
phone.consumeBattery(50); // Battery reduced to 20%
phone.checkBattery(); // ⚠️ Low battery warning!
phone.consumeBattery(25); // Battery dead! Powering off...
phone.takePhoto(); // ❌ iPhone is powered off!
}
// Output:
// iPhone powered on
// iPhone battery: 100%
// 📸 iPhone: Photo captured! (48 MP)
// iPhone: Battery reduced to 70%
// iPhone: Battery reduced to 20%
// iPhone battery: 20%
// ⚠️ Low battery! Consider power saving mode.
// 💀 iPhone: Battery dead! Powering off...
// iPhone: Battery reduced to 0%
// ❌ iPhone is powered off!ဒါဆိုရင်တော့ mixin နဲ့ ပတ်သက်ပြီး အတော်လေး နားလည်လောက်ပြီ ထင်ပါတယ်။ inheritance နဲ့ mixin ကို လိုအပ်သလို အသုံးချခြင်းအားဖြင့် code တွေကို ရှင်းရှင်းလင်းလင်း ထိထိရောက်ရောက် ရေးသွားနိုင်မှာပဲ ဖြစ်ပါတယ်။
ဒီနေ့ Day 8 အတွက်ကတော့ ဒီလောက်ပဲ ဖြစ်ပါတယ်။ အဆုံးထိ ဖတ်ပေးတဲ့အတွက် အများကြီး ကျေးဇူးတင်ပါတယ်။ နားမလည်တာတွေ အဆင်မပြေတာတွေ ရှိခဲ့ရင်လဲ အောက်မှာပေးထားတဲ့ discord server ထဲမှာ လာရောက်ဆွေးနွေးနိုင်ပါတယ်။ နောက်နေ့တွေမှာလဲ ဆက်လက်ပြီး sharing လုပ်သွားပေးသွားမှာ ဖြစ်တဲ့အတွက် subscribe လုပ်ထားဖို့ ဖိတ်ခေါ်ပါတယ်။
- Youtube: https://www.youtube.com/@arkarmintun
- Newsletter: https://arkar.dev/
- Discord: https://discord.gg/3xUJ6k6dkH