Flutter Pulse
607 subscribers
376 photos
826 links
На канале будут новости про flutter с сайтов, информация об обновлении пакетов, а также авторский контент.
Download Telegram
🚀 Расширяем возможности Flutter с помощью FFI и Rust: Глубокое погружение в управление памятью!

🔥 Хотите выжать максимум из Flutter, подружив его с низкоуровневыми языками? В новом видео от эксперта Руслана Цицера — ключ к мощным оптимизациям!

📚 Что вас ждет:
🛠 Разбор Ownership в Rust — как система владения предотвращает утечки памяти и гарантирует безопасность.
🔧 Практика работы с unsafe операциями и добавление зависимостей в Rust-проекты.
⚙️ Компиляция Rust-кода в C-библиотеку для интеграции с Dart.
💡 Конвертация данных между Dart и Rust (строки, указатели) без ошибок.
🚫 Важные нюансы управления памятью при работе с FFI.

👉 Смотрите видео здесь:
Расширяем возможности Flutter с помощью FFI. Rust

👀 Уже в следующих выпусках — запуск Flutter-приложений с FFI! Не пропустите практические примеры.

👍 Поддержите Руслана: подписывайтесь на его канал на youtube, ставьте лайк и жмите 🔔!

Хотите больше полезного?
Подписывайтесь на Flutter Pulse и ищите другие крутые туториалы по хэштегу:
#FlutterPulseYoutube

#Flutter #Dart #FFI #Rust #FlutterPulse #FlutterPulseTips #CleanCode #FlutterArchitecture #StatefulWidget #fluttertutorial
🤔1💩1
Глупый и умный: создаём "глупый" конструктор и умные фабрики

Привет, друзья! 👋 Сегодня мы поговорим о том, как улучшить ваш код на Flutter/Dart, используя "глупые" конструкторы и умные фабрики. 📈

Что такое "глупый" конструктор?
"Глупый" конструктор - это конструктор, который только присваивает данные. Он не должен делать ничего другого! 🚫

Пример плохого конструктора:

class Device {
String? id;
String? name;
OperatingSystem? platform;

Device({
this.id,
}) :
// Плохая практика - присвоение значений в теле конструктора
name = null,
platform = null {
final deviceInfo = ...; // Получение информации об устройстве
name = deviceInfo.name;
platform = deviceInfo.platform;
}
}


Пример хорошего "глупого" конструктора:

class Device {
String? id;
String name; // Теперь обязательное поле
OperatingSystem platform; // Теперь обязательное поле

Device({
this.id,
required this.name, // Требуем имя устройства
required this.platform, // Требуем платформу устройства
});
}


Зачем использовать фабрики?
Фабрики идеально подходят для более сложных присвоений значений. Они позволяют создавать объекты более гибко и читаемо. 🌟

Пример фабрики:

factory Device.current() {
final deviceInfo = ...; // Получение информации об устройстве
return Device(
name: deviceInfo.name, // Присваиваем имя устройства
platform: deviceInfo.platform, // Присваиваем платформу устройства
);
}


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

Оцените нашу новую рубрику! 👍 Нам важно ваше мнение. Оставляйте свои комментарии и предложения. 💬

Все подобные новости вы можете найти по хэштегу #FlutterPulseTips

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDevelopment #CodingTips #Programming #SoftwareDevelopment
👍31
Избегайте тестирования с помощью моков
Моки отражают вашу реализацию

Лучшие тесты не отражают вашу реализацию.
Они позволяют рефакторить код, не задумываясь о том, как они работают.
Вы тестируете то, что они возвращают, а не как они это делают.
Таким образом, вы можете рефакторить код, пока они продолжают работать.



test('on receive message, should dispatch as a notification', () async {
final repository = AppNotificationsRepository(
notificationsApi: fakeNotificationsApi,
notificationPublisher: dispatcher,
);

Notification? receivedNotification;
dispatcher.subscribe((notification) => receivedNotification = notification);
fakeNotificationsApi.sendForegroundMessage(
const RemoteMessage(
data: {
'title': 'title',
'body': 'example body',
},
),
);
await Future.delayed(const Duration(milliseconds: 100));
expect(receivedNotification, isNotNull);
expect(receivedNotification!.title, 'title');
expect(receivedNotification!.body, 'example body');
});





