superlikers_core 0.1.1
superlikers_core: ^0.1.1 copied to clipboard
Servicios, entidades, repositorios y datasources compartidos entre las apps Flutter de Superlikers (mismo backend/ambientes, distinta campaign/apiKey por cliente).
superlikers_core #
Servicios, entidades, repositorios, datasources, modelos y mappers
compartidos entre las apps Flutter de Superlikers. Todas las apps hablan
con el mismo backend y los mismos ambientes (dev/prod); lo único que
cambia por cliente es la campaign y el apiKey.
Estructura #
package:superlikers_core/superlikers_core.dart— config, entidades de dominio y contratos abstractos (AuthDatasource,AuthRepository, etc.). Es lo que usa la capa de presentación/estado de cada app.package:superlikers_core/infraestructure.dart— implementaciones concretas: modelos JSON, mappers, datasource (Dio) y repository. Se importa aparte (normalmente con aliasas data) porque sus modelos comparten nombre con las entidades de dominio (ej.Participant).package:superlikers_core/inputs.dart— inputs de Formz reutilizables entre apps (email, password, teléfono, OTP, etc.). Se exporta aparte para que quien solo necesite la capa de datos no cargue con la dependencia deformz.
Uso #
En el main_dev.dart / main_prod.dart de cada app cliente:
import 'package:superlikers_core/superlikers_core.dart';
void main() {
SuperlikersConfig.setupDev(
campaign: 'uo',
apiKey: 'aaa138b17fe628900805dba5650c0b98',
);
app.bootstrap();
}
// main_prod.dart
SuperlikersConfig.setupProd(
campaign: 'uo',
apiKey: 'aaa138b17fe628900805dba5650c0b98',
);
Al armar el repository de un módulo:
import 'package:superlikers_core/superlikers_core.dart';
import 'package:superlikers_core/infraestructure.dart' as data;
final AuthRepository authRepository = data.AuthRepositoryImpl();
// `login`/`register` son genéricos: cada app arma el mapa de campos que
// su campaign necesite (login puede ser id_empleado, email, cédula, etc.;
// register puede pedir plaza/región/tienda o un set totalmente distinto).
final login = await authRepository.login(
participation: {'id_empleado': '123', 'password': 'secret'},
// en otro cliente: {'email': 'x@y.com', 'password': 'secret'}
);
final register = await authRepository.register(
properties: {
'nombres': 'Juan',
'apellidos': 'Pérez',
'email': 'juan@ejemplo.com',
'password': 'secret',
'password_confirm': 'secret',
'terms': true,
// + lo que pida esa campaign (plaza/región/tienda, cédula, etc.)
},
);
// `Participant.properties` trae cualquier campo del participante que el
// backend devuelva y no sea parte del set fijo (id/email/name/avatar/uid/
// uidType/state) — varía según el schema de cada campaign.
final plaza = login.participant.properties['plaza'];
Para cerrar sesión automáticamente ante un 401 de una llamada autenticada:
import 'package:superlikers_core/superlikers_core.dart';
onSessionExpired = () {
// limpiar el storage local y navegar al login
};
Módulos incluidos #
- Config / Environment (
SuperlikersConfig,Environment) - Network (
createPinnedDio,SessionExpiredInterceptor) KeyValueStorageService(token/uid cifrados, resto en shared_preferences)- Achievement, Auth (login/registro/passwords + verificación de teléfono, 2FA por email/SMS y por TOTP, cambio de contraseña autenticado, login form y requisitos de login), Blog, Comments (comentarios de blog), Documents (subida de documentos), Entries, External (+ External Forms: info/list/fillup/update), Goal (+ goal status), Localization, Metrics, Participant (+ ranking, ranking por segmento, puntos por periodo, public info, update avatar/user, búsqueda por tags, extracto y redención de puntos), Prizes, Redeemed prizes, Referrals, Upload photo, Votes
Todos siguen el mismo patrón: entidad de dominio -> datasource/repository abstractos -> modelo JSON -> mapper -> datasource/repository impl.
Inputs (Formz) #
package:superlikers_core/inputs.dart trae los validadores de formulario
que se repetían copy-pasteados entre apps: Email, Password,
ConfirmPassword, RequiredText, OtpCode, TermsAndConditions,
CountryCode, PhoneNumber, Username, NumberDocument, IdUser.
Cada uno expone sus reglas como parámetros opcionales del constructor (largo mínimo/máximo, requisitos de password, patrón de regex, lista de códigos de país, etc.) en vez de tenerlas fijas — la idea es que una app pueda ajustar las reglas sin tener que esperar una nueva versión de la librería:
import 'package:superlikers_core/inputs.dart';
// Reglas por defecto (min. 5 caracteres, 1 mayúscula, 1 carácter especial):
final password = Password.dirty(value);
// Una app que exige también número y mínimo 8 caracteres:
final strictPassword = Password.dirty(
value,
minLength: 8,
requireNumber: true,
);
final otp = OtpCode.dirty(value, length: 4); // OTP de 4 dígitos en vez de 6
No incluye validadores específicos de una sola app (ej. usuario de POS de corresponsalías) — esos se quedan en la app que los usa.
Qué se dejó fuera a propósito (no tienen datasource/repository en heroesoxxo, es decir no son servicios de API sino entidades derivadas o de UI/local, o son específicos de una sola app):
BadgesyPushMessage(notificaciones locales/FCM)deleteAccountUrl(URL estática específica de una app, no un servicio)football(proveedor externo ajeno al backend de Superlikers, específico del evento Mundial 2026 de heroesoxxo — no es un servicio compartido)
Si algún cliente los necesita como parte del SDK, se agregan después siguiendo el mismo patrón.
Seguridad de red #
Todo lo que sale por createPinnedDio() (usado por todos los datasource)
aplica:
- Certificate pinning a la CA raíz de
*.superlikers.com(Amazon Root CA 1) — rechaza cualquier certificado que no encadene a esa CA, aunque el sistema operativo confíe en él. - Solo HTTPS:
createPinnedDiolanzaArgumentErrorsibaseUrlno empieza conhttps://(defensa en profundidad ante un typo o un ambiente mal configurado). - Sin redirects automáticos (
followRedirects: false): evita que una respuesta 3xx baje de https a http sin que el caller se entere. - Timeouts de conexión/envío/recepción (15s/30s/30s) para no dejar conexiones — y credenciales en memoria — colgadas indefinidamente.
SessionExpiredInterceptor: cierra sesión ante un 401 solo si la request llevabaAuthorization(evita cerrar sesión por un 401 de login/credenciales).
KeyValueStorageServiceImpl guarda token/uid con flutter_secure_storage
usando EncryptedSharedPreferences en Android y first_unlock en iOS
(no se incluye en backups de iCloud/iTunes).
Ninguno de estos controles estaba explícito en heroesoxxo salvo el
certificate pinning (que sí se portó tal cual); el resto se agregó al
consolidar la librería porque ahora la comparten varios clientes.
Consumir el paquete desde una app #
Publicado en pub.dev:
dependencies:
superlikers_core: ^0.1.0
Para iterar en paralelo sin esperar un publish, también sirve como dependencia de path o git:
dependencies:
superlikers_core:
path: ../superlikers-library-flutter
# o, apuntando a un repo git:
# git:
# url: https://github.com/Wpcarmona/flutter-library-superlikers.git
# ref: main