Flutter Pulse
511 subscribers
314 photos
670 links
На канале будут новости про flutter с сайтов, информация об обновлении пакетов, а также авторский контент.
Download Telegram
Использование ИИ в вашем приложении с помощью Gemini

Привет, разработчики Flutter! 🤖💻 Сегодня мы расскажем, как добавить Gemini в ваше приложение с помощью Firebase Functions безопасным способом 🔒.

Почему не стоит использовать плагин flutter_gemini?

Из соображений безопасности я настоятельно предпочитаю не раскрывать свой ключ API Gemini в приложении 🔑.

Шаги по интеграции Gemini с Firebase:

1. Создайте новый проект Firebase с помощью команды firebase init genkit или установите необходимые зависимости 📦.
- Установите следующие пакеты:
- @genkit-ai/ai
- @genkit-ai/core
- @genkit-ai/dotprompt
- @genkit-ai/firebase
- @genkit-ai/flow
- @genkit-ai/googleai
- zod

2. Установите genkit глобально: npm install -g genkit 🌐.

3. Инициализируйте gemini, используя configureGenkit 🔧.



import { initializeApp } from "firebase/app";
import { defineString } from "firebase-functions/params";
import { configureGenkit } from '@genkit-ai/core';
import { firebase } from '@genkit-ai/firebase';

defineString('GOOGLE_GENAI_API_KEY');
const firebaseApp = initializeApp();

configureGenkit({
plugins: [
firebase(),
googleAI({ apiKey: defineString('GOOGLE_GENAI_API_KEY') }),
],
// ...
enableTracingAndMetrics: true,
});



Добавьте ключ API в файл .env и не забудьте удалить .env из .gitignore, иначе Firebase не сможет его использовать 🚫.



export const suggestionFlow = onFlow({
name: "suggestionFlow",
httpsOptions: { cors: true },
region: "europe-west1",
inputSchema: z.object({ uid: z.string(), query: z.string(), language: z.string() }),
outputSchema: z.string(),
authPolicy: (auth, input) => {
// Проверка аутентификации пользователя
if (auth.uid != input.uid) {
throw new Error("You can only access your own data");
}
// Проверка поддерживаемого языка
if (input.language != "fr" && input.language != "en") {
throw new Error("Only French and English are supported for now");
}
// Разрешить доступ только аутентифицированным пользователям
return !!auth.uid;
},
}, async (input) => {
const exercicesContext = JSON.stringify(exercices);
const userLanguage = input.language;
const prompt = `You are an AI assistant that helps users with XXX. You will speak in the user's language: $userLanguage.
You will answer with the JSON format below:
{"days": [{"day": number, "steps": {...}, "description": string}]}
Here is the user query: ${input.query}`;

const llmResponse = await generate({
model: gemini15Flash,
prompt,
config: {
temperature: 1,
maxOutputTokens: 3000,
},
});

return llmResponse.text();
});



Разверните эту функцию на Firebase с помощью команды firebase deploy --only functions 🚀.

Вызов функции из Flutter:


Future<String> fetchStretchingSuggestionFlow(
String uid,
String query,
String userLanguage,
) async {
final callable = FirebaseFunctions.instance.httpsCallable('suggestionFlow');
final result = await callable.call<String>({
'uid': uid,
'query': query,
'language': userLanguage,
});
final data = (await decode(result.data)) as Map<String, dynamic>;
return AIexercice.fromJson(data);
}



Оцените нашу новую рубрику и оставьте свои отзывы! 😊👍

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

#flutter #dart #flutterpulse #FlutterPulseTips #Firebase #Gemini #AI #FlutterTips #MobileDev #AppDev
🔧 DevScreen на Flutter: свой путь к инструменту для логирования, тестирования и смены окружений

🗣 Спикер: Павел, Flutter-разработчик с 10+ годами опыта, один из первых пользователей Flutter в РФ, соавтор книги «Основы Flutter», лектор СФУ и активный участник комьюнити.

На конференции он представил разработку своей команды — DevScreen, универсальный внутренний инструмент для мобильной отладки, логирования, диагностики и смены окружений.
🔎 Зачем нужен DevScreen?

В боевой разработке часто не хватает:

🎛 экрана с настройками (окружения, прокси)