class FakeNotificationsApi implements NotificationsApi {
OnRemoteMessage? _foregroundHandler;

@override
void setForegroundHandler(OnRemoteMessage handler) {
_foregroundHandler = handler;
}

void sendForegroundMessage(RemoteMessage message) {
_foregroundHandler?.call(message);
}
}



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

👋 Прощай, mockito!

Оцените новую рубрику и напишите своё мнение! 👍
Все подобные новости можно найти по хэштегу #FlutterPulseTips
#flutter #dart #flutterpulse #FlutterPulseTips #MobileDevelopment #TestingTips #CodingBestPractices
💯2
Добавьте кастомные переходы страниц с GoRouter

Переходы между страницами важны для пользовательского опыта

Добавьте этот небольшой помощник, чтобы определить кастомный переход страницы:


Page<dynamic> Function(BuildContext, GoRouterState) buildPageTransition(
Widget child,
) => (BuildContext context, GoRouterState state) {
return CustomTransitionPage(
key: state.pageKey,
child: child,
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
FadeThroughTransition( // можно создать свой собственный переход или использовать из пакета animations на pub.dev
animation: animation,
secondaryAnimation: secondaryAnimation,
child: child,
),
);
};


Теперь оберните свою страницу в переход, используя pageBuilder для вашего маршрута:


GoRoute(
path: '/signin',
builder: (context, state) => const SigninPage(),
pageBuilder: buildPageTransition(const SigninPage()),
),


Вы также можете настроить стандартный переход страницы прямо в вашей теме:


pageTransitionsTheme: PageTransitionsTheme(
builders: {
TargetPlatform.android: const ZoomPageTransitionsBuilder(),
TargetPlatform.iOS: const CupertinoPageTransitionsBuilder(),
},
),


Оцените нашу новую рубрику! 👍💡
Все подобные новости можно найти по хэштегу #FlutterPulseTips
#flutter #dart #flutterpulse #FlutterPulseTips #MobileDev #AppDev #UIUX #FlutterTips
👍5
Создание последовательных анимаций
Создайте пользовательскую цепочку эффектов, чтобы повторно использовать ее во всем приложении с помощью flutter_animate.

Создайте виджет с Animate и списком эффектов


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

class MoveFadeAnim extends StatelessWidget {
final int? delayInMs;
final Widget child;

const MoveFadeAnim({
super.key,
required this.child,
this.delayInMs,
});

@override
Widget build(BuildContext context) {
return Animate(
effects: [
// Эффект затухания с задержкой и продолжительностью 700 мс
FadeEffect(
delay: Duration(milliseconds: delayInMs ?? 0), // Задержка перед началом анимации
duration: const Duration(milliseconds: 700), // Продолжительность анимации
curve: Curves.easeIn, // Кривая анимации для эффекта затухания
),
// Эффект перемещения с задержкой и продолжительностью 450 мс
MoveEffect(
delay: Duration(milliseconds: delayInMs ?? 0), // Задержка перед началом анимации
duration: const Duration(milliseconds: 450), // Продолжительность анимации
curve: Curves.easeOut, // Кривая анимации для эффекта перемещения
begin: const Offset(0, 50), // Начальное смещение
end: Offset.zero, // Конечное смещение (нет смещения)
),
],
child: child, // Дочерний виджет, к которому применяются эффекты
);
}
}


Повторно используйте свою анимацию везде в приложении для последовательного поведения анимации


return MoveFadeAnim(
delayInMs: index * 150 + 50, // Вычисление задержки на основе индекса элемента
child: MenuCard(
height: 130, // Высота карточки меню
),
);


Оцените новую рубрику! 👍💡
Все подобные новости можно найти по хэштегу #FlutterPulseTips
#flutter #dart #flutterpulse #FlutterPulseTips #MobileAppDevelopment #Animation #UIUX #FlutterTips
👍2
🔥 Он вам не просто логер! Почему Talker нужен вашему Flutter проекту?

Привет, Flutter-разработчики! Готовы сэкономить часы, дни и даже месяцы на отладке? Мы нашли для вас 🔥горячее видео, которое перевернет ваш подход к работе с ошибками!

