Modul comú OSAM Flutter
May 14, 2026 · View on GitHub
Historial de versions (Flutter)
8.2.1
- Control de versions avançat: S'han afegit camps per filtrar per versió del sistema operatiu (
osVersion) i per model de dispositiu (models). - Noves funcionalitats: S'ha afegit el mètode
isOnlineper comprovar la connectivitat amb el backend de manera asíncrona. - Personalització de UI: Els mètodes
versionControliratingara accepten els paràmetresisDarkModeiapplyComModStylesper a una millor integració visual. - Millores en analítica: S'ha implementat el nou event
language_changei s'ha millorat el registre d'events inicials. - Gestió del context: S'ha afegit el mètode
setActivityper a Android per gestionar millor els canvis de context i cicle de vida. - Timeout: Implementació de missatges d'error per timeout quan el backend no respon.
8.1.0 - 8.1.8
- FCM Token: S'ha afegit el suport per obtenir el token de Firebase Cloud Messaging (FCM) mitjançant el mètode
getFCMToken. - FirstTimeOrUpdate: Aquest mètode s'encarrega de subscriure's automàticament al tòpic de la versió en l'idioma de l'aplicació si és la primera vegada que s'obre, o d'actualitzar-lo si la versió ha canviat.
- Subscribe and unsubscribe from topic: 2 mètodes que ajuden amb les subscripcions i cancel·lacions de subscripcions a topics d'idioma i versió.
- Millores d'UI i accessibilitat al popup de control de versions: S'ha millorat l'accessibilitat al popup del control de versions, incrementat el contrast dels seus elements i assegurat diferents tipus de navegació accessible.
8.0.0
- Gestió de Topics: S'han implementat els mètodes
firstTimeOrUpdateEvent,subscribeToCustomTopiciunsubscribeToCustomTopicper a una gestió completa de les notificacions push. - Event de canvi d'idioma: S'ha afegit
changeLanguageEventper orquestrar el canvi d'idioma, analítica i topics. - Migració a KMP: Refactorització important per utilitzar Kotlin Multiplatform de manera més eficient i unificar la lògica de negoci.
- Temps d'acceptar en control de versions: Quan al popup del control de versions es prem a acceptar en info o lazy mode el popup no tornarà a aparèixer en una quantitat de temps que sigui definida.
6.0.0
- Cal utilitzar la versió de Dart SDK ^3.5.0 i Flutter 3.24.3.
- El fallback d'idioma ha canviat de Català (
ca) a Anglès (en).
De 5.0.x a 6.0.0
- El paquet s'ha reanomenat de
common_module_flutteraosam_common_module_flutter. - Cal actualitzar el
pubspec.yamli els imports. - Per importar, utilitzeu només:
import 'package:osam_common_module_flutter/osam_common_module_flutter.dart';.
Introdució
Des de la OSAM es proporcionen mòduls per realitzar un conjunt de tasques comunes a totes les apps publicades per l'Ajuntament de Barcelona. El mòdul comú (Flutter) està disponible com a repositori a Github
Useful links:
- Lògica de negoci
- Backend (json)
- Modul comú requirements catalá o espanyol
A partir de la versió 3.0.0 la llibreria es un wrapper de la desenvolupada en Kotlin Multiplatform
Com es fa servir?
-
Afegeix aquesta dependència al pubspec:
osam_common_module_flutter: git: url: https://github.com/AjuntamentdeBarcelona/modul_comu_osam_flutter.git ref: '8.2.0'En cas que les dependències de Firebase Analytics, Firebase Performance, Firebase Messaging i Firebase Crashlytics fallin, cal afegir aquestes aquesta configuració al pubspec:
dependency_overrides: firebase_crashlytics_platform_interface: 3.1.13 firebase_analytics_platform_interface: 3.0.5 firebase_performance_platform_interface: ^0.1.1+18 firebase_messaging_platform_interface: ^5.0.0 -
Actualitzar mitjançant el comandament 'flutter packages get' les dependències.
-
Afegir el import on sigui necessari:
import 'package:osam_common_module_flutter/osam_common_module_flutter.dart'; -
Per utilitzar la llibreria necessitem crear una instància de la clase OSAM, la qual es fa a través del mètode estàtic (i asíncron)
init().Tenim un example de inicialització on example/lib/di/di.dart
En el cas de l'app d'exemple, accedim a la instància creada a través de OsamRepository
Configuració de l'entorn
En l'exemple anterior tenim la URL del mòdul comú hardcoded a la pròpia lògica. Però aquesta URL s'ha d'externalitzar com a variable de configuració, d'aquesta manera es pot generar la versió de producció només substituint el fitxer amb els valors correctes.
Per fer-ho només cal crear el fitxer ".env" a l'arrel del projecte de Flutter i carregar aquest fitxer amb el plugin flutter_dotenv. En aquesta URL hi ha la documentació de com afegir aquest fitxer i com carregar les dades al codi
El fitxer ".env" ha de contenir la URL del mòdul comú en la variable "COMMON_MODULE_URL". Quedaria de la següent manera:
COMMON_MODULE_URL=https://dev-osam-modul-comu.dtibcn.cat/
Configuració a la part nativa
Android
Versió de Kotlin
S'ha d'utilitzar, com a mínim, la versió 1.9.0 de Kotlin.
compileSdkVersion
L'atribut compileSdkVersion del fitxer build.gradle de l'aplicació d'Android ha de tenir el valor
31 o superior.
Multidex
Si la versió mínima de Android a la que es dona suport (minSdkVersion) a l'aplicació d'Android es inferior a 21, serà necessari habilitar multidex, ja que, si no, apareixerà un error al compilar.
Estil
Si volem que els pop-ups es vegin amb l'estil correcte, haurem d'utilitzar un estil que extengui d' AppCompat. A l'aplicació de Android que crea Flutter s'hauria de modificar els fitxers styles.xml
iOS
Podfile
En el Podfile del projecte d'iOS creat por Flutter haurem d'incloure el pod del mòdul comú de Kotlin Multiplatform Mobile. Això es fa incloent la següent linea:
pod 'OSAMCommon', :git => 'https://github.com/AjuntamentdeBarcelona/modul_comu_osam.git', :tag => '3.2.0'
El Podfile quedaria de la següent manera
Implementació control de versions
Per crear el missatge d'alerta, únicament hem de cridar a la funció que descarregarà el json amb les variables ja definides i mostrarà l'alerta segons els valors rebuts. Exemple:
void _onVersionControl() async {
final result = await DI.osamSdk.versionControl(
language: Language.CA,
);
switch (result) {
case VersionControlResponse.ACCEPTED:
break;
case VersionControlResponse.DISMISSED:
break;
case VersionControlResponse.CANCELLED:
break;
case VersionControlResponse.ERROR:
break;
}
}
No és necessari capturar la resposta si no es necessita realitzar cap acció adicional, ja que el mòdul comú s'encarregarà de mostrar el popup si es compleixen els requeriments per a que es mostri
Mostrar el control de versions cada vegada que l'app torna a primer pla
Si és necessari executar el control de versions cada vegada que l'app torna a primer pla, a l'app demo hem implementat aquesta funcionalitat en un mixin anomenat OsamVersionChecker:
class _MyHomePageState extends State<MyHomePage> with OsamVersionChecker {}
Hem utilitzat el package flutter_fgbg:
mixin OsamVersionChecker<T extends StatefulWidget> on State<T> implements RouteAware {
StreamSubscription<FGBGType> subscription;
@override
void initState() {
super.initState();
subscription = FGBGEvents.stream.listen((event) async {
if (event.toString() == "FGBGType.foreground") {
await DI.osamRepository.checkForUpdates(context);
}
});
}
}
Implementació control de valoració
Per crear el missatge d'alerta, únicament hem de cridar a la funció que descarregarà el json amb les variables ja definides i mostrarà l'alerta segons els valors rebuts. Exemple:
void _onRating() async {
final result = await DI.osamSdk.rating(
language: Language.EN,
);
switch (result) {
case RatingControlResponse.ACCEPTED:
break;
case RatingControlResponse.DISMISSED:
break;
case RatingControlResponse.CANCELLED:
break;
}
}
No és necessari capturar la resposta si no es necessita realitzar cap acció adicional, ja que el mòdul comú s'encarregarà de mostrar el popup si es compleixen els requeriments per a que es mostri
Implementació d'obtenció de informació de dispositiu i applicació
Per obtenir la informació del dispositiu i de l'aplicació Exemple:
void _onDeviceInformation(BuildContext context) async {
final result = await DI.osamRepository.deviceInformation();
_showToast(context, "${result.platformName}");
_showToast(context, "${result.platformVersion}");
_showToast(context, "${result.platformModel}");
}
void _onAppInformation(BuildContext context) async {
final result = await DI.osamRepository.appInformation();
_showToast(context, "${result.appName}");
_showToast(context, "${result.appVersionName}");
_showToast(context, "${result.appVersionCode}");
}
Implementació per enviar l'esdeveniment de canvi d'idioma a l'aplicació
Aquesta funció orquestra tota la lògica associada al canvi d'idioma de l'aplicació. En ser invocada, actualitza les preferències locals desant tant l'idioma nou com l'anterior, registra un esdeveniment language_change a Firebase Analytics amb els detalls del canvi, i actualitza la subscripció als topics de notificacions de Firebase Cloud Messaging per garantir que l'usuari rebi les comunicacions en l'idioma seleccionat.
Exemple:
void _onChangeLanguageEvent(BuildContext context) async {
final result = await DI.osamRepository.changeLanguageEvent();
if (context.mounted) {
switch (result) {
case AppLanguageResponse.SUCCESS:
_showToast(context, AppLanguageResponse.SUCCESS.name);
break;
case AppLanguageResponse.UNCHANGED:
_showToast(context, AppLanguageResponse.UNCHANGED.name);
break;
case AppLanguageResponse.ERROR:
_showToast(context, AppLanguageResponse.ERROR.name);
break;
}
}
}
Com funciona el event del'esdeveniment d'inici o actualització de l'app
Aquesta funció, que s'ha de cridar en iniciar l'aplicació, gestiona la subscripció inicial o l'actualització dels topics de notificacions per assegurar que el dispositiu estigui subscrit al canal correcte de Firebase Cloud Messaging. El procés recupera l'últim topic registrat, construeix el nou basant-se en la versió de l'app i l'idioma del dispositiu, actualitza la subscripció a Firebase donant de baixa l'antic i registrant el nou, i finalment desa localment la informació de la nova versió per a futures execucions.
void _onFirstTimeOrUpdateEvent(BuildContext context) async {
final result = await DI.osamRepository.firstTimeOrUpdateEvent();
if (context.mounted) {
switch (result) {
case AppLanguageResponse.SUCCESS:
_showToast(context, AppLanguageResponse.SUCCESS.name);
break;
case AppLanguageResponse.UNCHANGED:
_showToast(context, AppLanguageResponse.UNCHANGED.name);
break;
case AppLanguageResponse.ERROR:
_showToast(context, AppLanguageResponse.ERROR.name);
break;
}
}
}
Com funciona el event de la subscripció a un topic personalitzat
Aquesta funció permet subscriure l'aplicació a un topic de notificacions de Firebase amb un nom específic i personalitzat.
És útil per a campanyes de màrqueting o per segmentar usuaris en grups que no depenen de la versió de l'app o de l'idioma. La funció s'encarrega de gestionar la comunicació amb Firebase per realitzar la subscripció de manera asíncrona.
void _onSubscribeToCustomTopic(BuildContext context, String topic) async {
final result = await DI.osamRepository.subscribeToCustomTopic(topic: topic);
if (context.mounted) {
switch (result) {
case SubscriptionResponse.accepted:
_showToast(context, SubscriptionResponse.accepted.name);
break;
case SubscriptionResponse.error:
_showToast(context, SubscriptionResponse.error.name);
break;
}
}
}
Com funciona el event de la desubscripció a un topic personalitzat
Aquesta funció permet desubscriure l'aplicació d'un topic de notificacions de Firebase amb un nom específic i personalitzat.
És l'operació inversa a subscribeToCustomTopic i és útil per aturar la recepció de notificacions d'una campanya concreta o per netejar subscripcions quan ja no són necessàries. La funció s'encarrega de gestionar la comunicació amb Firebase per realitzar la desubscripció de manera asíncrona.
void _onUnsubscribeToCustomTopic(BuildContext context, String topic) async {
final result = await DI.osamRepository.unsubscribeToCustomTopic(topic: topic);
if (context.mounted) {
switch (result) {
case SubscriptionResponse.accepted:
_showToast(context, SubscriptionResponse.accepted.name);
break;
case SubscriptionResponse.error:
_showToast(context, SubscriptionResponse.error.name);
break;
}
}
}
Com funciona el event per obtenir el token de Firebase
Aquesta funció permet obtenir el token de registre de Firebase Cloud Messaging (FCM) del dispositiu. Aquest token és un identificador únic que s'utilitza per enviar notificacions push directament a un dispositiu específic. L'operació es realitza de manera asíncrona.
void _onGetFCMToken(BuildContext context) async {
final result = await DI.osamRepository.getFCMToken();
if (context.mounted) {
switch (result) {
case Success(token: final token):
_showToast(context, 'SUCCESS: $token');
case Error(error: final error):
_showToast(context, 'ERROR: ${error.toString()}');
}
}
}
Perquè aquesta funció s’executi correctament, és necessari inicialitzar la lògica del MessagingEvent. Pots veure’n un exemple a example.lib.di.di.dart, tal com es mostra a continuació:
static _onMessagingEvent(String topic, String action) async {
switch (action) {
case 'subscribe':
await FirebaseMessaging.instance.subscribeToTopic(topic);
break;
case 'unsubscribe':
await FirebaseMessaging.instance.unsubscribeFromTopic(topic);
break;
case 'getToken':
cacheTokenForNative();
break;
default:
debugPrint("MessagingEvent error: Unknown action $action");
}
}
static Future<void> cacheTokenForNative() async {
String? token = await FirebaseMessaging.instance.getToken();
if (token != null) {
// Save token where native side can access it, e.g. SharedPreferences
final prefs = await SharedPreferences.getInstance();
await prefs.setString('fcm_token', token);
}
}
Flutter app demo
Hi ha una app demo en aquest mòdul flutter amb moltes funcionalitats disponibles que poden ajudar a entendre com funciona el mòdul comú i veure quines accions es duen a terme i com.