Flutter Pulse
493 subscribers
294 photos
615 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
Проверка статуса продления подписки пользователя

Привет, 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
Обработка ошибок с 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
Несколько навигаторов
В вашем приложении может быть несколько навигаторов. Например, основной навигатор и вторичный навигатор внутри него. Вторичный навигатор может использоваться для onboarding процесса.



class OnboardingPage extends ConsumerWidget {
const OnboardingPage({super.key});

@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
body: Navigator( // Вторичный навигатор
onGenerateRoute: (settings) => switch (settings.name) {
'feature_1' => OnboardingRouteTransition(
builder: (context) => const MultimediaOnboardingStep(),
settings: settings,
),
...
},
),
);
}
}



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



Navigator.of(context, rootNavigator: true).pushNamed("premium")



Оцените новую рубрику по Flutter советам! 👍💡 Ваши мысли нам очень важны! 🤔

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

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDevelopment #AppDevelopment #ProgrammingTips #FlutterTips
Создание изображения из виджета

Привет, 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
Создаём изображение из виджета с помощью пакета

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

Шаг 1: Установка пакета screenshot
Для начала необходимо установить пакет screenshot. Для этого добавьте следующую строку в файл pubspec.yaml и выполните команду flutter pub get:

import 'package:screenshot/screenshot.dart';


Шаг 2: Создание изображения из виджета
Теперь вы можете создать изображение из любого виджета, указав его размер и соотношение пикселей. Вот пример кода:


final controller = ScreenshotController();
final imgBytes = await controller.captureFromWidget(
widget, // ваш виджет
targetSize: const Size(320, 520), // размер изображения
pixelRatio: 3, // соотношение пикселей
);



Этот код позволяет вам программно создавать изображения любых виджетов, что может быть полезно для различных задач, например, для создания скриншотов или генерации контента. 📱💻

Оцените нашу новую рубрику и оставьте свои комментарии! 💬 Нам важно ваше мнение. 👍

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

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDevelopment #FlutterTips #CodingTips #AppDevelopment
Flutter Canvas: что делают canvas.save() и canvas.restore()?

Привет, разработчики Flutter! 👋 Сегодня мы рассмотрим важный аспект работы с Canvas во Flutter - методы save() и restore(). Эти методы крайне полезны при создании сложных графических элементов и анимаций. 🔍

Когда вы работаете с Canvas, вы часто выполняете различные трансформации, такие как вращение, масштабирование или перемещение. Эти трансформации изменяют текущее состояние Canvas, и иногда вам нужно временно сохранить это состояние, чтобы позже вернуться к нему. Именно здесь на помощь приходят save() и restore(). 🔄

canvas.save() сохраняет текущее состояние Canvas, включая все примененные трансформации и настройки отрисовки. Это позволяет вам временно изменить состояние Canvas, выполнив необходимые операции, а затем вернуться к сохраненному состоянию с помощью canvas.restore().

Рассмотрим пример кода:


class WorldPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
canvas.save(); // сохраняем текущее положение
canvas.translate(size.width/2, size.height/2); // перемещаемся в центр
...
canvas.drawLine(Offset(0, 0), model.offset, whitePainter); // выполняем необходимые действия
canvas.restore(); // сбрасываем до последнего сохраненного положения
}
}



В этом примере мы сохраняем текущее состояние Canvas перед тем, как переместиться в центр экрана. После выполнения необходимых операций мы восстанавливаем сохраненное состояние, возвращая Canvas в исходное положение.

Использование save() и restore() позволяет вам создавать сложные графические элементы, сохраняя чистоту и управляемость кода. 💻

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

#flutter #dart #flutterpulse #FlutterPulseTips #MobileDevelopment #CanvasTips #FlutterTips #ProgrammingTips
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
Имитация вызовов REST API
Привет, разработчики Flutter! 👋 Сегодня мы рассмотрим важный аспект тестирования приложений — имитацию вызовов REST API. 📱💻

Имитация вызовов API позволяет тестировать ваше приложение без фактического обращения к серверу, что делает процесс тестирования более быстрым и надежным. ⚡️

Как это работает?
1. Импортируйте пакет mocktail:
import 'package:mocktail/mocktail.dart';


2. Создайте класс HttpClientMock, который имитирует поведение HttpClient:
class HttpClientMock extends Mock implements HttpClient {}
final httpClientMock = HttpClientMock();


3. Используйте when для определения поведения имитированного клиента:
when(() => httpClientMock
.get(Uri.parse('myapi/...')))
.thenAnswer((_) async => Response('''
{"id":"testId","route":"myPage"}
''', 200));


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

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

Все подобные новости можно найти по хэштегу #FlutterPulseTips
#flutter #dart #flutterpulse #FlutterPulseTips #MobileAppDevelopment #AppTesting #MockingAPI #FlutterTips
Триггер событий при навигации по страницам с помощью 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
Flutter советы - Как связать анимации в цепочку

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

Создание сложных анимаций
Вы можете создать сложные анимации, связывая несколько анимаций в одну цепочку с помощью AnimationController. Это позволяет вам управлять несколькими анимациями одновременно и создавать более интересные и динамичные пользовательские интерфейсы 🎨



fadeAnimController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 2000));
backgroundAnimation = CurvedAnimation(
parent: fadeAnimController,
curve: Interval(0, .4, curve: Curves.easeIn));
titleOpacityAnimation = CurvedAnimation(
parent: fadeAnimController,
curve: Interval(.4, .5, curve: Curves.easeIn));
titleSizeAnimation = CurvedAnimation(
parent: fadeAnimController,
curve: Interval(.4, .6, curve: Curves.easeInOutBack));
// ... свяжите другие анимации



В этом примере мы создаем AnimationController с длительностью 2 секунды и три CurvedAnimation, которые привязаны к этому контроллеру. Каждая CurvedAnimation имеет свой собственный интервал и кривую, что позволяет нам создавать сложные анимации 🔩

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

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

#flutter #dart #flutterpulse #FlutterPulseTips #AnimationTips #UIUX #MobileDevelopment #FlutterTips