Flutter & Dart | Мобильный трудоголик Telegram channel photo

Flutter & Dart | Мобильный трудоголик

@hardworkerflutterPublic Channel

Пишу простым языком про разработку на Flutter & Dart (iOS, Android, macOS, Windows) и мобильную разработку в целом. Обо мне: https://t.me/hardworkerFlutter/2 Чат: @flutterDevChat Другие мои каналы: @hardworkerIT и @itDenisov

141

Last views

414 (414 subscribers)
Участники
—
Rating
43
Total Posts
Oct 16, 2026, 1:37 PM
Обновлено
Apr 17, 2026
Created
Stats

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

Channel Information

Channel Information
Channel NameFlutter & Dart | Мобильный трудоголик
Username@hardworkerflutter
CategoryМобильные приложения
Languageрусский
CountryРоссия
Участники414
Channel TypePublic Channel
Created17 апр. 2026 г.
Last Updated16 окт. 2026 г. • 5 дней назад
StatusActive

Рейтинг

Глобальный рейтинг
#36939+99
Рейтинг по языку
#15010+25
Рейтинг по категории
#172+1

Рост участников

Всего: 41430 Days growth: +348.9%
09/1709/2209/2710/0210/0710/1210/16
Рост участников - 30 Days
DateSubscribers
17:09:2026380
18:09:2026380
19:09:2026380
20:09:2026380
21:09:2026380
22:09:2026380
23:09:2026380
24:09:2026380
25:09:2026380
26:09:2026380
27:09:2026380
28:09:2026380
29:09:2026380
30:09:2026380
01:10:2026380
02:10:2026380
03:10:2026380
04:10:2026380
05:10:2026380
06:10:2026380
07:10:2026380
08:10:2026380
09:10:2026380
10:10:2026380
11:10:2026380
12:10:2026380
13:10:2026380
14:10:2026380
15:10:2026380
16:10:2026414
Рост участников - Weekly
DateSubscribers
Week of 13:04:2026131
Week of 20:04:2026131
Week of 27:04:2026131
Week of 04:05:2026158
Week of 11:05:2026158
Week of 18:05:2026158
Week of 25:05:2026158
Week of 01:06:2026158
Week of 08:06:2026158
Week of 15:06:2026158
Week of 22:06:2026158
Week of 29:06:2026158
Week of 06:07:2026286
Week of 13:07:2026286
Week of 20:07:2026286
Week of 27:07:2026286
Week of 03:08:2026286
Week of 10:08:2026286
Week of 17:08:2026361
Week of 24:08:2026361
Week of 31:08:2026369
Week of 07:09:2026380
Week of 14:09:2026380
Week of 21:09:2026380
Week of 28:09:2026380
Week of 05:10:2026380
Week of 12:10:2026414
Рост участников - Monthly
DateSubscribers
April 2026131
May 2026158
June 2026158
July 2026286
August 2026369
September 2026380
October 2026414
Рост участников - Yearly
DateSubscribers
2026414

Top 10 posts

Most viewed posts from this channel

By views
  1. #1👣 Как запустить Flutter-приложение на iPhone без Mac и подписки Apple2.3K
  2. #2🏋️ Установка APK на Android усложняется: придется ждать сутки2.0K
  3. #3👣 Почему Flutter - идеальный выбор для разработки с ИИ-агентами1.8K
  4. #4👣 Релиз Flutter 3.47: главные изменения для разработчиков1.8K
  5. #5👣 GenUI в действии: команда Flutter раздала 3000 чашек кофе с ИИ-рису1.7K
  6. #6👣 Анонсирован Genkit для Dart: фреймворк для ИИ-приложений1.6K
  7. #7👣 Почему primary constructors в Dart сложнее, чем кажется1.6K
  8. #8👣 Архитекторы, тестировщики и кодеры. Создаем мультиагентную команду1.4K
  9. #9👣 Знаете Flutter? Значит, можете писать бэкенд на Dart920
  10. #10👣 Релиз Dart 3.13: что принесла новая версия827

Последние посты

Video
👣 Переиспользуемая пагинация во Flutter на Riverpod

Бесконечная лента - привычная часть многих приложений. Но реализация пагинации часто превращается в повторяющийся код. Хочется один раз собрать универсальный механизм и переиспользовать его в разных экранах. В статье на примере Riverpod рассказывается, как это сделать без сторонних пакетов.


В чем суть проблемы:

Готовые пакеты для пагинации существуют. Но многие из них устарели, используют устаревшие классы или предполагают, что номер страницы всегда число. Это не всегда так. Например в банковском приложении транзакции могут группироваться по датам. Тогда индекс страницы - это дата или строка, а не целое число.

Решение - построить собственный контроллер поверх AsyncNotifier. Он будет хранить текущий индекс, загружать первую страницу и добавлять следующую к уже полученным элементам.


Как устроен контроллер:

Контроллер принимает два типа: T для данных и I для индекса страницы. Ему нужны три вещи: начальный индекс, способ загрузки страницы и способ вычисления следующего индекса.


abstract class PaginationController<T, I> {
I get initialPage;
late I currentPage = initialPage;

FutureOr<List<T>> loadPage(I page);
Future<void> loadNextPage();

I nextPage(I currentPage);
}


Затем создается механизм, который реализует этот интерфейс поверх AsyncNotifier.


mixin AsyncPaginationController<T, I> on AsyncNotifier<List<T>>
implements PaginationController<T, I> {
@override
late I currentPage = initialPage;

@override
FutureOr<List<T>> build() async => loadPage(initialPage);

@override
Future<void> loadNextPage() async {
final newState = await AsyncValue.guard(() async {
currentPage = nextPage(currentPage);
final elements = await loadPage(currentPage);
return [...?state.valueOrNull, ...elements];
});
state = newState;
}
}


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


Как выглядит использование:


class RepositoriesViewModel extends AsyncNotifier<List<Repository>>
with AsyncPaginationController<Repository, int> {
@override
int get initialPage => 1;

@override
FutureOr<List<Repository>> loadPage(int page) async {
final (_, items) = await repository.fetchRepositories(page: page);
return items;
}

@override
int nextPage(int currentPage) => currentPage + 1;
}


Все, что требуется от экрана - задать три параметра.


Как подключить в интерфейсе:

Осталось отслеживать конец прокрутки. Для этого используется NotificationListener.


NotificationListener(
onNotification: (ScrollNotification info) {
if (info is ScrollEndNotification &&
info.metrics.pixels >= info.metrics.maxScrollExtent) {
viewModel.loadNextPage();
}
return true;
},
child: CustomScrollView(...),
)


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


🔗 Читать подробнее


💡 Вывод:

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

Такой подход дает чистую архитектуру, поддержку нечисловых индексов и легкое расширение поиском, фильтрацией и обновлением жестом.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
141005
Video
👣 Релиз Genkit Dart 1.0: ИИ-функции и агенты на Dart и Flutter

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

Раньше фреймворк был в статусе preview. Обратная связь от сообщества помогла доработать API и расширить набор инструментов для задач на проде. Теперь релиз готов к использованию в реальных проектах.


Как установить:

Добавить Genkit в проект можно одной командой:


dart pub add genkit


Также можно установить скиллы для ИИ-ассистентов, чтобы они знали актуальные API и практики Genkit Dart:


npx skills add genkit-ai/skills



Единый API для разных моделей:

Genkit поддерживает Google Gemini, Anthropic Claude, OpenAI и модели, совместимые с OpenAI, через один интерфейс. Провайдеры регистрируются как плагины и их можно менять без переписывания логики приложения.


final ai = Genkit(plugins: [googleAI(), anthropic()]);
final prompt = 'Suggest a weekend getaway from San Francisco.';

final fromGemini = await ai.generate(
model: googleAI.gemini('gemini-flash-latest'),
prompt: prompt,
);

final fromClaude = await ai.generate(
model: anthropic.model('claude-sonnet-5-5'),
prompt: prompt,
);



Типобезопасность через flows:

Genkit позволяет оборачивать ИИ-логику в flows - строго типизированные функции, которые легко тестировать и разворачивать как HTTP-эндпоинты. Схемы данных описываются один раз на Dart и используются и на сервере и в приложении.


@Schema()
abstract class $TripRequest {
String get destination;
int get days;
}


Это дает сквозную типобезопасность между бэкендом и Flutter-приложением.


Где выполняется код:

ИИ-логика пишется на стандартном Dart, поэтому ее можно запускать где угодно:

🔹Напрямую во Flutter: для быстрого прототипирования или сценариев, где пользователь использует свой ключ API.

🔹На Dart-сервере: для сложных flows и защиты приватных промптов. Flutter вызывает их через defineRemoteAction.

🔹Во Flutter с удаленными моделями: ИИ-логика остается в приложении, но запросы к модели идут через легкий Genkit-бэкенд, который защищает ключи API и проверяет авторизацию.


Инструменты и отладка:

Genkit включает локальный Developer UI для тестирования flows, экспериментов с промптами и пошаговой проверки трассировок выполнения.


genkit start -- dart run server/bin/server.dart



Агентские сценарии:

С момента preview Genkit Dart расширился возможностями для многошаговых агентских рабочих процессов: прерывания с участием человека, middleware для генерации, управление промптами, телеметрией на проде.

Инструменты позволяют моделям вызывать Dart-функции для получения данных или выполнения действий. Когда действие требует подтверждения пользователя, инструмент может приостановить генерацию, вернув .interrupt(...) вместо .response(...). Это гарантирует, что модель не обойдет проверку.

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


🔗 Читать подробнее


💡 Вывод:

Genkit Dart 1.0 - это стабильный инструмент для построения ИИ-функционала и агентов на Dart. Единый API для разных моделей, сквозная типобезопасность, поддержка агентских сценариев и телеметрии на проде.

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


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
342005
Video
👣 Как заставить ИИ-агента делегировать вычисления Dart-коду

В Flutter с GenUI агент может собирать экраны во время работы приложения. Но арифметику лучше не передавать ему: это тратит токены, требует ответа по сети и делает результат менее предсказуемым. Клиентские функции A2UI позволяют зарегистрировать Dart-функцию, ее параметры и тип результата. Агент вызывает ее из сообщения, а приложение выполняет код на устройстве.


Что решают клиентские функции:

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

Разделение труда выглядит так. Flutter-клиент объявляет доступные функции в каталоге GenUI: сообщает агенту, какие локальные операции можно вызвать, какие параметры они ожидают и что возвращают. Агент решает, когда и где показать компонент, и отправляет A2UI-выражение с вызовом функции и нужными аргументами. Клиент выполняет выражение локально и сразу отображает результат.

Это избавляет от лишних сетевых запросов, обеспечивает единообразное форматирование и снижает расход токенов.


Как устроена клиентская функция:

Синхронные клиентские функции наследуются от SynchronousClientFunction. Вот пример функции для расчета стоимости ингредиента:


class CalculateCostFunction extends SynchronousClientFunction {
const CalculateCostFunction();

@override
String get name => 'calculateCost';

@override
String get description =>
'Calculates the cost for a certain quantity of an ingredient. '
'Returns a formatted dollar string (for example, \$4.50).';

@override
ClientFunctionReturnType get returnType => ClientFunctionReturnType.string;

@override
Schema get argumentSchema => S.object(
properties: {
'ingredient_id': S.string(description: 'The ID of the ingredient.'),
'quantity': S.number(description: 'The quantity of the ingredient.'),
},
required: ['ingredient_id', 'quantity'],
);

@override
Object? executeSync(JsonMap args, ExecutionContext context) {
final ingredientId = args['ingredient_id'].toString();
final quantity = num.tryParse(args['quantity'].toString())?.toDouble();

if (quantity == null || quantity < 1) {
return '\$0.00';
}

final cost = CostService().fetchPrice(ingredientId, quantity);
return '\$${cost.toStringAsFixed(2)}';
}
}


Ключевые моменты:

🔹name: уникальный идентификатор, который агент использует при вызове функции в A2UI-сообщениях.

🔹description: краткое объяснение для LLM, чтобы он знал, когда и зачем вызывать функцию и какой формат результата ожидать.

🔹returnType: тип возвращаемого значения. В данном случае строка.

🔹argumentSchema: схема, которая сообщает LLM, какие параметры обязательны: идентификатор ингредиента и количество.

🔹executeSync: метод, который выполняется на устройстве. Он парсит аргументы, вызывает локальный сервис и возвращает отформатированную строку.

Этот паттерн похож на тот, что используется для элементов каталога UI-компонентов. Оба предоставляют агенту метаданные для рассуждений и Dart-логику, которая делает что-то полезное: создает виджеты или вычисляет значение.


Регистрация в каталоге:

Чтобы агент знал о функции, она регистрируется в каталоге GenUI:


final commisCatalog = Catalog(
[
cateringJobItem,
recipeLineCatalogItem,
ingredientLineCatalogItem,
navigationCardCatalogItem,
simpleCardCatalogItem,
],
functions: [
CalculateCostFunction(),
],
catalogId: 'commis_catalog',
);


При инициализации сессии PromptBuilder из genui инспектирует каталог и автоматически извлекает объявления клиентских функций, добавляя их имена, описания и схемы в системный промпт для Gemini.


🔗 Читать подробнее


💡 Вывод:

Клиентские функции A2UI позволяют вынести вычисления из ИИ-агента в Dart-код. Это снижает задержку, экономит токены и делает результат предсказуемым.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
447005
Video
👣 Как встроить Unity игру во Flutter приложение

Flutter отлично справляется с интерфейсом, навигацией и логикой приложения. Но он не умеет в 3D, физику, AR и рендеринг в реальном времени. Зато это умеет Unity. Многие команды хотят иметь оба инструмента в одном приложении: Flutter-оболочку и Unity-сцену внутри нее.

Автор статьи делится современным способом такой интеграции. Раньше это требовало ручного экспорта Unity-библиотеки, правки settings.gradle, написания method channels, отдельной UnityPlayerActivity и повторения всего этого для iOS. Работало, но было хрупко и переделывалось при каждом изменении Unity-проекта.


Что предлагается:

Автор предложил Game Framework - преемник flutter_unity_widget от той же команды. Это CLI и SDK, которые автоматизируют экспорт и синхронизацию Unity-билда под обе платформы. Unity-сцена превращается в обычный Flutter-виджет с типобезопасным обменом сообщениями в обе стороны.


Как это работает:

Процесс состоит из нескольких шагов:

Установка CLI через Homebrew:


brew tap xraph/tap
brew install game-cli

Добавление пакетов в pubspec.yaml:


gameframework: ^0.0.3
gameframework_unity: ^0.0.4

Инициализация в main():


UnityEnginePlugin.initialize();

Экспорт Unity-проекта одной командой:

game export unity --platform android,ios

Синхронизация с Flutter-проектом:

game sync unity --platform android,ios

Эта команда сама подключает Gradle-модуль для Android и iOS-framework, без ручного редактирования settings.gradle и шаблонного кода method channels.


Unity как виджет:

После синхронизации Unity-сцена становится обычным виджетом:


GameWidget(
engineType: GameEngineType.unity,
onEngineCreated: (controller) => _controller = controller,
onMessage: _onMessage,
config: const GameEngineConfig(
fullscreen: false,
runImmediately: true,
unloadOnDispose: true,
),
)


Обмен сообщениями:

Flutter может отправлять команды в Unity:


await _controller?.sendMessage('GameManager', 'SetSpeed', '2.5');
await _controller?.sendJsonMessage('GameManager', 'UpdateScore', {
'score': 100,
'stars': 3,
});

Unity может отправлять данные обратно через небольшой мост на C#. В Flutter эти сообщения приходят в обработчик onMessage.


🔗 Читать подробнее


💡 Вывод:

Интеграция Unity и Flutter перестала быть рутинной работой с Gradle и method channels. Game Framework автоматизирует экспорт, синхронизацию и обмен сообщениями. Unity-сцена становится обычным виджетом, который можно встроить в любое Flutter-приложение.

Проект бесплатный и с открытым исходным кодом. Если вы хотите добавить 3D, физику или AR в свое Flutter-приложение, стоит посмотреть на этот инструмент.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
496006
Photo1/2
👨‍💻 Гонка за самую мощную ИИ-модель подходит к концу?

Последние несколько лет за развитием генеративного ИИ было удобно следить как за спортивной таблицей. OpenAI выпускала GPT-4, Google отвечала Gemini, Anthropic показывала новую Claude, затем начинался следующий круг. Каждая компания приносила набор бенчмарков и объясняла, где ее модель теперь первая.

В 2026 году эта логика начала ломаться. Не потому, что модели перестали становиться мощнее. Наоборот, 1 сентября Anthropic выпустила Claude Fable 5.1 - новую топовую модель для программирования и сложной интеллектуальной работы. Проблема в другом: возможность построить более сильную модель больше не означает, что большинство клиентов захотят использовать ее для большинства задач.


Что показывают цифры:

Financial Times обратила внимание на показательный пример. Fable 5 вышла 9 июня 2026 года как одна из самых мощных моделей Anthropic. Спустя примерно два месяца на нее приходилось около 11% расходов на продукты Anthropic среди корпоративных клиентов.

Это не 11% всей выручки Anthropic, а показатель внутри конкретной выборки корпоративных расходов. Кроме того, доступ к модели был отключен 12 июня из-за американских экспортных ограничений, а глобальный релиз восстановили 1 июля. Цифра не идеальна. Но сама тенденция интереснее конкретных 11%. Компании все чаще выбирают более дешевые модели, если дополнительное качество frontier-модели не влияет на результат.


Что изменилось к 2026 году:

Разница между классами моделей стала огромной. Claude Sonnet 5 стоит $2 за миллион входных и $10 за миллион выходных токенов. Fable 5.1 - $10 и $50 соответственно.

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

В релизе Fable 5.1 Anthropic рассказывает не только о новых результатах на тестах, но и о снижении стоимости cache reads на 75%. То есть даже релиз самой мощной модели теперь приходится объяснять через экономику ее эксплуатации.


На смену лидербордам приходит оркестрация:

Perplexity делает ставку на multi-model orchestration. Разные модели используются внутри одной системы и получают те части работы, для которых подходят лучше. В 2026 году Perplexity развивает Model Council - один запрос может параллельно отправляться нескольким моделям, после чего отдельный слой сравнивает ответы и формирует итоговый результат.

Для прода именно оркестрация становится важнее, чем выбор модели. Нужно решить, какие запросы требуют дорогого inference, когда включать reasoning, сколько контекста передавать, где использовать кеш, когда переключаться на другого провайдера.

Хорошая оркестрация может влиять на стоимость и качество продукта сильнее, чем очередная замена одной frontier-модели на другую.


Так гонка закончилась или нет:

На исследовательском уровне нет. OpenAI, Google, Anthropic, xAI, DeepSeek продолжат выпускать более сильные модели. Frontier по-прежнему важен: именно там появляются возможности, которые позднее становятся дешевле и переходят в массовые модели.

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


🔗 Читать подробнее


💡 Вывод:

К 2026 году рынок раздробился. Есть быстрые модели, reasoning-модели, системы с огромным контекстом, open-weight решения, специализированные модели и архитектуры, которые автоматически переключаются между несколькими вариантами.

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

Максимальный интеллект остается ценным ресурсом, но использовать его на каждом запросе больше нет смысла. Вопрос сместился с «какая модель лучше?» на «какая минимальная модель достаточно хороша для этой задачи?». И это, вероятно, главный итог четырехлетней гонки.


Подписаться на канал:
➡️ Кот Денисова
304005
Video
👣 Как async* упрощает работу с потоками в Dart

В Dart есть инструмент, который многие недооценивают. Это оператор async*. Он позволяет создавать потоки данных так же легко, как обычные функции. Без лишних классов, ручного управления подписками и бесконечных циклов с обработчиками ошибок.


Что такое генераторы:

Генераторы - это функции, которые возвращают данные по частям, а не все сразу. Они позволяют экономить память и время выполнения. Но обычно такие функции синхронные. А что делать, если нужна асинхронность?

Здесь и вступает async*. Вместо Iterable такая функция возвращает Stream. Внутри нее доступны все возможности генератора плюс await.


Как это выглядит на практике:

Вот пример функции, которая генерирует числа с задержкой:


Stream<int> generateNumbers() async* {
for (var i = 0; i < 5; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}


Все просто. Мы используем привычные конструкции: цикл, await, yield. Никаких StreamController, никаких подписок, никаких ручных закрытий.


Что делает ключевое слово yield:

yield - это оператор, который возвращает очередное значение из генератора или асинхронного генератора. Когда функция помечена как async*, она не выполняется целиком сразу. Вместо этого она возвращает Stream. Каждый раз, когда выполнение доходит до yield, значение отправляется в поток и функция приостанавливается до следующего запроса.

Простыми словами: yield - это как return, но не завершает функцию, а отдает одно значение и ждет, когда попросят следующее. Функция продолжает работу с того же места, а не начинает заново.

Есть еще yield*, который передает управление другому генератору и отдает все его значения подряд. Это удобно, когда нужно объединить несколько источников данных в один поток.


🔗 Читать подробнее


💡 Вывод:

async* - это не просто синтаксический сахар. Это способ писать потоки данных чисто и предсказуемо. Он экономит код, упрощает чтение и делает логику более явной. Логика генерации данных находится в одном месте и выглядит как обычный код. Все, что связано с созданием потока, обработкой ошибок и закрытием, берет на себя язык.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
5930011
Video
👣 Влияет ли выбор state management на производительность Flutter?

Можно ли ускорить Flutter-приложение, просто выбрав другой state management? Автор данной статьи решил проверить это не на искусственном тесте, а на реальном приложении. Результат оказался неожиданным.


Как проводили тестирование:

Для эксперимента использовалось специально подготовленное Android-устройство с контролем температуры, CPU/GPU scaling и других факторов. Тестирование длилось пять дней, было проведено 2100 испытаний и 300 раундов на каждый подход.

Сравнивались семь подходов: setState как базовое решение, ChangeNotifier + Provider, Freezed + Provider + ValueNotifier, Bloc, Riverpod, GetX и Signals.

Измерялось прежде всего время build, а не искусственная скорость изменения состояния.


Результаты:

Provider + ChangeNotifier практически не отличается от обычного setState, как и Freezed + Provider + ValueNotifier.

GetX, Riverpod и Signals дают примерно +5-6 микросекунд к времени сборки. Звучит неплохо для бенчмарка, но в реальном приложении это практически ничего.

При 120 Гц бюджет одного кадра составляет примерно 8333 микросекунды. Дополнительные 6 микросекунд - это около 0,07% бюджета кадра. Разница находится далеко за пределами того, что имеет практическое значение для пользователя.


Что насчет Bloc:

Интереснее всего оказался результат Bloc. В тесте он позволил сократить среднее время сборки примерно на 21 микросекунду. Это по-прежнему ничтожно малая величина (0,263% при частоте обновления экрана 120 Гц), но, если рассматривать общую картину, это уже тот показатель, который теоретически может иметь значение.

Проблема в том, что реализация на Bloc отличалась не только библиотекой. В ней было больше мелких обновлений виджетов, а некоторые части иерархия виджетов располагалась иначе. И здесь всплывает главная сложность любых бенчмарков: мы сравниваем не state management, а архитектуру приложения.


🔗 Читать подробнее


💡 Вывод:

На основании этого эксперимента нельзя честно сказать, что Riverpod быстрее Provider или Bloc быстрее GetX. В реальном приложении разница между популярными state management решениями оказалась настолько маленькой, что выбирать библиотеку только по производительности практически бессмысленно.

Гораздо важнее, сколько виджетов обновляется, насколько большая часть иерархии виджетов пересобирается, что выполняется внутри метода build, сколько времени занимает расчет расположения и отрисовка, какие операции выполняются в главном потоке.

То есть проблема обычно не в том, выбрали вы Riverpod или Provider. Проблема в том, что именно вы заставляете Flutter перестраивать.

Выбор state management должен основываться на удобстве, читаемости и поддерживаемости кода. Производительность здесь - не главный критерий. Если ваше приложение тормозит, проблема почти наверняка не в библиотеке, а в том, как организована иерархия виджетов и сколько лишних обновлений виджетов происходит.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
699007
Video
👣 Плагин Flutter для VS Code обновился до версии 3.142.0

Вышло обновление плагина Flutter для VS Code. Изменений немного, но есть полезные: deep-link в DevTools вернулись, рефакторинги теперь запрашивают имя, а мелкие исправления делают работу чуть стабильнее.


Deep-link в DevTools вернулись:

Плагин снова поддерживает deep-link в DevTools. Теперь при возникновении ошибок вроде Overflow появляется уведомление с кнопкой, которая открывает нужную страницу DevTools и переходит к проблемному виджету или месту в коде.

Это экономит время: не нужно вручную искать проблему в Widget Inspector. Система сама подсказывает, где смотреть.


Рефакторинги запрашивают имя:

С выходом стабильной версии Flutter рефакторинги «Add a name to the constructor» и «Add a prefix to the import» теперь запрашивают имя у пользователя. Раньше плагин подставлял имя, сгенерированное сервером. Теперь можно ввести свое.

Также в будущих версиях SDK появится валидация при вводе. Ошибки будут блокировать подтверждение, а предупреждения - нет. Это позволит избежать опечаток и некорректных имен.


Мелкие исправления:

🔹Убрали текст «Starting debug session…», который мог отображаться слишком долго при запуске тестов без вывода. Это мелочь, но раздражала.

🔹Удалили обходной путь для проблемы с Copilot, который мутировал переменные окружения. Copilot выпустил исправление еще в июне, так что необходимость в обходе отпала.

🔹Поддержка Firebase Studio удалена в связи с закрытием сервиса.


🔗 Читать подробнее


💡 Вывод:

Обновление плагина - это набор точечных улучшений. Deep-link в DevTools возвращают удобство при отладке Overflow и других проблем. Рефакторинги с запросом имени дают больше контроля. Мелкие исправления делают работу стабильнее.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
538008
Photo1/2
👣 Архитекторы, тестировщики и кодеры. Создаем мультиагентную команду

Разработка с ИИ-агентами за последний год сделала большой шаг вперед. Но многие все еще используют одного агента для всего: архитектуры, тестов, отладки. На маленьких задачах это работает. Когда проект становится сложнее, агент теряет контекст, начинает галлюцинировать и допускать ошибки.

Недавно в блоге Flutter вышла статья, в которой автор экспериментирует с мультиагентным подходом. Вместо одного универсального агента он создал команду из четырех специализированных ролей: архитектор, тестировщик, кодер и координатор. Задача была не простая: портировать популярную Python-библиотеку python-statemachine в статически типизированный Dart-пакет без использования рефлексии (dart:mirrors во Flutter запрещены).


Как устроена мультиагентная команда:

Основа эффективной агентной команды - четко определенные роли с ограничениями на то, что агент может делать.

Автор создал общий воркфлоу-скилл (tdd-dart-workflow) с правилами TDD и разрешениями, а также четыре ролевых скилла:

🔹Архитектор: анализирует исходный код, создает architecture_blueprint.md и спецификации модулей в specs/. Не может писать в lib/, test/ и example/.

🔹Тестировщик: пишет падающие тесты в test/ на основе спецификаций архитектора. Не может просматривать или изменять lib/ и specs/.

🔹Кодер: создает скелеты и реализует код в lib/src/, чтобы тесты проходили. Не может редактировать test/ и specs/.

🔹Координатор: управляет родительским агентом. Отвечает за коммиты, запуск dart analyze и dart test, делегирует задачи суб-агентам.

Строгое разделение ролей не дает ни одному агенту изменять и тесты и код одновременно. Координатор получает только краткий отчет о завершении задачи. Это создает когнитивный файрвол: история проб и ошибок суб-агента отбрасывается после завершения его задачи. Контекст координатора остается чистым, без переполнения токенами.

Для предотвращения гонок и непроверенных коммитов доступ к файловой системе ограничен:

🔹Координатор: чтение и запись везде, но без прямых изменений кода.

🔹Архитектор: чтение везде, запись только в specs/ и skills/.

🔹Тестировщик: чтение везде, запись только в test/ и example/.

🔹Кодер: чтение везде, запись только в lib/ и example/.


Статическая типизация и TDD:

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

Решение: сначала тестировщик пишет тесты по спецификации. Затем кодер создает скелеты в lib/src/ с заглушками, которые возвращают dummy-значения или бросают UnimplementedError. Когда скелет удовлетворяет компилятору, координатор запускает dart analyze && dart test и проверяет, что тесты падают по правильным причинам. Комментарии отделяют временные заглушки от постоянных тестовых утилит.


Результат:

Итоговый Dart-пакет state_machine сохранил полную функциональность оригинальной Python-библиотеки. Кодер реализовал fluent-билдеры для поддержки Dart-синтаксиса.


🔗 Читать подробнее


💡 Вывод:

Мультиагентный подход с разделением ролей работает для Flutter-разработки. Он предотвращает потерю контекста, уменьшает галлюцинации и повышает качество кода. Когнитивный файрвол позволяет сохранять чистоту контекста координатора, отбрасывая историю суб-агентов после завершения задач.

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

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


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
1,420007
👣 Как уменьшить размер Flutter-приложения с помощью оптимизации ассетов

Размер установочного файла напрямую влияет на конверсию. Пользователи не любят ждать и редко скачивают большие приложения, если есть альтернатива поменьше. Особенно это критично для Android, но и для iOS размер тоже имеет значение.

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


Изображения:

Самый эффективный способ уменьшить размер картинок - использовать формат WebP. Он разработан Google, дает лучшее сжатие, чем PNG и JPEG, и при этом поддерживает прозрачность. В большинстве случаев замена PNG на WebP уменьшает размер файла в два-три раза без видимой потери качества.

Но формат - это только половина дела. Вторая - размер самого изображения. Если картинка отображается в виджете 100x100 точек, а вы кладете в ассеты изображение 2000x2000 пикселей, вы добавляете в приложение десятки лишних мегабайт. На больших экранах такие размеры тоже не нужны. Достаточно рассчитать максимальный размер с учетом плотности экрана: размер виджета умножить на коэффициент плотности (обычно не больше 4). Все, что больше - просто лишний вес.


Иконки:

С иконками все сложнее. Есть три основных способа хранить их в проекте.

Первый - использовать SVG. Это векторный формат, он масштабируется без потери качества. Но во Flutter он не поддерживается из коробки - нужна отдельная библиотека, которая добавляет лишнюю нагрузку и не всегда работает стабильно.

Второй - конвертировать SVG во Flutter-вектор (например через https://pub.dev/packages/flutter_svg или специальные тулы). Это лучше, чем тянуть SVG-библиотеку, но иконки все равно остаются отдельными файлами, которые влияют на размер и время отрисовки.

Третий - хранить иконки в шрифте. Это самый эффективный способ. Вы получаете IconData и при сборке приложения неиспользуемые иконки автоматически исключаются. Минус - такой подход подходит только для монохромных иконок, и сложные изображения не всегда получается корректно конвертировать в шрифт.

На практике лучше всего комбинировать второй и третий подходы. Для простых монохромных иконок использовать шрифт. Для сложных цветных - векторный формат. А SVG лучше оставить для прототипов или случаев, когда без него действительно не обойтись.


Другие рекомендации:

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


🔗 https://appsell.su/blog/den-apps-1/flutter-razrabotka/kak-umenshit-razmer-flutter-prilozheniya-s-pomoschyu-optimizacii-assetov-596


💡 Вывод:

Размер приложения - это не просто технический параметр. Это влияет на восприятие пользователей и скорость скачивания. Оптимизация ассетов - самый простой и эффективный способ уменьшить установочный файл без потери качества.

Используйте WebP для изображений. Следите за их размером. Для иконок выбирайте шрифты или векторные форматы вместо SVG.


Подписаться на канал:
➡️

Закрытый канал:
🚀 Мобильный трудоголик PRO
684005
👣 Новая версия плагина Flutter для VS Code

Вышла новая версия плагина Flutter для VS Code - v3.138.0. Обновление не косметическое: есть и технические улучшения и удаление старого функционала и работа над стабильностью. Давайте разберем, что изменилось.


Поддержка LSP v3.18:

Плагин обновил LSP-клиент до версии 3.18. Это открывает возможности для Dart Analysis Server использовать новые оптимизации в будущих релизах SDK. Пока самих оптимизаций нет, но фундамент заложен. Когда они появятся, анализ кода станет эффективнее без дополнительных действий со стороны разработчика.


Flutter Outline удалили:

Из сайдбара убрали вкладку Flutter Outline. Для тех, кто пользовался ей регулярно, это заметное изменение. Но функциональность не пропала полностью - стандартный Outline остался на вкладке Explorer, а предпросмотр Flutter UI Guides доступен через настройку dart.previewFlutterUiGuides. Так что привычный способ навигации по виджетам можно вернуть, просто через другой интерфейс.


Улучшения завершения работы расширений:

В прошлых версиях при закрытии редактора расширения могли завершаться слишком резко, не давая серверам времени корректно завершить работу. Теперь это исправлено. Система дожидается корректного завершения Analysis Server и других сервисов, что должно снизить количество сбоев и странных ошибок при перезапуске VS Code.


Dart Tooling Daemon:

Еще одно изменение касается DTD. Раньше изменения активного редактора отправлялись во все открытые панели, включая вывод и результаты тестов. Теперь этот трафик отключен для второстепенных редакторов. Мелочь, но она снижает нагрузку на систему.


🔗 https://appsell.su/blog/den-apps-1/flutter-razrabotka/novaya-versiya-plagina-flutter-dlya-vs-code-592


💡 Вывод:

Обновление плагина - это смесь технических улучшений, удаления старого функционала и точечных исправлений. LSP v3.18 закладывает основу для будущих оптимизаций. Удаление Flutter Outline может показаться неудобным, но у него есть альтернативы. А улучшения завершения работы и исправления тестов делают работу стабильнее.


Подписаться на канал:
➡️

Закрытый канал:
🚀 Мобильный трудоголик PRO
729004
👣 Знаете Flutter? Значит, можете писать бэкенд на Dart

Если вы работаете с Flutter, вы знаете Dart. Вы понимаете async/await, работаете с моделями и репозиториями, привыкли к чистой архитектуре. Вы запускали приложения на реальных устройствах. Между этим и умением написать и запустить рабочий бэкенд - пропасть меньше, чем кажется. Не нужно учить новый язык. Нужно понять, как Dart работает, когда нет виджетов, нет BuildContext, нет Flutter. Есть только процесс, который принимает HTTP-запросы, ходит в базу данных и отправляет ответы.

Автор https://www.freecodecamp.org/news/how-to-build-and-ship-production-rest-apis-with-dart-and-shelf/ показывает этот путь на примере API для управления пользователями и профилями. Все на знакомом Dart и фреймворке Shelf. Проект поднимается в Docker с PostgreSQL, проверяет пользователей через JWT-токены и деплоится на Fly.io.


Как Dart работает на сервере:

В Flutter-приложении ваш код работает поверх огромного количества логики: дерево виджетов, пайплайн рендеринга, управление состоянием, обработка событий платформы. На сервере ничего этого нет.

Есть процесс, который слушает порт, получает HTTP-запросы, делает работу и отправляет ответы. В стандартной библиотеке Dart есть все необходимое для этого на самом низком уровне.


import 'dart:io';

void main() async {
final server = await HttpServer.bind('0.0.0.0', 8080);
print('Сервер запущен на порту 8080');

await for (final request in server) {
request.response
..statusCode = 200
..write('Привет из Dart')
..close();
}
}


Это рабочий HTTP-сервер. Никаких пакетов, никаких фреймворков. Каждый запрос приходит через HttpServer, и вы пишете ответ напрямую.

Но как только появляются маршруты, middleware, аутентификация и обработка ошибок, работать с dart:io становится неудобно. Здесь в игру вступает Shelf.


Что такое Shelf:

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

В Shelf есть четыре ключевых понятия:

🔵Handler - функция, которая принимает Request и возвращает Response. Все в Shelf в итоге сводится к хендлеру.

🔵Middleware - функция, которая оборачивает хендлер, добавляя поведение до или после его выполнения. Логирование, аутентификация и обработка ошибок - это middleware.

🔵Pipeline - цепочка middleware с хендлером в конце. Запрос проходит через все middleware, прежде чем добраться до хендлера.

🔵Router - сопоставляет URL-паттерны и HTTP-методы с конкретными хендлерами.

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


База данных и миграции:

В проекте используется PostgreSQL. Все поднимается через Docker Compose. Миграции применяются автоматически при старте приложения.

Структура базы данных простая: таблица users и таблица profiles, связанная один к одному. Код написан так, что разработчику не нужно писать сырые SQL-запросы в хендлерах - вся работа с базой инкапсулирована в репозиториях.


🔗 https://appsell.su/blog/den-apps-1/flutter-razrabotka/znaete-flutter-znachit-mozhete-pisat-bekend-na-dart-589


💡 Вывод:

Эта статья - хороший пример того, что знание Dart не заканчивается на Flutter. Те же модели, репозитории, асинхронность и архитектурные подходы работают и на сервере. Нужно только сменить контекст: вместо виджетов - обработка запросов, вместо State - база данных.

Shelf не делает за вас выборов. Он дает кирпичики, а архитектуру вы собираете сами. Это философски близко к Flutter, где вы тоже строите UI из базовых компонентов.

Если вы знаете Dart, бэкенд уже не выглядит чем-то недоступным.


Подписаться на канал:
➡️

Закрытый канал:
🚀 Мобильный трудоголик PRO
9200011
👣 Как правильно делать задержку в Flutter и не блокировать интерфейс

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

Давайте разберемся почему так происходит и как делать задержки правильно.


Почему нельзя блокировать основной поток:

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

На андроиде если заблокировать главный поток больше чем на 5 секунд, система вообще предложит пользователю закрыть приложение. На iOS такого жесткого порога нет, но интерфейс все равно перестает реагировать, что мгновенно портит пользовательский опыт.

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


Как правильно сделать задержку во Flutter:

Во Flutter есть несколько способов сделать асинхронную задержку. Самый простой и правильный - использовать Future.delayed():


Future.delayed(Duration(seconds: 2), () {
// код, который выполнится через 2 секунды
});


Этот метод возвращает Future, который завершится через указанное время. Все это время основной поток продолжает работать, интерфейс остается отзывчивым, а через 2 секунды выполняется переданный код.

Если нужно подождать внутри асинхронной функции, используется await:


void doSomething() async {
await Future.delayed(Duration(seconds: 2));
// код после задержки
}



Когда еще может понадобиться задержка:

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

Например после того как пользователь что-то отправил, можно показать иконку успеха, а через полторы секунды автоматически закрыть экран. Или сделать паузу между шагами в обучающем процессе.

Во всех этих случаях Future.delayed() - оптимальный выбор.


Чего делать не стоит:

Самый простой и опасный способ сделать задержку - использовать sleep(). Это синхронная блокировка текущего потока на указанное время. Если вызвать ее на главном потоке - интерфейс полностью перестанет реагировать на касания.

Также не стоит пытаться эмулировать задержку через циклы с проверкой времени. Это не только блокирует UI, но и бессмысленно грузит процессор.


Если нужно больше контроля - Timer:

В некоторых сценариях Future.delayed() не подходит. Например, если нужно выполнять действие периодически, отменять его или запускать несколько раз с интервалом.

Для этого во Flutter есть класс Timer:


Timer(Duration(seconds: 2), () {
// выполнится один раз через 2 секунды
});


И для периодического выполнения:


Timer.periodic(Duration(seconds: 2), (timer) {
// будет выполняться каждые 2 секунды
});


Timer работает асинхронно и не блокирует UI. Его легко отменить, вызвав timer.cancel().


🔗 https://appsell.su/blog/den-apps-1/flutter-razrabotka/kak-pravilno-delat-zaderzhku-v-flutter-i-ne-blokirovat-interfeys-585


💡 Вывод:

Для создания задержек во Flutter используйте асинхронные методы Future.delayed() или Timer. Они не блокируют основной поток, интерфейс остается отзывчивым, а пользователь не видит зависаний.

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


Подписаться на канал:
➡️

Закрытый канал:
🚀 Мобильный трудоголик PRO
772005
Photo
👣 GenUI в действии: команда Flutter раздала 3000 чашек кофе с ИИ-рисунками на пенке

Команда Flutter решила поделиться опытом создания необычного демо-проекта, который они показали посетителям Google Cloud Next и Google I/O. В https://blog.flutter.dev/how-we-built-a-flutter-powered-ai-coffee-shop-878c60a11f1a разработчики рассказывают, как построили приложение для кофейни, где каждый посетитель мог заказать латте с изображением, сгенерированным нейросетью прямо на пенке. Проект назывался GenLatte. За два мероприятия команда раздала 3000 чашек.


Как это работает:

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

Дальше в дело вступала нейросеть Nano Banana. Система брала короткую фразу и разворачивала ее в полноценный промпт для генерации изображения. При этом создавалось сразу четыре варианта картинки, все разные по композиции и настроению. Пользователь выбирал понравившийся и изображение отправлялось на печать на пенке латте.


Персонализация через GenUI:

Самое интересное происходило после того, как пользователь видел сгенерированные изображения. Под каждой картинкой была кнопка Tweak - это была не просто доработка фильтра, а полноценная генеративная настройка через UI.

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

Это и есть GenUI в действии - интерфейс, который собирается на лету под конкретную задачу.

После того как пользователь отвечал на вопросы, система отправляла обновленный промпт обратно в Nano Banana и генерировалась новая версия изображения. Все это происходило в реальном времени.


Техническая архитектура:

Проект построен на Flutter и Firebase. Вся логика собрана в монорепозитории, где объединены Flutter-приложение, Firebase-бэкенд и общий код для бизнес-логики. Такой подход позволил избавиться от дублирования зависимостей, переиспользовать код и выполнять атомарные деплои.

Внутри самого Flutter-приложения было реализовано пять отдельных экранов:

🔵Экран заказа для посетителей.

🔵Экран бариста с актуальными заказами.

🔵Экран модератора для проверки безопасности контента.

🔵Экран очереди для ожидающих.

🔵Экран с недавними заказами в виде плавающих пузырьков.


🔗 https://appsell.su/blog/den-apps-1/flutter-razrabotka/genui-v-deystvii-komanda-flutter-razdala-3000-chashek-kofe-s-ii-risunkami-581


💡 Вывод:

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

Конечно, код проекта доступен в репозитории https://github.com/flutter/demos/tree/main/genlatte, но авторы предупреждают: он не поддерживается и предназначен только для вдохновения. А вдохновения там действительно много. Когда в ближайшее время задумаетесь о том, как можно применить генеративный ИИ в своем проекте - вспомните историю о латте с нейросетевым рисунком. Это хорошая иллюстрация того, насколько широко могут разойтись технологии, которые вчера казались просто игрушками.


Подписаться на канал:
➡️
1,690004
👣 Container vs SizedBox во Flutter: в чем разница и когда использовать

Привет! Во Flutter есть два виджета, которые на первый взгляд делают одно и то же: Container и SizedBox. Оба управляют размерами и расположением дочерних виджетов. Но между ними есть принципиальная разница и использование не того виджета в нужном месте может привести к лишней вложенности, нечитаемому коду и проблемам с производительностью.


Что такое Container:

Container - это универсальный виджет, который объединяет в себе несколько функций: управление размерами, отступами (padding и margin), фоном, рамкой и тенью. Он может одновременно задавать ширину, высоту, цвет, скругление углов и отступы внутри себя.

Container удобен, когда нужно настроить внешний вид блока: задать фон, обрезать по краям, добавить отступы. Он умеет адаптироваться под размеры дочернего виджета, если явно не заданы ширина и высота.


Что такое SizedBox:

SizedBox - это простой и легкий виджет. Он делает только одно: задает фиксированный размер. Либо ширину, либо высоту, либо оба параметра сразу. Если ему не передать child, он просто создаст пустое пространство заданного размера - это удобно для создания отступов между виджетами, альтернатива использованию Padding или Spacer.

SizedBox не умеет работать с фоном, рамками, тенями или отступами. Его задача - только размер.


Ключевые отличия:

Container может существовать без явно заданных размеров и растягиваться по содержимому. SizedBox всегда либо фиксированного размера, либо, если размер не указан, вообще ничего не отображает.

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

Container - для стилизации. SizedBox - для контроля размера.


Когда использовать SizedBox:

SizedBox лучше использовать в трех случаях:

🔵Когда нужно задать фиксированные ширину или высоту без дополнительной стилизации.

🔵Когда нужно добавить пустое пространство между виджетами.

🔵Когда вы явно ограничиваете размеры дочернего виджета, не добавляя при этом никаких декоративных элементов.


Когда использовать Container:

Container подходит для всего остального:

🔵Когда нужно не только задать размер, но и добавить фон, рамку, тень или скругление.

🔵Когда нужны внутренние отступы.

🔵Когда нужно, чтобы виджет сам подстраивался под контент и при этом имел декоративное оформление.


🔗 https://appsell.su/blog/den-apps-1/flutter-razrabotka/container-vs-sizedbox-vo-flutter-v-chem-raznica-i-kogda-ispolzovat-577


💡 Вывод:

Container и SizedBox - разные инструменты для разных задач. SizedBox - это про размеры и пустые пространства, Container - про внешний вид и универсальность. Не используйте Container там, где достаточно SizedBox, и не пытайтесь стилизовать SizedBox - он для этого не предназначен.

И главное: не превращайте верстку в матрешку. Если видите лишнюю вложенность - пересмотрите выбор виджета. Чистый и читаемый код - это не только про логику, но и про правильно подобранные строительные блоки.


Подписаться на канал:
➡️

Закрытый канал:
🚀 Мобильный трудоголик PRO
742006
Photo
👣 Почему Flutter - идеальный выбор для разработки с ИИ-агентами

Привет. В последнее время все только и говорят о разработке с помощью ИИ-агентов. Кто-то в восторге, кто-то скептичен. Но мало кто задумывается, как выбор технологий влияет на эффективность работы с LLM. Майкл Томсен, инженер из команды Flutter, выпустил https://blog.flutter.dev/flutters-multiplatform-value-for-agentic-development-cb5c7da7c2bc на эту тему. Он объясняет, почему мультиплатформенная архитектура Flutter дает неожиданные преимущества в эпоху агентской разработки. Основная мысль простая: одна кодовая база вместо трех - это не только удобно для людей, но и критически важно при работе с ИИ.


В чем была ценность Flutter раньше:

Еще до эры агентов Flutter решал классическую проблему: вместо трех команд (iOS, Android, веб) можно иметь одну, которая пишет код на Dart и запускает его везде. От 95% до 99% кода в реальных проектах переиспользуется. Это давало:

🔵Быстрый выход на рынок на всех платформах.

🔵Гарантированную консистентность фич и дизайна.

🔵Нативную производительность (компиляция в машинный код).

🔵Безопасность благодаря строгой типизации Dart.


Что изменилось с приходом ИИ-агентов:

Когда разработкой занимаются агенты, нативный подход (Swift под iOS, Kotlin под Android, JavaScript под веб) начинает пробуксовывать. Агенту нужно сгенерировать одну и ту же логику трижды, на разных языках, с разными нюансами. Это приводит к трем проблемам.

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


Почему Flutter решает эти проблемы:

Flutter предлагает единую кодовую базу на Dart. Агент пишет все один раз, а не три. Токенов тратится меньше, генерация быстрее, логика не расходится.

Кроме того, у Flutter есть архитектурные особенности, которые делают его удобным для агентской разработки. Сильная статическая типизация Dart работает как система самопроверки: если агент сгенерировал не то, компилятор сразу укажет на ошибку. Это снижает количество галлюцинаций.


🔗 https://appsell.su/blog/den-apps-1/flutter-razrabotka/pochemu-flutter-idealnyy-vybor-dlya-razrabotki-s-ii-agentami-573


💡 Вывод:

Flutter был удобен для команд, которые хотят поддерживать несколько платформ без тройного объема работы. В эпоху ИИ-агентов этот подход становится еще более актуальным. Меньше кода - меньше токенов. Одна логика - нет платформенного различия. Сильная типизация - меньше галлюцинаций. Горячая перезагрузка - быстрая проверка.

Для агентской разработки Flutter выглядит прагматичным выбором.


Подписаться на канал:
➡️

Закрытый канал:
🚀 Мобильный трудоголик PRO
1,8200010
📱 Что делает команда git cherry-pick?

Команда git cherry-pick позволяет перенести отдельный коммит из одной ветки в другую, не сливая ветки целиком. Это полезно, когда нужно взять только конкретные изменения из другой ветки.


Как это работает:

🔵Находит коммит (по хешу) в истории Git.

🔵Применяет его изменения к текущей ветке, создавая новый коммит (с другим хешем).


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


# Переключимся в ветку, куда нужно перенести коммит
git checkout feature

# Перенесем коммит с хешем a1b2c3
git cherry-pick a1b2c3



Вывод:

git cherry-pick – это точечный инструмент для переноса отдельных изменений. Используйте его аккуратно, чтобы не запутать историю коммитов.


Подписаться на канал:
➡️

Закрытый канал:
🚀 Мобильный трудоголик PRO
763007
👣 Expansible - новый виджет для плавно раскрывающегося списка

Во Flutter 3.32 появился новый виджет - Expansible. Он пришел на смену старому ExpansionTile и его контроллеру, который больше не поддерживается. Давайте разберем на простом примере, как он работает.


Что такое Expansible:

Expansible - это StatefulWidget, который умеет плавно раскрываться и сворачиваться. У него есть две части:

🔵header - заголовок, который всегда на виду.

🔵body - содержимое, которое появляется при раскрытии.

Анимацией управляет AnimationController, поэтому все работает плавно и без дерганий, в отличие от старого ExpansionTile.


Простой пример:

Самый частый сценарий - когда по тапу на заголовок раскрывается дополнительный контент. Вот как это выглядит:


class ExpansibleExample extends StatefulWidget {
@override
State<ExpansibleExample> createState() => _ExpansibleExampleState();
}

class _ExpansibleExampleState extends State<ExpansibleExample> {
final _controller = ExpansibleController();

@override
Widget build(BuildContext context) {
return Expansible(
controller: _controller,
headerBuilder: (context, animation) => ListTile(
title: Text('Нажми, чтобы раскрыть'),
onTap: () {
if (_controller.isExpanded) {
_controller.collapse();
} else {
_controller.expand();
}
},
trailing: RotationTransition(
turns: Tween<double>(begin: 0.0, end: 0.5).animate(animation),
child: Icon(Icons.arrow_drop_down),
),
),
bodyBuilder: (context, animation) => SizeTransition(
sizeFactor: animation,
child: Text('А вот и скрытый контент!'),
),
);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}
}


Обратите внимание на несколько важных моментов. Во-первых, ExpansibleController нужно создавать и не забывать вызывать dispose(). Во-вторых, для плавного поворота стрелочки используется animation, которая приходит в headerBuilder - она уже содержит нужное значение от 0 до 0.5 оборота. В-третьих, SizeTransition с animation анимирует высоту body. Все вместе дает аккуратный эффект без лишних телодвижений.


Важный нюанс про ListView:

Если вы используете Expansible внутри ListView или SingleChildScrollView, обязательно передавайте PageStorageKey. Иначе при скролле виджет будет терять свое состояние (раскрыт или свернут) и пользователь удивится.


Expansible(
key: PageStorageKey('unique_key_1'), // каждому Expansible свой ключ
controller: _controller,
headerBuilder: ...
bodyBuilder: ...
)



🔗 https://appsell.su/blog/den-apps-1/flutter-razrabotka/expansible-novyy-vidzhet-dlya-plavno-raskryvayuschegosya-spiska-560


💡 Вывод:

Expansible - это не просто замена устаревшему виджету. Это инструмент, который решает давнюю боль Flutter-разработчиков: плавная анимация высоты, сохранение состояния при скролле и чистый API через контроллер. Пример выше можно скопировать и вставить в любой проект - он работает из коробки. А если нужно больше гибкости, подключайте expansibleBuilder и настраивайте под свои задачи. Если вы обновились до Flutter 3.32, присмотритесь к этому виджету - он реально удобнее.


Подписаться на канал:
➡️

Закрытый канал:
🚀 Мобильный трудоголик PRO
7750010
Photo1/2
👣 Почему primary constructors в Dart сложнее, чем кажется

В Dart 3.13 появилась фича, которую ждали годами - primary constructors. Теперь класс с полями и конструктором можно объявить в одну строку. Но путь к этому решению был долгим. Команда Dart потратила много времени на обсуждение деталей и принятие компромиссов. Боб Нюстром написал большой разбор о том, через что пришлось пройти. Давайте разберем самые важные моменты из данного разбора.


Почему синтаксический сахар важен:

Primary constructors не добавляют в язык новых возможностей. Все, что можно сделать через них, уже можно было написать старым способом. Это чистый синтаксический сахар.

Вопрос в том, зачем он нужен. Синтаксический сахар оправдан в нескольких случаях.

🔹Первый - когда новый способ просто лучше старого. Например, в Dart 2.0 сделали необязательным ключевое слово new при создании объектов. Старый синтаксис остался для совместимости, но никто его не использует.

🔹Второй - когда синтаксис сильно упрощает частый сценарий. В Dart изначально не было enum. Их эмулировали через классы с константами. Когда добавили настоящие enum, код стал не только короче, но и понятнее.

🔹Третий - когда синтаксис делает намерение более явным. Код в классах с константами не давал понять, что это enum. А настоящий enum сразу говорит о определенном наборе возможных значений.


Зачем нужен primary constructors:

Primary constructors решают конкретную проблему: объявление класса с конструктором, который просто сохраняет параметры в поля.

Без primary constructors код выглядит так:

class Point {
final int x;
final int y;
Point(int x, int y) : x = x, y = y;
}

С использованием initializing formals:

class Point {
final int x;
final int y;
Point(this.x, this.y);
}

Но разработчики хотели еще короче. Чтобы можно было просто написать так:

class Point(final int x, final int y);


Основная проблема:

Главная сложность: синтаксический клиф. Это когда ты начинаешь с простого синтаксиса, а потом хочешь добавить маленькое изменение, но оно не вписывается, и ты вынужден переписывать все.

Например, у класса есть primary constructor с кучей параметров. Потом нужно добавить логирование в конструктор. Если primary constructor не поддерживает тело, придется переписать все обратно на старый синтаксис.

Чтобы избежать этого, в Dart добавили блоки this { ... } внутри класса. Они позволяют добавить тело или инициализатор для primary constructor без переписывания всего.

class FormatterOptions(
final int indent = 0,
final int pageWidth = 80,
) {
this {
log.write('Created options.');
}
}


Новый синтаксис конструкторов:

Еще одно изменение касается объявления конструкторов. Раньше нужно было повторять имя класса:

class LongClassName {
LongClassName(); // безымянный
LongClassName.create(); // именованный
}

Теперь можно использовать ключевое слово new:

class LongClassName {
new(); // безымянный
new create(); // именованный
}

Для фабричных конструкторов:

class AnotherLongClass {
factory () { ... } // фабричный
factory create() { ... } // именованный фабричный
}

Это короче, особенно для длинных имен классов. И решает проблему с typedef и расширениями, где имя класса не всегда доступно.


🔗 Читать подробнее


💡 Вывод:

Primary constructors в Dart 3.13 - это не просто очередной синтаксический сахар. Это результат долгого размышления о том, как сделать язык удобнее и проще.

Команда языка учла опыт других языков, добавила защиту от синтаксических клифов и изменила объявление конструкторов на более простое. В итоге код стал короче, , а язык чуть более приятным для повседневной работы.

Это не последнее изменение. Язык продолжает развиваться. Но primary constructors - хороший пример того, как дизайн языка может быть одновременно эволюционным и продуманным.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
1,640006
Video
👣 Работа с растровыми изображениями во Flutter

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


Что такое devicePixelRatio:

Коэффициент devicePixelRatio показывает, сколько физических пикселей содержится в одной логической точке. На старых устройствах это 1x. На современных - 2x, 3x и даже 4x. Чем выше коэффициент, тем больше пикселей нужно для отображения одной точки.

Flutter автоматически выбирает нужную версию изображения, основываясь на плотности экрана устройства.


Сколько версий нужно:

Обычно достаточно подготовить изображения для коэффициентов 1x, 1.5x, 2x, 3x и 4x. Больше требуется редко. Меньше - может привести к размытию на устройствах с высокой плотностью экрана.

Каждая версия должна иметь размер, соответствующий ее коэффициенту. Например, если изображение в точках имеет размер 100x100, то для 2x нужно подготовить 200x200 пикселей, для 3x - 300x300.


Как организовать хранение:

Flutter ожидает, что файлы для разных коэффициентов будут лежать в отдельных папках. Имена папок должны соответствовать коэффициенту: x1.5, x2.0, x3.0, x4.0.


assets/
images/
x1.5/
photo.png
x2.0/
photo.png
x3.0/
photo.png
x4.0/
photo.png
photo.png


В pubspec.yaml достаточно указать корневую папку. Flutter сам найдет нужные файлы в зависимости от плотности экрана.


flutter:
assets:
- assets/images/



🔗 Читать подробнее


💡 Вывод:

Подготовка изображений под разные плотности экрана - обязательный шаг для качественного отображения. Несколько версий файлов в папках x1.5, x2.0, x3.0, x4.0 и правильное указание в pubspec.yaml - все, что нужно.

Flutter делает выбор автоматически. Разработчику остается только подготовить ассеты и правильно организовать структуру. Это несложно, но влияет на внешний вид приложения на устройствах с разными экранами.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
692006
Photo1/2
👣 Релиз Dart 3.13: что принесла новая версия

Вышла новая версия Dart 3.13. Главное изменение - primary constructors стали стабильными. Это сокращает количество шаблонного кода при объявлении классов. Кроме того, улучшили форматирование, добавили tree-shaking для нативных библиотек и обновили поддержку веба.


Primary constructors стали стабильными:

В Dart 3.13 primary constructors наконец-то добавили в стабильную версию. Теперь класс с полями и конструктором можно объявить в одну строку.

class Point(final int x, final int y);

Больше не нужно писать отдельный конструктор и повторять имена полей. Для миграции добавили новые линты с автофиксами и рефакторинг «Convert to primary constructor» прямо в IDE.


Форматер стал умнее:

В dart format несколько изменений, которые делают код чище. Исправлена ошибка, из-за которой методы с большими коллекциями форматировались некрасиво. Теперь вызовы разбиваются более читаемо.

Также форматер начал автоматически разделять секции импортов пустыми строками, как того требует Effective Dart. Импорты dart:, package: и относительные пути теперь визуально отделены друг от друга.


Tree-shaking нативных библиотек:

Появилась возможность удалять неиспользуемый нативный код из финального бинарника. Раньше даже если вы вызывали только одну функцию из большой нативной библиотеки, вся библиотека оставалась в сборке. Теперь это можно исправить.

Нужно пометить FFI-биндинги аннотацией @RecordUse(), а в link hook через package:record_use оставить только те символы, которые действительно вызываются. Если биндинги не используются - библиотека выкидывается целиком. Это значительно уменьшает размер финального приложения.


Web - deferred loading и отказ от dart:html:

Для dart2wasm появилась экспериментальная поддержка отложенной загрузки (--enable-deferred-loading). Это улучшает время начальной загрузки больших приложений.

Также библиотека dart:html объявлена устаревшей. Вместо нее нужно использовать dart:js_interop и package:web. Многие пакеты уже мигрировали, так что обновление зависимостей часто решает проблему автоматически.


🔗 Читать подробнее


💡 Вывод:

Dart 3.13 - насыщенное обновление. Primary constructors сокращают шаблонный код. Форматер делает импорты чище. Tree-shaking нативных библиотек уменьшает размер бинарников. Веб-инструменты двигаются в сторону dart2wasm и современных библиотек.

Primary constructors упростят код, а tree-shaking пригодится в проектах с тяжелыми нативными зависимостями. Особенно если вы собираете релизные сборки, где каждый мегабайт имеет значение.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
8270010
Photo1/2
👣 Релиз Flutter 3.47: главные изменения для разработчиков

Вышла новая версия Flutter 3.47. Обновление заметное. Material и Cupertino наконец-то выехали из SDK в отдельные пакеты. Impeller стал рендерером по умолчанию на десктопе. Плюс подготовка к осенним обновлениям Apple и другие улучшения. Давайте разберем все основные изменения.


Material и Cupertino теперь отдельные пакеты:

Главное изменение. Material и Cupertino больше не встроены в SDK. Теперь это отдельные пакеты на pub.dev: material_ui и cupertino_ui. Это значит, что обновления виджетов теперь могут выходить независимо от релизов Flutter.

Пока это опционально. Старые импорты из package:flutter/material.dart все еще работают, но они объявлены устаревшими и будут удалены в ноябре.

Для миграции достаточно выполнить команду:

dart fix --apply --code=migrate_design_widgets

После этого импорты обновятся автоматически. Если что-то пошло не так, можно добавить пакеты вручную:

flutter pub add material_ui
flutter pub add cupertino_ui

Важный момент для пакетов с зависимостями: если вы еще не обновились, а ваши зависимости уже используют новый подход, поможет MaterialUiCompatibilityBridge.

Также вместе с UI-пакетами разобрали flutter_localizations. Теперь делегаты локализации живут внутри material_ui и cupertino_ui, а не в отдельном пакете.


Подготовка к iOS 27 и macOS 27:

Apple осенью выпустит Xcode 27 с новыми требованиями. Минимальная версия iOS поднята до 15, macOS до 12. iOS 27 SDK требует жизненного цикла UIScene. Приложения без него не запустятся на новых устройствах.

Для большинства проектов CLI мигрирует автоматически. Но если в проекте есть кастомный AppDelegate, придется править руками. Лучше проверить сейчас.

Также Flutter постепенно сворачивает поддержку Intel Mac. В этой версии только предупреждения, но в будущем сборка на Intel перестанет работать. Можно уже сейчас переключиться на ARM64-only:

flutter config --enable-macos-arm64-only

По Swift Package Manager прогресс: 92 из топ-100 плагинов уже перешли на SPM. Если вы еще не включили SPM, можно попробовать:

flutter config --enable-swift-package-manager


Impeller теперь на десктопе по умолчанию:

Impeller стал рендерером по умолчанию на macOS, Windows и Linux. Если вы не знаете, что это - новый рендеринг-движок, который компилирует шейдеры на этапе сборки, а не в рантайме. Это решает проблему шейдерных джанков. Первая анимация работает так же плавно, как и все последующие.

На macOS также включили Wide Gamut Color - более насыщенные и точные цвета.

Отключить Impeller можно, но в будущих версиях опция будет удалена.


Flavors для десктопа:

Flavors, которые работали на мобилках, теперь доступны и на Windows и Linux. Можно использовать разные ассеты и настройки для разных окружений.

flutter build windows --flavor production
flutter build linux --flavor staging

Экспериментальный многооконный режим тоже доработали. На Windows и Linux появились popup-окна. Можно делать контекстные меню и палитры.


🔗 Читать подробнее


💡 Вывод:

Flutter 3.47 - важное обновление. Главное - Material и Cupertino выехали из SDK. Это упростит обновления виджетов и снизит зависимость от релизов Flutter.

Impeller на десктопе - большой шаг для производительности. Flavors и многооконный режим делают десктоп-разработку более гибкой. А подготовка к осеннему релизу Apple заставляет проверить проекты на совместимость.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
1,770008
Video
👣 Плагин Flutter для VS Code обновился до версии 3.140.0

Вышло обновление плагина Flutter для VS Code. Изменения в основном точечные, но есть несколько полезных вещей: приоритет выбора пути до Flutter SDK изменился, появилась команда для TODOs и рефакторинги теперь запрашивают ввод от пользователя.


Flutter SDK - изменился приоритет:

Раньше плагин мог использовать SDK, найденный через .dart_tool/package_config.json, даже если в PATH был другой. Теперь SDK из PATH имеют приоритет. Если нужно задать явный путь, есть настройка dart.flutterSdkPath.


Новая команда - Toggle Show TODOs:

Появилась команда Dart: Toggle Show TODOs. Она включает или отключает отображение TODO-комментариев в редакторе. Раньше для этого нужно было лезть в настройки. Теперь можно сделать через панель команд.


Рефакторинг с вводом пользователя:

В рефакторингах появился новый механизм сбора пользовательского ввода. Раньше плагин генерировал имя автоматически. Теперь для «Add a name to the constructor» и «Add a prefix to the import» будет появляться диалог для ввода имени.


Синтаксис и подсветка:

Обновили TextMate-грамматику для начальной подсветки синтаксиса, пока сервер не передаст Semantic Tokens. Исправили случай, когда незакрытые блоки кода в dartdoc-комментариях ломали подсветку. Также код-блоки в dartdoc больше не теряют цвета при замене грамматики.


Тестирование:

Для package:test_reflective_loader исправили отображение setUp/tearDown в тестовом эксплорере. Теперь они навигируют на конкретные методы, а не на defineReflectiveSuite. Также методы больше не отображаются на всех классах, где их нет.


Команды создания проекта:

Команды Dart: New Project и Flutter: New Project переименовали в Dart: Create New Project и Flutter: Create New Project. Теперь они появляются в результатах поиска по словам dart create и flutter create.


Firebase Studio:

Поддержка Firebase Studio объявлена устаревшей и будет удалена. Это связано с закрытием самого сервиса.


🔗 Читать подробнее


💡 Вывод:

Обновление плагина - это смесь мелких улучшений и подготовки к будущим изменениям в SDK. Из полезного: новый приоритет выбора SDK, команда для TODOs и рефакторинг с вводом от пользователя. Остальное - исправления и подготовка к будущим релизам.

Изменений не много, но они делают работу немного удобнее. Особенно для тех, кто активно использует TODOs и рефакторинги.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
630007
Video
👣 Как работает сборщик мусора в Dart. Память, указатели и smi

Сборщик мусора в Dart - тема, которая часто всплывает на собеседованиях, но найти внятное объяснение непросто. Статьи либо слишком сложные, либо уходят в дебри реализации VM. Давайте разберем основы: как Dart управляет памятью, что такое указатели и почему числа не влияют на производительность.


Биты, байты и указатели:

Память компьютера - это длинный ряд ячеек, каждая из которых содержит бит (0 или 1). Процессоры работают с группами битов. В 64-битных процессорах это группы по 64 бита (8 байт). Это машинное слово.

Указатель в Dart занимает ровно одно машинное слово - 8 байт.

Указатель - это не сам адрес, а место, где этот адрес записан. Если представить адрес дома, то листок бумаги - это указатель, а то, что на нем написано - сам адрес.


Как Dart отличает число от объекта:

Dart числа и объекты хранятся по-разному. Если переменная содержит число, оно хранится прямо в указателе - никакого перехода по адресу не требуется. Если переменная содержит объект, в указателе хранится адрес этого объекта, и чтобы получить доступ к объекту, нужно перейти по этому адресу.

Как Dart понимает, что именно лежит в указателе? Все решает последний бит.

Адреса объектов в памяти кратны 16. Это сделано специально. Если адрес кратен 16, его последние биты всегда нули. Вот это пустующее место Dart использует как флажок.

Последний бит говорит, чем является значение в указателе:

🔹1 - ссылка на объект

🔹0 - число, искать объект по адресу не нужно

Пример: объект лежит по адресу 0x00A03F50. Этот адрес кратен 16. В двоичном виде последний байт: 0101 0000. Dart ставит единицу в конце: 0101 0001 → 0x00A03F51. Теперь это помеченный адрес объекта.

Чтобы дойти до реального объекта, нужно убрать эту единицу и получить исходный адрес.


Smi: числа, которые живут в указателе:

Если число умещается в указателе, оно называется smi (small integer). Smi не занимает места в куче. Оно живет прямо в указателе. Сборщик мусора за ним не следит.

Пример: число 7 = 111 в битах. Когда Dart упаковывает его в smi, он сдвигает биты влево на 1 позицию. Получается 1110. Ноль на конце означает, что это число, а не ссылка. При распаковке число делится на 2, возвращая исходное значение.


Handles - почему C++ не теряет объекты:

Сборщик мусора периодически двигает объекты в памяти, чтобы бороться с фрагментацией. Объект переезжает на новый адрес. Для Dart-кода это не проблема - сборщик знает все ссылки и обновляет их.

Но есть код на C++ (движок, нативные библиотеки). Сборщик не знает, где у C++ лежат ссылки. Если объект переедет, C++ останется со старым адресом - программа упадет.

Решение: handles. Это ссылка на ссылку. C++ держит не объект, а handle с адресом объекта. Когда объект переезжает, сборщик обновляет адрес внутри handle. C++ всегда смотрит на актуальный адрес через handle.


🔗 Читать подробнее


💡 Вывод:

Dart управляет памятью хитро, но логично. Указатели хранят либо числа (smi), либо ссылки на объекты. Различие определяется последним битом. Сборщик следит за объектами в куче, но не трогает числа. Handles решают проблему с C++ ссылками при перемещении объектов.

На практике это значит: числа не влияют на производительность, объекты - да. Создание лишних объектов напрямую влияет на нагрузку сборщика. Понимание этих основ помогает писать более эффективный код.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
7800010
Video
👨‍💻 Конец золотой лихорадки: как изменились правила игры в ИТ-индустрии.

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


Эпоха золотой лихорадки и ее завершение:

Последнее десятилетие было аномальным периодом стремительного роста для ИТ. Спрос на разработчиков значительно превышал предложение. Компании готовы были нанимать специалистов с минимальным опытом или даже после непродолжительных курсов, предлагая при этом конкурентоспособные по местным меркам зарплаты. Это создало иллюзию, что попасть в ИТ и быстро вырасти - это простая и быстрая дорога к финансовому успеху.


Критерии изменились - как оценивают разработчиков в эпоху выбора:

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

🔹Работодатель теперь выбирает. Если раньше компаниям часто приходилось брать первого, кто откликнулся, то сейчас на одну позицию сотни кандидатов. Это позволяет выбирать не просто того, кто знает синтаксис языка, а того, кто обладает наиболее подходящим набором hard и soft скиллов, понимает бизнес-контекст и умеет решать комплексные задачи.

🔹Профессионализм выходит на первый план. Эпоха, когда годы опыта в резюме автоматически гарантировали высокий оффер, уходит. Ценность представляет не стаж сам по себе, а конкретные навыки, качество кода, архитектурные решения, умение работать в команде и брать на себя ответственность.


Почему это в итоге хорошо для индустрии и для настоящих специалистов:

🔹Для бизнеса: снижается доля балласта - сотрудников, которые не приносят значимой ценности. Это повышает общую эффективность команд, снижает издержки и позволяет создавать более качественные продукты.

🔹Для разработчиков: из отрасли уходят те, для кого программирование было лишь способом хорошо заработать без особой страсти к делу. В результате в командах остаются мотивированные специалисты, которые действительно любят свою работу и стремятся развиваться. Это повышает уровень коллаборации и делает работу более осмысленной.

🔹Для новичков: исчезает иллюзия быстрого и легкого успеха. Это заставляет более осознанно подходить к выбору карьеры, фундаментальнее учиться и сразу формировать правильные профессиональные привычки. Те, кто приходит в индустрию из интереса и готов вкладываться в развитие, в итоге оказываются в выигрыше.


Стратегия выживания и успеха в новых условиях:

Адаптация к новым правилам требует действий:

🔹Глубокое освоение стека и смежных областей. Перестать быть просто кодером. Изучать архитектуру, принципы DevOps, базовое понимание бизнес-процессов в своей предметной-области.

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

🔹Умение продавать себя. Качественное резюме, ухоженный LinkedIn/GitHub и другие соц. сети, способность четко рассказать о своих достижениях и проектах на собеседовании - это обязательная часть работы современного разработчика.

🔹Непрерывное обучение. Технологии развиваются стремительно. Постоянное изучение нового (будь то новый фреймворк, язык или парадигма) - это не опция, а необходимость.


💡 Вывод:

Тезис «ИТ-индустрия умерла» - это эмоциональная реакция на окончание эпохи аномально высокого и не всегда заслуженного спроса. Реальность такова, что индустрия не умерла, она повзрослела и стала более здоровой, селективной и требовательной. Это сложный, но правильный переход от рынка, где ценилась масса, к рынку, где в цене - качество и экспертиза.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
685007
Video
👣 Как запустить Flutter-приложение на iPhone без Mac и подписки Apple Developer

Всем привет! Недавно наткнулся на статью, в которой разработчик делится опытом тестирования Flutter-приложения на iPhone друга без Mac и без подписки Apple Developer. Знакомая ситуация: есть приложение на Flutter, нужно показать кому-то с iPhone. Mac нет, платить $99 в год за Apple Developer Program жалко. Оказывается, есть рабочий путь, и автор его подробно описал. Не магия, а грамотная сборка цепочки из четырех инструментов.


Почему iOS сложнее Android:

На Android тестовая установка занимает пятнадцать минут. Включил отладку по USB, запустил adb install, готово. Google Play Console - $25 единоразово. RuStore - бесплатно.

С iPhone все иначе. Официальный путь Apple: Mac для Xcode, подписка $99 в год, TestFlight. Дорого и не всегда доступно.


Схема из четырех шагов:

Есть обходной путь, который обходится без Mac и без платной подписки.

🔵Первый шаг: включить на iPhone режим разработчика. Начиная с iOS 16, Apple вынесла этот тумблер в отдельный раздел, но он не появляется, пока на устройство не установлено dev-signed приложение. Чтобы обойти это, используется утилита Tenorshare iCareFone на Windows. Подключаете телефон, нажимаете «Enable Developer Mode», подтверждаете на телефоне. Тумблер появляется в настройках. Включаете его один раз.

🔵Второй шаг: собрать неподписанный .ipa через GitHub Actions. В бесплатных macOS-раннерах запускается сборка Flutter под iOS. Важно зафиксировать версию Flutter, использовать flutter precache --no-android --no-web для экономии времени и собирать с флагом --no-codesign. После сборки артефакт сохраняется в Actions.

🔵Третий шаг: подписать и установить через Sideloadly на Windows. Утилита подписывает .ipa вашим бесплатным Apple ID и ставит на телефон по USB. Подпись действует семь дней, максимум три приложения одновременно. Для тестирования достаточно.

🔵Четвертый шаг: подтвердить доверие на устройстве. При первом запуске iOS попросит подтвердить доверие профилю разработчика в настройках. Один раз - и приложение запускается.


Нюансы, которые важно знать:

🔵У бесплатного Apple ID подпись живет 7 дней. Через неделю приложение перестанет запускаться - нужно перекатать через Sideloadly.

🔵Sideloadly может менять Bundle ID, добавляя суффикс. В этом случае при обновлении приложение будет восприниматься как новое, данные пользователя сбросятся.

🔵Если используется Firebase, файлы конфигурации должны быть в репозитории. Иначе сборка на CI упадет.

🔵Версию macOS-раннера лучше фиксировать явно, потому что macos-latest переключается на новые версии и сборка может сломаться.


🔗 Читать подробнее


💡 Вывод:

Тестировать Flutter-приложение на iPhone без Mac и без $99 реально. Схема из четырех инструментов работает. Основная сложность не в настройке, а в том, чтобы найти все куски и собрать их в одно целое.

Для инди-разработчика это рабочий путь, чтобы показать приложение друзьям и тестировщикам. Для полноценного релиза в App Store придется решать вопрос с оплатой подписки. Но тестирование на железе перестает быть барьером.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
2,260005
Video
👨‍💻 Когда вакансий меньше, а требований больше: стратегия выживания в ИТ.

Разговоры о кризисе в ИТ уже не панические слухи, а констатация факта. Вакансий меньше, зарплаты замерли, конкуренция выросла в разы. Но важно понимать: рынок не умирает, он болезненно меняется. Эпоха, когда достаточно было знать модный фреймворк и получать несколько офферов в неделю, закончилась. Начинается фаза, где выживут не просто технически сильные, а адаптивные и стратегически мыслящие специалисты.


Что на самом деле происходит:

Проблема не в том, что ИТ стал ненужным. Проблема в том, что изменился тип спроса. Раньше компании массово нанимали «на рост», создавая команды с запасом. Сегодня каждый найм - это точечное закрытие конкретной болевой точки. Отсюда парадоксальные, на первый взгляд, тренды:

🔹 Спрос на узких экспертов, а не специалистов широкого профиля: раньше ценился разработчик, который умеет немного в backend, немного во frontend. Сейчас ценность смещается в сторону глубокой экспертизы в одной, но критически важной для бизнеса области.

🔹 ИИ не забирает работу: он меняет ее порог входа. Автоматизация рутинного кодинга и первичного скрининга резюме не означает конец профессии. Это означает, что ценность базового навыка написания кода падает. Ценность навыка решения сложных, нестандартных проблем - взлетает. Если ваша работа сводилась к переводу ТЗ в код, вы в зоне риска.

🔹 Кризис менеджмента, а не разработки: сильнее всего сокращения ударили по слоям управления, которые не приносят прямого технического или продуктового результата. Растет спрос на две противоположные роли:

🔹Сильный техлид/архитектор, который может привести команду к результату.

🔹Автономный senior-разработчик, который сам ведет фичу от идеи до продакшена.


Новая система координат - что оценивают теперь:

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

🔹 Эффективность, а не занятость. Не «я работал в компании X 3 года», а «я спроектировал и внедрил систему кэширования, которая снизила p95-латентность API с 2с до 200мс и сэкономила X на инфраструктуре».

🔹 Влияние на бизнес-метрики. Разработчику все чаще нужно понимать, как его код влияет на LTV, конверсию или удержание пользователей. Вы перестаете быть исполнителем задач и становитесь соучастником бизнеса.

🔹 Скорость обучения и адаптации. Технологический стек устаревает быстрее. Ценится не знание конкретного фреймворка, а способность за 2-3 недели глубоко погрузиться в новую для себя технологию или домен и начать приносить пользу.


Тактика выживания и роста в новых условиях:

Стратегия «просто продолжать хорошо делать свою работу» больше не работает. Нужна активная позиция.

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

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

🔹 Сдвигайтесь в сторону продуктового мышления. Перестаньте быть просто «исполнителем задач». Начинайте задавать вопросы: «Какую пользовательскую проблему мы решаем?», «Как мы измерим успех этой фичи?». Разработчики, которые мыслят как мини-продакт-менеджеры, становятся незаменимыми.

🔹 Стройте сеть, а не просто рассылайте резюме. В условиях, когда на одну вакансию приходят сотни откликов, решающую роль часто играет рекомендация изнутри. Активно общайтесь с коммьюнити, выступайте, вносите вклад в open-source. Ваша сеть контактов - это ваш запасной парашют.


🔗 Ссылка на подробную статью


💡 Вывод:

Текущий кризис в ИТ - это не апокалипсис, а болезненный, но необходимый переход от рынка перегретого спроса к рынку взвешенного, осмысленного спроса. Он отсеивает тех, кто пришёл в индустрию за «легкими деньгами», и укрепляет позиции тех, кто относится к разработке как к ремеслу и стратегической функции бизнеса.


➡️ Кот Денисова
341006
Video
👣 Создаем виджеты для Android и iOS во Flutter-приложении

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


Что такое виджеты:

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

Виджеты бывают разных типов:

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

🔵Виджеты коллекций отображают несколько элементов одного типа - например, последние статьи из новостного приложения или фотографии из галереи.

🔵Виджеты управления предоставляют быстрый доступ к часто используемым действиям - запуск поиска, создание заметки, управление плеером.

🔵Гибридные виджеты комбинируют функции разных типов. Например, музыкальный виджет одновременно показывает текущий трек (информационная часть) и предоставляет кнопки управления (управляющая часть).


Нативные инструменты:

Для каждой платформы используется свой набор инструментов.

На Android виджеты можно создавать двумя способами: через XML или с помощью Jetpack Glance. Glance - более современное решение на основе Compose. Оно позволяет писать декларативный код, не заморачиваясь с XML-макетами и управлением состояниями. Glance поддерживает большинство компонентов Compose: текст, кнопки, контейнеры.

На iOS виджеты реализуются через WidgetKit, доступный с iOS 14. Они создаются на SwiftUI и хорошо интегрируются в экосистему Apple. WidgetKit работает с TimelineProvider, который отвечает за данные и расписание обновлений и Entry - моделью данных для виджета.


Библиотека home_widget:

Для связи Flutter-приложения с нативными виджетами используется библиотека home_widget. Она предоставляет удобные методы для сохранения данных и обновления виджетов.

Основные методы:

🔵saveWidgetData - сохраняет данные в хранилище виджета.

🔵updateWidget - обновляет виджет на экране.

🔵getWidgetData - читает данные из виджета обратно в Flutter.

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


Future<void> _sendAndUpdate(int? value) async {
await HomeWidget.saveWidgetData(_countKey, value);
await HomeWidget.updateWidget(
androidName: 'CounterGlanceWidgetReceiver',
iOSName: 'CounterWidget'
);
}


Сначала данные сохраняются через saveWidgetData, затем вызывается updateWidget с указанием имени виджета для каждой платформы.


🔗 Читать подробнее


💡 Вывод:

Виджеты - это полезный способ повысить вовлеченность пользователей. Во Flutter нет встроенной поддержки виджетов, но есть надежный путь через нативные инструменты. На Android - Glance (или классические XML-виджеты). На iOS - WidgetKit.

Библиотека home_widget связывает Flutter-код с нативной реализацией, позволяя сохранять данные и обновлять виджеты из Dart. Это не самый простой путь, но он рабочий и хорошо документированный.

Если вы хотите добавить виджеты в свое Flutter-приложение, начните с изучения Glance для Android и WidgetKit для iOS. А библиотека home_widget поможет соединить все воедино.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
693007
Video
👣 Plumix: фреймворк, который переносит архитектуру Flutter в мир .NET

Нашел интересную статью на Хабре, в которой мобильный разработчик Егор рассказывает о своем проекте Plumix - фреймворке, который переносит архитектуру Flutter в мир .NET. Автор честно делится, почему затеял эту авантюру, как работает с ИИ-агентами и что умеет Plumix сейчас. В статье рассказано про боль десктопной разработки на Flutter, про полученное удовольствие от Avalonia и про неожиданный подарок от Google в виде Impeller.


Почему Flutter на десктопе - боль:

С мобильной разработкой на Flutter все отлично. Hot reload, декларативная верстка, нативные ощущения - автор получает удовольствие. А вот десктоп - другая история. Контролы под десктоп не заточены. Попробуйте собрать таблицу на десятки тысяч строк, контекстные меню, drag-and-drop - все это либо пишется руками, либо собирается из сторонних пакетов разной степени заброшенности. Десктоп на Flutter часто ощущается как мобилка, растянутая на весь монитор.

С Avalonia ровно наоборот. Десктопы на ней пишутся с кайфом: DataGrid, меню, хоткеи, работа с мышью - все родное. А мобилка - боль. Нет физики скролла, нет жестов, нет pull-to-refresh. Технически запускается, но пользователь чувствует: что-то не так.

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


Фреймворк, который пишут агенты:

Самое интересное - работа с ИИ. Автор признается: в одиночку переписать рендер-пайплайн Flutter, систему жестов и Sliver-протокол - неподъемная задача. Но на дворе 2026 год и работа идет не в одиночку.

Plumix строился как репозиторий, оптимизированный под ИИ-агентов. Не «иногда прошу чат-бота написать функцию», а полноценный конвейер. Агент получает задачу «портируй Material-контрол X», сам находит контекст, пишет код, тесты и документацию. Автор выступает архитектором и ревьюером.

Отдельный прием - эталонное приложение. В репозитории живут два одинаковых сэмпла: на C# и на Dart. Автор запускает их рядом и сравнивает визуальное поведение.

Правило единицы поставки: один контрол за одну итерацию, целиком. API, состояния, layout, отрисовка, тесты - все в одном заходе. Если разрешить агенту дробить работу, получается вечный недострой.

Результат: портирование контрола перестало быть исследовательской задачей и стало конвейерной. Changelog разросся до такой степени, что пришлось ввести правило ротации - когда файл переваливает за 100 КБ, старая половина уезжает в архив.


Impeller в Avalonia - подарок для Plumix:

В ноябре 2025 команда Avalonia объявила о партнерстве с командой Flutter: в .NET приходит Impeller - GPU-first рендер, который Google написала для Flutter вместо Skia. Инициатива исходила от самого создателя Impeller - Chinmay Garde.

Для Plumix это важно. Потому что архитектура такая: Plumix владеет layout’ом и логикой отрисовки, но пиксели на экран выводит графический бэкенд Avalonia. Сейчас это Skia. Когда Avalonia доведет Impeller до стабильности, Plumix получит его автоматически, без единой строчки изменений.


🔗 Читать подробнее


💡 Вывод:

Автор признается: три года назад такой проект в одиночку был бы авантюрой. Сегодня, с ИИ-агентами и правильно выстроенным процессом, это рабочие будни. Plumix - это не просто фреймворк. Это эксперимент, который показывает, насколько далеко можно уехать на связке «человек-архитектор + ИИ-исполнители» в сложном системном проекте. И, по словам автора, ответ: значительно дальше, чем он думал.


Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик

Закрытый канал:
🚀 Мобильный трудоголик PRO
662005
👣 Что важно знать о BuildContext в Flutter

Для многих начинающих разработчиков BuildContext остается чем-то вроде магической переменной - появляется из ниоткуда, что-то делает, но как именно непонятно. На самом деле это интерфейс к элементу в дереве виджетов. Когда вы пишете виджет, вы описываете конфигурацию. А context - это ваш пропуск к тому, что реально существует в памяти.


Как на самом деле работает поиск:

Когда вы вызываете Theme.of(context), Flutter начинает подниматься вверх по дереву от текущего контекста, пока не найдет ближайшего предка нужного типа. Если у вас вложенные темы, context определяет, какую из них вы получите.

Отсюда и берутся проблемы: Navigator.of(context) иногда не работает, потому что контекст находится выше того навигатора, который вы пытаетесь использовать. Или Scaffold.of(context) не видит Scaffold, потому что context создан раньше.


Где чаще всего спотыкаются:

🔵initState - запретная зона. Нельзя использовать context напрямую в initState. Если нужно что-то показать при открытии экрана, приходится использовать addPostFrameCallback. Иначе приложение упадет.

🔵Асинхронные операции - ловушка. Вы сделали запрос, ждали ответа, а пользователь уже закрыл экран. Если попробовать использовать старый context будет краш. Поэтому после каждого await нужно проверять if (!context.mounted) return;

🔵Диалоги - отдельная история. Чтобы закрыть диалог изнутри, нужно использовать контекст, который приходит в builder, а не тот, что был снаружи. Иначе навигатор не поймет, что именно закрывать.


🔗 https://appsell.su/blog/den-apps-1/razrabotka/chto-vazhno-znat-o-buildcontext-v-flutter-249


💡 Вывод:

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


➡️
462006

Showing 30 of 43 posts

Рейтинг

Требуется вход

Frequently Asked Questions

О чём Telegram-канал Flutter & Dart | Мобильный трудоголик?+

Flutter & Dart | Мобильный трудоголик (@hardworkerflutter) — Telegram-канал в категории Мобильные приложения на tgdio. На профиле есть рейтинг, отзывы, ранг и свежие посты — решите до подписки в Telegram. Сейчас на tgdio указано около 414 подписчиков.

Как вступить в Flutter & Dart | Мобильный трудоголик (@hardworkerflutter) в Telegram?+

Откройте профиль канала на tgdio, нажмите Join / Open in Telegram и подпишитесь на @hardworkerflutter в приложении или веб-версии Telegram бесплатно.

Стоит ли подписываться на Flutter & Dart | Мобильный трудоголик (Мобильные приложения)?+

На tgdio можно проверить рейтинг, отзывы, ранг и свежие посты Flutter & Dart | Мобильный трудоголик до подписки. Сейчас на tgdio указано около 414 подписчиков. Сравните с похожими каналами Мобильные приложения.

Где смотреть статистику и посты Flutter & Dart | Мобильный трудоголик?+

На tgdio откройте страницу @hardworkerflutter: статистика подписчиков, ранг, рейтинг и посты. Ссылка Stats даёт графики роста и активности.