Flutter Pulse
614 subscribers
395 photos
868 links
На канале будут новости про flutter с сайтов, информация об обновлении пакетов, а также авторский контент.
Download Telegram
Эффективная передача стиля текста виджетам

Привет, разработчики Flutter! 👋 Сегодня мы поделимся с вами полезным советом о том, как эффективно передавать стиль текста вашим виджетам. 📝

Вы когда-нибудь сталкивались с ситуацией, когда приходилось копировать стиль текста для дочерних элементов? 🤔 Теперь вы можете этого избежать! 😉

Использование DefaultTextStyle.merge


class MyWidget extends StatelessWidget {
final Widget title;

const TheBestCustomWidget({
super.key,
required this.title,
});

@override
Widget build(BuildContext context) {
return WidgetContainer(
children: [
DefaultTextStyle.merge(
style: Theme.of(context).textTheme.headlineMedium,
child: title,
),
],
);
}
}



MyWidget(
title: Text(
'Signup now',
style: Theme.of(context).textTheme.headlineLarge.copyWith(
color: Theme.of(context).colorScheme.onBackground,
),
),
)



MyWidget(
title: Text(
'Signup now',
style: TextStyle(
color: Theme.of(context).colorScheme.onBackground,
),
),
)


Преимущества использования DefaultTextStyle.merge:

• Позволяет задать только те свойства текста, которые вы хотите переопределить при использовании вашего виджета.
• Упрощает код и делает его более читаемым.

👍 Оцените новую рубрику FlutterPulseTips и поделитесь своими мыслями! 🤔

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

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDevelopment #UIUX #CodingTips #AppDevelopment
👍1
Как протестировать Изолят
Запуск функции изолята в модульных тестах

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

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


testWidgets('upload file and save avatar', (tester) async {
final file = await rootBundle.load('assets/images/splashscreen.png');
final bytes = file.buffer.asUint8List();
final xfile = XFile.fromData(bytes);

final jpgData = await compute(_avatarThumbnail, file);
}

// Функция, выполняющаяся в изоляте
Future<Uint8List> _avatarThumbnail(XFile file) {
return file.toJpeg(file, 300, 80);
}


Такой тест зависнет и никогда не завершится, поскольку функция compute запускает _avatarThumbnail в изоляте, но тест не ожидает завершения изолята.

Решение: Используйте tester.runAsync() для запуска асинхронного кода в тесте:


testWidgets('upload file and save avatar', (tester) async {
await tester.runAsync(() async {
final file = await rootBundle.load('assets/images/splashscreen.png');
final bytes = file.buffer.asUint8List();
final xfile = XFile.fromData(bytes);

final jpgData = await compute(_avatarThumbnail, file);
});
});

// Функция, выполняющаяся в изоляте
Future<Uint8List> _avatarThumbnail(XFile file) {
return file.toJpeg(file, 300, 80);
}


Таким образом, вы сможете корректно протестировать функции, выполняющиеся в изоляте.

Оцените новую рубрику и напишите своё мнение! 👍💬

Все подобные новости можно найти по хэштегу #FlutterPulseTips
#flutter #dart #flutterpulse #FlutterPulseTips #MobileDevelopment #Testing #Isolate #FlutterTips #DartTips
👍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
Создаём кастомную панель приложений
Потому что вы можете 😉

Вы можете создать кастомную 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
👍5
Локальная база данных
Когда вы хотите, чтобы ваше приложение работало офлайн 🔄

Шаг 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
Сделайте текст выбираемым

По умолчанию текст не является выбираемым. 🤔

Почему? Виджет SelectionArea позволяет выбирать текст, указывая Flutter обрабатывать отрисовку и взаимодействие для выбора текста. 📝


return MaterialApp(
home: SelectionArea(
child: Scaffold(
appBar: AppBar(title: const Text('SelectionArea Sample')),
body: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Row 1'),
Text('Row 2'),
Text('Row 3'),
],
),
),
),
),
);


ИЛИ 🔄


const SelectableText(
'Hello! How are you?',
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.bold),
)


Используйте SelectableText вместо Text 🔁

Как выбрать между SelectionArea и SelectableText? 🤔
Используйте selectionArea, если вы хотите включить выбор на нескольких виджетах, а не только на тексте. 📚
Для одного текста просто используйте SelectableText 👍

