Flutter: Push уведомления firebase

С PUSH уведомлениями, во Flutter, вроде бы всё и просто, но вот я провозился три дня, чтобы заставить приложение стабильно их показывать. Большая часть проблем вытекает из-за постоянно развивающейся кодовой базы Flutter, в результате чего большая часть примеров в сети — уже не рабочие.

Проблема №1

Пакет flutter_webview_plugin_ios_android, который оказался не совместим с вызовом FirebaseMessaging.onBackgroundMessage, после инициализации которого, перестали работать кэлбеки webview на переходы на другие url (GitHub Issue).

Выход: не бросаться на простой в использовании пакет, а использовать максимально популярный webview_flutter. Хотя и оный у меня заработал, только в самой последней версии — до этого были проблемы с отображением некоторых сайтов с не понятными SSL сертификатами.

Проблема №2

Вызов ensureInitialized:

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
...  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
...

который у «всех работает а у меня не работает». И вызывает ошибку

Unhandled Exception: [core/duplicate-app] A Firebase App named "[DEFAULT]" already exists

Пришлось обернуть в:

  try {
    await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  } catch (e) {}

Проблема №3

Подобрать комбинацию пакетов Flutter и окружение Android Studion, чтобы «всё заработало». В итоге удовлетворительно заработало при используемых версия пакетов Flutter:

firebase_messaging: ^14.4.0
flutter_local_notifications: ^13.0.0

И зависимостей build.grade для Android Studio:

\android\app\build.gradle:

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.yandex.android:maps.mobile:4.2.2-full'
    implementation 'com.google.firebase:firebase-bom:31.4.0'
    implementation 'com.google.firebase:firebase-messaging:23.1.2'
    implementation 'com.android.support:multidex:1.0.3'
}

\android\build.gradle:

buildscript {
    ext.kotlin_version = '1.6.10'
    repositories {
        google()
        mavenCentral()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:7.1.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.google.gms:google-services:4.3.15'
    }
}

Итак, вот текущая минимальная обвязка для работы с PUSH уведомлениями получилась:

import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'firebase_options.dart';
import 'permissions.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';

FirebaseMessaging messaging = FirebaseMessaging.instance;
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

  await setupFlutterNotifications();
  showFlutterNotification(message);
  print('Handling a background message ${message.messageId}');
}

late AndroidNotificationChannel channel;
bool isFlutterLocalNotificationsInitialized = false;
Future<void> setupFlutterNotifications() async {
  if (isFlutterLocalNotificationsInitialized) {return;}
  channel = const AndroidNotificationChannel(
    'high_importance_channel', // id
    'High Importance Notifications', // title
    description: 'High Importance Notifications', // description
    importance: Importance.high,
  );
  flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
  await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(channel);
  await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(alert: true, badge: true, sound: true,);
  isFlutterLocalNotificationsInitialized = true;
}
void showFlutterNotification(RemoteMessage message) {
  RemoteNotification? notification = message.notification;
  AndroidNotification? android = message.notification?.android;
  if (notification != null && android != null && !kIsWeb) {
    print("Пришло ПУШ:");
    print(notification.title);
    flutterLocalNotificationsPlugin.show(
      notification.hashCode,
      notification.title,
      notification.body,
      NotificationDetails(
        android: AndroidNotificationDetails(
          channel.id,
          channel.name,
          channelDescription: channel.description,
          icon: 'launch_background',
        ),
      ),
    );
  }
}
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized(); // требуется для PUSH сервиса
  // инициализация PUSH уведомлений
  try {
    await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  } catch (e) {}
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  await setupFlutterNotifications();
  // получаем токен уведомлений
  FirebaseMessaging.instance.getToken().then((token) {
    print("Token: $token"); // Выплюнем токен в консольку
  });
  runApp(MyApp());
}
...
...
 @override
  Widget build(BuildContext context) {
    String? _token;
    String? initialMessage;
    bool _resolved = false;
    FirebaseMessaging.instance.getInitialMessage().then(
      value) => () {
            _resolved = true;
            initialMessage = value?.data.toString();
      },
    );
    FirebaseMessaging.onMessage.listen(showFlutterNotification); // показ PUSH уведомлений при открытом приложении
    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      print('A new onMessageOpenedApp event was published!');
    });
