به دنیای برنامه نویسی من خوش اومدین! اینجا مینویسم تا هم چیزایی که یاد میگیرم رو ثبت کنم، هم باهاتون در ارتباط باشم
آشنایی با StatefulWidget در Flutter
اگه بخوای توی Flutter UI زنده باشه و با کلیک/اسکرول/دادهی جدید عوض بشه، باید بری سراغ StatefulWidget.
یه ویجت + یه State که مثل «حافظهی کوتاهمدت» همون ویجته. باهاش میتونی داده نگه داری، UI رو آپدیت کنی و رفتار صفحه رو کنترل کنی.
⚡️ StatefulWidget چیه؟
StatefulWidget مثل چراغ مطالعهست:
خود چراغ عوض نمیشه (Widget immutable)
ولی حالتش روشن/خاموش میشه (State تغییر میکنه)
تو هم با یه دکمه (setState) میگی «وضعیت عوض شد، دوباره بساز!» 😎
📌 مهمترین بخشهای StatefulWidget:
🔹 1. createState()
اتصال ویجت به کلاس State. همیشه یه کلاس State میسازی که منطق و وضعیت رو نگه میداره.
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
@override
State<MyWidget> createState() => _MyWidgetState();
}🔹 2. کلاس State
اینجا متغیرهای قابل تغییرت رو میذاری و UI رو میسازی.
class _MyWidgetState extends State<MyWidget> {
int counter = 0;
@override
Widget build(BuildContext context) {
return Text('$counter');
}
}🔹 3. setState(() { ... })
به Flutter میگه «وضعیت عوض شد» تا متد build دوباره اجرا بشه.
🔹 4. initState - dispose
initState: یکبار برای مقداردهی اولیه (کنترلر، تایمر، استریم)
dispose: جمعکردن منابع برای جلوگیری از Memory Leak
🔹 5. build(context)
خروجی UI بر اساس وضعیت فعلی. قرار نیست اینجا کار سنگین یا Side Effect انجام بدی.
🔹 6. mounted
میگه State هنوز توی درخته یا نه. بعد از async قبل از setState چک کن.
🔹 7. didUpdateWidget / didChangeDependencies
وقتی props والد عوض بشه یا Inheritedها تغییر کنن (Theme/Locale/MediaQuery)، اینا به کار میان.
🚀 یک مثال کامل StatefulWidget:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _count = 0;
late final TextEditingController _controller;
bool _loading = false;
String? _greeting;
@override
void initState() {
super.initState();
_controller = TextEditingController();
}
@override
void dispose() {
_controller.dispose(); // خیلی مهم!
super.dispose();
}
Future<void> _sayHi() async {
setState(() => _loading = true);
// شبیهسازی درخواست غیرهمزمان
await Future.delayed(const Duration(milliseconds: 800));
final name = _controller.text.trim().isEmpty
? 'دوست عزیز'
: _controller.text.trim();
if (!mounted) return; // جلوگیری از setState بعد از dispose
setState(() {
_greeting = 'سلام $name 👋';
_loading = false;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("مثال StatefulWidget")),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('$_count', style: const TextStyle(fontSize: 48)),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: () => setState(() => _count++),
icon: const Icon(Icons.add),
label: const Text('اضافه کن'),
),
const SizedBox(height: 24),
TextField(
controller: _controller,
decoration: const InputDecoration(
labelText: 'اسمت رو بنویس',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 8),
_loading
? const CircularProgressIndicator()
: ElevatedButton(
onPressed: _sayHi,
child: const Text('سلام بده'),
),
if (_greeting != null) ...[
const SizedBox(height: 8),
Text(_greeting!, style: const TextStyle(fontSize: 18)),
],
],
),
),
),
);
}
}
🎯 خروجی این کد:
شمارندهای که با هر کلیک آپدیت میشه
یک TextField برای گرفتن اسم
دکمهای که بعد از یه تأخیر کوتاه سلام میده (با رعایت mounted)
همهچی داخل یک StatefulWidget مدیریت میشه
✨ نکتهها و ترفندها:
setState داخل build صدا نزن. اگه لازم شد کاری بعد از اولین رندر انجام بدی:
WidgetsBinding.instance.addPostFrameCallback((_) {
// مثلا نمایش SnackBar
});هر منبعی ساختی (Controller/Timer/AnimationController/StreamSubscription) رو تو dispose آزاد کن.
وقتی props والد عوض میشه و باید State هماهنگ بشه، از didUpdateWidget با احتیاط استفاده کن.
برای بهبود عملکرد، از const استفاده کن و UI رو به ویجتهای کوچکتر بشکن تا محدودهی rebuild کم بشه.
وضعیت خیلی ساده داری؟ ValueNotifier + ValueListenableBuilder گزینهی سبکه.
وضعیت بین چند صفحه/بخش مشترک شد؟ برو سراغ Provider/Riverpod/BLoC.
میخوای وضعیت یه تب حفظ بشه؟ از AutomaticKeepAliveClientMixin استفاده کن.
🆚 کی Stateless و کی Stateful؟
فقط نمایش داده از بیرون → StatelessWidget
وضعیت محلی و موقتی (فرم، شمارنده، انیمیشن کوتاه) → StatefulWidget
وضعیت اشتراکی/پیچیده → ابزار مدیریت وضعیت
موفق باشید ✨
📌 خیلی خلاصه مهمترین بخشهای StatefulWidget:
createState: اتصال ویجت به کلاس State
کلاس State: جایی برای متغیرهای قابل تغییر و ساخت UI
setState: اعلام تغییر وضعیت برای rebuild
initState و dispose: شروع و جمعکردن منابع (Controller/Timer/Stream)
didUpdateWidget / didChangeDependencies: واکنش به تغییر props والد یا Inheritedها
mounted: قبل از setState بعد از async چک کن
پ.ن— باتوجه به پست قبلی که در مورد آشنایی «pubspec.yaml» بود
برای فعالکردن فارسی و راستبهچپ، پکیج رسمی flutter_localizations (جزئی از خود Flutter) رو به dependencies اضافه کردم و بعد توی MaterialApp لوکال رو fa ست کردم:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter✅ با همین چند خط، TextField و کل UI راستبهچپ میشن.
مطلبی دیگر از این انتشارات
به زبان برنامه نویسی محدود نشید
مطلبی دیگر از این انتشارات
پردرآمدترین زبان های برنامه نویسی در ایران و جهان چیست؟
مطلبی دیگر از این انتشارات
۵ اشتباه رایج برنامهنویسان تازهکار (و چگونه از آنها اجتناب کنیم)