Flutter ve Performans Optimizasyonu

 

Flutter - Performans Optimizasyonu

Herkese merhaba. Bu blog yazısında sizlere Flutter uygulamalarımızı daha hızlı ve sorunsuz hale getirmek için kullanabileceğimiz bazı temel teknikler ve ipuçlarından bahsetmek istiyorum. GooglePlay’de ikinci uygulamamı yayınlamak üzereyim ve bu süreçte görseli, tasarımı, UI elementleri üzerine düşündüğümden daha fazlasını performans konusu için düşünmek zorunda olduğumu fark ettim. Çünkü uygulamanız ne kadar şık görünürse görünsün, performans sorunları varsa kullanıcı deneyiminin olumlu olması mümkün değil. Bu yüzden bu konuyu araştırdım ve bulduklarımı aşağıda sıraladım.

1. Render Performansı: Gereksiz Yeniden Çizimleri Engelleyin

Bir Flutter uygulamasında, her değişiklik ekranın yeniden çizilmesine neden olabilir. Bu, performans açısından çoğu zaman büyük bir sorun yaratır. Gereksiz yeniden çizimlerden kaçınarak uygulamanızı optimize edebilirsiniz.

  • const Anahtar Kelimesi: const ile tanımlanan widget'lar değişmezdir ve gereksiz yere yeniden çizilmezler. Özellikle statik yapılar oluştururken const kullanmak, performans üzerinde büyük bir iyileştirme sağlayabilir.
const Text(
'Merhaba Dünya!',
style: TextStyle(fontSize: 24),
);

Bu şekilde tanımlanan widget, değişmediği sürece yeniden çizilmeyecektir.

  • 'shouldRebuild' Kontrolü: Bazı widget'lar içinde shouldRebuild işlevini kullanarak, yalnızca belirli koşullar sağlandığında widget'ın yeniden çizilmesini sağlayabilirsiniz.
@override
bool shouldRebuild(covariant CustomWidget oldWidget) {
return oldWidget.value != value;
}

2. Liste Performansı: Büyük Listeleri Optimize Etme

Büyük veri kümeleriyle çalışmak, özellikle uzun listeler görüntülenirken performans sorunlarına neden olabilir. Flutter, bu tür durumlar için çeşitli optimizasyon araçları sunar.

  • ListView.builder: Sabit bir uzunluktaki veya büyük bir listede, tüm öğeleri aynı anda yüklemek yerine, yalnızca görünümdeki öğeleri yükleyen ListView.builder kullanmak daha performanslıdır.
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index]),
);
},
);
  • ReorderableListView: Sıralanabilir listeler oluştururken, performansı korumak için bu widget kullanılabilir. Aynı şekilde, yalnızca gerekli olan widget'ları oluşturur ve performansı artırır.

3. Asenkron Kodlama: UI’yi Kilitlemeden İşlemleri Yönetin

Ağ istekleri veya uzun süren işlemler gibi zaman alan işlemler, UI’yi dondurabilir. Flutter’da asenkron işlemleri yönetmek için birkaç araç bulunuyor.

  • FutureBuilder: FutureBuilder, bir asenkron işlemin tamamlanmasını beklerken, UI'de bekleme durumu göstermenizi sağlar. Bu sayede, uygulamanız kullanıcılara sorunsuz bir deneyim sunar.
FutureBuilder(
future: fetchData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text('Hata: ${snapshot.error}');
} else {
return Text('Veri: ${snapshot.data}');
}
},
);
  • async ve await: Bu anahtar kelimeler, işlemlerin asenkron şekilde yapılmasını sağlar. await, işlemin bitmesini beklerken, async işlemin tamamlanmasını belirtir. UI'nin donmasını engellemek için bu yapıları doğru kullanmak önemlidir.
Future<void> fetchData() async {
final response = await http.get('https://api.example.com/data');
// İşlemlerin burada yapılması gerekir
}

4. Gereksiz Paketlerden Kurtulma: Kod Tabanınızı Hafifletin

Flutter uygulamanıza paket eklemek, geliştirme sürecini hızlandırabilir. Ancak, gereksiz veya fazla paketler uygulamanızı şişirebilir ve performansı olumsuz etkileyebilir.

  • Bağımlılık Yönetimi: pubspec.yaml dosyanızdaki bağımlılıkları düzenli olarak gözden geçirin. Kullanılmayan paketleri kaldırın ve performans üzerindeki etkilerini ölçün.
