pragma_design_system

Flutter library focused on mobile experiences that bundles Pragma's design tokens, base themes, and reusable UI components.

Features

  • Consistent color, typography, spacing, and responsive grid tokens.
  • PragmaTheme with light/dark variants and Material 3 enabled by default.
  • Glow-based loading components (PragmaLoadingWidget) with circular and linear variants.
  • Multi-state tables (PragmaTableWidget) with hover glow, tone presets, and compact density.
  • Neon pagination rows (PragmaPaginationWidget) with gradient capsule, summary builder, per-page dropdown, and light/dark surfaces.
  • Filter capsules (PragmaFilterWidget) with multi-select overlays, helper text, tag summaries, and light/dark surfaces.
  • Gradient tooltips (PragmaTooltipWidget) with arrow placements, optional title/icon/button combos, delays, and touch-friendly toggles.
  • Search-first input (PragmaSearchWidget) with neon glow, tone presets, size options, and dropdown-ready callbacks.
  • Rich text areas (PragmaTextAreaWidget) with multi-line support, focus glow, validation states, and optional character counter.
  • Neon tags (PragmaTagWidget) with gradient capsules, avatar slot, hover/pressed glow, and removable actions.
  • Radio pills (PragmaRadioButtonWidget) with neon stroke, optional helper text, hover/pressed glow, and disabled styling.
  • Glow checkboxes (PragmaCheckboxWidget) with multi-select support, indeterminate state, dense mode, and hover/pressed neon outline.
  • Status badges (PragmaBadgeWidget) with light/dark palettes, icon slot, tone presets, and compact padding.
  • Accessible components (PragmaButton, PragmaCard, PragmaIconButtonWidget, PragmaInputWidget, PragmaToastWidget, PragmaAccordionWidget, PragmaColorTokenRowWidget, PragmaThemeEditorWidget, PragmaLogoWidget).
  • Theme lab sample that lets you edit colors/typography in real time and export a JSON payload backed by ModelThemePragma.
  • PragmaGridTokens, viewport helpers, and the PragmaGridContainer widget to debug layouts.
  • Component modeling (ModelPragmaComponent, ModelAnatomyAttribute) to sync documentation and showcases from JSON.
  • Example app ready to run and validate (includes a "Grid debugger" page).

Installation

Add the package to your pubspec.yaml:

dependencies:
	pragma_design_system: ^1.3.0

Then run:

flutter pub get

Quick start

import 'package:flutter/material.dart';
import 'package:pragma_design_system/pragma_design_system.dart';

class PragmaApp extends StatelessWidget {
	const PragmaApp({super.key});

	@override
	Widget build(BuildContext context) {
		return MaterialApp(
			title: 'Pragma Design System',
			theme: PragmaTheme.light(),
			darkTheme: PragmaTheme.dark(),
			home: const PragmaHome(),
		);
	}
}

Tokens and components

  • Colors: PragmaColors exposes brand-aware ColorScheme definitions for light and dark modes.
  • Typography: PragmaTypography defines responsive scales built on top of Google Fonts.
  • Spacing: PragmaSpacing concentrates 4pt-system values and handy utilities.
  • Radius: PragmaBorderRadiusTokens and PragmaBorderRadius keep rounded corners consistent in 4/8dp steps.
  • Rounded-corner guidance lives in doc/rounded_corners.md, covering increments, implementation, and heuristics per component size.
  • Glow search guidance vive en doc/search.md, explicando anatomia, estados y patrones con dropdown list.
  • Text area guidance vive en doc/textarea.md, detallando anatomía, estados focus/error/success y mejores prácticas para copy largo.
  • Tag guidance vive en doc/tags.md, cubriendo anatomía, estados active/hover/pressed/disabled y flujos para remover participantes.
  • Radio guidance vive en doc/radio_button.md, describiendo anatomía, tokens y combinaciones unselected/hover/disabled para grupos exclusivos.
  • Checkbox guidance vive en doc/checkbox.md, explicando estados unchecked/checked/indeterminate, glow morado y patrones de "seleccionar todos".
  • Filter guidance vive en doc/filter.md, cubriendo estados default/hover/open, paneles con checkboxes y uso de tags persistentes.
  • Tooltip guidance vive en doc/tooltip.md, detallando anatomía light/dark, delays, arrow placements y patrones touch.
  • Pagination guidance vive en doc/pagination.md, documentando cápsula, gaps, summary y selector por página.
  • Badge guidance vive en doc/badge.md, detallando tonos light/dark, anatomía y casos de uso informativos.
  • Opacity: PragmaOpacityTokens and PragmaOpacity constrain overlays to 8/30/60 intervals using Color.withValues for Flutter 3.22+.
  • Domain models: ModelPragmaComponent, ModelAnatomyAttribute, ModelFieldState, ModelColorToken, and ModelThemePragma serialize the documentation sourced from Figma, power the input widgets, and guarantee JSON roundtrips.
  • Grid: PragmaGridTokens, getGridConfigFromContext, PragmaGridContainer, and PragmaScaleBox help replicate the official grid, respect gutters, and scale full mockups.
  • Components: Widgets such as PragmaPrimaryButton, PragmaSecondaryButton, PragmaButton.icon, PragmaCard, PragmaCardWidget, PragmaDropdownWidget, PragmaInputWidget, PragmaToastWidget, PragmaAvatarWidget, PragmaBreadcrumbWidget, PragmaAccordionWidget, PragmaColorTokenRowWidget, PragmaThemeEditorWidget, PragmaLogoWidget, PragmaCalendarWidget, PragmaLoadingWidget, or PragmaTableWidget ship consistent states and elevation.