🧾 доступа к логам прямо в приложении

🐞 удобного сбора стектрейсов для QA

📉 отладки UI, FPS, границ, состояния виджетов

🧪 триггера запуска debug-инструментов без пересборки

💬 «Качество — это ответственность всей команды», — говорит Павел. А значит, и тестировщики, и дизайнеры, и разработчики должны иметь доступ к полезной внутренней информации прямо в приложении.
🧰 Что уже есть на рынке?

Рассмотрели 2 популярных решения:
1. Ume

умеет логировать запросы
показывает FPS, границы виджетов, device info
не умеет работать с окружениями и прокси
нет нормального логирования ошибок
триггер ручной (нужно писать самому)

💡 Хорош для дизайнеров и UI-отладки, но слабо помогает QA и не масштабируется под все нужды.

2. Talker

готовый экран логов
отличное логирование (включая ошибки, кастомные события, API-запросы)
удобен и в приложении, и в консоли
не умеет менять окружение и прокси
триггер — тоже руками
нет UI-отладки

💬 «Talker — это скорее логер, чем полноценный DevScreen. Мы хотели большего».


Так родилась идея своего DevScreen MVP, который:
🔓 открывается по shake-жесту или скрытой зоне (не мешает пользователю)
🌐 позволяет менять окружения, добавлять прокси
🧾 логирует ошибки, API-запросы, аналитику и кастомные события
🔄 сбрасывает кэш, симулирует логаут, показывает device info
🛠 кастомизируется под проект, легко расширяется
🔐 имеет авторизацию (доступ по роли или логину)
🧪 отделён от боевого UI, не ломает UX обычного пользователя
⚙️ поддерживает показ границ виджетов, FPS, и другую системную инфу

💡 Всё настраивается модульно. Хочешь только прокси? Подключай только его. Нужны только логи? Без проблем.

🤝 Кто пользуется внутри?
Разработчики — смотрят логи, стектрейсы, ошибки
Тестировщики — не пишут баг на глаз, а прикладывают API-ответ и stacktrace
Дизайнеры — проверяют реализацию UI прямо в приложении (границы, цвета, размеры)

🚀 Что дальше?

В планах:
🧩 сделать DevScreen переиспользуемым пакетом
🛡 вынести доступ к нему за фичефлаг
🧪 покрыть всё юнит- и интеграционными тестами
📢 возможно — опубликовать в open-source, если будет спрос

📺 Полное видео доклада (YouTube):
📌 Такой подход реально экономит часы работы QA и разработчиков, а главное — повышает качество продукта уже на уровне debug. Всё прозрачно, логируемо и воспроизводимо.

🧵 #flutter #flutterpulse #debug #talker #devtools #qa #мобилкарф #тестирование #логирование #devscreen #mobiledev #dart #flutterмного #конференция
Проверка статуса продления подписки пользователя

Привет, Flutter-разработчики! 👋 Сегодня мы рассмотрим полезный совет по проверке статуса продления подписки пользователя с помощью RevenueCat. 📈

Зачем проверять статус продления подписки?

Проверка статуса продления подписки позволяет вам:
Показывать промо-офферы перед тем, как потерять пользователя
Управлять доступом к premium-функциям вашего приложения

Пример кода на Dart:


Future<bool> hasRenewal() async {
final customerInfo = await Purchases.getCustomerInfo();
final entitlements = customerInfo.entitlements.active.values.firstOrNull;
return entitlements?.willRenew ?? false;
}


В этом коде мы:
1. Получаем информацию о покупателе с помощью `Purchases.getCustomerInfo()`
2. Извлекаем активные права доступа (`entitlements`)
3. Проверяем, будет ли подписка продлена (`willRenew`)

Используйте эту информацию, чтобы улучшить удержание пользователей! 📊

Оцените новую рубрику и напишите в комментариях, какие темы вам интересны! 🤔

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

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDev #SubscriptionManagement #RevenueCat #FlutterTips
👍3
Обработка ошибок с Future
Catch them all 🎯

Неправильная функция

Future<void> functionInError() async {
throw "I am failing"; // Выбрасываем ошибку
}


Неправильный способ обработки

functionInError()
.catchError((err) => print("error catched")) // Перехватываем ошибку
.then(
(value) => print("success"), // Успешное выполнение
onError: (err) => print("I failed"), // Обработка ошибки
);

