StatefulWidget হল একটি Flutter উইজেট যা পরিবর্তনযোগ্য স্টেট সহ, UI-কে ব্যবহারকারীর ক্রিয়া, অ্যাসিঙ্ক্রোনাস ইভেন্ট এবং ডেটা স্ট্রিমগুলিতে প্রতিক্রিয়া জানাতে দেয়। অফিসিয়াল Flutter ডকুমেন্টেশন (Flutter.dev, 2026) অনুসারে, StatefulWidget অ্যাপ্লিকেশনের সমস্ত ইন্টারেক্টিভ উপাদানের জন্য ব্যবহৃত হয়: ইনপুট ফর্ম, অ্যানিমেশন, চেকবক্স, সুইচ এবং স্ক্রিন যা নেটওয়ার্ক থেকে ডেটা লোড করে। StatelessWidget-এর বিপরীতে, এটি একটি পৃথক State অবজেক্ট তৈরি করে যা তার সমগ্র জীবনচক্র জুড়ে স্থায়ী হয় এবং উইজেটটি পুনরায় তৈরি না করেই পুনর্নির্মাণ করা যেতে পারে।
মূল বিষয়
StatefulWidget হল একটি Flutter ক্লাস যা ব্যবহারকারীর ক্রিয়া, সিস্টেম ইভেন্ট বা অ্যাসিঙ্ক্রোনাস অপারেশনের প্রতিক্রিয়ায় তার স্টেট পরিবর্তন করতে পারে। StatelessWidget-এর বিপরীতে, StatefulWidget সরাসরি রেন্ডার হয় না — এটি একটি State অবজেক্ট তৈরি করে যা রেন্ডারিং পরিচালনা করে। দুটি ক্লাসে (Widget এবং State) এই বিভাজন Flutter-কে উইজেটটি পুনরায় তৈরি না করেই UI পুনর্নির্মাণ করতে দেয়, যা ঘন ঘন আপডেটের সময় একটি উল্লেখযোগ্য কর্মক্ষমতা সুবিধা প্রদান করে।
StatefulWidget-এর আর্কিটেকচার “পরিবর্তনযোগ্য এবং অপরিবর্তনীয়ের পৃথকীকরণ” প্যাটার্ন অনুসরণ করে: উইজেট নিজেই অপরিবর্তনীয় থাকে (StatelessWidget-এর মতো), যখন সমস্ত পরিবর্তনযোগ্য স্টেট একটি পৃথক State অবজেক্টে সংরক্ষণ করা হয়। এটি Flutter-কে প্রকার এবং Key দ্বারা তুলনা করে উইজেট পুনরায় ব্যবহার করতে দেয়, পাশাপাশি পুনর্নির্মাণের মধ্যে প্রকৃত স্টেট সংরক্ষণ করে।
Google (Flutter Architectural Overview, 2026) অনুসারে, StatefulWidget সেই পরিস্থিতির জন্য সর্বোত্তম যেখানে উইজেটের জীবনকালে স্টেট একাধিকবার পরিবর্তিত হয়: টেক্সট ফিল্ড, অ্যানিমেশন, টাইমার, ডেটা স্ট্রিম, অ্যাসিঙ্ক্রোনাস লোড। এককালীন আরম্ভের জন্য, StatelessWidget যথেষ্ট।
StatefulWidget বাধ্যতামূলক যখন উইজেটকে বাহ্যিক ইভেন্টে প্রতিক্রিয়া জানাতে হবে: বাটন ক্লিক, HTTP অনুরোধ সম্পূর্ণতা, ডেটাবেস ডেটা আপডেট, WebSocket সাবস্ক্রিপশন। এটি অ্যানিমেশন, কন্ট্রোলার সহ টেক্সট ফিল্ড এবং ফোকাস পরিচালনাকারী উপাদানের জন্যও প্রয়োজনীয়। যদি একটি উইজেট শুধুমাত্র ডেটা প্রদর্শন করে এবং ইভেন্ট তৈরি না করে, তাহলে StatelessWidget ব্যবহার করুন।
StatefulWidget দুটি ক্লাস নিয়ে গঠিত: নিজে StatefulWidget (হালকা, অপরিবর্তনীয়) এবং State (ভারী, পরিবর্তনযোগ্য)। ফ্রেমওয়ার্ক createState() পদ্ধতির মাধ্যমে State তৈরি করে, যা ট্রিতে সন্নিবেশ করার সময় একবার কল করা হয়। State widget বৈশিষ্ট্যের মাধ্যমে উইজেটের একটি রেফারেন্স পায় এবং জীবনচক্রের যেকোনো সময়ে এর ফিল্ড অ্যাক্সেস করতে পারে।
জীবনচক্র StatefulWidget-এর ছয়টি প্রধান পর্যায় নিয়ে গঠিত, যার প্রতিটি নির্দিষ্ট কাজ সম্পাদনের জন্য ওভাররাইডযোগ্য পদ্ধতি প্রদান করে। সঠিক সম্পদ ব্যবস্থাপনা এবং মেমরি লিক এড়ানোর জন্য এই পর্যায়গুলি বোঝা গুরুত্বপূর্ণ।
createState জীবনচক্রের প্রথম পদ্ধতি, যা StatefulWidget ট্রিতে সন্নিবেশ করার সময় কল করা হয়। এটিকে এই উইজেটের সাথে যুক্ত একটি নতুন State ইনস্ট্যান্স ফিরিয়ে দিতে হবে। এই পদ্ধতিটি উপাদানের সমগ্র জীবনকালে ঠিক একবার কল করা হয়। এখানে ভারী অপারেশন না করা গুরুত্বপূর্ণ — createState যতটা সম্ভব হালকা হওয়া উচিত।
initState State তৈরি হওয়ার পরপরই, প্রথম UI নির্মাণের আগে কল করা হয়। এখানে করা হয়: কন্ট্রোলার আরম্ভ (TextEditingController, AnimationController), ডেটা স্ট্রিমে সাবস্ক্রাইব (StreamSubscription), টাইমার সেটআপ এবং ফিল্ডের প্রাথমিক আরম্ভ। Flutter ডক্স (Flutter.dev, 2026) অনুসারে, initState-এ BuildContext.of() কল করা যাবে না — ট্রি এখনও সম্পূর্ণরূপে মাউন্ট করা হয়নি।
didChangeDependencies initState-এর পরে এবং InheritedWidget নির্ভরতা পরিবর্তিত হলে প্রতিবার কল করা হয়। এটি MediaQuery.of(context) কল করা বা Theme-এ সাবস্ক্রাইব করার জন্য উপযুক্ত জায়গা — মান যা অ্যাপ্লিকেশনের রানটাইমের সময় পরিবর্তিত হতে পারে। যদি একটি উইজেট InheritedWidget ব্যবহার করে, তাহলে আরম্ভের যুক্তি এখানে হওয়া উচিত, initState-এ নয়।
build হল প্রধান পদ্ধতি যা উইজেট ট্রি ফিরিয়ে দেয়। এটি initState-এর পরে, didChangeDependencies-এর পরে এবং প্রতিটি setState-এর পরে কল করা হয়। didUpdateWidget কল করা হয় যখন পিতামাতা পুনর্নির্মাণ করে এবং নতুন প্যারামিটার সহ StatefulWidget পাস করে। এখানে পুরানো এবং নতুন উইজেট ফিল্ড তুলনা করা যেতে পারে এবং যদি প্রয়োজন হয়, স্টেট আপডেট করা যেতে পারে।
dispose জীবনচক্রের চূড়ান্ত পর্যায়। এখানে সমস্ত সম্পদ মুক্ত করা হয়: স্ট্রিম থেকে সাবস্ক্রিপশন বাতিল, কন্ট্রোলার মুছে ফেলা, টাইমার বাতিল। dispose না কল করলে মেমরি লিক হয়। dispose-এর পরে, State মৃত বলে বিবেচিত হয় — এর ভিতরে setState কল করলে ব্যতিক্রম হয়।
StatefulWidget-এর কাজের প্রক্রিয়া তিনটি সত্তার সমন্বিত কাজের উপর ভিত্তি করে: Widget (হালকা বিবরণ), Element (মধ্যবর্তী স্তর) এবং State (ডেটা সঞ্চয়)। যখন Flutter বিবরণে StatefulWidget খুঁজে পায়, এটি StatefulElement তৈরি করে, যা createState কল করে এবং State অবজেক্টের একটি রেফারেন্স সংরক্ষণ করে। যখন পিতামাতা পুনর্নির্মাণ করে, Flutter নতুন উইজেটটিকে বর্তমান Element-এর সাথে তুলনা করে — যদি প্রকার এবং Key মিলে যায়, Element আপডেট হয় এবং State একই থাকে।
স্টেট শুধুমাত্র setState কলের মাধ্যমে পরিবর্তিত হয়, যা ফ্রেমওয়ার্ককে পুনর্নির্মাণের প্রয়োজনীয়তা সম্পর্কে জানায়। এটি বোঝা গুরুত্বপূর্ণ: setState স্বয়ংক্রিয়ভাবে স্টেট পরিবর্তন করে না — এটি শুধুমাত্র উইজেটটিকে “নোংরা” হিসেবে চিহ্নিত করে। ডেভেলপার স্বাধীনভাবে setState-এ পাস করা কলব্যাকে State ফিল্ড আপডেট করে। কলব্যাক সম্পূর্ণ হওয়ার পরে, Flutter build কল করে এবং UI আপডেট করে।
Dart/Flutter টিম (Dart Language Specification, 2026) অনুসারে, এই বিভাজন নিশ্চিত করে যে build কল করার আগে সমস্ত স্টেট পরিবর্তন সিঙ্ক্রোনাসভাবে ঘটে, সেই পরিস্থিতি দূর করে যেখানে UI আংশিকভাবে আপডেট করা ডেটা প্রদর্শন করে। এটি Flutter-এ ইন্টারফেস সামঞ্জস্যের একটি মূল প্রক্রিয়া।
আসুন একটি সহজ StatefulWidget দেখি — একটি বাটন ক্লিক কাউন্টার। এটি মৌলিক প্যাটার্ন প্রদর্শন করে: State তৈরি, initState-এ ফিল্ড আরম্ভ, setState-এর মাধ্যমে পরিবর্তন:
class CounterScreen extends StatefulWidget {
const CounterScreen({super.key});
@override
State<CounterScreen> createState() => _CounterScreenState();
}
class _CounterScreenState extends State<CounterScreen> {
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Count: $_count'),
ElevatedButton(
onPressed: _increment,
child: const Text('Increment'),
),
],
);
}
}
অ্যাসিঙ্ক্রোনাস ডেটা লোডিং এবং জীবনচক্র ব্যবস্থাপনার সাথে একটি উদাহরণ। StatefulWidget নেটওয়ার্ক থেকে ডেটা লোড করে এবং লোডিং অবস্থা প্রদর্শন করে:
class UserProfilePage extends StatefulWidget {
final String userId;
const UserProfilePage({super.key, required this.userId});
@override
State<UserProfilePage> createState() => _UserProfilePageState();
}
class _UserProfilePageState extends State<UserProfilePage> {
UserModel? _user;
bool _isLoading = true;
@override
void initState() {
super.initState();
_loadUser();
}
Future<void> _loadUser() async {
final user = await UserService.fetchUser(widget.userId);
setState(() {
_user = user;
_isLoading = false;
});
}
@override
Widget build(BuildContext context) {
if (_isLoading) return const CircularProgressIndicator();
return Text('Hello, ${_user!.name}');
}
}
দ্বিতীয় উদাহরণে, এটি লক্ষ্য করা গুরুত্বপূর্ণ: initState একটি অ্যাসিঙ্ক্রোনাস অপারেশন শুরু করে, কিন্তু পদ্ধতিটি নিজে অ্যাসিঙ্ক্রোনাস নয়। অ্যাসিঙ্ক্রোনসিটি একটি পৃথক পদ্ধতি _loadUser-এর ভিতরে async/await-এর মাধ্যমে প্রয়োগ করা হয়, যা অনুরোধ সম্পূর্ণ হওয়ার পরে setState-এর মাধ্যমে স্টেট আপডেট করে। এই পদ্ধতি নিশ্চিত করে যে ডেটা পাওয়ার আগে উইজেটটি সঠিকভাবে লোডিং নির্দেশক প্রদর্শন করে।
StatefulWidget এবং StatelessWidget-এর মধ্যে পছন্দ শুধুমাত্র স্টেট থাকার বিষয়ে নয়। StatefulWidget initState, didChangeDependencies, didUpdateWidget এবং dispose পদ্ধতি সহ একটি সম্পূর্ণ জীবনচক্র প্রদান করে, যা কন্ট্রোলার, অ্যানিমেশন এবং স্ট্রিমের সাথে কাজ করার জন্য প্রয়োজনীয়। StatelessWidget, অন্যদিকে, এই পদ্ধতিগুলি নেই এবং ফ্রেমওয়ার্কের জন্য সর্বদা হালকা।
Flutter টিমের সুপারিশ (Flutter docs, 2026) অ্যাপ্লিকেশনে StatefulWidget-এর সংখ্যা কমানো, ট্রিতে স্টেট উপরে তোলার (State Hoisting) বা স্টেট ম্যানেজমেন্ট সমাধান (Riverpod, Bloc, Provider) ব্যবহার করে। প্রতিটি StatefulWidget একটি State অবজেক্ট তৈরি করে যা উপাদান অপসারণ না হওয়া পর্যন্ত বেঁচে থাকে — যত বেশি এই ধরনের উইজেট, মেমরি লোড তত বেশি।
| মানদণ্ড | StatefulWidget | StatelessWidget |
|---|---|---|
| স্টেট | পরিবর্তনযোগ্য | অপরিবর্তনীয় |
| জীবনচক্র | ৬টি ধাপ | শুধু build |
| State অবজেক্ট | আলাদাভাবে তৈরি | প্রয়োজন নেই |
| setState | উপলব্ধ | উপলব্ধ নয় |
| সাবস্ক্রিপশন | initState/dispose | সমর্থিত নয় |
| const কনস্ট্রাক্টর | সীমিত | সম্পূর্ণ সমর্থিত |
| মেমরি খরচ | অধিক | কম |
StatefulWidget-এর State অবজেক্ট তৈরি এবং বজায় রাখার প্রয়োজনীয়তার কারণে StatelessWidget-এর তুলনায় বেশি সম্পদের প্রয়োজন হয়। তবে, StatefulWidget-এর সঠিক ব্যবহার কর্মক্ষমতা সমস্যা সৃষ্টি করে না যদি কিছু নিয়ম অনুসরণ করা হয়। প্রথম, StatefulWidget-এর গভীর নেস্টিং এড়িয়ে চলুন — প্রতিটি স্তর ট্রি ট্রাভার্সালে ওভারহেড যোগ করে। দ্বিতীয়, জটিল StatefulWidget-কে কয়েকটি সরল উইজেটে ভাগ করুন, প্রতিটি তার স্টেটের নিজস্ব অংশের জন্য দায়ী।
Flutter কর্মক্ষমতা গবেষণা (Flutter.dev, ফেব্রুয়ারি 2026) অনুসারে, FPS কমার সবচেয়ে সাধারণ কারণ পিতামাতা উইজেটে setState কল করা যা সমস্ত বংশধর পুনর্নির্মাণ করে, যার মধ্যে StatelessWidgets অন্তর্ভুক্ত যারা তাদের প্রদর্শন পরিবর্তন করেনি। সমাধান হল UI-এর পরিবর্তনযোগ্য অংশটি একটি পৃথক StatefulWidget-এ নিষ্কাশন করা যাতে setState শুধুমাত্র ন্যূনতম প্রয়োজনীয় উইজেটগুলি পুনর্নির্মাণ করে।
State-এর ভিতরে const ব্যবহার করা আরেকটি গুরুত্বপূর্ণ কৌশল। যদি চাইল্ড উইজেটগুলি const হিসাবে ঘোষণা করা হয়, Flutter পিতামাতায় setState কল করলে সেগুলি পুনর্নির্মাণ করবে না। এটি ফ্রেমওয়ার্কের লোড কমায় এবং ফ্রেম রেন্ডারিং সময় হ্রাস করে।
প্রতিটি setState কল সম্পূর্ণ উইজেট পুনর্নির্মাণ শুরু করে। যদি স্টেট উচ্চ ফ্রিকোয়েন্সিতে পরিবর্তিত হয় (উদাহরণস্বরূপ, অ্যানিমেশন বা ডেটা স্ট্রিম), ম্যানুয়ালি setState কল করার পরিবর্তে AnimatedBuilder, ValueListenableBuilder বা StreamBuilder ব্যবহার করার কথা বিবেচনা করুন। এই উইজেটগুলি পুনর্নির্মাণ অপ্টিমাইজ করে, শুধুমাত্র UI-এর সেই অংশটি আপডেট করে যা প্রকৃতপক্ষে পরিবর্তিত হয়েছে।
StatefulWidget-এর সাথে প্রথম সাধারণ ভুল হল dispose-এর পরে setState কল করা। যখন একটি উইজেট ট্রি থেকে সরানো হয়, State মৃত বলে বিবেচিত হয়, এবং যেকোনো setState কল “setState called after dispose” ব্যতিক্রম ছুঁড়ে দেয়। এটি প্রায়শই ঘটে যখন উইজেট সরানোর পরে একটি অ্যাসিঙ্ক্রোনাস অপারেশন সম্পূর্ণ হয়। সমাধান হল setState কল করার আগে mounted ফ্ল্যাগ পরীক্ষা করা বা dispose-এ অ্যাসিঙ্ক্রোনাস অপারেশন বাতিল করা।
দ্বিতীয় ভুল হল build পদ্ধতিতে ভারী গণনা করা। যেহেতু build প্রতিটি setState এবং প্রতিটি পিতামাতা পুনর্নির্মাণে কল করা হয়, সমস্ত গণনা যতটা সম্ভব হালকা হওয়া উচিত। যদি একটি সংস্থান-নিবিড় অপারেশনের প্রয়োজন হয়, এটি একটি পৃথক Isolate-এ সরান বা State ফিল্ডে ফলাফল ক্যাশ করুন।
তৃতীয় ভুল হল super.initState() এবং super.dispose() কল না করা। এই পদ্ধতিগুলি ওভাররাইড করার সময়, ডেভেলপারকে পিতামাতার প্রয়োগ কল করতে হবে। তা না করলে ফ্রেমওয়ার্ক Element স্টেট সঠিকভাবে পরিচালনা করতে পারবে না, যা ট্র্যাক করা কঠিন বাগ সৃষ্টি করে।
mounted পরীক্ষা করুনsuper.initState() এবং super.dispose() কল করতে ভুলবেন নাসচরাচর জিজ্ঞাস্য
StatefulWidget setState-এর মাধ্যমে তার স্টেট পরিবর্তন করতে পারে, এর একটি জীবনচক্র (initState, dispose) আছে এবং একটি পৃথক State অবজেক্ট তৈরি করে। StatelessWidget স্টেট পরিবর্তন করতে পারে না এবং এর জীবনচক্র পদ্ধতি নেই — এটি কেবল পাস করা ডেটা প্রদর্শন করে।
createState প্রতিটি StatefulElement ইনস্ট্যান্সের জন্য ঠিক একবার কল করা হয়। এমনকি যদি পিতামাতা একাধিকবার পুনর্নির্মাণ করে, যতক্ষণ উইজেটের প্রকার এবং Key পরিবর্তন না হয়, createState কল করা হয় না — বিদ্যমান State অবজেক্ট ব্যবহার করা হয়।
সম্পদ মুক্তি পাবে না: কন্ট্রোলার ব্যাকগ্রাউন্ডে কাজ করতে থাকবে, স্ট্রিম সাবস্ক্রিপশন সক্রিয় থাকবে, টাইমার বাতিল হবে না। এটি মেমরি লিকের দিকে নিয়ে যায় এবং dispose-এর পরে setState কল করতে পারে, যা ব্যতিক্রম ছুঁড়ে দেয়।
হ্যাঁ, StatefulWidget-এর কনস্ট্রাক্টর const হতে পারে। তবে, এটি StatelessWidget-এর মতো একই সুবিধা প্রদান করে না — State অবজেক্ট প্রথম সন্নিবেশে এখনও তৈরি হবে। const শুধুমাত্র উইজেটকেই প্রভাবিত করে (হালকা মোড়ক), State-কে নয়।
didUpdateWidget কল করা হয় যখন পিতামাতা নতুন প্যারামিটার সহ StatefulWidget পাস করে। নতুন ডেটার সাথে স্টেট সিঙ্ক্রোনাইজ করার জন্য এটি প্রয়োজনীয় — উদাহরণস্বরূপ, যদি প্যারামিটারে userId পরিবর্তিত হয়, নতুন ব্যবহারকারীর প্রোফাইল লোড করতে হবে।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন