5 Основных шаблонов Проектирования, которые должен знать каждый Flutter инженер

5 Essential Design Patterns Every Flutter Engineer Should Master

Шаблоны проектирования важны для разработчиков, так как они предлагают решения для распространённых проблем, встречающихся в разработке ПО. Для Flutter-разработчиков понимание и использование этих шаблонов может значительно повысить эффективность, масштабируемость и поддержку их приложений. В этой статье рассматриваются пять ключевых шаблонов проектирования, которые должен знать каждый Flutter-разработчик: Singleton, Provider, Builder, Observer и MVC (Model-View-Controller).

1. Паттерн Singleton

Паттерн Singleton гарантирует, что у класса будет только один экземпляр и обеспечивает глобальную точку доступа к нему. Это особенно полезно в Flutter для управления общими ресурсами, такими как настройки конфигурации, базы данных или сетевые соединения.

Во Flutter паттерн Singleton можно реализовать с помощью приватного конструктора и статического экземпляра. Вот пример:

class MySingleton {
  static final MySingleton _instance = MySingleton._internal();

  // Приватный конструктор
  MySingleton._internal();

  // Публичный фабричный метод для возврата одного и того же экземпляра
  factory MySingleton() {
    return _instance;
  }

  void someMethod() {
    print("Вызван метод экземпляра Singleton");
  }
}

При вызове MySingleton() вы всегда получаете один и тот же экземпляр, что гарантирует единообразный доступ к общим ресурсам.

Случаи использования

  • Подключение к базе данных: Обеспечивает единственный экземпляр подключения, чтобы избежать множественных подключений.
  • Настройки конфигурации: Поддерживает настройки для всего приложения.

2. Паттерн Provider

Паттерн Provider, являющийся основным в сообществе Flutter, упрощает управление состоянием, предоставляя простой способ доступа к данным и бизнес-логике из дерева виджетов. Он придерживается принципов инверсии управления (IoC) и внедрения зависимостей (DI).

Пакет Provider предлагает простой способ реализации этого паттерна. Вот базовый пример:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => Counter(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Пример использования Provider')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'Вы нажали кнопку столько раз:',
              ),
              Consumer<Counter>(
                builder: (context, counter, child) => Text(
                  '${counter.count}',
                  style: Theme.of(context).textTheme.headline4,
                ),
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => context.read<Counter>().increment(),
          tooltip: 'Увеличить',
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

Случаи использования

  • Управление состоянием: Управление и передача состояния в разных частях приложения.
  • Внедрение зависимостей: Легкое внедрение зависимостей в дерево виджетов.

3. Паттерн Builder

Паттерн Builder помогает пошагово создавать сложные объекты. Он отделяет конструирование объекта от его представления, что позволяет использовать один и тот же процесс создания для различных представлений.


Во Flutter виджет Builder является классическим примером и часто используется для создания виджетов, которые зависят от BuildContext, недоступного на момент создания виджета.

import 'package:flutter/material.dart';

class MyCustomWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Builder(
      builder: (BuildContext context) {
        return Text(
          'Привет, Flutter!',
          style: Theme.of(context).textTheme.headline4,
        );
      },
    );
  }
}

Случаи использования

  • Сложные элементы пользовательского интерфейса: Создание сложных виджетов, зависящих от контекста.
  • Условный рендеринг: Отображение виджетов в зависимости от условий выполнения.

4. Паттерн Observer

Паттерн Observer определяет зависимость «один ко многим» между объектами, так что при изменении состояния одного объекта все его зависимые объекты уведомляются и автоматически обновляются. Этот паттерн важен для реализации систем обработки событий.

В Flutter этот паттерн широко используется в решениях для управления состоянием, таких как ChangeNotifier и ValueNotifier.

import 'package:flutter/material.dart';

class Counter extends ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

void main() {
  final counter = Counter();

  counter.addListener(() {
    print('Счетчик изменился: ${counter.count}');
  });

  counter.increment();
}