Avatar quick sample

PragmaAvatarWidget(
	radius: 28,
	initials: 'PD',
	imageUrl: 'https://cdn.pragma.co/avatar.jpg',
	style: PragmaAvatarStyle.primary,
	tooltip: 'Pragma Designer',
)

Button quick sample

PragmaPrimaryButton(
	label: 'Guardar cambios',
	one: PragmaButtonTone.brand,
	onPressed: () {},
)

PragmaButton.icon(
	label: 'Ver detalles',
	icon: Icons.open_in_new,
	hierarchy: PragmaButtonHierarchy.tertiary,
	onPressed: () {},
)

Icon button quick sample

PragmaIconButtonWidget(
	icon: Icons.add,
	style: PragmaIconButtonStyle.filledLight,
	onPressed: () {},
)

PragmaIconButtonWidget(
	icon: Icons.close,
	style: PragmaIconButtonStyle.outlinedDark,
	size: PragmaIconButtonSize.compact,
	onPressed: () {},
)

Input quick sample

final PragmaInputController controller = PragmaInputController(
	ModelFieldState(
		suggestions: <String>['Discovery Lab', 'Growth', 'Mobile Core'],
	),
);

PragmaInputWidget(
	label: 'Nombre del squad',
	controller: controller,
	placeholder: 'Escribe un equipo',
	helperText: 'Filtramos sugerencias automáticamente',
	enablePasswordToggle: true,
	obscureText: true,
	onChanged: (String value) {
		controller
			..setValidation(isDirty: true, isValid: value.isNotEmpty)
			..setError(value.isEmpty ? 'Dato requerido' : null);
	},
);

Search quick sample

final TextEditingController searchController = TextEditingController();

PragmaSearchWidget(
	controller: searchController,
	placeholder: 'Busca squads o features',
	tone: PragmaSearchTone.dark,
	size: PragmaSearchSize.large,
	infoText: 'Escribe una palabra clave o abre el dropdown list',
	onChanged: (String value) {
		// Actualiza sugerencias o dropdown list
	},
	onSubmitted: (String value) {
		debugPrint('Buscar: $value');
	},
	onClear: () {
		debugPrint('Busqueda reiniciada');
	},
);

Text area quick sample

final TextEditingController notesController = TextEditingController();

PragmaTextAreaWidget(
	label: 'Notas del requerimiento',
	controller: notesController,
	placeholder: 'Describe alcance, riesgos y pendientes...',
	description: 'Ideal para copy largo o acuerdos del squad.',
	maxLength: 320,
	minLines: 4,
	successText: 'Notas listas para compartir con el squad.',
);

Tag quick sample

PragmaTagWidget(
	label: 'eugenia.sarmiento@pragma.com.co',
	leading: SizedBox(
		width: 28,
		height: 28,
		child: CircleAvatar(
			backgroundColor: Colors.white.withValues(alpha: 0.2),
			child: const Text('ES'),
		),
	),
	onPressed: () {
		debugPrint('Abrir perfil de Eugenia');
	},
	onRemove: () {
		debugPrint('Eliminar tag de Eugenia');
	},
);

Radio button quick sample

class _AccessLevelField extends StatelessWidget {
	const _AccessLevelField({required this.value, required this.onChanged});

	final String value;
	final ValueChanged<String?> onChanged;