// Результат:
// error catched
// success


catchError перехватывает ошибку, но не возвращает её. Поэтому then выведет success вместо "I failed".

Правильный способ обработки

functionInError()
.catchError((err) {
print("error catched"); // Печатаем сообщение об ошибке
throw err; // Пробрасываем ошибку дальше
})
.then(
(value) => print("success"), // Успешное выполнение
onError: (err) => print("I failed"), // Обработка ошибки
);

// Результат:
// error catched
// I failed


Лучше обрабатывать ошибки в колбэке onError. Но если хотите цеплять функции друг за другом, не забудьте пробросить ошибку дальше.

Цепочка нескольких Future и обработка ошибок

void main() {
functionInError()
.then((res) => workingFuture()) // Продолжаем цепочку
.then((res) => print("ended")) // Завершаем цепочку
.onError((err) => print("error catched")); // Обрабатываем ошибку
}

Future<void> functionInError() async {
throw "I am failing"; // Выбрасываем ошибку
}

Future<void> workingFuture() async {
print("working future"); // Печатаем сообщение о работе
}

// Результат:
// error catched


Ошибка передаётся в последний onError.

Или обработка ошибок через try-catch с await

main() async {
try {
await functionInError(); // Ждём завершения функции
} catch (err) {
print("I failed"); // Обрабатываем ошибку
}
}


Оцените новую рубрику! 👍💬

Все подобные новости можно найти по хэштегу #FlutterPulseTips
#flutter #dart #flutterpulse #FlutterPulseTips #Future #ErrorHandling #FlutterTips #MobileDev #CodingTips
👍1
Отслеживание видимости клавиатуры
Без каких-либо плагинов 😉

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

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

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



import 'package:flutter/material.dart';

// состояние клавиатуры
enum KeyboardVisibilityState { visible, hidden }

// определение функции слушателя
typedef OnKeyboardStateChanged = void Function(KeyboardVisibilityState state);

// stateful виджет для отслеживания изменения видимости клавиатуры
class KeyboardVisibility extends StatefulWidget {
final Widget child;
final OnKeyboardStateChanged onKeyboardStateChanged;

const KeyboardVisibility({
super.key,
required this.child,
required this.onKeyboardStateChanged,
});

@override
State<KeyboardVisibility> createState() => _KeyboardVisibilityState();
}

class _KeyboardVisibilityState extends State<KeyboardVisibility> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}

@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}

@override
void didChangeMetrics() {
super.didChangeMetrics();
checkState();
}

void checkState() {
final value = WidgetsBinding.instance.platformDispatcher.views.first.viewInsets.bottom;
switch (value != 0.0) {
case true:
widget.onKeyboardStateChanged(KeyboardVisibilityState.visible);
case false:
widget.onKeyboardStateChanged(KeyboardVisibilityState.hidden);
}
}

@override
Widget build(BuildContext context) {
return widget.child;
}
}



Использование:
Виджет KeyboardVisibility можно использовать следующим образом:



KeyboardVisibility(
onKeyboardStateChanged: (state) => _showOrHideBottomAction(state),
child: Form(...),
)



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

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

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDev #UIUX #CodingTips #AppDev #DevTips
1👍1
Создание изображения из виджета

Привет, Flutter-разработчики! 👋 Сегодня мы рассмотрим интересную и полезную тему: создание изображения из любого виджета в вашем приложении. 📸

Шаг 1: Добавьте RepaintBoundary поверх виджета, который хотите экспортировать

Чтобы создать изображение из виджета, сначала нужно обернуть его в RepaintBoundary. Для этого потребуется GlobalKey, который поможет найти нужный виджет в дереве рендеринга.



final GlobalKey _repaintBoundaryKey = GlobalKey();

RepaintBoundary(
key: _repaintBoundaryKey,
child: CustomPaint(
painter: MyPainter(),
...
),
)



Этот ключ позволит нам найти объект рендеринга и вызвать метод toImage. 📚 Для более глубокого понимания можно изучить, что такое Widget tree, Element tree и RenderObject tree.

Шаг 2: Вызовите метод для экспорта изображения в галерею телефона

Теперь напишем функцию, которая будет экспортировать изображение:



import 'package:image_gallery_saver/image_gallery_saver.dart';

Future<void> exportImage() async {
try {
RenderRepaintBoundary boundary = _repaintBoundaryKey.currentContext!.findRenderObject() as RenderRepaintBoundary;
ui.Image image = await boundary.toImage(pixelRatio: 3.0);
ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
Uint8List pngBytes = byteData!.buffer.asUint8List();

final directory = await getDownloadsDirectory();
final imgName = 'myapp-${DateTime.now().microsecondsSinceEpoch}';
final result = await ImageGallerySaver.saveImage(pngBytes, quality: 100, name: imgName);

if (result['isSuccess']) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Изображение экспортировано')),
);
}
} catch (e) {
// Обработайте ошибку (покажите сообщение пользователю, сохраните отчет о сбое)
}
}



Мы используем пакет image_gallery_saver, чтобы сохранить изображение в галерею телефона. 📁📸

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

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

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDev #AppDev #CodingTips #UIUX #FlutterTips
🔥1
Приостановка потока, когда нет подписчиков

Не теряйте события, когда некому их слушать 😉

В Flutter и Dart при работе с потоками (Stream) важно уметь правильно управлять подпиской и событиями. Один из полезных советов - приостанавливать поток, когда нет подписчиков, чтобы не терять важные события.

Пример реализации:



// Этот класс отвечает за рассылку уведомлений приложению
// Он также отвечает за прослушивание уведомлений
class AppEventsDispatcher {
final StreamController<AppEvent> _controller;
late final Stream<AppEvent?> _stream;

Stream<AppEvent?> get stream => _stream;

final List<AppEvent> _onNotificationEventsSubscriber;

AppEventsDispatcher()
: _onNotificationEventsSubscriber = [],
_controller = StreamController() {
_stream = _controller.stream.asBroadcastStream(
onCancel: (c) => c.pause(),
onListen: (el) {
if (el.isPaused) {
el.resume();
}
},
);
}

void dispose() {
_onNotificationEventsSubscriber.clear();
_controller.close();
}

void publish(AppEvent event) {
_controller.add(event);
}
}

final dispatcher = AppEventsDispatcher();
// Публикуем событие
dispatcher.publish(AppEvent('Новое уведомление'));
// Ждём немного
await Future.delayed(Duration(seconds: 1));
// Подписываемся на поток
final subscription = dispatcher.stream.listen((event) {
print('Получено событие: $event');
// так как мы приостановили поток, мы получим события
});



В этом примере мы создаём класс AppEventsDispatcher, который управляет потоком событий AppEvent. Когда нет подписчиков, поток приостанавливается, а при появлении нового подписчика - возобновляется с последнего события.

👍 Оцените нашу новую рубрику FlutterPulseTips и напишите в комментариях, что хотите видеть в следующих постах! 🤔

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

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDev #StreamManagement #AppDevelopment #CodingTips
👍2
Canvas с GestureDetector: обработка событий только в пределах радиуса

Привет, Flutter-разработчики! 👋 Сегодня мы поделимся с вами полезным советом о том, как использовать GestureDetector с CustomPaint для обработки жестов только в определенной области. 📱💡

Вы когда-нибудь сталкивались с ситуацией, когда ваш CustomPaint должен реагировать на жесты только в определенной области? Например, вам нужно, чтобы нажатие обрабатывалось только если оно произошло в пределах определенного радиуса вокруг объекта? 🔍

Для этого можно использовать метод hitTest в вашем CustomPainter. Вот пример кода:


class WorldPainter extends CustomPainter {
...
bool hitTest(Offset position) {
// вычисляем расстояние от позиции до нужной точки
var distance = position.distanceTo(this.location);
// возвращаем true, если расстояние меньше радиуса обнаружения
return distance < detectionRadius;
}
...
}



Затем оберните ваш CustomPaint в GestureDetector:


class MyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => print("do what you want"), // действие при нажатии
child: CustomPaint(
size: Size.infinite, // размер canvas
painter: WorldPainter(), // ваш кастомный painter
),
);
}
}



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

