Flutter Friendly
754 subscribers
121 photos
53 videos
1 file
109 links
Канал Friflex о разработке на Flutter. Обновления, плагины, полезные материалы — превращаем знания в реальный опыт, доступный каждому разработчику.

🔗 Наш канал для разработчиков: @friflex_dev
🔗 Канал о продуктовой разработке: @friflex_product
Download Telegram
This media is not supported in your browser
VIEW IN TELEGRAM
Привет, это Екатерина. Я Flutter-разработчик в команде Friflex.

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

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

Кажется, что было бы здорово использовать его в проекте. Добавляем следующие зависимости в pubspec:
dependencies:
flutter:
sdk: flutter

# Для получения данных о погоде
http: ^1.2.2
# Для иконок погоды
weather_icons: ^3.0.0


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

🔴Библиотека содержит в себе более 200 ассетов, когда нам нужны условные 15

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

🔴Пакет обновлялся в последний раз больше года назад, а это всегда риск остановки поддержки библиотеки и последующего рефакторинга приложения

Как быть?
Свести зависимость от чужих решений в проекте к минимуму. Особенно если речь идет о решении небольших задач, которые можно сделать самостоятельно.

В нашем примере можно убрать библиотеку для иконок и добавить ассеты в проект в том количестве, в котором они нужны именно в нем.
dependencies:
flutter:
sdk: flutter
# Для получения данных о погоде
http: ^1.2.2

flutter:
assets:
- assets/icons/


Итог: подстраиваем инструменты под приложение, а не создаем приложение под инструменты.

💬 Делитесь, какие паттерны в разработке на Flutter — личная боль?
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍5👏2
Завтра идем на @crossconf — самую большую конференцию по кроссплатформенным технологиям!

Программа потока Flutter — на карточках. Если напишете, какой доклад особенно заинтересовал, можем рассказать подробнее о каждом в следующих постах👇
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
8👍5🔥3
Media is too big
VIEW IN TELEGRAM
🎙Провели время на CrossConf @crossconf максимально полезно.

В перерывах между докладами поймали Стаса Ильина, Lead Flutter developer и автора канала @frezycode. Поставили его перед сложным выбором — Flutter или Kotlin. Шутка… Все вопросы, конечно, были про Flutter. Ответы — в нашем ролике 🖱
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍43👨‍💻1
This media is not supported in your browser
VIEW IN TELEGRAM
#пакет_недели — Hugeicons. Коллекция из 4000+ иконок для мобильных приложений.

Главное о пакете Hugeicons
🔸Он предлагает иконки в разных стилях и вариантах. Например, rounded или sharp.

🔸Он позволяет изменять цвет и размер иконок, а также анимировать их.

🔸Он дает настраивать иконки по размеру и масштабировать их под любые экраны.

🔸 Он относительно легкий по весу. Это круто, потому что не перегружает приложение.

🔸Иконки загружаются и компилируются вместе с приложением. Это удобно для приложений, которые работают автономно.

Как использовать
Импортируем пакет и вызываем иконку с помощью виджета HugeIcon.
import 'package:hugeicons/hugeicons.dart';

HugeIcon(
icon: HugeIcons.strokeRoundedHome01, // Выбор иконки
color: Colors.red, // Цвет иконки
size: 30.0, // Размер иконки
),
icon:


HugeIcons.strokeRoundedHome01 — здесь указываем иконку, которая будет отображаться. У нас — дом с закругленными углами.

color: Colors.red задает цвет иконки. Наш дом будет красным.

size: 30.0 задает размер иконки в пикселях.

❗️Если у вас простой проект с минимальным набором иконок, Hugeincons может оказаться слишком массивным.

🔥Мне это нужно
🎨Рисую иконки в Paint с душой
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥52👍2👎2🎄1
This media is not supported in your browser
VIEW IN TELEGRAM
Привет, это Екатерина, Flutter-разработчик в команде Friflex