Стас (Frezyx) на Flutter Conf в Москве устроил настоящий мастер-класс по своей open-source библиотеке Talker. И знаете что? Даже режиссёр трансляции признался: "Это единственный доклад, который я понял!" 😄

👇 Почему это must-watch?
🚀 Talker — не просто логер. Это "губка", которая впитывает ВСЁ:
- Ошибки виджетов, сети, платформы
- HTTP-запросы, навигацию, стейт-менеджмент
- Кастомные события (даже аналитику!)

💡 Главный секрет: история в runtime! Представьте:
Тестировщик присылает не "у меня сломалось", а готовый файл логов
На проде за 2 минуты находите корень проблемы (и вините бэкендеров 😉)
Показываете ошибки через SnackBar, модалки или даже свой UI

🎁 Что ещё внутри:
- Модульность: подключайте только нужное (Bloc, Dio, Riverpod!)
- Кастомизация логов: от цветов 🎨 до формата
- Интеграция с Firebase, Sentry, Grafana
- Готовый экран логов в приложении

👉 Смотрите запись выступления здесь или тут — без воды, с мемами и реальными кейсами!

Стас показал, как Talker, получивший грант Яндекса (600К ₽ 💰) и скачанный 320К+ раз, делает жизнь разработчика проще. Не верите? Проверьте сами:
📦 pub.dev: talker
🐙 GitHub: Frezyx/talker

💬 P.S. Уже пробовали Talker? Делитесь опытом в комментариях! А если нет — самое время начать. Ваши выходные без дебаггинга начинаются здесь 👇

#Flutter #Dart #FlutterPulse #FlutterPulseTips #FlutterPulseYoutube
Подписывайтесь → @FlutterPulse
Ещё крутые видео → #FlutterPulseYoutube
Создаём кастомную панель приложений
Потому что вы можете 😉

Вы можете создать кастомную AppBar, реализовав интерфейс PreferredSizeWidget. Это даст вам гибкость в настройке панели приложений под нужды вашего приложения.



class EditorAppBar extends StatelessWidget implements PreferredSizeWidget {
const EditorAppBar({
super.key,
});

@override
Widget build(BuildContext context) {
return Row(...); // Здесь вы можете настроить свой собственный дизайн
}

@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
// Используйте эту константу для рекомендуемой высоты
}



Чтобы использовать эту кастомную AppBar, просто передайте её в свойство appBar виджета Scaffold:


child: Scaffold(
appBar: EditorAppBar(),
...
),



Оцените нашу новую рубрику по Flutter советам 👍! Все подобные новости можно найти по хэштегу #FlutterPulseTips
#flutter #dart #flutterpulse #FlutterPulseTips #MobileDevelopment #CustomAppBar #AppBarDesign #FlutterTips
👍31
Создай расширение темы
Упростите доступ к свойствам темы вашего приложения 💻

Расширение темы позволяет упростить доступ к свойствам темы вашего приложения. Для этого нужно создать расширение класса BuildContext.



extension ApparenceKitThemeExt on BuildContext {
ApparenceKitColors get colors => Theme.of(this).extension<ApparenceKitColors>()!;
// Получение цветов темы
TextTheme get textTheme => Theme.of(this).textTheme;
// Получение текстовой темы
ApparenceKitTextTheme get fonts => Theme.of(this).extension<ApparenceKitTextTheme>()!;
// Получение шрифтов темы
ThemeData get theme => Theme.of(this);
// Получение данных темы
Brightness get brightness => Theme.of(this).brightness;
// Получение яркости темы
ApparenceKitThemeData get kitTheme => ThemeProvider.of(this).current.data;
// Получение данных темы ApparenceKit
}



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

До: сложный доступ к свойствам темы


class MyWidget extends StatelessWidget {
const MyWidget({super.key});

@override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).colorScheme.primary,
);
}
}



После: упрощенный доступ с расширением


class MyWidget extends StatelessWidget {
const MyWidget({super.key});

@override
Widget build(BuildContext context) {
return Container(
color: context.colors.primary,
);
}
}



Оцените новую рубрику по достоинству! 👍💬 Оставляйте ваши отзывы и предложения в комментариях! 💬👇

Все подобные новости можно найти по хэштегу #FlutterPulseTips

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDevelopment #UIUX #CodingTips #AppDevelopment #SoftwareDevelopment
👍4
Принудительный портретный режим
Когда вы в последний раз использовали приложение в ландшафтном режиме? 🤔