dependencies:
flutter:
sdk: flutter
# Kullanılmayan paketleri kaldırın
  • Minimal Paket Kullanımı: Mümkünse, yalnızca gerçekten gerekli olan paketleri ekleyin. Daha hafif paketler tercih edin ve alternatifleri değerlendirin.

5. Flutter DevTools: Performans Analizi ve Hata Ayıklama

Flutter, performans sorunlarını izlemek ve çözmek için güçlü bir araç olan Flutter DevTools’u sunar. Bu araç ile uygulamanızın CPU ve bellek kullanımını analiz edebilir, UI gecikmelerini tespit edebilir ve performans iyileştirmeleri yapabilirsiniz.

  • CPU Profili: Uygulamanızın CPU kullanımını izlemek, hangi işlemlerin fazla kaynak tükettiğini anlamanızı sağlar.
  • Timeline (Zaman Çizelgesi): UI’nin nerede geciktiğini görmek için zaman çizelgesi aracını kullanabilirsiniz. Bu araç, hangi işlemlerin en fazla süreyi aldığını gösterir.
  • Hafıza Profili: Hafıza sızıntılarını tespit edebilir ve optimizasyon yapabilirsiniz. Fazla bellek tüketen işlemleri belirleyip, bellek kullanımı konusunda iyileştirmeler yapabilirsiniz.

Sonuç: Performans Optimizasyonu Bir Süreçtir

Performans optimizasyonu, ne yazık ki, bir seferlik bir işlem değil. Uygulamayı geliştirdikçe ve yeni özellikler ekledikçe, performans sorunları tekrar tekrar ortaya çıkabiliyor, bunu çok yaşadım. Basit bir değişiklik bir butonun Bu nedenle, düzenli olarak performans analizi iyileştirmeler yapmak gerekiyor.

Araştırmalarım sonucu burada ele aldığım tekniklerin, Flutter uygulamanızı daha performanslı hale getirmenize yardımcı olacağını umuyorum. Ancak tabi ki her uygulama farklıdır ve kendi özel gereksinimlerine göre optimize etmeyi unutmamalıyız.

Herkese iyi kodlamalar!

Selin.

Best Practices for Clean Code in Flutter

 

Hello,
I’m working on a new mobile app these days. I’ve set myself a deadline and I plan to release my app on GooglePlay at the end of August. The writing part of the program is mostly done, now I’m researching how I can make my code cleaner, more maintainable, how I can refactor it.
At this stage, I wanted to share what I learned here. Now let’s examine what we can do for clean code.

1. Follow Flutter’s Official Guidelines

The first recommendation we came across was Flutter and Dart’s own documentation.

  • Use Dart’s effective practices: Dart’s official guidelines, known as Effective Dart, provide comprehensive advice on how to write readable, maintainable, and effective Dart code. It covers style, documentation, design, and usage guidelines.
  • Flutter documentation: The Flutter documentation includes extensive tutorials, sample codes, and best practices. Regularly refer to it to stay updated on the best ways to implement Flutter features.

2. Structure Your Project

Foldering turned out to be more important than I thought. I used to open and name the dart files under the lib folder respectively, but that’s not how it should be done.

  • Feature-based structure: Instead of organizing files by type (e.g., putting all models in one folder, all views in another), organize your project by features. For instance, have separate directories for features like authenticationhomeprofile. Each feature directory can contain subdirectories for models, views, controllers, etc.
lib/
features/
authentication/
models/
views/
controllers/
home/
models/
views/
controllers
  • Separate business logic: Patterns like BLoC (Business Logic Component), Provider, or Riverpod help you separate business logic from the UI code. This separation makes the code more modular, testable, and maintainable.

3. Use State Management Wisely

It is also necessary to choose the type of state management carefully. We should not say that I learned provider, I will use this in all my projects.

  • Choose appropriate state management: Evaluate different state management solutions such as Provider, Riverpod, BLoC, and Redux. Choose one that fits your project’s needs and complexity. For simpler apps, Provider might be sufficient, while larger apps may benefit from BLoC or Redux.
  • Keep widgets simple: Widgets should primarily be concerned with rendering UI. Avoid embedding business logic within widgets. This keeps your UI code clean and your logic reusable.

4. Write Reusable Widgets

