Flutter and chill – Telegram
Flutter and chill
124 subscribers
34 photos
1 video
21 links
Разработка и жизнь
Download Telegram
Осень - мое любимое время для посещения музеев

Приобщились к прекрасному, посетив сразу две временные выставки в Русском музее 🤠

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

И конечно не могли пройти мимо выставки работ Архипа Куинджи, мастера по работе со светом. Впервые с его творчеством я познакомилась, впервые посетив Третьяковскую галерею. Помню, что зависла на несколько минут перед "Ночью на Днепре", казалось что отражение луны в ночном пейзаже светится изнутри.
В Русский музей ее конечно тоже привезли, но мне показалось странным решение выделить под нее отдельный зал с тусклым освещением и установленными прожекторами по бокам. 💪

А вы находите время ходить на выставки? Если да, какая последняя вас реально зацепила? ☺️
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
9👍5💅4💘2
Спонсор моих ранних подъемов ❤️
Please open Telegram to view this post
VIEW IN TELEGRAM
10💅3💘2
🧑‍💻 Поговорим про тестирование?

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

Зачем вообще писать тесты? 😊

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

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

🔵документирование кода - написанные тесты по сути выступают описанием фичи: что происходит и какой результат ожидаем.

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

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

А вы как относитесь к тестам? Пишете их или обходитесь ручным тестированием? Может, есть свои лайфхаки? Делитесь в комментариях 🍿
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥1
Поздравляю всех с пятницей 💜

Знаю, что у кого-то и на выходных работа не заканчивается, но стоит иногда взять паузу и похвалить себя за хорошую работу 🐈
Please open Telegram to view this post
VIEW IN TELEGRAM
💘64💅3🔥1
В Pinterest появилась настройка видеть меньше AI слопа 😳

Сразу же все отключила, т.к частенько захожу на Pinterest в поисках вдохновения, но количество нейронных картинок угнетало 🥺
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥73💅3👍1
Как упростить себе жизнь при тестировании

В предыдущем посте про тестирование я упомянула про хорошую структуру кода для написания тестов, хочу сегодня раскрыть эту тему 😮

👩‍💻 Тестируемый код - это код, в котором внешние зависимости и влияния среды выполнения (например, текущее время, состояние файловой системы, ответы сети) изолированы от основной бизнес-логики и могут быть подменены при тестировании.

🤕 Антипаттерн - создание зависимостей внутри метода:

class UserService {
String getUserName() {
final api = ApiClient(); // каждый раз создается новый инстанс 🥲
final user = api.fetchUser();

return user.name;
}
}


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

✔️ Теперь можно замокать наш ApiClient, используя mocktail:

class UserService {
final ApiClient api;

UserService(this.api);

String getUserName() {
final user = api.fetchUser();
return user.name;
}
}

// Это упрощенная версия теста, в обычном случае создаем мок, доступный внутри группы тестов
test('getUserName возвращает имя', () {
final mockApi = MockApiClient(); // моковый апи клиент
when(() => mockApi.fetchUser())
.thenReturn(User(name: 'user')); // пишем нужную заглушку для тестируемого метода

final service = UserService(mockApi); // прокидываем мок в конструктор
final result = service.getUserName();

expect(result, 'user');
});


😠 Коварные статические методы, например DateTime.now(), SharedPreferences.getInstance().

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

String getGreeting() {
final hour = DateTime.now().hour; // зависит от реального времени
return hour < 12 ? 'Доброе утро!' : 'Добрый день!';
}


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

✔️ Для решения этой проблемы существует пакет clock - это провайдер текущего времени, позволяющий зафиксировать время в тестах.

String getGreeting() {
final hour = clock.now().hour; // заменили DateTime на clock
return hour < 12 ? 'Доброе утро!' : 'Добрый день!';
}

// В тесте фиксируем время на 9 утра
test('В 9 утра возвращается верный результат', () {
final fixedClock = Clock.fixed(DateTime(2025, 1, 1, 9));

withClock(fixedClock, () {
final greeting = getGreeting();
expect(greeting, 'Доброе утро!');
});
});


🤥 В SharedPreferences есть встроенный метод setMockInitialValues для инициализации с заданным набором значений для тестирования:

test('Получение значения темы из Preferences', () async {
SharedPreferences.setMockInitialValues({'theme': 'dark'});

expect(await getTheme(), 'dark');
});


🙄 Работа с асинхронными операциями (например, Future.delayed, Timer, Stream.periodic)

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

В ней также есть интеграция с пакетом clock и при его использовании не требуется дополнительных действий, так как FakeAsync автоматически оверрайдит его на том же уровне, что и классы dart:async.