Фиксация ориентации экрана в Flutter
Чтобы заставить приложение работать только в портретном режиме, добавьте следующий код в функцию main() перед запуском приложения:


SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp, // Портретная ориентация вверх
DeviceOrientation.portraitDown, // Портретная ориентация вниз
]);


Теперь приложение не сможет перейти в ландшафтный режим 📱💻

Результат:
Приложение останется в портретном режиме 👍

Оцените нашу новую рубрику FlutterPulseTips! 👍 Ваши отзывы помогут нам улучшать контент. 💬

Все подобные новости можно найти по хэштегу #FlutterPulseTips

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDev #AppDev #ProgrammingTips #UIUX #DevTips
👍3
Загрузка байтов изображения и отображение на холсте
Не так сложно сделать с помощью Flutter 😉

Вы когда-нибудь задумывались, как можно загрузить изображение и отобразить его на холсте в Flutter? 🤔 Давайте разберемся в этом вместе! 💡

Шаг 1: Загрузка изображения
Сначала нам нужно загрузить изображение из наших ресурсов. Для этого мы используем метод load класса rootBundle:


import 'dart:ui' as ui;
import 'package:flutter/services.dart';

// Загружаем изображение из ресурсов
final ByteData data = await rootBundle.load(path);
// Преобразуем в Uint8List
final Uint8List bytes = data.buffer.asUint8List();
// Преобразуем в ui.Image
final ui.Image image = await decodeImageFromList(bytes);



Шаг 2: Отображение на холсте
Теперь, когда у нас есть изображение в формате ui.Image, мы можем отобразить его на холсте. Для этого создадим собственный класс CustomPainter:


// Создаем новый CustomPainter
class MyPainter extends CustomPainter {
final ui.Image image;

MyPainter(this.image);

@override
void paint(Canvas canvas, Size size) {
// Рисуем изображение на холсте
canvas.drawImageRect(image, srcRect, dstRect, Paint());
}
}



Вот и все! Теперь вы знаете, как загрузить байты изображения и отобразить их на холсте во Flutter. Просто и эффективно, не правда ли? 😊

Оцените нашу новую рубрику и оставьте свои комментарии! 👇

Все подобные новости можно найти по хэштегу #FlutterPulseTips
#flutter #dart #flutterpulse #FlutterPulseTips #mobiledev #appdev #codingtips #FlutterTips
👍3🔥1
Локальная база данных
Когда вы хотите, чтобы ваше приложение работало офлайн 🔄

Шаг 1: Использование пакета Drift
Для работы с локальной базой данных мы будем использовать пакет Drift. 📦


dart pub add drift
dart pub add drift_flutter
dart pub add drift_dev



Шаг 2: Создание базы данных


@DriftDatabase(tables: [TaskTable])
class Database extends $Database {
Database([QueryExecutor? e]) : super(e ?? driftDatabase(
name: 'todo-app',
native: const DriftNativeOptions(),
databaseDirectory: getApplicationSupportDirectory,
));

@override
int get schemaVersion => 2; // Версия базы данных

@override
MigrationStrategy get migration {
return MigrationStrategy(
onCreate: (m) async {
await m.createAll();
// Добавьте миграции здесь, если версия новая
},
);
}
}



Шаг 3: Создание таблицы


@DataClassName('TaskEntry')
class TaskTable extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get description => text()();

// Добавьте ваши запросы здесь
static Stream<List<TaskEntry>> getAllItems(Database database) =>
database.select(database.taskTable).watch();
}



Шаг 3: Создание или редактирование build.yaml
в корневой папке вашего Flutter-приложения 📁


targets:
$default:
builders:
drift_dev:
# Эти опции изменяют способ генерации кода
options:
databases:
default: lib/modules/drift/database.dart
sql:
dialect: sqlite
options:
version: "3.38"
modules: [fts5]


Запустите сборщик, чтобы регенерировать схему базы данных 🔄

Оцените новую рубрику! 👍💬
Все подобные новости можно найти по хэштегу #FlutterPulseTips
#flutter #dart #flutterpulse #FlutterPulseTips #MobileDevelopment #LocalDatabase #DriftPackage #FlutterTips #AppDevelopment
👍2🔥1