Случаи использования

  • Обработка событий: Уведомление и обновление слушателей при изменении состояния.
  • Реактивное программирование: Реализация реактивных потоков данных.

5. Паттерн MVC (Model-View-Controller)

Паттерн MVC разделяет приложение на три взаимосвязанных компонента: Model (логика данных), View (логика интерфейса) и Controller (бизнес-логика). Такое разделение помогает управлять сложностью приложения путем декомпозиции кода на управляемые части.
Хотя Flutter не навязывает определенный архитектурный шаблон, MVC можно реализовать вручную. Вот простой пример:

Model: Определяет структуру данных и бизнес-логику.

class CounterModel {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
  }
}

View: Представляет пользовательский интерфейс.

import 'package:flutter/material.dart';

class CounterView extends StatelessWidget {
  final int count;
  final VoidCallback onIncrement;

  CounterView({required this.count, required this.onIncrement});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Пример MVC')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('Вы нажали кнопку столько раз:'),
            Text(
              '$count',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: onIncrement,
        tooltip: 'Увеличить',
        child: Icon(Icons.add),
      ),
    );
  }
}

Controller: Связывает Model и View, обрабатывает ввод пользователя и обновляет Model.

import 'package:flutter/material.dart';

class CounterController {
  final CounterModel _model;

  CounterController(this._model);

  int get count => _model.count;

  void increment() {
    _model.increment();
  }
}

void main() {
  final model = CounterModel();
  final controller = CounterController(model);

  runApp(MyApp(controller: controller));
}

class MyApp extends StatelessWidget {
  final CounterController controller;

  MyApp({required this.controller});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterView(
        count: controller.count,
        onIncrement: controller.increment,
      ),
    );
  }
}

Случаи использования

  • Сложные приложения: Разделение задач для управления сложностью.
  • Масштабируемость: Легкое добавление новых функций без изменения других частей.

Заключение
Шаблоны проектирования — это важные инструменты в арсенале Flutter-разработчика. Освоив паттерны Singleton, Provider, Builder, Observer и MVC, вы сможете создавать надёжные, масштабируемые и поддерживаемые приложения. Эти паттерны не только решают распространённые проблемы, но и способствуют применению лучших практик и принципов проектирования в разработке ПО. Независимо от того, управляете ли вы состоянием, создаете ли сложные объекты, обрабатываете ли события или организуете архитектуру приложения, эти паттерны помогут вам найти эффективные и действенные решения.

Источник: https://medium.com/@ayhamxv12instagram/5-essential-design-patterns-every-flutter-engineer-should-master-09206ec077ee

Переход на Flutter 3.22 для Web

Flutter 3.22 представляет значительные улучшения для веб-разработки, сосредоточенные на настройке инициализации приложения. Если вы переходите с более старой версии или начинаете с нуля, это руководство проведет вас через весь процесс. Мы рассмотрим интеграцию платформы, инициализацию веб-приложения и настройку процесса инициализации с использованием файла flutter_bootstrap.js.

Инициализация веб-приложения Flutter
Интеграция Flutter в вашу веб-платформу включает настройку правильного процесса инициализации для обеспечения плавной работы и функциональности. Flutter 3.22 предоставляет более гибкий способ настройки этого процесса, что позволяет удовлетворить различные требования приложения.

Процесс инициализации веб-приложения
Процесс инициализации веб-приложения Flutter упрощен, но поддается настройке. Он вращается вокруг сценария flutter_bootstrap.js, который генерируется в процессе сборки и управляет последовательностью запуска приложения.

Сценарий flutter_bootstrap.js
Когда вы собираете свое веб-приложение Flutter с помощью команды flutter build web, в каталоге build/web создается сценарий flutter_bootstrap.js. Этот сценарий необходим для инициализации и запуска вашего приложения.

Чтобы включить этот сценарий в ваш index.html, добавьте следующий код:

<html>
  <body>
    <script src="flutter_bootstrap.js" async></script>
  </body>
</html>

Кроме того, вы можете встроить сценарий, вставив токен {{flutter_bootstrap_js}}:

<html>
  <body>
    <script>
      {{flutter_bootstrap_js}}
    </script>
  </body>
</html>

Настройка инициализации
По умолчанию команда flutter build web генерирует базовый сценарий flutter_bootstrap.js. Однако вам может понадобиться настроить эту инициализацию по различным причинам, таким как установка пользовательской конфигурации или изменение настроек сервис-воркера.

Для настройки процесса инициализации создайте собственный файл flutter_bootstrap.js в каталоге web вашего проекта. Этот пользовательский сценарий заменит стандартный, сгенерированный в процессе сборки.

Токены для настройки
В своем пользовательском файле flutter_bootstrap.js вы можете использовать несколько токенов:

  • {{flutter_js}}: Делает объект _flutter.loader доступным.
  • {{flutter_build_config}}: Устанавливает метаданные для FlutterLoader.
  • {{flutter_service_worker_version}}: Представляет версию сборки сервис-воркера.

Написание пользовательского flutter_bootstrap.js
Пользовательский сценарий flutter_bootstrap.js должен содержать три основных компонента:

  • Токен {{flutter_js}}.
  • Токен {{flutter_build_config}}.
  • Вызов _flutter.loader.load() для запуска приложения.

Базовый пример выглядит так:

{{flutter_js}}
{{flutter_build_config}}

_flutter.loader.load();

API _flutter.loader.load()
Функция _flutter.loader.load() может принимать необязательные аргументы для более индивидуализированной инициализации:

  • config: Объект конфигурации для вашего приложения.
  • onEntrypointLoaded: Функция обратного вызова, вызываемая при готовности движка к инициализации.
  • serviceWorkerSettings: Конфигурация для сервис-воркера.

Настройки конфигурации
Объект config может включать различные поля:

  • assetBase: Базовый URL для каталога с ресурсами.
  • canvasKitBaseUrl: Базовый URL для загрузки canvaskit.wasm.
  • canvasKitVariant: Указывает вариант CanvasKit (auto, full, или chromium).
  • canvasKitForceCpuOnly: Принудительное использование только CPU-рендеринга, если установлено значение true.
  • canvasKitMaximumSurfaces: Максимальное количество наложенных поверхностей.
  • debugShowSemanticNodes: Отображение дерева семантики на экране для отладки.
  • hostElement: HTML-элемент, в котором Flutter отрисовывает приложение.
  • renderer: Указывает веб-рендерер (canvaskit или html).

Настройка конфигурации Flutter на основе параметров URL-запроса
Вот пример, который настраивает конфигурацию на основе параметров URL-запроса:

{{flutter_js}}
{{flutter_build_config}}

const searchParams = new URLSearchParams(window.location.search);
const forceCanvaskit = searchParams.get('force_canvaskit') === 'true';
const userConfig = forceCanvaskit ? {'renderer': 'canvaskit'} : {};
_flutter.loader.load({
  config: userConfig,
  serviceWorkerSettings: {
    serviceWorkerVersion: {{flutter_service_worker_version}},
  },
});

Использование обратного вызова onEntrypointLoaded
Функция onEntrypointLoaded позволяет вам выполнять пользовательскую логику на различных этапах инициализации:

{{flutter_js}}
{{flutter_build_config}}

const loading = document.createElement('div');
document.body.appendChild(loading);
loading.textContent = "Загрузка точки входа...";
_flutter.loader.load({
  onEntrypointLoaded: async function(engineInitializer) {
    loading.textContent = "Инициализация движка...";
    const appRunner = await engineInitializer.initializeEngine();
    loading.textContent = "Запуск приложения...";
    await appRunner.runApp();
  }
});