	@override
	Widget build(BuildContext context) {
		return Column(
			children: <Widget>[
				PragmaRadioButtonWidget<String>(
					value: 'full',
					groupValue: value,
					label: 'Acceso total',
					description: 'Puede editar entregables y aprobar despliegues.',
					onChanged: onChanged,
				),
				PragmaRadioButtonWidget<String>(
					value: 'readonly',
					groupValue: value,
					label: 'Solo lectura',
					description: 'Ideal para stakeholders o clientes.',
					onChanged: onChanged,
				),
			],
		);
	}
}

Checkbox quick sample

class _ScopeChecklist extends StatefulWidget {
	const _ScopeChecklist({super.key});

	@override
	State<_ScopeChecklist> createState() => _ScopeChecklistState();
}

class _ScopeChecklistState extends State<_ScopeChecklist> {
	bool design = true;
	bool qa = false;

	bool? get selectAll {
		if (design && qa) return true;
		if (!design && !qa) return false;
		return null;
	}

	@override
	Widget build(BuildContext context) {
		return Column(
			children: <Widget>[
				PragmaCheckboxWidget(
					value: selectAll,
					tristate: true,
					label: 'Seleccionar todo',
					onChanged: (bool? value) {
						final bool shouldSelect = value ?? false;
						setState(() {
							design = shouldSelect;
							qa = shouldSelect;
						});
					},
				),
				PragmaCheckboxWidget(
					value: design,
					label: 'Diseño listo',
					description: 'Entregables revisados y compartidos.',
					onChanged: (bool? value) {
						setState(() => design = value ?? false);
					},
				),
				PragmaCheckboxWidget(
					value: qa,
					label: 'QA finalizado',
					onChanged: (bool? value) {
						setState(() => qa = value ?? false);
					},
				),
			],
		);
	}
}

Badge quick sample

Wrap(
	spacing: PragmaSpacing.xs,
	runSpacing: PragmaSpacing.xs,
	children: <Widget>[
		PragmaBadgeWidget(
			label: 'Nuevo',
			icon: Icons.bolt,
		),
		PragmaBadgeWidget(
			label: 'QA',
			tone: PragmaBadgeTone.success,
			brightness: PragmaBadgeBrightness.dark,
		),
		PragmaBadgeWidget(
			label: 'Alert',
			tone: PragmaBadgeTone.warning,
		),
	],
);

Color token row quick sample

ModelColorToken token = ModelColorToken(
	label: 'Primary',
	color: '#6750A4',
);

PragmaColorTokenRowWidget(
	token: token,
	onChanged: (ModelColorToken updated) {
		token = updated;
	},
);

Logo quick sample

PragmaLogoWidget(
	width: 200,
	variant: PragmaLogoVariant.wordmark,
);

PragmaLogoWidget(
	width: 96,
	variant: PragmaLogoVariant.isotypeCircle,
);

Calendar quick sample

final PragmaCalendarController controller = PragmaCalendarController(
	initialMonth: DateTime.now(),
);

PragmaCalendarWidget(
	controller: controller,
	selectionMode: PragmaCalendarSelectionMode.range,
	onSelectionChanged: (PragmaCalendarSelection value) {
		debugPrint('Rango: ${value.start} -> ${value.end}');
	},
);

Loading quick sample

PragmaLoadingWidget(
	value: 0.75,
	caption: 'Circular',
);

PragmaLoadingWidget(
	variant: PragmaLoadingVariant.linear,
	value: 0.5,
	linearWidth: 280,
	caption: 'Progress bar',
);

Table quick sample

final List<PragmaTableColumn> columns = <PragmaTableColumn>[
	const PragmaTableColumn(label: 'Nombre', flex: 3),
	const PragmaTableColumn(label: 'Proyecto', flex: 2),
	const PragmaTableColumn(
		label: 'Acción',
		flex: 1,
		alignment: Alignment.centerRight,
	),
];

PragmaTableWidget(
	columns: columns,
	rows: <PragmaTableRowData>[
		PragmaTableRowData(
			cells: <Widget>[
				const Text('Andreina Yajaira Francesca Serrano'),
				const Text('Discovery Lab'),
				PragmaTertiaryButton(
					label: 'Abrir',
					size: PragmaButtonSize.small,
					onPressed: () => debugPrint('Abrir ficha'),
				),
			],
		),
	],
);

Theme editor quick sample

ModelThemePragma theme = ModelThemePragma();