Например:

Future<DataModel> loadData() async {
await Future.delayed(const Duration(minutes: 2));
return DataModel(status: 'loaded');
}

test('данные загружаются корректно', () {
fakeAsync((async) {
final future = loadData();

expect(future, completion(equals(DataModel(status: 'loaded'))));

// пропускаем 2 минуты
async.elapse(const Duration(minutes: 2));
});
});


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

Интересно узнать, с какими проблемами вы сталкивались при тестировании. Делитесь в комментариях (кто смог дочитать до конца) 🍿
Please open Telegram to view this post
VIEW IN TELEGRAM
10🔥12👍6💘42
Когда тревога перед новым делом берёт верх, задайте себе вопрос: а что интересного я могу узнать в процессе?

Такой подход помогает почувствовать интерес и любопытство, а не давление от ожидания идеального результата
7🔥3💘2💅1
Объединение потоков во Flutter

👀 Недавно вышло видео с интригующим названием The great thread merge, где мистер Craig рассказывает о прошлом и будущем флаттера в контексте потоков (threads).

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

🔥 С версии 3.29 ситуация изменилась: Dart-код теперь исполняется в том же потоке, что и платформенный UI. Это означает, что вызовы нативных функций могут быть синхронными, без необходимости оборачивать их в Future. Типобезопасность теперь обеспечивается механизмом FFI (Foreign Function Interface) и инструментом ffigen, который генерирует Dart-биндинги к C-API.

⚡️ На практике это открывает новые возможности: плагины могут быть переписаны так, чтобы вызовы типа получения уровня батареи, статуса сети или доступа к датчикам возвращали значение сразу. Виджеты становятся проще - вместо StatefulWidget можно обойтись обычным StatelessWidget, если данные доступны синхронно.

🧑‍💻 Сейчас возможно отключить объединение потоков на Windows и macOS используя флаг:

project.set_ui_thread_policy(flutter::UIThreadPolicy::RunOnSeparateThread)

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

🧑‍💻 Это серьезное архитектурное изменение, которое позволит писать больше Dart кода, но не стоит забывать о производительности: тяжёлые операции должны выноситься в изоляты, иначе они заблокируют UI 🔪
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥11👍3💅31💘1
Сегодня хочу поделиться полезным видео по асинхронному программированию в Dart 😦

Затронуты практически все основные темы, которые, к тому же, часто спрашивают на собеседовании:

🟣Что такое async/await

🟡Виды Future и можно ли его отменить, обработка ошибок

🔵Принцип работы Event loop

🟢Очень много базы про стримы (Stream), работа с StreamController и Zone

Да, видео с таким количеством информации не может быть коротким, но подача хорошая и довольно много наглядных примеров 😱
Please open Telegram to view this post
VIEW IN TELEGRAM
17🔥5
Небольшая задача на асинхронность 👍

В каком порядке выполнятся функции? В следующем посте будет опрос с правильным вариантом ответа 🧑‍💻


void testEventLoop() {
print(1);

scheduleMicrotask(() => print(2));

Future(() => print(3))
.then((_) => print(4));

Future.microtask(() => print(5));

Timer.run(() => print(6));

Future.delayed(Duration.zero, () => print(7));

Future(() async {
print(8);
await Future.delayed(Duration(milliseconds: 1));
print(9);
});

Future.sync(() => print(10));

print(11);
}
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8💅4
Update Dart Spec 🍿

В репозиторий спецификации языка добавили сразу несколько черновиков новых фич 🔫

1️⃣ Implied Parameter/Record Field Names
Позволяет в момент вызова метода, функции или конструктора опускать имя аргументов, если у передаваемой ему на вход переменной (функции) аналогичное имя:

Пример с функциями/методами:
// before
var subnoscription = stream.listen(
onData,
onError: onError,
onDone: onDone,
cancelOnError: cancelOnError,
);

// after
var subnoscription = stream.listen(
onData,
:onError,
:onDone,
:cancelOnError,
);


Пример с переменными:
typedef Color = ({int red, int green, int blue, int alpha});

// before
Color colorWithAlpha(Color color, int newAlpha) {
var (:red, :green, :blue, alpha: _) = color;
return (red: red, green: green, blue: blue, alpha: newAlpha);
}

// after
Color colorWithAlpha(Color color, int alpha) {
var (:red, :green, :blue, alpha: _) = color;
return (:red, :green, :blue, :alpha);
}


2️⃣ Anonymous Methods (AM)