Обновление старого проекта
Если вы обновляете проект с версии Flutter 3.21 или более ранней, выполните следующие шаги для создания нового файла index.html:

  1. Удалите существующие файлы из вашего каталога /web.
  2. Выполните следующую команду в каталоге вашего проекта:
   flutter create . --platforms=web

Заключение
Переход на Flutter 3.22 для веб-разработки предлагает улучшенную гибкость и возможность настройки инициализации приложений. Понимая и используя сценарий flutter_bootstrap.js, вы можете точно настроить процесс запуска в соответствии с конкретными потребностями вашего приложения. Независимо от того, настраиваете ли вы конфигурацию или обновляете старый проект, это руководство предоставляет подробные шаги для начала работы.

Источник: https://yawarosman.medium.com/switching-to-flutter-3-22-for-web-7e405e9b56f6

Как использовать flavor во Flutter и почему это так важно

flautter flavors

В современной разработке приложений управление различными версиями приложения необходимо для их целевой направленности на разные среды, группы пользователей или рыночные сегменты. Эти версии, называемые «флейворами» (flavors), позволяют разработчикам легко настраивать поведение и внешний вид приложения, поддерживать его, включать или отключать функции для различных версий и конфигураций без необходимости поддерживать несколько кодовых баз.

Флейворы позволяют эффективно управлять различными конфигурациями для таких сред, как разработка, промежуточное тестирование (staging) и производство, гарантируя, что каждая среда имеет свои уникальные настройки, API-эндпоинты и учетные данные. Это снижает риск ошибок и повышает эффективность разработки и тестирования.

Например, версия вашего приложения для разработки может использовать dev.api.myapp.com, тогда как производственная версия использует api.myapp.com. Вместо того чтобы жестко прописывать эти значения и вручную собирать приложение для каждой среды, вы можете использовать флейворы, чтобы задавать эти значения в качестве конфигураций во время сборки, ускоряя процесс разработки.

Использование флейворов во Flutter с помощью flutter_flavorizr

flutter_flavorizr упрощает создание и управление множественными флейворами в приложениях на Flutter. Он автоматизирует процесс конфигурации как для Android, так и для iOS, помогая разработчикам поддерживать единообразие конфигурации и минимизировать время, затрачиваемое на ручную настройку. Как это сделать? Итак…

Шаг 1 — Установка flutter_flavorizr

Добавьте flutter_flavorizr в ваш файл pubspec.yaml в разделе dev_dependencies.

Шаг 2 — Настройка флейворов

Создайте новый файл с именем flavorizr.yaml в корне проекта и определите имена флейворов, затем запустите команду

flutter pub run flutter_flavorizr

для генерации необходимых файлов и конфигураций.

flavors:
  development:
    app:
      name: "MyApp DEV"
    android:
      applicationId: "com.example.myapp.dev"
    ios:
      bundleId: "com.example.myapp.dev"
    macos:
      bundleId: "com.example.myapp.dev"
  production:
    app:
      name: "MyApp"
    android:
      applicationId: "com.example.myapp"
    ios:
      bundleId: "com.example.myapp"
    macos:
      bundleId: "com.example.myapp"

Эта команда создаёт соответствующие файлы и каталоги для каждого флейвора, включая отдельные файлы main.dart и конфигурационные файлы.

Шаг 3 — Доступ к конфигурациям флейворов в коде

Используйте класс FlavorConfig для доступа к конфигурациям, специфичным для каждого флейвора, в вашем коде на Flutter.

import 'package: flutter/material.dart';
import 'package:flutter_flavorizr/flutter_flavorizr.dart';

void main {
    runApp (MyApp) ;
}

class MyApp extends StatelessWidget {
  @override
  Widget build (BuildContext context) {
    final flavor = FlavorConfig.instance.name;

    return MaterialApp( 
      title: 'Flutter Flavors - $flavor',
      home: HomeScreen,
    );
  }
}