Оцените новую рубрику и напишите своё мнение! 💬

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

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDevelopment #UIUX #CodingTips #AppDevelopment #FlutterTips
6👍1
🚀 Расширяем возможности Flutter с помощью FFI: бинарники и компиляция под капотом!

🔥 Готовы погрузиться в мир нативной магии? В новом видео от эксперта Руслана Цицера — ключевые секреты интеграции C/C++ библиотек во Flutter через FFI! Узнайте, как избежать подводных камней и заставить "железо" работать на вас.

👉 СМОТРЕТЬ ВИДЕО 👈

💡 В этом выпуске:
🔧 Две главные проблемы FFI и их элегантные решения:
1️⃣ Выбор компилятора — почему для iOS, Android и macOS нужны разные инструменты и как их настроить
2️⃣ Интеграция бинарников — куда поместить скомпилированные библиотеки, чтобы Flutter их "увидел"

📦 Практические примеры:
- Работа с Makefile и скриптами сборки
- Особенности подключения под Android (jniLibs) и iOS (Framework + Info.plist)
- Автоматизация переноса бинарников

🚨 Внимание, лайфхак! Для iOS показан работающий метод подключения через "кустарные" CocoaPods — минимум конфигурации, максимум результата!

🔗 Ресурсы:
- GitHub с примерами кода

💬 "Если вы работаете с нативным кодом — это видео сэкономит вам часы поисков!"

👍 Не пропустите! Узнайте, как:
- Собрать универсальные бинарники под любую платформу
- Избежать ошибок ABI-совместимости
- Оптимизировать процесс сборки

👉 Подписывайтесь на канал, жмите 🔔 колокольчик, чтобы не пропустить новые выпуски! Ваши лайки и комментарии — лучшая мотивация для автора 💙

И про реакции на нашем канале тоже не забывайте!) 👍

#Flutter #Dart #FlutterPulse #FlutterPulseTips #FlutterPulseYoutube #FFI #Rust #NativeCode #MobileDevelopment
2
Вложенная навигация с Go_Router
Давайте создадим боковую панель с Flutter Web и go_router 🤔

Вместо того, чтобы напрямую добавлять все маршруты (GoRoute), мы обернём их в поднавигацию 📦

Мы обернём все подмаршруты в StatefulShellRoute, чтобы управлять состоянием поднавигации 🔄

Для каждого подмаршрута у нас будет StatefulShellBranch, который может содержать несколько маршрутов 🌐

Каждый элемент будет иметь свой собственный стек навигации 📚



import 'package:go_router/go_router.dart'; // Импорт библиотеки go_router

GoRouter generateRouter() {
return GoRouter(
routes: [
// Страница без боковой панели
GoRoute(
name: 'signin',
path: '/signin',
builder: (context, state) => const SignInPage(),
),
// Страница с боковой панелью
StatefulShellRoute(
parentNavigatorKey: navigatorKey, // Ключ навигатора родительского маршрута
// Боковая панель будет отображаться слева
builder: (context, state, navigationShell) => Row(
children: [
SideBar(state: state), // Боковая панель
Expanded(child: navigationShell), // Содержимое навигации
],
),
// builder будет вызван, когда маршрут активируется
// navigationShell - виджет, который отображает содержимое маршрута
// Он будет показывать текущую последнюю страницу стека навигации для каждой ветки
navigatorContainerBuilder: (
BuildContext context,
StatefulNavigationShell navigationShell,
List<Widget> children,
) {
if (children.isEmpty) {
return SizedBox(); // Пустой контейнер, если нет дочерних элементов
}
return Scaffold(
body: children[navigationShell.currentIndex], // Отображение текущего дочернего элемента
);
},
branches: [
StatefulShellBranch(
routes: [
GoRoute(
name: 'users',
path: '/users',
builder: (context, state) => const UsersPage(),
),
GoRoute(
name: 'user profile',
path: '/users/:userId',
builder: (context, state) => const UserProfilePage(),
),
],
),
StatefulShellBranch(
routes: [
GoRoute(
name: 'notifications',
path: '/notifications',
builder: (context, state) => const NotificationsPage(),
),
],
),
],
),
],
);
}



Оцените новую рубрику 👍👏! Все подобные новости можно найти по хэштегу #FlutterPulseTips. Не забудьте подписаться и следить за новыми советами 🔔

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDevelopment #FlutterTips #GoRouter #NestedNavigation #FlutterWeb
👍4👨‍💻1