Сегодня расскажу еще про один инструмент, который может принести больше проблем, чем пользы — shrinkWrap: true.

Если вы сталкивались с проблемой неограниченного размера ListView, то могли наткнуться на советы установить этот виджет. Это и правда решит проблему красного экрана:
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: [
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: 10000,
itemBuilder: (_, __) => const Text('Hello World!'),
),
],
),
);

}


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

Как сделать лучше?
Самый простой способ — использовать Expanded или Flexible.
@override
Widget build(BuildContext context) {
return Column(
children: [
Flexible(
child: ListView.builder(
itemCount: 10000,
itemBuilder: (_, __) => const Text('Hello World!'),
),
),
],
);

}


❗️Если проблема возникла из-за использования ListView внутри другого ScrollView (к примеру, ListView внутри SingleChildScrollView), то поможет замена этих виджетов на CustomScrollView и SliverList.
Please open Telegram to view this post
VIEW IN TELEGRAM
14👍3🔥2🤔1💯1🤝1
💳Запускаем серию постов по созданию Flutter-приложения для оплаты через СБП без натива

Вспомним структуру. Обычная ссылка для оплаты отправляет пользователя на сайт СБП, где из нее формируется QR-код, который нужно сканировать. Это не очень удобно, потому что код нужно сканировать в приложении банка. После этого открывается окно оплаты и появляются данные о приемщике платежа.

Если мы возьмем поле schema из этой ссылки со списком банков и вставим его в ссылку оплаты вместо https, то получим диплинк, который сразу будет открывать приложение нужного банка.

Переходим к созданию приложения:

Шаг 1
Получаем список банков.

Шаг 2
Формируем список банков. Для этого достаточно трех полей: bankName, logoURL и schema.

Шаг 3
Вставляем эти поля в объект. Создаем список, в котором отображаются все банки на экране.

Шаг 4
Меняем в ссылках оплаты https на schema соответствующего банка и открываем через url_launcher.

Шаг 5
Прослушиваем жизненный цикл приложения с помощью AppLifecycleListener. Используем коллбек onRestart, в нем ставим флаг. Второй флаг ставим после перехода в приложение банка.

Шаг 6
После возвращения из приложения банка отправляем запрос на проверку статуса оплаты.

В проекте нам понадобится всего два пакета. В pubspec.yaml указываем:
dependencies: 
http: any
url_launcher: any


В следующих постах расскажем о каждом из шагов подобнее.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥25👍82
This media is not supported in your browser
VIEW IN TELEGRAM
#пакет_недели — Patrol. Он помогает писать интеграционные тесты. Например, такие, которые требуют взаимодействия с настоящими устройствами или эмуляторами.

Главное о Patrol
🔴Он строится на основе flutter_test и integration_test.

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

🔴Его нужно использовать вместе с patrol_cli.

Как использовать
Две главные функции Patrol — нативная автоматизация и кастомный поиск. Здесь мы разберем только первую, а подробно про кастомный поиск можно почитать в документации.

Используем Patrol в тесте, который проверяет, как ведет себя приложение, когда взаимодействует с кнопками Home, Recent Apps и панелью уведомлений.
import 'package:example/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:patrol/patrol.dart';