Шаг 4 — Сборка и запуск флейворов

Используйте следующие команды для сборки и запуска конкретного флейвора:

flutter run --flavor development -t lib/main_development.dart
flutter run --flavor production -t lib/main_production.dart

И всё готово!

Почему это важно?

При разработке в реальной среде необходимо быть быстрым и эффективным. Использование флейворов во Flutter предоставляет несколько ключевых преимуществ, которые делают его важным как для разработчиков, так и для бизнеса.

  • Упрощение процесса разработки: Флейворы позволяют поддерживать одну кодовую базу, адаптируя приложение под разные среды (разработка, промежуточное тестирование, производство) или рыночные сегменты (бесплатная, премиум версия). Это уменьшает сложность и трудозатраты, связанные с управлением отдельными кодовыми базами для каждой версии вашего приложения.
  • Повышение качества тестирования и контроля качества (QA): Флейворы позволяют создавать специфические конфигурации для различных сценариев тестирования. Вы можете иметь флейвор, который имитирует производственную среду, но использует промежуточный API. Это гарантирует, что ваша команда QA сможет тестировать приложение в условиях, максимально приближенных к реальной производственной среде.
  • Упрощение кастомизации: Флейворы позволяют легко включать или отключать функции в зависимости от целевой аудитории или рынка. Например, можно иметь флейвор с дополнительными функциями для премиум-пользователей, сохраняя упрощённую версию для бесплатных пользователей.
  • Упрощенная развертка и конфигурация: Управление различными конфигурациями, такими как API-эндпоинты, настройки темы или учетные данные сторонних сервисов, становится более эффективным с флейворами. Определяя эти настройки в централизованном конфигурационном файле, вы снижаете риск ошибок и обеспечиваете согласованность между средами.
  • Ускорение разработки: Инструменты, такие как flutter_flavorizr, значительно сокращают ручные усилия, необходимые для настройки и управления несколькими флейворами. Это позволяет разработчикам сосредоточиться больше на кодировании.
  • Подготовка к будущему: По мере роста приложения появляется необходимость поддержки дополнительных сред или рыночных сегментов. Использование флейворов с самого начала делает архитектуру вашего приложения более масштабируемой и адаптируемой в долгосрочной перспективе.

Спасибо за чтение! Теперь вы успешно настроили флейворы в своём проекте Flutter всего за несколько простых шагов. Для более глубоких вариантов настройки вы также можете адаптировать флейворы под свои сценарии. Удачного кодинга!

Источник: https://bilalrehman08.medium.com/how-to-use-flavor-in-flutter-and-why-its-important-for-you-0f53c71b59ff

Flutter: async или isolate. Параллелизм

Многие не понимают разницу между async и isolate (параллелизмом и конкурентностью). В этой статье мы разберём эти понятия, а в следующей покажу, как реализовать изоляты в Dart.

ASYNC
Сначала разберём async, так как большинство из нас уже использовало этот подход хотя бы раз.

void readData() async {
  var url = "www.example.com";
  var content = await http.get(url); // Асинхронный вызов
  var data = parsingData(content); // Здесь будем использовать изолят, так как операция тяжелая
}

Этот код выполняется в обычном режиме до вызова загрузки файла, после чего программа ждёт, пока файл будет загружен. Пока она ждёт, другие части программы могут продолжать работать. Однако, обратите внимание, что в любой момент времени работает только одна часть приложения — ведь у нас всего один поток, один процессор. Асинхронность не делает ваше приложение многопоточным автоматически. После загрузки файла метод продолжит выполнение. Это и есть async. Когда задача (future) завершается, выполнение продолжается с следующей строки кода. В большинстве случаев async достаточно.

В примере выше наш код будет ждать на второй строке (await http.get(url)) до тех пор, пока данные не будут получены с интернета, и после этого мы начнем их обработку.