PragmaThemeEditorWidget(
	theme: theme,
	onChanged: (ModelThemePragma updated) {
		theme = updated;
		final ThemeData data = PragmaThemeBuilder.buildTheme(updated);
		// Usa [data] para recargar tu MaterialApp o persistir el JSON generado.
	},
);

Theme Lab workflow

  1. Ejecuta el example/ y presiona el botón Theme lab del AppBar (junto al Grid debugger) para abrir la página dedicada.
  2. Ajusta la tipografía, el modo (claro/oscuro) y cada ModelColorToken desde los PragmaColorTokenRowWidget dentro del panel izquierdo.
  3. Observa los cambios en vivo en el panel derecho: los botones, tarjetas (PragmaCardWidget) y gradientes usan el ThemeData generado por PragmaThemeBuilder.
  4. Copia el JSON resultante desde la sección "JSON listo para exportar" para versionarlo o compartirlo con tu squad.
  5. Carga el JSON dentro de tu app (ModelThemePragma.fromJson(payload)) y construye un ThemeData con PragmaThemeBuilder.buildTheme para aplicar el tema en caliente o persistirlo como configuración.

Toast quick sample

PragmaToastService.showToast(
	context: context,
	title: 'Operación exitosa',
	message: 'Synced con Discovery Lab hace 10 segundos.',
	variant: PragmaToastVariant.success,
	duration: const Duration(milliseconds: 4500),
	alignment: PragmaToastAlignment.topRight,
	actionLabel: 'Ver log',
	onActionPressed: () {
		debugPrint('Mostrar detalles');
	},
);
PragmaBreadcrumbWidget(
	items: const <PragmaBreadcrumbItem>[
		PragmaBreadcrumbItem(label: 'Home', onTap: _navigateHome),
		PragmaBreadcrumbItem(label: 'Components', onTap: _goToComponents),
		PragmaBreadcrumbItem(label: 'Breadcrumb', isCurrent: true),
	],
	type: PragmaBreadcrumbType.underline,
)

Filter quick sample

final List<PragmaFilterOption> statusOptions = <PragmaFilterOption>[
	const PragmaFilterOption(value: 'new', label: 'Nuevo'),
	const PragmaFilterOption(value: 'qa', label: 'QA'),
	const PragmaFilterOption(value: 'done', label: 'Completado'),
];

class _StatusFilter extends StatefulWidget {
	const _StatusFilter();

	@override
	State<_StatusFilter> createState() => _StatusFilterState();
}

class _StatusFilterState extends State<_StatusFilter> {
	Set<String> statuses = <String>{'new'};

	@override
	Widget build(BuildContext context) {
		return PragmaFilterWidget(
			label: 'Estado',
			options: statusOptions,
			selectedValues: statuses,
			helperText: 'Selecciona múltiples estados para refinar el listado.',
			summaryLabel: 'Filtros activos',
			onChanged: (Set<String> values) {
				setState(() => statuses = values);
				debugPrint('Filtros aplicados: $values');
			},
		);
	}
}

Pagination quick sample

class _PaginationDemo extends StatefulWidget {
	const _PaginationDemo();

	@override
	State<_PaginationDemo> createState() => _PaginationDemoState();
}

class _PaginationDemoState extends State<_PaginationDemo> {
	int currentPage = 2;
	int itemsPerPage = 25;
	final int totalItems = 280;

	int get totalPages => (totalItems / itemsPerPage).ceil();

	@override
	Widget build(BuildContext context) {
		return PragmaPaginationWidget(
			currentPage: currentPage,
			totalPages: totalPages,
			itemsPerPage: itemsPerPage,
			itemsPerPageOptions: const <int>[10, 25, 50, 100],
			totalItems: totalItems,
			tone: PragmaPaginationTone.dark,
			onPageChanged: (int page) {
				setState(() => currentPage = page);
				_fetchPage();
			},
			onItemsPerPageChanged: (int value) {
				setState(() {
					itemsPerPage = value;
					currentPage = 1;
				});
				_fetchPage();
			},
		);
	}

	void _fetchPage() {
		// Refresca la tabla o lista con los nuevos parámetros.
	}
}

Tooltip quick sample

class _TooltipPreview extends StatelessWidget {
	const _TooltipPreview();