А вот это уже зверь из области функциональщины, который позволяет добавлять объекту анонимные методы и выстраивать цепочки из них, передавая результат одного анонимного метода в вызов другого 😏
Допустим, у нас есть блок кода, который ну никак не представить в виде цепочки последовательных вызовов с помощью каскадного оператора ..
void main() {
final String halfDone, result;
final sb = StringBuffer('Hello');
sb.write(',');
halfDone = sb.function toString() { [native code] }();
sb.write(' ');
sb.write('world!');
result = sb.function toString() { [native code] }();
print('Creating an important string: $halfDone then $result');
}


С добавлением одной из вариаций анонимного метода (=>) и использованием каскадного оператора его можно преобразить следующим образом:
void main() {
final String halfDone, result;
final sb = StringBuffer('Hello')
..write(',')
..=> halfDone = function toString() { [native code] }() // AM
..write(' ')
..write('world!')
..=> result = function toString() { [native code] }(); // AM
print('Creating an important string: $halfDone then $result');
}


Другая вариация подразумевает конструкции типа O.{что-то делаем} или O.{что-то делаем}.{получаем результат предыдущей АМ и снова что-то делаем}.{и т.д.}. Благодаря ней мы можем переписать код так:
// применяем AM к StringBuffer('Hello').
void main() => StringBuffer('Hello').{
final String halfDone, result;
write(',');
halfDone = function toString() { [native code] }();
write(' ');
write('world!');
result = function toString() { [native code] }();
print('Creating an important string: $halfDone then $result');
};


или так
void main() {
// применяем AM к StringBuffer('Hello').
StringBuffer('Hello').{
write(', world!');
return function toString() { [native code] }();
}.{ // определяем следующий анонимный метод
// `this` is the string returned by
`toString()`.print(length); // Prints '13'.
return length > 10;
}.=> print('That was a ${this ? 'very' : '') long string!');
}


Если же мы хотим избежать конфликта имен или явно работать захваченным ранее объектом, который передаем в анонимный метод можно использовать конструкции типа O.(имя и тип арг1, [арг 2, арг3, ...]){что-то делаем} и выстраивать с ними цепочки различной длины:
class A {
void bar() {}
void foo() {
StringBuffer('Hello').(sb) { // sb -> StringBuffer('Hello')
sb.write(', world!');
this.bar(); // `this` refers to the current instance of `A`.
return sb.function toString() { [native code] }();
}.(s) { // снова АМ
print(s.length);
bar(); // An implicit `this` also refers to the current `A`.
return s.length > 10;
}.(cond) => print('That was a ${cond ? 'very' : '') long string!'); // АМ
}
}


Как вам новые фичи Dart?

👍 – О, да, детка!!!
👌 – Сомнительно, но Окей
👎 – Отстой!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍2👎1
Октябрь 2025: первые посты, впечатления от посещения конференции, тесты, асинхронка

Нас уже 85 🥳
Планирую продолжать делиться новостями, полезными материалами и темами, которые считаю важными в разработке 🧑‍💻

Очень радуюсь вашему интересу к контенту и активности под постами, кого-то даже получилось вдохновить на написание тестов ❤️

А вот посты за прошедший месяц:

🟣Вводный пост
рефлексия по созданию этого канала, возможно в будущем дополню

🟣Впечатления от конференции Стачка 2025 впервые посетила такую большую конфу, перечислила новые и интересные для меня темы

🟣Долгожданное обновления хэндбука по Flutter
добавили много новых тем, советую ознакомиться

🟣Сходила на выставки в Русском музее
мои впечатления о выставках искусства начала 20 века

🟣Когда стоит покрывать тестами код
размышления о тестировании, подчеркнула важность написания тестируемого кода

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

🟣Архитектурные изменения во Flutter - The great thread merge
важная новость о будущем Flutter, новый способ взаимодействия с платформой

🟣Видео с базой по асинхронному программированию в Dart
поделилась полезным видео с базой по асинхронке

🟣Задача на асинхронность
проверили знание порядка выполнения операций
Please open Telegram to view this post
VIEW IN TELEGRAM
107🔥5💅2
Уровень моей удачи - слечь с температурой на выходные и праздники 🤧
Восприняла это как сигнал о необходимости передохнуть, скоро вернусь с новыми силами и постами 😶
Please open Telegram to view this post
VIEW IN TELEGRAM
115👎2
Спасибо за вашу поддержку, уже восстанавливаюсь и ресерчу интересные темы 🌹

Вы, кстати, также можете помочь развитию канала, вкинув тему, которую вам интересно было бы разобрать 💃

Недавно закидывала вам на подумать задачку, большинство молодцы - ответили правильно ☺️

Заложила в ней небольшую проверку на внимательность и знание нечасто встречающегося функционала Future:

Future.sync() немедленно вызывает переданную функцию, в данном случае она выполнится синхронно 😏

Желаю всем приятных и продуктивных выходных 🥰
Please open Telegram to view this post
VIEW IN TELEGRAM
110🔥4💅3
Завтра в 22:00 по мск ожидаем лайвстрим от команды Flutter по последним обновлениям, Q&A и многое другое 📖

https://www.youtube.com/watch?v=RTb3gP4p5bw
Please open Telegram to view this post
VIEW IN TELEGRAM
💅3👍2🔥2
🧑‍💻 Что самое главное в Flutter 3.38?

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

🤏Dot shorthands
Новая фича которая позволяет не указывать название класса с именованным конструктором и название enum

Например для указания alignment в верстке можно указывать только значение


// With shorthands
Column(
mainAxisAlignment: .start,
crossAxisAlignment: .center,
children: [ /* ... */ ],
),


А для padding указывать только значение

Padding(
padding: .all(8.0),
child: Text('Hello world'),
),


🌐 Конфигурация для Web
Новый файл web_dev_config.yaml позволяет задать параметры для локального веб-запуска Flutter-приложения.

Можно указывать порт, хост, сертификаты HTTPS, заголовки и прокси-маршруты

👩‍🍳 Теперь WidgetState
Вы по-любому встречались с MaterialStateProperty. Параметры для настройки конкретных стетов материал виджетов (нажат, наведен, отключен)

Теперь вместо Material используем WidgetState, который подходит и для Cupertino виджетов. Ещё одно упрощение работы с платформозависимым UI


✈️ Новый API OverlayPortal
Позволяет размещать всплывающие элементы интерфейса (диалоги, попапы, уведомления и т.п.) в любом Overlay выше по дереву виджетов, а не только в ближайшем

Благодаря этому можно, например, легко показывать глобальные уведомления поверх всего приложения, даже если вызов идёт из вложенного экрана — без костылей и ручного поиска корневого Overlay

🤔Ещё из интересного:

- Куча всяких оптимизаций опять в Engine, Vulkan, OpenGL ES

- Predictive back-анимации на Android теперь по умолчанию

- На Windows появился расширенный доступ к параметрам дисплеев

- Поддержка 16 KB page size для Android

📊 В релизе 825 коммитов от 145 участников сообщества

🔗 Все подробности можно узнать в блоге команды
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12
Взаимодействие с нативным кодом во Flutter: что нового?

В поисках информации об ffi нашла свежий доклад о текущих возможностях взаимодействия с платформой.

Отличный обзор Platform Channels и Direct native interop - много примеров кода, плюсы и минусы разных подходов к вызову нативного кода.

🟡 Platform Channels - это достаточно простой и стабильный способ получения различных видов сообщений, подходит для большинства задач, но требуется писать много boilerplate кода.

Принцип работы - сообщение переводится в бинарный формат, Binary Messenger отправляет его с помощью Platform Dispatcher на нужную платформу, а на стороне платформы нужно написать нативный код, обрабатывающий нужный метод и возвращающий результат.

🔵 jnigen - по заверению докладчика уже может использоваться в продакшене. Интересно, что для генерации биндингов необходимо сначала собрать apk, а затем запустить команду генерации. После этого появляется доступ к андроид контексту и на примере показано, как получить список библиотек на устройстве.

🟢 swiftgen на данный момент в экспериментальной фазе, под капотом использует ffigen и дает доступ только к открытой части кода.

Больше информации можно найти в официальной документации dart-lang/native 🧑‍💻

🔥 Также вышла статья про Objective-C и Swift Interop с использованием ffigen, на примере генерации биндингов к AVAudioPlayer.

Пока еще остается много вопросов, например, какой подход стоит использовать при разработке нового плагина? Насколько ffi быстрее, чем Platform Channels?

Надеюсь со временем найти ответы на эти вопросы, а если у меня получится что-то крутое, обязательно поделюсь с вами результатом ❤️
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍7💅52🔥1
Слабая аура: искать инфу самостоятельно

Сильная аура: задать вопрос команде флаттера ✔️

Продолжая тему нативного интеропа с ffigen, воспользовалась возможностью задать вопрос команде флаттера в комментариях на ютубе 😘

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

Еще тизернули что скоро выйдет эпизод Flutter Build Show на эту тему. Посмотрим, что нам интересного расскажут 💃
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥133💅3
Выпал первый снег ❄️

Я/мы: 🎄 ❄️ 🎅
Please open Telegram to view this post
VIEW IN TELEGRAM
1💅53💘3👎2