Теперь представьте, что файл очень большой, и его обработка требует много ресурсов процессора. Это может вызвать задержки в работе приложения? Многие могут подумать, что нет, так как вычисления происходят в асинхронном методе. Но это не так. Почему? Даже если метод асинхронный, в нём всё равно может происходить много синхронных операций. Эта обработка данных (parsingData) выполняется синхронно. Она занимает процессор, и другие части вашего приложения не смогут выполняться. Интерфейс пользователя не обновится, пока тяжелая работа не будет завершена, и пользователь увидит задержки и «подтормаживания».

Решение? Многопоточность с помощью изолятов!

ИЗОЛЯТЫ
Dart позволяет выполнять код параллельно с помощью изолятов. Изолят запускает другой процесс Dart, фактически, на другом потоке, на другом процессоре. Эти два изолята полностью изолированы друг от друга. Один не блокирует другой. Таким образом, ваше приложение может выполнять тяжелые вычисления и одновременно оставаться отзывчивым для пользователя. В этой статье я не буду углубляться в реализацию изолятов, так как это немного сложнее, и цель статьи — разъяснить разницу между async и изолятами.

Изолят можно представить как отдельное пространство на машине со своим собственным участком памяти и одним потоком, выполняющим события. В отличие от языков, таких как C++, где несколько потоков могут разделять одну память и выполнять произвольный код, в Dart каждый поток работает в своем собственном изоляте, с собственной памятью и обрабатывает только события.

Когда использовать изоляты?

Большинство приложений Dart работают в одном изоляте, но можно использовать больше, если это необходимо. Например, если вам нужно выполнить вычисление, которое может занять слишком много времени в основном изоляте и привести к «подтормаживанию», вы можете использовать Isolate.spawn() или функцию compute в Flutter, чтобы создать отдельный изолят для выполнения тяжелых вычислений, оставляя основной изолят свободным для перерисовки интерфейса.

Этот новый изолят получит свой собственный цикл событий и свою память, к которой родительский изолят не имеет доступа. Изоляты называются так потому, что они изолированы друг от друга. Они могут взаимодействовать только посредством передачи сообщений. Один изолят отправляет сообщение другому, и тот обрабатывает его в своем цикле событий.

Подведем итоги:
Async позволяет ждать выполнения других операций без блокировки текущего потока. Async не создаёт отдельный поток; Dart просто переключается между разными частями программы по мере необходимости. Но если необходимо выполнение параллельных задач, вам нужны изоляты. Они сложнее в настройке, но позволяют решать задачи, требующие интенсивных вычислений, без задержек в работе пользовательского интерфейса.

Изоляты играют важную роль в обеспечении конкурентной обработки. Выполнение ресурсоемких операций в основном потоке может негативно сказаться на производительности приложения. Использование изолятов позволяет значительно повысить производительность и эффективность приложений Flutter.

Источник: https://medium.com/@AryanBeast/async-vs-isolate-in-flutter-parallelism-in-flutter-ae3954fb5d4c

Освоение циклов в Dart: while и for

Циклы — это фундаментальные конструкции в программировании, которые позволяют выполнять блок кода многократно, основываясь на условии или наборе значений. В Dart основными конструкциями для организации циклов являются while и for. В этой статье мы подробно рассмотрим эти конструкции и дадим вам знания, необходимые для их эффективного использования в программах на Dart.

Циклы while

Цикл while выполняет блок кода до тех пор, пока определённое условие истинно. Это полезно, когда количество итераций заранее неизвестно и зависит от условия.

Синтаксис

while (condition) {
// Код выполняется пока condition истинно
}

Пример использования

void main() {
    int counter = 0;
    
    while (counter < 5) {
      print( 'Counter: $counter');
      counter++;
  }
}

В этом примере программа выводит значение переменной counter от 0 до 4. Цикл продолжает выполняться, пока counter меньше 5.

Бесконечный цикл

Будьте осторожны с циклами while, чтобы избежать создания бесконечных циклов, где условие никогда не становится ложным.

