pizzacorn_ui 0.0.114
pizzacorn_ui: ^0.0.114 copied to clipboard
Pizzacorn UI: a Flutter design system with configurable theme, typography, colors and reusable UI widgets.
🍕 Pizzacorn UI #
Pizzacorn UI es el design system oficial de Pizzacorn para Flutter: widgets reutilizables, tokens visuales, tipografía configurable, helpers de navegación, componentes responsive y un motor de paginación para Firestore listo para producción.
Está pensado para construir apps rápidas, limpias y con una identidad visual consistente en móvil, web y desktop.
✨ Qué incluye #
- 🎨 Theme tokens dinámicos: colores, paddings, radios, alturas y breakpoints configurables.
- 🔤 Sistema tipográfico: textos semánticos como
TextTitle,TextBodyyTextCaption. - 🔥 Paginación Firebase + Riverpod:
PaginationController,SliverListCustomySliverGridCustom. - 🍏 Segmented controls estilo Apple: controles simples, Cupertino y tipo tabs.
- 🧩 Widgets custom: botones, inputs, appbars, overlays, imágenes, dropdowns, calendarios y helpers.
- 💻 Adaptadores responsive: layouts y popups adaptados a web/escritorio.
📦 Instalación #
Añade la dependencia en tu pubspec.yaml:
dependencies:
pizzacorn_ui:
git:
url: https://github.com/Pizzacorn/pizzacorn_ui
Importa la librería en tus archivos Dart:
import 'package:pizzacorn_ui/pizzacorn_ui.dart';
Regla Pizzacorn: en los proyectos de app usamos barrel import desde
lib/config/imports.dart, así que normalmente exportaremospizzacorn_uidesde ahí y cada archivo de pantalla importará solo ese archivo global.
🚀 Configuración inicial #
Configura colores, layout y tipografías antes de runApp().
void main() async {
WidgetsFlutterBinding.ensureInitialized();
ConfigurePizzacornColors(
accent: const Color(0xFF5256D6),
background: const Color(0xFFFFFFFF),
backgroundSecondary: const Color(0xFFF4F4F6),
text: const Color(0xFF151727),
subtext: const Color(0xFF8E929E),
);
ConfigurePizzacornLayout(
radius: 8,
paddingSize: 20,
paddingSmallSize: 10,
buttonHeight: 44,
fieldHeight: 44,
webSize: 1100,
);
PizzacornTextConfig.configure(
primaryFontFamily: 'Montserrat',
secondaryFontFamily: 'Inter',
textSet: const PizzacornTextSet(
big: PizzacornTextStyleSet(
size: 32,
fontWeight: FontWeight.w700,
),
title: PizzacornTextStyleSet(
size: 22,
fontWeight: FontWeight.w700,
),
subtitle: PizzacornTextStyleSet(
size: 18,
fontWeight: FontWeight.w600,
),
body: PizzacornTextStyleSet(
size: 14,
fontWeight: FontWeight.w400,
),
button: PizzacornTextStyleSet(
size: 14,
fontWeight: FontWeight.w700,
),
caption: PizzacornTextStyleSet(
size: 12,
fontWeight: FontWeight.w400,
),
small: PizzacornTextStyleSet(
size: 10,
fontWeight: FontWeight.w400,
),
),
);
ConfigurePizzacornPagination(
databaseName: 'mi-base-firestore',
);
runApp(MyApp());
}
ConfigurePizzacornPagination es opcional. Si no lo llamas, o si pasas null, '' o '(default)', la paginación seguirá usando la base Firestore (default).
🧠 Reglas de oro #
- No uses
consten widgets de Pizzacorn UI. Muchos leen tokens dinámicos en runtime. - Textos con string posicional:
TextBody("Hola"), noTextBody(text: "Hola"). - Espaciado con tokens: usa
Space(SPACE_SMALL),PADDING,PADDING_ALL, etc. - Paginación con placeholder:
SliverListCustomySliverGridCustomsiempre deben recibiritemPlaceholder. - Firestore con modelos: el
fromJsondePaginationParams<T>debe construir tu modelo.
🎨 Design tokens #
Colores #
| Token | Uso |
|---|---|
COLOR_BACKGROUND |
Fondo principal |
COLOR_BACKGROUND_SECONDARY |
Fondos secundarios, inputs o scaffolds |
COLOR_BACKGROUND_TERCIARY |
Superficies elevadas |
COLOR_ACCENT |
Acción principal y marca |
COLOR_ACCENT_SECONDARY |
Acción secundaria |
COLOR_TEXT |
Texto principal |
COLOR_SUBTEXT |
Texto secundario |
COLOR_TEXT_BUTTONS |
Texto sobre botones de acento |
COLOR_BORDER |
Bordes y contornos |
COLOR_DIVIDER |
Separadores |
COLOR_ERROR |
Errores |
COLOR_ALERT |
Avisos |
COLOR_DONE |
Éxitos |
COLOR_INFO |
Información |
Layout #
| Token | Uso |
|---|---|
RADIUS |
Radio global |
WEBSIZE |
Breakpoint responsive |
BUTTON_HEIGHT |
Altura estándar de botones |
FIELD_HEIGHT |
Altura estándar de campos |
DOUBLE_PADDING |
Padding base numérico |
DOUBLE_PADDING_SMALL |
Padding compacto numérico |
PADDING |
Padding horizontal base |
PADDING_SMALL |
Padding horizontal compacto |
PADDING_ALL |
Padding completo base |
PADDING_ALL_SMALL |
Padding completo compacto |
Espacios #
| Token | Valor | Uso |
|---|---|---|
SPACE_BIGGER |
60 |
Separación grande entre bloques |
SPACE_BIG |
40 |
Separación amplia |
SPACE_MEDIUM |
20 |
Separación estándar |
SPACE_SMALL |
10 |
Elementos relacionados |
SPACE_SMALLEST |
5 |
Ajustes finos |
Column(
children: [
TextTitle("Resumen"),
Space(SPACE_SMALL),
TextBody("Contenido de la sección"),
],
)
🔤 Textos #
Los widgets de texto usan la configuración global de PizzacornTextConfig y los colores del tema.
| Widget | Uso recomendado |
|---|---|
TextBig("Texto") |
Mensajes de gran impacto |
TextTitle("Texto") |
Títulos de sección |
TextSubtitle("Texto") |
Subtítulos |
TextBody("Texto") |
Texto estándar |
TextButtonCustom("Texto") |
Texto dentro de botones |
TextCaption("Texto") |
Texto secundario, fechas, ayudas |
TextSmall("Texto") |
Microtexto |
TextCustom("Texto") |
Control manual de tamaño, peso y espaciado |
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextBig("Dashboard"),
Space(SPACE_SMALL),
TextTitle("Ventas de hoy"),
TextBody("Aquí se muestra el rendimiento de la tienda."),
TextCaption("Actualizado hace 2 minutos"),
],
)
También puedes usar estilos directamente:
Text(
"Texto nativo con estilo Pizzacorn",
style: styleBody(color: COLOR_TEXT, fontWeight: WEIGHT_BOLD),
)
🔥 Paginación Firestore #
La paginación combina:
PaginationParams<T>: configuración de colección, query y parser.paginationProvider: provider Riverpod auto-dispose.PaginationController<T>: carga inicial, refresh y fetch more.SliverListCustom<T>/SliverGridCustom<T>: render con skeleton, vacío, error y scroll infinito.
0. Configuración opcional de base de datos #
Por defecto se usa la base Firestore (default). Si tu proyecto usa otra base de datos, configúrala una vez al iniciar la app:
ConfigurePizzacornPagination(
databaseName: 'mi-base-firestore',
);
Para volver a (default), no llames a la función o pásale null, '' o '(default)'.
1. Define los parámetros #
final params = PaginationParams<UserModel>(
collection: "users",
limit: 20,
identifier: "users_home",
fromJson: (data) => UserModel.fromJson(data),
query: (q) => q.orderBy("createdAt", descending: true),
);
2. Lista paginada #
class UsersPage extends ConsumerWidget {
UsersPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final params = PaginationParams<UserModel>(
collection: "users",
limit: 20,
identifier: "users_home",
fromJson: (data) => UserModel.fromJson(data),
query: (q) => q.orderBy("createdAt", descending: true),
);
return Scaffold(
backgroundColor: COLOR_BACKGROUND,
body: CustomScrollView(
slivers: [
CupertinoSliverRefreshControl(
onRefresh: () {
return ref.read(paginationProvider(params).notifier).refresh();
},
),
SliverPadding(
padding: PADDING,
sliver: SliverListCustom<UserModel>(
params: params,
itemPlaceholder: UserModel(),
idExtractor: (userModel) => userModel.id,
itemBuilder: (userModel) {
return UserWidget(userModel: userModel);
},
),
),
],
),
);
}
}
3. Grid paginado #
SliverGridCustom<ProductModel>(
params: productParams,
itemPlaceholder: ProductModel(),
crossAxisCount: 2,
childAspectRatio: 0.78,
mainAxisSpacing: SPACE_SMALL,
crossAxisSpacing: SPACE_SMALL,
idExtractor: (productModel) => productModel.id,
itemBuilder: (productModel) {
return ProductCard(productModel: productModel);
},
)
Estados incluidos #
| Estado | Qué hace |
|---|---|
isLoading |
Muestra skeleton inicial |
isFetchingMore |
Muestra skeleton inferior |
hasMore |
Controla si se solicita la siguiente página |
error |
Muestra error inicial o error al cargar más |
🍏 Segmented controls #
SegmentedCupertinoCustom #
Ideal para filtros principales con look nativo Apple.
SegmentedCupertinoCustom(
items: ["Hoy", "Semana", "Mes"],
itemsSecondary: ["12", "48", "130"],
currentIndex: state.currentTab,
onValueChanged: (index) {
controller.changeTab(index);
},
)
SegmentedControlCustom #
Control custom con thumb animado y altura configurable.
SegmentedControlCustom(
items: ["Pendientes", "Pagadas"],
currentIndex: state.currentStatus,
height: 38,
thumbRadius: RADIUS,
onValueChanged: (index) {
controller.changeStatus(index);
},
)
SegmentedTabCustom #
Selector tipo tab con línea inferior animada.
SegmentedTabCustom(
items: ["Explorar", "Mis reservas"],
currentIndex: state.currentTab,
onValueChanged: (index) {
controller.changeTab(index);
},
)
🔘 Botones #
ButtonCustom #
Botón principal con soporte para texto, rich text, iconos IconData, SVG, borde, gradiente y semántica.
ButtonCustom(
text: "Guardar cambios",
prefixIcon: Icons.save_rounded,
onPressed: () {
controller.save();
},
)
ButtonCustom(
text: "Cancelar",
border: true,
color: Colors.transparent,
textColor: COLOR_TEXT,
borderColor: COLOR_BORDER,
onPressed: () => Navigator.pop(context),
)
ButtonCustomIcon #
Botón solo icono con fondo, borde, SVG o IconData.
ButtonCustomIcon(
icon: Icons.edit_rounded,
color: COLOR_TEXT,
colorBackground: COLOR_BACKGROUND_SECONDARY,
borderColor: COLOR_BORDER,
borderWidth: 1,
onPressed: () {
controller.edit();
},
)
OptionButton #
Fila accionable para ajustes, perfiles o menús.
OptionButton(
title: "Métodos de pago",
prefixIcon: Icons.credit_card_rounded,
suffixIcon: Icons.chevron_right_rounded,
onTap: () => goTo(context, PaymentsPage()),
)
MoreMenuButton #
Menú rápido de acciones comunes: editar, eliminar, duplicar y reportar.
MoreMenuButton(
onEdit: () {
controller.edit();
},
onDelete: () {
controller.delete();
},
)
📝 Formularios e inputs #
TextFieldCustom #
Campo de texto con tokens visuales, iconos, password, validación, max length, teclado numérico y shadow.
TextFieldCustom(
controller: nameController,
hintText: "Nombre",
prefixIcon: Icons.person_rounded,
errorText: "Escribe un nombre",
onChanged: (value) {
controller.changeName(value);
},
)
TextFieldCustom(
controller: passwordController,
hintText: "Contraseña",
password: true,
)
Otros campos #
| Widget | Uso |
|---|---|
TextFieldPhoneCustom |
Teléfono con selector de país |
DatePickerField |
Campo que abre selector de fecha |
TimePickerField |
Campo que abre selector de hora |
TitleAndTextField |
Título + campo en bloque |
SelectorList |
Lista de opciones seleccionables |
CheckboxPolitics |
Checkbox para políticas/condiciones |
🔽 Dropdowns #
DropdownCustom<T> #
Dropdown basado en PopupMenuButton, con nombre por función.
DropdownCustom<CategoryModel>(
items: categories,
initialItem: state.categoryModel,
hintText: "Selecciona categoría",
tooltip: "Categorías",
getName: (categoryModel) => categoryModel.name,
onChanged: (categoryModel) {
controller.changeCategory(categoryModel);
},
)
DropdownSearch<T> #
Versión con búsqueda para listas más largas.
DropdownSearch<UserModel>(
items: users,
initialItem: state.userModel,
hintText: "Buscar usuario",
tooltip: "Usuarios",
getName: (userModel) => userModel.name,
onChanged: (userModel) {
controller.changeUser(userModel);
},
)
🧭 AppBars y navegación #
| Helper / Widget | Uso |
|---|---|
AppBarBack |
AppBar con back estándar |
AppBarBackAction |
AppBar con back y acción |
AppBarClose |
AppBar con cierre |
AppBarDrag |
Barra para bottom sheets o zonas drag |
AppBarHome |
AppBar principal con menú, logo y perfil |
goTo |
Navega con back |
goToNoBack |
Navega sin animación/back estándar |
goToClear |
Limpia stack y navega |
goBack |
Cierra pantalla devolviendo resultado opcional |
appBar: AppBarBack(
context: context,
title: "Detalle",
)
goTo(context, UserDetailPage(userModel: userModel));
🧊 Overlays, loading y sheets #
Loading #
Envuelve una pantalla y muestra bloqueo cuando loading es true.
Loading(
loading: state.isLoading,
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: TextTitle("Contenido"),
),
],
),
)
LoadingWithText #
LoadingWithText(
loading: state.isLoading,
text: "Guardando...",
child: body,
)
LoadingCustomWidget #
Loader compacto para botones, cards o zonas pequeñas donde no necesitas bloquear toda la pantalla.
LoadingCustomWidget(
size: 22,
strokeWidth: 2.5,
)
Bottom sheets #
openBottomSheet(
context,
BottomSheetCustomTwoButtons(
leftTitle: "Cancelar",
rightTitle: "Eliminar",
onLeftPressed: () => goBack(context),
onRightPressed: () => controller.delete(),
rightColor: COLOR_ERROR,
),
);
BottomSheetPopUps #
Barra inferior de acciones para popups o editores con acción principal y eliminación opcional.
BottomSheetPopUps(
title: "Guardar",
onPressedSave: () {
controller.save();
},
onPressedDelete: () {
controller.delete();
},
)
Widgets disponibles:
| Widget / Helper | Uso |
|---|---|
Loading |
Overlay bloqueante con loader centrado |
LoadingWithText |
Overlay bloqueante con texto de estado |
LoadingCustomWidget |
Loader compacto reutilizable |
BottomSheetCustomOneButton |
Sheet inferior con un botón |
BottomSheetCustomTwoButtons |
Sheet inferior con dos botones |
BottomSheetPopUps |
Barra inferior para guardar/eliminar en popups |
openBottomSheet |
Abre bottom sheet estándar con altura configurable |
openStupidSheet |
Abre sheet flotante usando stupid_simple_sheet |
openStupidCupertinoSheet |
Abre sheet Cupertino con navegación integrada |
openBottomNoBack |
Sheet no descartable |
openDialog |
Dialog helper |
openSnackbar |
Snackbar helper con estados isError, isAlert, isDone |
🖼️ Imágenes, iconos y media #
ImageCustom #
Imagen de red con placeholder, borde, radio, modo circular y overlay.
ImageCustom(
imageUrl: productModel.image,
height: 180,
borderRadius: RADIUS,
overlay: Align(
alignment: Alignment.bottomLeft,
child: Padding(
padding: PADDING_ALL_SMALL,
child: TextCaption("Nuevo", color: COLOR_TEXT_BUTTONS),
),
),
)
Otros widgets #
| Widget / Helper | Uso |
|---|---|
ProfileImageCustom |
Avatar de usuario |
FullScreenImagePage |
Visor de imagen a pantalla completa |
SvgCustom |
SVG con color |
SvgCustomNoColor |
SVG sin recolorear |
FeaturedIconCustom |
Icono destacado |
FeaturedTitle |
Bloque con icono y título |
FeaturedSubtitle |
Bloque con icono, título y subtítulo |
IconPickerCustom |
Selector de iconos Material |
IconGalleryPage |
Galería de iconos UIcons |
📅 Calendarios y pickers #
| Widget | Uso |
|---|---|
MonthlyCalendar |
Calendario mensual con selección simple o por rango |
SliderCalendar |
Calendario horizontal |
DatePickerCustom |
Selector custom de fecha |
TimePickerCustom |
Selector custom de hora |
IconPickerCustom |
Selector de IconData en grid |
IconGalleryPage |
Galería buscable de iconos UIcons Pro |
DatePickerField |
Campo de formulario que abre selector de fecha |
TimePickerField |
Campo de formulario que abre selector de hora |
MonthlyCalendar(
startDate: DateTime.now(),
initialDate: state.selectedDate,
selectionMode: CalendarSelectionMode.single,
markedDates: state.reservationDates,
blockedWeekdays: [DateTime.sunday],
style: CalendarStyle(),
onDaySelected: (date) {
controller.changeDate(date);
},
)
DatePickerCustom #
Picker Cupertino para fecha, pensado para abrirse dentro de un bottom sheet. Soporta fecha mínima y máxima.
openBottomSheet(
context,
DatePickerCustom(
initialDateTime: state.selectedDate,
minimumDate: DateTime.now(),
maximumDate: DateTime.now().add(Duration(days: 365)),
onDateTimeChanged: (date) {
controller.changeDate(date);
},
),
height: 350,
);
TimePickerCustom #
Picker Cupertino para hora en formato 24h.
openBottomSheet(
context,
TimePickerCustom(
initialDateTime: state.selectedTime,
onDateTimeChanged: (date) {
controller.changeTime(date);
},
),
height: 350,
);
IconPickerCustom #
Selector visual de IconData. Puedes usar la lista interna por defecto o pasar una lista propia.
IconPickerCustom(
selectedIcon: state.icon,
accentColor: COLOR_ACCENT,
crossAxisCount: 8,
onSelected: (icon) {
controller.changeIcon(icon);
},
)
IconGalleryPage #
Galería de iconos UIcons Pro con búsqueda, paginación interna y selección por nombre.
goTo(
context,
IconGalleryPage(
initialSelectedIconName: state.iconName,
onIconSelected: (iconName) {
controller.changeIconName(iconName);
},
),
);
💻 Responsive web #
| Widget | Uso |
|---|---|
WebAdapterCustom |
Adapta layouts según WEBSIZE |
WebPopupCustom |
Contenedor/popup optimizado para desktop |
HoverCustom |
Feedback visual en hover |
BlurCustom |
Efecto blur |
ShowUpCustom |
Animación de entrada |
ShimmerCustom |
Placeholder shimmer |
🌍 Multiidioma #
Pizzacorn UI incluye helpers para cargar traducciones desde Excel y selectores de idioma.
await initMultilanguage(defaultLang: 'es');
| Widget / Helper | Uso |
|---|---|
loadExcelTranslations |
Carga traducciones desde Excel |
initMultilanguage |
Inicializa localización |
LanguageSelector |
Selector completo de idioma |
LanguageSmallSelector |
Selector compacto |
getFlagEmoji |
Devuelve bandera por código |
🧰 Catálogo rápido #
| Categoría | Widgets / helpers |
|---|---|
| Textos | TextBig, TextTitle, TextSubtitle, TextBody, TextButtonCustom, TextCaption, TextSmall, TextCustom |
| Paginación | ConfigurePizzacornPagination, PizzacornPaginationConfig, PaginationParams, PaginationState, paginationProvider, SliverListCustom, SliverGridCustom |
| Segments | SegmentedCupertinoCustom, SegmentedControlCustom, SegmentedTabCustom |
| Botones | ButtonCustom, ButtonCustomIcon, OptionButton, MoreMenuButton, PopupMenuOptions |
| Formularios | TextFieldCustom, TextFieldPhoneCustom, DatePickerField, TimePickerField, TitleAndTextField, SelectorList, CheckboxPolitics |
| Dropdowns | DropdownCustom, DropdownSearch |
| AppBars | AppBarBack, AppBarBackAction, AppBarClose, AppBarDrag, AppBarHome |
| Overlays | Loading, LoadingWithText, LoadingCustomWidget, BottomSheetCustomOneButton, BottomSheetCustomTwoButtons, BottomSheetPopUps, openBottomSheet, openStupidSheet, openStupidCupertinoSheet, openBottomNoBack, openDialog, openSnackbar |
| Imágenes | ImageCustom, ProfileImageCustom, FullScreenImagePage, ImagePublish, CropPage |
| Iconos | SvgCustom, SvgCustomNoColor, FeaturedIconCustom, FeaturedTitle, FeaturedSubtitle, IconPickerCustom, IconGalleryPage |
| Calendarios y pickers | MonthlyCalendar, SliderCalendar, DatePickerCustom, TimePickerCustom, IconPickerCustom, IconGalleryPage, DatePickerField, TimePickerField |
| Navegación | goTo, goToNoBack, goToClear, goBack, openBottomSheet, openDialog, openSnackbar |
| Decoración | DecorationCustom, BoxDecorationCustom, BorderRadiusCustomAll, BoxShadowCustom |
| Efectos | ShimmerCustom, ShowUpCustom, HoverCustom, BlurCustom |
| Login | LoginCustomPage, SelectorCustomPage, LoginCustomAuthRepository, VerifyEmailCustomPage |
| Modelos | FileModel, SocialModel |
| Utils | parseDate, BestOnColor, ContrastRatio, closeKeyboard, UIconsMappingHelper |
✅ Ejemplo de pantalla Pizzacorn #
class ReservationsPage extends ConsumerWidget {
ReservationsPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(reservationsProvider);
final controller = ref.read(reservationsProvider.notifier);
final params = controller.getPaginationParams();
return Scaffold(
backgroundColor: COLOR_BACKGROUND,
body: Loading(
loading: state.isLoading,
child: CustomScrollView(
slivers: [
ReservationsAppBar(),
CupertinoSliverRefreshControl(
onRefresh: () {
return ref.read(paginationProvider(params).notifier).refresh();
},
),
SliverPadding(
padding: PADDING,
sliver: SliverToBoxAdapter(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextTitle("Reservas"),
Space(SPACE_SMALL),
TextBody("Gestiona las próximas reservas."),
Space(SPACE_MEDIUM),
SegmentedCupertinoCustom(
items: ["Próximas", "Historial"],
currentIndex: state.currentTab,
onValueChanged: (index) {
controller.changeTab(index);
},
),
Space(SPACE_MEDIUM),
],
),
),
),
SliverPadding(
padding: PADDING,
sliver: SliverListCustom<ReservationModel>(
params: params,
itemPlaceholder: ReservationModel(),
idExtractor: (reservationModel) => reservationModel.id,
itemBuilder: (reservationModel) {
return ReservationWidget(
reservationModel: reservationModel,
);
},
),
),
],
),
),
);
}
}
🧑🍳 Equipo #
Hecho con mimo por el equipo Pizzacorn.
Porque una app sin sistema visual es como una pizza sin queso: puede existir, pero no debería. 🍕✨