state management etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
state management etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

The 10 Most Common Mistakes in Flutter State Management and How to Fix Them

 

Hello, everyone. Today, I’m here to talk about StateManagement, a topic that many of us find complicated. Let’s get started.

You can read the full article here.

At first glance, your Flutter project may seem as innocent as a small “hello world” application. But as things grow and screens, API calls, user interactions, and data management come into play, state management can suddenly turn into a horror movie.

Incorrect state management:

  1. Slows down your application.
  2. Ruins the readability of your code.
  3. Makes teamwork difficult.
  4. And worst of all, makes debugging impossible in the future.

In this article, we will examine the 10 most common mistakes made when managing state in Flutter, with real code examples and solutions.

We will also discuss how you can spot these mistakes in advance and write more performant, sustainable code.

1. Putting All State in One Place

Mistake: Putting all of the application’s state into a single Provider, Bloc, or State class.

class AppState with ChangeNotifier {
String userName = '';
int cartItemCount = 0;
bool isDarkMode = false;
List<String> notifications = [];

void updateUserName(String name) {
userName = name;
notifyListeners();
}

void addNotification(String message) {
notifications.add(message);
notifyListeners();
}
}

This structure seems innocent in small projects. However, as the project grows:

  • Unnecessary rebuilds occur (even if only one field changes, all listeners are triggered).
  • The code becomes more complex.
  • Testing becomes more difficult.

Solution:
Modular state management → Separate state classes or Blocs for each feature.

class UserState with ChangeNotifier {
String userName = '';
void updateUserName(String name) {
userName = name;
notifyListeners();
}
}

class CartState with ChangeNotifier {
int itemCount = 0;
void addItem() {
itemCount++;
notifyListeners();
}
}

2. Unnecessary Rebuilds

Error: Wrapping the entire screen with Consumer or BlocBuilder.

Consumer<CartState>(
builder: (context, cart, child) {
return Scaffold(
appBar: AppBar(title: Text('Cart (${cart.itemCount})')),
body: ProductList(),
);
},
);

When itemCount changes, the entire screen is redrawn.

Solution:
Use Selectors or small widgets to rebuild only the necessary parts.

AppBar(
title: Selector<CartState, int>(
selector: (context, cart) => cart.itemCount,
builder: (context, count, child) => Text('Cart ($count)'),
),
)

3. Mixing Local and Global State

Mistake: Keeping UI-specific data in global state.

Example: Modal open/closed state is kept globally.

class AppState with ChangeNotifier {
bool isModalOpen = false;
}

Solution:
Keep screen-specific states in StatefulWidget or local provider.

class MyScreen extends StatefulWidget {
@override
State<MyScreen> createState() => _MyScreenState();
}

class _MyScreenState extends State<MyScreen> {
bool isModalOpen = false;
}

4. Mixing Business Logic with UI

Error: API calls or data processing code directly within widgets.

ElevatedButton(
onPressed: () async {
final data = await fetchData();
setState(() {
result = data;
});
},
child: Text("Load"),
)

Solution:
Move the business logic to the state management layer.

class DataState with ChangeNotifier {
String result = '';
Future<void> loadData() async {
result = await fetchData();
notifyListeners();
}
}

5. Use of Non-Immutable State

Error: Directly modifying the state object.

state.user.name = 'John'; // False

Solution:
Keep State immutable, use copyWith.

class User {
final String name;
User({required this.name});

User copyWith({String? name}) {
return User(name: name ?? this.name);
}
}

6. dispose() and Memory Management Neglect

Error: Failure to dispose of Streams, Controllers, or Timers.

class MyNotifier with ChangeNotifier {
final controller = StreamController();
}

Solution:
Add the dispose() method.

@override
void dispose() {
controller.close();
super.dispose();
}

7. Overuse of notifyListeners() in Provider

Error: Triggering all listeners for a single change.

void updateCart() {
itemCount++;
notifyListeners();
}

Solution:
Split the data into pieces or rebuild the target with context.select().

8. Managing Everything with a Single Event in Bloc

Mistake: Consolidating all operations into a single event.

class AppEvent {}

Solution:
Define a separate event for each transaction.

class LoadUser extends AppEvent {}
class UpdateCart extends AppEvent {}

9. Defining Unnecessary Reactive Variables in GetX

Error: Making even unchanging data .obs.

final appName = 'MyApp'.obs; // Not useful

Solution:
Make only the values that change and affect the UI reactive.

10. Wrong Library Selection

Error: Selecting a library because it is popular without analyzing the project requirements.

Solution:

  • Small projects → Provider / Riverpod
  • Medium projects → Riverpod / Bloc
  • Large team projects → Bloc / Clean Architecture

Conclusions and Recommendations

  1. Plan state management correctly at the beginning of the project.
  2. Use a modular approach.
  3. Reduce unnecessary rebuilds.
  4. Prefer immutable data structures.
  5. Don’t ignore testability.

Resources

Thank you for reading this far.

If you liked this article, don’t forget to click the clap button. You can subscribe to stay updated on my other content.

Thank you.

Selin.

Flutter State Management’ta En Sık Yapılan 10 Hata ve Çözüm Yolları

 


Herkese merhaba. Bugün sizlere çoğumuz için karmaşık gelen StateManagement konusu ile geldim. Hemen başlayalım.

Flutter projeniz ilk başta minik bir “merhaba dünya” uygulaması gibi masum görünebilir. Ama işler büyüyüp ekranlar, API çağrıları, kullanıcı etkileşimleri ve veri yönetimi devreye girdiğinde, state management konusu bir anda korku filmine dönüşebilir.