void main() {
    int counter = 0;
    
    while (true) {
      print( 'Counter: $counter');
      counter++;
      if (counter >= 5) {
       break;
      }
   }
}

В этом примере цикл while (true) управляется оператором break, который завершает выполнение цикла, когда counter достигает 5.

Циклы do-while

Цикл do-while похож на while, но гарантирует, что блок кода будет выполнен хотя бы один раз, так как условие проверяется после выполнения блока.

Синтаксис

do {
// Исполняемый код
} while (condition);
void main() {
  int counter = 0;

  do {
    print( 'Counter: $counter');
    counter++;
  } while (counter < 5);
}

В этом примере программа выводит значение переменной counter от 0 до 4. Цикл гарантирует, что блок кода выполнится хотя бы один раз перед проверкой условия.

Циклы for

Цикл for обычно используется, когда количество итераций известно заранее. Он состоит из инициализации, условия и операции инкремента/декремента.

for (initialization; condition; increment/decrement) {
  // Исполняемый код
}
void main () {
  for (int i = 0; i < 5; i++) {
    print('i: $i');
  }
}

В этом примере программа выводит значение переменной i от 0 до 4. Цикл инициализирует i значением 0, проверяет условие i < 5 и увеличивает i на 1 в каждой итерации.

Вложенные циклы for

Вы можете вкладывать циклы for друг в друга для обработки более сложных итераций, например, для обхода матрицы.

void main) {
  for (int i = 0; i < 3; i++) {
    for(intj=0; j<3; j++) {
      print('i: $i, j: $j');
    }
  }
}

В этом примере внешний цикл выполняется 3 раза, и для каждой итерации внешнего цикла внутренний цикл выполняется 3 раза, что в сумме дает 9 итераций и выводит значения i и j.

Циклы for-in

Цикл for-in используется для обхода элементов коллекции, такой как список или множество.

void main ( ) {
  List<String> fruits = ['Apple', 'Banana', 'Cherry'];
    
  for (var fruit in fruits) {
    print(fruit);
  }
}

В этом примере программа перебирает список fruits и выводит в консоль каждый фрукт.

Метод forEach

Коллекции в Dart также предоставляют метод forEach, который является функциональным подходом для обхода элементов.

void main ( ) {
  List<String> fruits = ['Apple', 'Banana', 'Cherry'];
  fruits. forEach((fruit) => print(fruit));
}

В этом примере метод forEach перебирает список fruits и выполняет заданную функцию для каждого элемента.

Лучшие практики при использовании циклов

  1. Избегайте бесконечных циклов: Убедитесь, что в ваших циклах есть условие завершения, чтобы предотвратить бесконечные циклы.
  2. Используйте описательные имена переменных: Применяйте понятные имена для переменных-счётчиков и элементов, чтобы улучшить читаемость кода.
  3. Сохраняйте циклы простыми: Избегайте сложной логики внутри циклов. При необходимости рефакторьте код в функции.
  4. Используйте break и continue разумно: Используйте break для досрочного выхода из цикла и continue для пропуска текущей итерации. Применяйте их с осторожностью, чтобы избежать путаницы в коде.
  5. Предпочитайте for-in и forEach для коллекций: При обходе коллекций используйте for-in или forEach для более чистого и читаемого кода.

Заключение

Понимание и эффективное использование циклов, таких как while, do-while, for и for-in, имеет решающее значение для управления потоком выполнения программ на Dart. Выбирая подходящую конструкцию цикла для ваших нужд и следуя лучшим практикам, вы сможете писать чистый, эффективный и удобный для поддержки код. Независимо от того, обрабатываете ли вы простые итерации или сложные вложенные циклы, Dart предоставляет все необходимые инструменты для управления повторяющимися задачами в ваших программах.

Источник: https://azimdev.medium.com/mastering-loops-in-dart-while-and-for-4d5e48aa9a03