Оцените нашу новую рубрику и оставьте свои комментарии! 💬 Все подобные новости вы можете найти по хэштегу #FlutterPulseTips. 👉 #flutter #dart #flutterpulse #FlutterPulseTips #MobileDev #UIUX #FlutterTips #CodingTips #AppDevelopment
Расширение цвета из шестнадцатеричной строки
Привет, разработчики Flutter! 👋 Сегодня мы рассмотрим полезный совет, который упростит работу с цветами в ваших приложениях. 🌈

Проблема: Часто цвета в приложениях представлены в виде шестнадцатеричных строк (например, "#FFFFFF" или "FF0000"). Но как легко преобразовать эти строки в объекты Color, с которыми можно работать в Flutter? 🤔

Решение: Мы создадим расширение для класса Color, которое позволит легко конвертировать шестнадцатеричные строки в цвета. 💡


extension HexColor on Color {
static Color fromHex(String hexString) {
final buffer = StringBuffer(); // Создаем буфер для формирования итоговой строки
if (hexString.length == 6 || hexString.length == 7) // Проверяем длину строки
buffer.write('ff'); // Если длина 6 или 7, добавляем 'ff' (полная непрозрачность)
buffer.write(hexString.replaceFirst('#', '')); // Удаляем символ '#' из строки
return Color(int.parse(buffer.toString(), radix: 16)); // Преобразуем строку в число и создаем цвет
}
}


Как это работает:
1. Мы проверяем длину строки: если она 6 или 7 символов, добавляем 'ff' для полной непрозрачности.
2. Удаляем символ '#' из строки, если он есть.
3. Преобразуем полученную строку в число в шестнадцатеричной системе и создаем объект Color.

Теперь вы можете легко использовать шестнадцатеричные строки для создания цветов в вашем приложении Flutter! 🎉

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

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDev #AppDevelopment #CodingTips #ColorUtils
👍2
Триггер событий при навигации по страницам с помощью Flutter RouteObserver

Привет, разработчики Flutter! 👋 Сегодня мы рассмотрим полезный совет по использованию RouteObserver для отслеживания навигации между страницами в вашем приложении. 📱

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

Пример использования:
Чтобы использовать `RouteObserver`, вам нужно создать экземпляр класса, наследующего от `RouteObserver<PageRoute<dynamic>>`, и переопределить нужные методы. Ниже приведён пример кода:



class MyNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
// Код, выполняемый при открытии новой страницы
}

@override
void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) {
// Код, выполняемый при замене маршрута
}

@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
// Код, выполняемый при закрытии текущей страницы
}
}



Затем добавьте этот наблюдатель в ваше `MaterialApp`:



class MyApp extends StatelessWidget {
final _navigatorKey = GlobalKey<NavigatorState>();
final navObserver = MyNavigatorObserver();

@override
Widget build(BuildContext context) =>
MaterialApp(
navigatorObservers: [navObserver],
// Другие свойства MaterialApp
);
}



Зачем это нужно?
Использование `RouteObserver` позволяет легко отслеживать события навигации, что может быть полезно для аналитики, логирования или выполнения определённых действий при переходе между экранами. 📊🔍

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

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDev #AppDevelopment #CodingTips #FlutterTips
👍3
🎯 RuStore — теперь не просто альтернатива, а необходимость
📲 Подписан закон, который обязывает при продаже смартфонов и планшетов в России обеспечивать доступ к установке и обновлению приложений через 🇷🇺 единый магазин RuStore.
💥 Что это значит?
— Устройства, продаваемые в РФ, должны поддерживать RuStore "из коробки"
Никаких блокировок, ограничений или "невидимых барьеров" для российских приложений
Запрещено препятствовать обновлениям, уведомлениям, платежам и работе функций, если они идут через RuStore
— Даже 🍏 устройства, которые традиционно "закрыты", формально обязаны обеспечить такую возможность
🚨 Причём:
📌 В законе отдельно упоминаются производители, ограничивающие доступ к сторонним магазинам.
📌 Закон напрямую запрещает такие ограничения — включая блокировку API, ограничение платежей или недоступность функций для сторонних приложений.