....
....

Flutter: адаптивные шрифты и размеры

С размерами блоков воFlutter относительно всё в порядке — очень помогает виджет Expanded. Некоторое же неудобство разработки как оказалось — это отсутствие в «из коробки» возможности указывать размер шрифтов в «адаптиве» под разные разрешения экрана.

Что делать? Решений множество. Ниже некоторые варианты:

Вариант 1:

Создать отдельный класс-обертку, через которую пропускать потом все размеры шрифтов:

class ScaleSize {
  static double textScaleFactor(BuildContext context, {double maxTextScaleFactor = 2}) {
    final width = MediaQuery.of(context).size.width;
    double val = (width / 1400) * maxTextScaleFactor;
    return max(1, min(val, maxTextScaleFactor));
  }
}

Text(
     intro.subtitle,
     style: Theme.of(context).textTheme.subtitle1,
     textAlign: TextAlign.center,
     textScaleFactor: ScaleSize.textScaleFactor(context),
   ),

Вариант 2:

Использовать различную отрисовку виджетов в зависимости от размера экранов. Как-то типа:

Widget build(BuildContext context) {

  final isSmall = MediaQuery.of(context).size.width < 500;

  return Provider.value(

    value: isSmall

        ? AdaptativeTheme(

            smallFontSize: 10,

            bigSpace: 20,

            smallSpace: 10,

          )

        : AdaptativeTheme(

            smallFontSize: 20,

            bigSpace: 40,

            smallSpace: 12,

          ),

    child: Child(),

  );

}

Вариант 3:

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

   return Sizer(
        builder: (context, orientation, deviceType) {
        return
          Scaffold(
...
...
                          child: Container(
                            width: 48.sp,
                            height: 48.sp,
...
...
child: Text(ListEZS[index]["nameazs"],style: TextStyle(fontSize: 14.0.sp, color: Colors.black)),

Flutter: переименование пакета

Ситуация: при создании приложения было выбрано не то имя для пакета.

Решение: для решения проблемы, в pubspec.yaml добавим пакет change_app_package_name (в секцию dev_depencies), после чего в командной строке в папке приложения выполним:

flutter pub run change_app_package_name:main ru.цву.увцувцу

1c 7.7 Выборка всех элементов справочника без группировки

1С 7.7 не умеет выбирать все элементы справочника в запросе без группировки по какому то реквизиту. Обычно группируют по Коду. А что делать, если какой-то талантливый программист, решил что поле Код в справочнике например может быть не уникальным? Тогда будем группировать по текущему элементу справочника. Например:

	ТекстЗапроса=
	"//{{ЗАПРОС(СформироватьСЧТ)
	|ОбрабатыватьДокументы все;
	|Обрабатывать НеПомеченныеНаУдаление;
	|Владелец = Справочник.Счетчики.Владелец;
	|Наименование = Справочник.Счетчики.Наименование;
	|ТекущийЭлемент = Справочник.Счетчики.ТекущийЭлемент;
	|Группировка ТекущийЭлемент  Без Групп;
	|"//}}ЗАПРОС
	;             
            
		Запрос = СоздатьОбъект("Запрос");		
		Запрос.Выполнить(ТекстЗапроса);		 
		Пока Запрос.Группировка() = 1 Цикл
...
...

Драйвер принтера TCS-210 Linux

Ну не хотят еще до сих пор разработчики драйверов под Linux писать их нормально. Ну как можно сделать драйвера, без возможности добавить свои размеры бумаги? Только хардкор, только ручная правка файла ppd для CUPS.

В ниже представленном драйвере, добавлен размер бумаги 58х28мм, один из распространённых размеров для печати этикеток.

1 49 50 51 52 53 311