void main() {
patrolTest(
'состояние счетчика сохраняется после перехода на домашний экран и возврата',
($) async {
await $.pumpWidgetAndSettle(const MyApp());

await $(FloatingActionButton).tap();
expect($(#counterText).text, '1');

await $.native.pressHome();
await $.native.pressDoubleRecentApps();

expect($(#counterText).text, '1');
await $(FloatingActionButton).tap();
expect($(#counterText).text, '2');

await $.native.openNotifications();
await $.native.pressBack();
},
);
}

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

$.native.pressHome() — эмулирует нажатие кнопки Home. Очень полезно, чтобы проверить, как ведет себя приложение, когда его сворачивают.

$.native.pressDoubleRecentApps() — имитирует двойное нажатие на кнопку списка последних приложений (Recent Apps). Можно использовать, чтобы проверить, как ведет себя приложение при возврате из других программ.

$.native.openNotifications() — эмулирует открытие панели уведомлений. Позволяет проверить, как приложение реагирует на системные события и взаимодействие с другими элементами ОС.

❤️ Это мне надо
🤷‍♂️ Нажму кнопки «Назад» и «Домой» руками
Please open Telegram to view this post
VIEW IN TELEGRAM
👍64🔥1
Создаем Flutter-приложение для оплаты через СБП

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

🔸Создаем объект для полей bankName, logoURL и schema. (Для безопасного парсинга данных рекомендуем использовать паттерн проектирования DTO):

```dart
class BankItem {
const BankItem({
required this.bankName,
required this.logoURL,
required this.schema,
});

final String bankName;
final String logoURL;
final String schema;

static BankItem? fromJson(Map<String, dynamic> json) {
final bankName = json['bankName'] as String?;
final logoURL = json['logoURL'] as String?;
final schema = json['schema'] as String?;

if (bankName == null || logoURL == null || schema == null) {
return null;
}

return BankItem(
bankName: utf8.decode(bankName.codeUnits),
logoURL: logoURL,
schema: schema,
);
}
}
```

*Здесь используем utf8 из-за проблем с кодировкой. Если у банка нет схемы, имени или картинки, не считаем его валидным.

🔸Создаем абстрактный репозиторий SbpRepository для управления запросами СБП и реализуем метод getBankList в NewtorkSbpRepository, чтобы получить список банков-участников СБП:
class NewtorkSbpRepository implements SbpRepository {
Future<Iterable<BankItem>> getBankList() async {
try {
final response = await http.get(
Uri.parse('https://qr.nspk.ru/proxyapp/c2bmembers.json'),
);
final decodedMap = jsonDecode(response.body) as Map<String, dynamic>;
final rawBanks = decodedMap['dictionary'] as List;

final banks = rawBanks.map((e) => BankItem.fromJson(e)).whereNotNull();

return banks;
} catch (error, trace) {
/// Добавить обработку особых ошибок
rethrow;
}
}
}

Реализованный метод можно вызывать на любом этапе бизнес-логики, где нужно получить список банков.

Для управления состояниями можно использовать любой стейт-менеджер, который вам нравится (например flutter_bloc, riverpod).

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

🔸Создаем экран для оплаты, который будет при инициализации запрашивать список банков из репозитория:


class SbpPayScreen extends StatefulWidget {
const SbpPayScreen({super.key});

@override
State<SbpPayScreen> createState() => _SbpPayScreenState();
}

class _SbpPayScreenState extends State<SbpPayScreen> {
late final Future<Iterable<BankItem>> bankList;

@override
void initState() {
bankList = SbpController.of(context).repository.getBankList();

super.initState();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Оплата СБП'),
),
body: FutureBuilder(
future: bankList,
builder: (context, snapshot) {
if (snapshot.hasData) {
final data = snapshot.data!;
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) => ListTile(
title: Text(data.elementAt(index).bankName),
),
);
}
if (snapshot.hasError) {
return const Text('Произошла ошибка');
}
return const Center(child: CircularProgressIndicator());
},
),
);
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥87👍5
Как оперативно проверить приложение в AppStore?

Своим способом поделился Сергей, Team Lead Flutter-команды Friflex. Если нужно быстро выпустить обновление приложения (например, чтобы исправить критическую ошибку или добавить сборку с важным событием), можно воспользоваться процедурой срочной проверки.

Как действовать:

1️⃣ Выгрузите сборку приложения на ревью в App Store.

2️⃣ Отправьте запрос на срочную проверку. Для этого используйте форму по ссылке.

3️⃣ Дождитесь ответа. Обычно это занимает от 15 минут до 4 часов.

4️⃣ Радуйтесь одобрению.

P.S не балуемся этим способом слишком часто. Злоупотребление может привести к отказам в срочной проверке в будущем.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥42👌1
This media is not supported in your browser
VIEW IN TELEGRAM
#пакет_недели — font_awesome_flutter. Это обертка для популярного набора иконок Font Awesome.

Главное о font_awesome_flutter
➡️Он меняет основные параметры иконок, такие как размер и цвет.
➡️Он применяет разные стили: solid, regular, light, и brands. В Pro версии стилей еще больше, включая duotone.
➡️Он поддерживает иконки брендов. Например, логотипы социальных сетей или известных компаний.

Как использовать
Добавляем зависимость и используем все стандартные иконки с помощью виджета Falcon:
import 'package:font_awesome_flutter/font_awesome_flutter.dart';

IconButton(
icon: FaIcon(FontAwesomeIcons.thumbsUp), // Иконка "пальцы вверх"
onPressed: () {
print("Liked!");
},
),

Настраиваем иконки, задаем цвет и стили:
FaIcon(

FontAwesomeIcons.heart,  // Иконка "сердце"
color: Colors.red,
size: 50.0,
),

Можно исключить ненужные стили иконок из процесса генерации, чтобы уменьшить размер пакета:
$ ./configurator.sh --exclude solid
$ ./configurator.sh --exclude solid,brands

Иконки можно получить динамически по их имени — используйте faIconNameMapping.
import 'package:font_awesome_flutter/name_icon_mapping.dart';

...
FaIcon(faIconNameMapping['solid abacus']);
...

🔥 Беру в работу
🤔 Слишком заморочено
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥32🤔2🥰1
Открываем рубрику #flutter_heroe — про людей из сообщества, за которыми интересно и полезно следить каждому, кто пишет на Flutter. Сегодня расскажем про Реми Русселе. Пользуетесь пакетами riverpod или provider? Это он их автор.

Вклад
🔴riverpod и provider: Реми разработал и поддерживает два из самых популярных пакетов для управления состоянием. Если provider стал стандартом для новичков, то riverpod предложил более гибкий и мощный подход.

🔴flutter_hooks: Этот пакет стал отличным инструментом для сокращения количества шаблонного кода и улучшения организации Flutter-компонентов.

Совет начинающим
«Не читайте слишком много кода. Сначала пишите код. Вы становитесь лучше не от чтения книг, а от практики. Но обязательно читайте исходный код того, что используете. Не до конца понимаете, как что-то работает? Прочтите его исходный код»

Где следить
🔗 Github
🔗 X
🔗 Stack Overflow
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥43
This media is not supported in your browser
VIEW IN TELEGRAM
#пакет_неделиgoogle_sign_in. Он помогает аутентифицировать пользователей через их аккаунт Google в приложениях на Flutter.

Главное о google_sign_in
🔸Он использует OAuth 2.0 для аутентификации и управления сессиями. Это позволяет безопасно идентифицировать пользователей и получать доступ к их данным (с их разрешения).

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

🔸Он работает на iOS, Android и Web, но настройка для каждой платформы может отличаться. Например, для Web нужно отдельно настроить OAuth.

Как использовать
Погрузиться во все детали можно с примером на Github.

❗️Приложение будет зависеть от того, есть ли у пользователя учетная запись в Google, и это может стать проблемой.

❤️ Точно в цель
🤔 Без аккаунта Google — не полетит
Please open Telegram to view this post
VIEW IN TELEGRAM
7👍4🔥3
Как создать эффективный BDUI-фреймворк?

Делимся ключевыми выводами из доклада Никиты Синявина из BetBoom. На CrossConf он поделился опытом использования DUIT. На карточках — ключевые требования к фреймворку, его преимущества и сравнение с DivKit от Яндекса🖱
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
5🔥3👍2