Breaking widgets into small pieces that can be reused is very useful when you want to fix or update the code later. However, it is important not to load more than one mission into a widget, especially about UI.

  • Break down UI into small widgets: Avoid creating large, monolithic widgets. Instead, break down the UI into smaller, reusable widgets that encapsulate specific pieces of functionality. This modularity makes it easier to test and reuse code.
  • Encapsulate widget functionality: Each widget should have a single responsibility. For instance, if you have a ProfileCard widget, it should only be concerned with displaying profile information, not fetching it from a server.

5. Follow Naming Conventions

Naming conventions are the first thing to learn. As the project grows, it needs to be planned so that it doesn’t turn into a tangle, fortunately I’m doing it well for now. :)

  • Consistent naming: Use a consistent naming convention throughout your codebase. For instance, use camelCase for variable names and PascalCase for class names.
  • Meaningful names: Choose descriptive names that convey the purpose of the variable or function. Avoid single-character names except in loop counters.

6. Keep Code DRY (Don’t Repeat Yourself)

That’s what reusable widgets are for.

  • Avoid duplication: If you find yourself writing the same code in multiple places, refactor it into a reusable function or widget. This reduces the risk of bugs and makes maintenance easier.
  • Centralize configurations: Store configuration values (e.g., API endpoints, theme data) in a single place. This makes it easier to update them and ensures consistency.

7. Comment and Document

Writing meaningful lines of commentary to help us remember what was done and where after some time has passed saves lives.

  • Document your code: Write meaningful comments to explain why certain decisions were made, especially for complex or non-obvious code blocks.
  • Use Dartdoc: Add Dartdoc comments to your public APIs to generate useful documentation for your codebase. This helps other developers (and your future self) understand how to use your code.

8. Use Linting and Code Analysis

I first heard about linter in Google Digital Workshop’s Flutter course. Certain rules can be added to the project file and the coding process can be accelerated or automated.

  • Enable linting: Use the Dart analyzer and linter to enforce coding standards and catch potential issues early. The analysis_options.yaml file can be configured to specify linting rules.
  • Customize linter rules: Adjust the linter rules to fit your team’s coding standards. For example, you might enforce a rule that all public methods have documentation comments.

9. Optimize Performance

I may have learned the importance of the const keyword too late, if it doesn’t need to be rebuilt every time, if it doesn’t change, it is a const.

  • Avoid unnecessary rebuilds: Use const constructors, keys, and efficient state management to minimize unnecessary widget rebuilds. For instance, using const constructors can help Flutter determine that a widget tree doesn't need to be rebuilt.
  • Profile your app: Use Flutter’s profiling tools (like the Dart DevTools) to identify and fix performance bottlenecks. This can help you understand where your app spends most of its time and optimize accordingly.

10. Write Tests

I continue to research the test writing phase. I will write a separate article on this.

  • Unit tests: Write unit tests for your business logic. This ensures that your logic works correctly and makes refactoring safer.
  • Widget tests: Write widget tests to ensure your UI behaves as expected. This can catch regressions and ensure your UI works correctly under various conditions.
  • Integration tests: Write integration tests to verify the complete flow of your app. These tests run on real or simulated devices and ensure that all parts of your app work together as expected.

11. Version Control

Git and GitHub are very important for keeping track of project versions. We may want to look retrospectively to see where and what I changed, or we can avoid a possible data loss. After every feature I add in my application, I always send it to git.

  • Use Git effectively: Commit changes frequently with meaningful messages. Use branches for new features and bug fixes to keep the main branch stable.
  • Code reviews: Conduct regular code reviews to maintain code quality and share knowledge among team members. This can catch potential issues early and ensure consistent coding standards.

12. Keep Dependencies Updated

  • Update regularly: Regularly update your Flutter and Dart SDKs, as well as package dependencies. This ensures you have the latest features, bug fixes, and security patches.
  • Check for breaking changes: Review the changelog for breaking changes when updating dependencies. This helps you understand what changes you need to make to your code to accommodate the updates.

13. Use Extensions and Utilities

Helper functions can be used to optimize simple operations. For example, in my last project, I wanted to pluralize the category names coming from the database and I used it in a similar way.

  • Create utility functions: Extract common functionality into utility functions or extensions. This helps keep your code DRY and organized. For example, you might create an extension on String to add a method for capitalizing the first letter
extension StringExtension on String {
String capitalize() {
return this[0].toUpperCase() + substring(1);
}
}

When I searched for what we can do to write clean and sustainable code in Flutter, I gathered what I found under these headings. If you have anything to add, you can write in the comments. I hope it was useful.

Thanks for reading.

Selin.