	@override
	Widget build(BuildContext context) {
		return PragmaTooltipWidget(
			title: 'Title (optional)',
			message: 'Texto descriptivo para explicar la acción.',
			icon: Icons.info_outline,
			action: PragmaTooltipAction(
				label: 'Button',
				onPressed: () => debugPrint('Tooltip action'),
			),
			tone: PragmaTooltipTone.dark,
			placement: PragmaTooltipPlacement.right,
			child: PragmaButton.icon(
				label: 'Hover me',
				icon: Icons.touch_app,
				hierarchy: PragmaButtonHierarchy.secondary,
				size: PragmaButtonSize.small,
				onPressed: () => debugPrint('CTA tocado'),
			),
		);
	}
}

See doc/opacity_tokens.md for the full opacity table and Color.withValues examples.

Read doc/component_modeling.md to structure JSON payloads and reuse them in showcases.

Explore lib/src for additional utilities, run the example app, and check doc/grid_utilities.md to adopt the grid helpers.

Review doc/logo.md for asset usage guidelines and doc/fonts.md for typography, licensing, and offline distribution tips. Check doc/loading.md for anatomy, gradients, and scenarios for PragmaLoadingWidget. Conoce la guía de filas, estados hover y casos de uso del componente en doc/tables.md.

Typography and license

  • See doc/fonts.md for the complete typography contract plus license highlights.
  • The official typeface is Poppins and PragmaTypography applies it through GoogleFonts.poppins.
  • The family ships under the SIL Open Font License 1.1; see the full text in licenses/Poppins-OFL.txt.
  • If your app must work offline on first launch, bundle the .ttf files in your assets and disable runtime fetching.
  • Follow doc/poppins_offline.md for the step-by-step guide and download links.

How to prepare an offline fallback

  1. Download the weights you use (for example Regular, SemiBold, and Bold) from Google Fonts and store them inside assets/fonts/.

  2. Declare them in pubspec.yaml:

    flutter:
    	 fonts:
    		 - family: Poppins
    			 fonts:
    				 - asset: assets/fonts/Poppins-Regular.ttf
    				 - asset: assets/fonts/Poppins-SemiBold.ttf
    				 - asset: assets/fonts/Poppins-Bold.ttf
    
  3. Disable runtime fetching during startup:

    void main() {
    	 GoogleFonts.config.allowRuntimeFetching = false;
    	 runApp(const PragmaApp());
    }
    

    Remember to import package:google_fonts/google_fonts.dart.

If you skip bundling the files, the responsibility of providing the typeface falls on the app that consumes this package.

Example

cd example
flutter run

The sample app toggles themes, tokens, and staple components.

Development

Card quick sample

PragmaCardWidget(
	title: 'Funnel weekly',
	subtitle: 'Actualizado hace 5 min',
	body: Column(
		crossAxisAlignment: CrossAxisAlignment.start,
		children: const <Widget>[
			Text('Sesiones: 18.4K'),
			SizedBox(height: PragmaSpacing.xs),
			Text('CTR: 4.1% vs semana anterior'),
		],
	),
	variant: PragmaCardVariant.tonal,
	actions: <Widget>[
		PragmaButton.icon(
			label: 'Ver dashboard',
			icon: Icons.open_in_new,
			hierarchy: PragmaButtonHierarchy.tertiary,
			onPressed: () {},
		),
	],
)
PragmaDropdownWidget<String>(
	label: 'Rol asignado',
	placeholder: 'Selecciona un rol',
	helperText: 'Usaremos este rol en los tableros',
	options: const <PragmaDropdownOption<String>>[
		PragmaDropdownOption(label: 'Product Designer', value: 'ux'),
		PragmaDropdownOption(label: 'Product Manager', value: 'pm'),
		PragmaDropdownOption(label: 'iOS Engineer', value: 'ios'),
	],
	onChanged: (String? value) {
		debugPrint('Role: $value');
	},
)
  • flutter test to run the tests.
PragmaDropdownListWidget<String>(
	label: 'Equipo colaborador',
	placeholder: 'Selecciona perfiles',
	options: const <PragmaDropdownOption<String>>[
		PragmaDropdownOption(label: 'UX Research', value: 'research'),
		PragmaDropdownOption(label: 'Mobile iOS', value: 'ios'),
		PragmaDropdownOption(label: 'Mobile Android', value: 'android'),
	],
	initialSelectedValues: const <String>['ios'],
	onSelectionChanged: (List<String> roles) {
		debugPrint('Seleccionados: $roles');
	},
	onItemRemoved: (String value) {
		debugPrint('Eliminado: $value');
	},
)
- `dart format .` to keep formatting consistent.
- `flutter analyze` to validate against `analysis_options.yaml`.

Libraries

pragma_design_system
Biblioteca principal que reexporta los tokens, temas y componentes del Design System de Pragma.