🔥 А значит — не исключено, что в ближайшем будущем можно будет ставить приложения на iOS через RuStore (в теории… но реализация покажет 😉).
📢 Flutter-разработчики, следим за ситуацией внимательно: возможно, скоро откроются новые каналы распространения даже для iOS. А пока — продолжаем пилить 🧱 🧑‍💻.
🔗 Подписывайтесь на FlutterPulse — мы разбираем важные законы, фреймворки, SDK и публикуем апдейты без воды.
💬 Что думаете о нововведении? Откроет ли это путь для Flutter-приложений на iOS вне App Store? Прогнётся ли Apple? Пишите в комментариях!
#RuStore #iOS #Закон #МагазинПриложений #MobileDev #Flutter #flutterdev #flutterpulse #россия #санкции #iosdev #androiddev #appdistribution #russia #technews
🔥5
Отслеживание жизненного цикла приложения
Привет, подписчики! 👋 Сегодня мы рассмотрим очень полезный совет по Flutter - отслеживание жизненного цикла приложения. 📱💻

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

В примере кода ниже показано, как это работает:


class CameraAwesomeState extends State<CameraAwesome> with WidgetsBindingObserver {
// ...
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.resumed:
if (!started) {
CamerawesomePlugin.start(); // Запуск камеры при возобновлении работы приложения
}
break;
case AppLifecycleState.inactive:
case AppLifecycleState.paused:
case AppLifecycleState.detached:
if (started) {
CamerawesomePlugin.stop(); // Остановка камеры при переходе в фон или закрытии приложения
}
break;
}
}
// ...
}



Жизненный цикл приложения включает четыре основных состояния:
- resumed - приложение активно и видимо для пользователя.
- inactive - приложение не активно, но ещё видимо.
- paused - приложение не активно и не видимо (находится в фоне).
- detached - приложение закрыто.

Используя эти знания, вы сможете лучше управлять ресурсами вашего приложения и улучшать пользовательский опыт! 🌟

Оцените новую рубрику и напишите в комментариях, что вы думаете о ней! 💬

Все подобные новости можно найти по хэштегу #FlutterPulseTips. 🔍
#flutter #dart #flutterpulse #FlutterPulseTips #MobileDev #AppDevelopment #ProgrammingTips #TechNews
👍5
Предоставление разрешений в интеграционных тестах на Android с помощью командной строки adb

Привет, разработчики Flutter! 👋 Сегодня мы поделимся с вами полезным советом о том, как предоставлять разрешения в интеграционных тестах на Android с помощью командной строки adb. 📱💻

Зачем это нужно?
При выполнении интеграционных тестов на Android ваше приложение может требовать определённые разрешения. Без этих разрешений тесты могут завершиться неудачей. 🤕

Решение
Используйте команду adb для предоставления разрешений вашему приложению перед выполнением тестов. Вот пример кода на Dart, который демонстрирует, как это можно сделать:



const List<String> permissions = [
'android.permission.WRITE_EXTERNAL_STORAGE', // Разрешение на запись во внешнее хранилище
'android.permission.RECORD_AUDIO', // Разрешение на запись аудио
];

const String _examplePackage = 'com.apparence.example'; // Имя пакета вашего приложения

Future<void> main() async {
// Предоставляем разрешения перед запуском тестов
permissions.forEach((permission) => Process.runSync(
'adb', ['shell', 'pm', 'grant', _examplePackage, permission]));

print('Starting test.'); // Начало теста
await integrationDriver(); // Запуск интеграционных тестов
print('Test finished. Revoking permissions...'); // Окончание теста и отзыв разрешений

// Отзываем разрешения после завершения тестов
permissions.forEach((permission) => Process.runSync(
'adb', ['shell', 'pm', 'revoke', _examplePackage, permission]));
}



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

Оцените нашу новую рубрику советов для Flutter-разработчиков! 👍 Ваши отзывы помогут нам сделать её ещё лучше. 💬

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

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDev #AndroidDev #IntegrationTesting #FlutterTips
👍2
Изменение модели родительского виджета из дочернего
с использованием виджета Actions и модели Intent 🤯

Привет, разработчики Flutter! 👋 Сегодня мы рассмотрим интересный трюк, который поможет вам изменить модель родительского виджета из дочернего компонента. Это очень полезный навык при создании сложных интерфейсов. 📱

Основная идея
Используем виджет Actions и модель Intent для взаимодействия между родительским и дочерним компонентами.

Пример кода