Yanlış state yönetimi:

  1. Uygulamanızı yavaşlatır.
  2. Kodun okunabilirliğini bitirir.
  3. Takım içi çalışmayı zorlaştırır.
  4. Ve en kötüsü, gelecekte hata ayıklamayı imkânsız hale getirir.

Bu yazıda, Flutter’da state yönetimi yaparken en sık karşılaşılan 10 hatayıgerçek kod örnekleri ve çözüm yollarıyla inceleyeceğiz.

Ayrıca, bu hataları nasıl önceden fark edebileceğinizi ve daha performanslı, sürdürülebilir kod yazabileceğinizi konuşacağız.

1. Tüm State’i Tek Yere Toplamak

Hata: Tek bir Provider, Bloc veya State sınıfı içine tüm uygulamanın state’ini koymak.

class AppState with ChangeNotifier {
String userName = '';
int cartItemCount = 0;
bool isDarkMode = false;
List<String> notifications = [];

void updateUserName(String name) {
userName = name;
notifyListeners();
}

void addNotification(String message) {
notifications.add(message);
notifyListeners();
}
}

Bu yapı küçük projede masum görünür. Ancak proje büyüdükçe:

  • Gereksiz rebuild’ler olur (tek alan değişse bile tüm dinleyiciler tetiklenir).
  • Kod karmaşıklaşır.
  • Test etmek zorlaşır.

Çözüm:
Modüler state yönetimi → Her feature için ayrı state sınıfları veya Bloc’lar.

class UserState with ChangeNotifier {
String userName = '';
void updateUserName(String name) {
userName = name;
notifyListeners();
}
}

class CartState with ChangeNotifier {
int itemCount = 0;
void addItem() {
itemCount++;
notifyListeners();
}
}

2. Gereksiz Rebuild’ler

Hata: Consumer veya BlocBuilder ile tüm ekranı sarmak.

Consumer<CartState>(
builder: (context, cart, child) {
return Scaffold(
appBar: AppBar(title: Text('Cart (${cart.itemCount})')),
body: ProductList(),
);
},
);

itemCount değiştiğinde tüm ekran yeniden çizilir.

Çözüm:
Sadece gerekli kısmı rebuild etmek için Selector veya küçük widget’lar kullanın.

AppBar(
title: Selector<CartState, int>(
selector: (context, cart) => cart.itemCount,
builder: (context, count, child) => Text('Cart ($count)'),
),
)

3. Local ve Global State’i Karıştırmak

Hata: UI’ye özgü verileri global state’te tutmak.

Örnek: Modal açık/kapalı durumu globalde tutuluyor.

class AppState with ChangeNotifier {
bool isModalOpen = false;
}

Çözüm:
Ekrana özel state’leri StatefulWidget içinde veya local provider’da tutun.

class MyScreen extends StatefulWidget {
@override
State<MyScreen> createState() => _MyScreenState();
}

class _MyScreenState extends State<MyScreen> {
bool isModalOpen = false;
}

4. Business Logic ile UI’nin Karışması

Hata: API çağrıları veya veri işleme kodunun doğrudan widget içinde olması.

ElevatedButton(
onPressed: () async {
final data = await fetchData();
setState(() {
result = data;
});
},
child: Text("Load"),
)

Çözüm:
İş mantığını state management katmanına taşıyın.

class DataState with ChangeNotifier {
String result = '';
Future<void> loadData() async {
result = await fetchData();
notifyListeners();
}
}

5. Immutable Olmayan State Kullanımı

Hata: State objesini doğrudan değiştirmek.

state.user.name = 'John'; // False

Çözüm:
State’i immutable tutmak, copyWith kullanmak.

class User {
final String name;
User({required this.name});

User copyWith({String? name}) {
return User(name: name ?? this.name);
}
}

6. dispose() ve Memory Management İhmali

Hata: Stream, Controller veya Timer’ları dispose etmemek.

class MyNotifier with ChangeNotifier {
final controller = StreamController();
}

Çözüm:
dispose() metodunu eklemek.

@override
void dispose() {
controller.close();
super.dispose();
}

7. Provider’da notifyListeners()’ın Fazla Kullanımı

Hata: Tek bir değişiklik için tüm dinleyicileri tetiklemek.

void updateCart() {
itemCount++;
notifyListeners();
}

Çözüm:
Veriyi parçalara ayırmak veya context.select() ile hedef rebuild yapmak.

8. Bloc’ta Tek Event ile Her Şeyi Yönetmek

Hata: Tüm işlemleri tek event’te toplamak.

class AppEvent {}

Çözüm:
Her işlem için ayrı event tanımlayın.

class LoadUser extends AppEvent {}
class UpdateCart extends AppEvent {}

9. GetX’te Gereksiz Reactive Variable Tanımlamak

Hata: Değişmeyecek verileri bile .obs yapmak.

final appName = 'MyApp'.obs; // Not useful

Çözüm:
Sadece değişen ve UI’yi etkileyen değerleri reactive yapın.

10. Yanlış Kütüphane Seçimi

Hata: Proje ihtiyacını analiz etmeden popüler olduğu için kütüphane seçmek.

Çözüm:

  • Küçük projeler → Provider / Riverpod
  • Orta projeler → Riverpod / Bloc
  • Büyük ekip projeleri → Bloc / Clean Architecture

Sonuç ve Öneriler

  1. State yönetimini proje başında doğru planlayın.
  2. Modüler yaklaşım kullanın.
  3. Gereksiz rebuild’leri azaltın.
  4. Immutable veri yapıları tercih edin.
  5. Test edilebilirliği göz ardı etmeyin.

Buraya kadar okuduğun için teşekkür ederim.

Bu yazıyı beğendiysen alkış butonuna tıklamayı unutma. Diğer içeriklerimden haberdar olmak için abone olabilirsin.

Teşekkür ederim.

Selin.