// Используем intent для изменения модели действия
class AppBarBuildIntent extends Intent {
final PreferredSizeWidget? appbar;
AppBarBuildIntent(this.appbar);
}

// Действие, которое можно вызвать из любого дочернего элемента
class BartAppbarAction extends Action<AppBarBuildIntent> {
final ValueNotifier<PreferredSizeWidget?> appbar;
BartAppbarAction(this.appbar);

@override
void invoke(covariant AppBarBuildIntent intent) {
this.appbar.value = intent.appbar;
}
}

class MyWidget extends StatelessWidget {
final ValueNotifier<PreferredSizeWidget?> appBarNotifier = ValueNotifier(null);

MyWidget({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Actions(
actions: <Type, Action<Intent>>{
AppBarBuildIntent: BartAppbarAction(appBarNotifier),
},
child: AnimatedBuilder(
animation: appBarNotifier,
builder: (ctx, child) => ...,
),
);
}
}

// Вызов в дочернем элементе для изменения родителя (appBar)
Actions.invoke(context, AppBarBuildIntent(AppBar(title: Text("title text"))));



Этот подход позволяет элегантно управлять состоянием родительского виджета из дочерних компонентов. 👍

Оцените новую рубрику FlutterPulseTips! 😊 Нам важно ваше мнение!

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

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDev #WidgetMagic #StateManagement #FlutterTips
Действия клавиатуры ввода в Flutter

Улучшите опыт пользователей вашего приложения с помощью действий клавиатуры ввода! 🌟 Они помогают пользователям быстрее заполнять формы, предоставляя интуитивно понятные и удобные элементы управления. 🚀

Как это работает? 🤔
1. Покажите действие "следующее поле" на клавиатуре и свяжите его с отправкой формы.
2. При нажатии на "следующее поле" текущее поле теряет фокус, а следующее поле получает фокус.



TextFormField(
focusNode: _emailFocus,
textInputAction: TextInputAction.next, // Действие "следующее поле"
onFieldSubmitted: (term) {
_emailFocus.unfocus(); // Убрать фокус с текущего поля
FocusScope.of(context.buildContext).requestFocus(_pwdFocus); // Передать фокус следующему полю
},
keyboardType: TextInputType.emailAddress, // Тип клавиатуры для ввода email
)



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

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

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDev #UIUX #FlutterTips #DartLang
1
Сохранение позиции прокрутки страницы

Привет, разработчики Flutter! 👋 Сегодня мы поделимся полезным советом о том, как сохранить позицию прокрутки страницы при навигации между разными экранами. 📱💻

Вы когда-нибудь сталкивались с ситуацией, когда пользователь прокручивал список или контент на странице, а затем возвращался обратно, и страница снова оказывалась в самом верху? 😒 Это может быть неудобно для пользователей, особенно если они хотят вернуться к тому месту, где они остановились.

Решение: использование PageStorage и PageStorageBucket

Чтобы решить эту проблему, мы можем использовать PageStorage и PageStorageBucket. Эти инструменты позволяют сохранять состояние прокрутки страницы и восстанавливать его при возвращении на эту страницу.

Вот пример кода:


final PageStorageBucket _bucket = PageStorageBucket(); // Создание хранилища для страниц

final pages = <Widget>[ // Список страниц
Page1(key: const PageStorageKey('page1'), ...), // Первая страница с уникальным ключом
Page2(key: const PageStorageKey('page2'), ...), // Вторая страница с уникальным ключом
];

@override
Widget build(BuildContext context) {
return Scaffold( // Основной виджет страницы
body: PageStorage( // Обертка для сохранения состояния прокрутки
child: pages[currentTab], // Текущая отображаемая страница
bucket: _bucket, // Передача хранилища
),
);
}



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

1. Мы создаем экземпляр PageStorageBucket, который будет хранить состояние наших страниц.
2. Каждой странице присваиваем уникальный ключ с помощью PageStorageKey. Это позволяет PageStorage идентифицировать каждую страницу и сохранять её состояние.
3. Обернём наши страницы в виджет PageStorage, передав туда текущего ребёнка (текущую страницу) и наше хранилище (_bucket).

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

Оцените новую рубрику и напишите в комментариях, какие темы вы хотели бы видеть в наших следующих постах! 💬

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

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDev #AppDev #UIUX #DevTips
👍4