leulit_flutter_fullresponsive 1.4.0
leulit_flutter_fullresponsive: ^1.4.0 copied to clipboard
Librería agnóstica de alto rendimiento para responsividad usando InheritedWidget y Extension Methods.
Leulit Flutter Full Responsive #
Una librería agnóstica de alto rendimiento para responsividad en Flutter usando InheritedWidget y Extension Methods. Proporciona una API simple y eficiente para crear interfaces que se adapten a diferentes tamaños de pantalla.
✨ Características #
- 🚀 Alto rendimiento: Utiliza
InheritedWidgetpara propagación eficiente - 📱 Completamente responsive: Adapta automáticamente el ancho, alto y tamaño de fuente
- 🎯 API intuitiva: Extension methods simples (.w(), .h(), .sp())
- ♿ Accesibilidad: Respeta la configuración de escala de texto del usuario
- 🔒 Type-safe: Aprovecha el null safety de Dart
- 📦 Ligero: Sin dependencias externas, solo Flutter SDK
🔧 Instalación #
Desde pub.dev #
Agrega la dependencia a tu pubspec.yaml:
dependencies:
leulit_flutter_fullresponsive: ^1.2.0
Ejecuta:
flutter pub get
Instalación local (para desarrollo) #
Si quieres usar la versión local del paquete:
- Clona el repositorio:
git clone https://github.com/leulit/leulit_flutter_fullresponsive.git
cd leulit_flutter_fullresponsive
- En tu proyecto Flutter, agrega la dependencia local al
pubspec.yaml:
dependencies:
leulit_flutter_fullresponsive:
path: ../path/to/leulit_flutter_fullresponsive
- Ejecuta:
flutter pub get
🚀 Uso Básico #
1. Configuración inicial #
Envuelve tu MaterialApp o CupertinoApp con ScreenSizeInitializer:
import 'package:flutter/material.dart';
import 'package:leulit_flutter_fullresponsive/leulit_flutter_fullresponsive.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ScreenSizeInitializer(
child: MaterialApp(
title: 'Mi App Responsive',
home: HomeScreen(),
),
);
}
}
2. Usando las extensiones #
Una vez configurado, puedes usar las extensiones en cualquier parte de tu aplicación. Ahora soporta dos formatos:
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
// Formato tradicional (0-100)
width: 80.w(context), // 80% del ancho de pantalla
height: 50.h(context), // 50% del alto de pantalla
// O formato decimal (0-1) para mayor precisión
// width: 0.8.w(context), // Equivalente a 80%
// height: 0.5.h(context), // Equivalente a 50%
// Valores ultra precisos
// width: 0.076543.w(context), // 7.6543% exacto
child: Text(
'Texto responsive',
style: TextStyle(
fontSize: 4.sp(context), // Tamaño de fuente responsive
// o fontSize: 0.03.sp(context), // Formato decimal
),
),
),
);
}
}
📚 API Reference #
Extension Methods #
.w(BuildContext context)
Calcula un porcentaje del ancho de pantalla. Acepta dos formatos:
// Formato tradicional (0-100)
Container(width: 50.w(context)) // 50% del ancho
// Formato decimal (0-1) para mayor precisión
Container(width: 0.5.w(context)) // 50% del ancho
Container(width: 0.076543.w(context)) // 7.6543% exacto
.h(BuildContext context)
Calcula un porcentaje del alto de pantalla. Acepta dos formatos:
// Formato tradicional (0-100)
Container(height: 30.h(context)) // 30% del alto
// Formato decimal (0-1) para mayor precisión
Container(height: 0.3.h(context)) // 30% del alto
Container(height: 0.123456.h(context)) // 12.3456% exacto
.sp(BuildContext context)
Calcula un tamaño de fuente responsive que respeta la configuración de accesibilidad. Acepta dos formatos:
// Formato tradicional
Text(
'Hola',
style: TextStyle(fontSize: 3.sp(context)), // Fuente responsive
)
// Formato decimal para mayor control
Text(
'Hola',
style: TextStyle(fontSize: 0.025.sp(context)), // Control preciso
)
🆕 Funciones Multi-Plataforma #
w(BuildContext context, {...})
Ancho responsive específico por plataforma:
// Ejemplo con diferentes plataformas
width: w(context, web: 0.4, mobile: 0.9, tablet: 0.6, fallback: 0.5)
// Solo especificar algunas plataformas
width: w(context, web: 30, mobile: 80) // Formato porcentaje
// Específico por sistema operativo
width: w(context, ios: 0.8, android: 0.85, web: 0.4)
h(BuildContext context, {...})
Alto responsive específico por plataforma:
height: h(context, web: 0.3, mobile: 0.5, tablet: 0.4, fallback: 0.35)
sp(BuildContext context, {...})
Tamaño de fuente responsive específico por plataforma:
fontSize: sp(context, web: 0.02, mobile: 0.04, tablet: 0.03, fallback: 0.025)
Widgets #
ScreenSizeInitializer
Widget que debe envolver tu aplicación para inicializar el sistema de responsividad.
ScreenSizeInitializer(
child: MaterialApp(...),
)
💡 Ejemplos Avanzados #
Comparación de formatos #
class FormatComparison extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
// Formato tradicional (0-100)
Container(
width: 75.w(context), // 75%
height: 10.h(context), // 10%
color: Colors.blue,
),
// Formato decimal (0-1) - Equivalente
Container(
width: 0.75.w(context), // 75%
height: 0.1.h(context), // 10%
color: Colors.green,
),
// Alta precisión solo posible con decimales
Container(
width: 0.618033.w(context), // Proporción áurea exacta
height: 0.08333.h(context), // 1/12 exacto
color: Colors.orange,
),
],
);
}
}
🚀 NUEVO: Funciones Multi-Plataforma #
Ahora puedes especificar valores diferentes para cada plataforma:
class MultiPlatformExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
// Diferentes anchos según la plataforma
width: w(context,
web: 0.3, // 30% en web
mobile: 0.9, // 90% en móviles (iOS y Android)
tablet: 0.6, // 60% en tablets
fallback: 0.5, // 50% como fallback
),
// Diferentes alturas según la plataforma
height: h(context,
web: 0.4, // 40% en web
ios: 0.25, // 25% específico para iOS
android: 0.3, // 30% específico para Android
tablet: 0.35, // 35% en tablets
fallback: 0.28, // 28% como fallback
),
child: Text(
'Responsive Multi-Platform',
style: TextStyle(
// Diferentes tamaños de fuente según la plataforma
fontSize: sp(context,
web: 0.02, // Tamaño para web
mobile: 0.04, // Tamaño para móviles
tablet: 0.03, // Tamaño para tablets
fallback: 0.025,
),
),
),
);
}
}
Plataformas soportadas:
web: Aplicaciones web y desktopios: Específico para iOSandroid: Específico para Androidmobile: Ambos iOS y Androidtablet: Tablets (detectado por tamaño de pantalla ≥600px)desktop: Aplicaciones de escritoriofallback: Valor por defecto si no se encuentra una plataforma específica
Layout responsive completo #
class ResponsiveLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'App Responsive',
style: TextStyle(fontSize: 4.sp(context)),
),
),
body: Padding(
padding: EdgeInsets.all(5.w(context)),
child: Column(
children: [
Container(
width: 100.w(context),
height: 25.h(context),
color: Colors.blue,
child: Center(
child: Text(
'Header',
style: TextStyle(
fontSize: 5.sp(context),
color: Colors.white,
),
),
),
),
SizedBox(height: 2.h(context)),
Expanded(
child: Row(
children: [
Container(
width: 30.w(context),
color: Colors.grey[300],
child: Center(
child: Text(
'Sidebar',
style: TextStyle(fontSize: 3.sp(context)),
),
),
),
SizedBox(width: 2.w(context)),
Expanded(
child: Container(
color: Colors.grey[100],
child: Center(
child: Text(
'Content',
style: TextStyle(fontSize: 3.sp(context)),
),
),
),
),
],
),
),
],
),
),
);
}
}
🎨 NUEVO: Extensiones Especializadas #
📏 ResponsiveSize - Para Iconos, Padding, Margins #
Ahora puedes hacer responsive fácilmente los tamaños de iconos, padding, margins y otros elementos UI pequeños:
class IconExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
// Iconos responsive
Icon(
Icons.star,
size: 24.size(context), // Tamaño responsive automático
color: Colors.gold,
),
// Con valores específicos por plataforma
Icon(
Icons.favorite,
size: 20.sizeFor(context,
mobile: 18, // Más pequeño en móviles
tablet: 24, // Medio en tablets
desktop: 32, // Más grande en desktop
),
color: Colors.red,
),
// Padding responsive
Padding(
padding: EdgeInsets.all(16.size(context)),
child: Text('Padding responsive'),
),
// Margins responsive
Container(
margin: EdgeInsets.symmetric(
horizontal: 20.size(context),
vertical: 12.size(context),
),
child: Text('Margin responsive'),
),
],
);
}
}
🔄 ResponsiveRadius - Para Border Radius #
Crea esquinas redondeadas que se adapten perfectamente a diferentes pantallas:
class RadiusExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
// Border radius simple
Container(
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(12.radius(context)),
),
child: Text('Esquinas responsive'),
),
// Border radius específico por plataforma
Container(
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(
8.radiusFor(context,
mobile: 6, // Esquinas más suaves en móvil
tablet: 12, // Intermedias en tablet
desktop: 20, // Más pronunciadas en desktop
),
),
),
child: Text('Multi-platform radius'),
),
// Diferentes esquinas
Container(
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16.radius(context)),
topRight: Radius.circular(8.radius(context)),
bottomLeft: Radius.circular(4.radius(context)),
bottomRight: Radius.circular(20.radius(context)),
),
),
child: Text('Esquinas asimétricas'),
),
],
);
}
}
📐 ResponsiveFlex - Para Layouts Flexibles #
Optimiza tus layouts con valores de flex que se adapten al tipo de dispositivo:
class FlexExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
// Row con flex responsive automático
Row(
children: [
Expanded(
flex: 3.flexValue(context), // Se ajusta automáticamente
child: Container(
color: Colors.red,
height: 100,
child: Center(child: Text('Flex 3')),
),
),
Expanded(
flex: 2.flexValue(context), // Se ajusta automáticamente
child: Container(
color: Colors.blue,
height: 100,
child: Center(child: Text('Flex 2')),
),
),
],
),
SizedBox(height: 20),
// Row con flex específico por plataforma
Row(
children: [
Expanded(
flex: 4.flexFor(context,
mobile: 3, // Más equilibrado en móvil
tablet: 5, // Más prominente en tablet
desktop: 6, // Dominante en desktop
),
child: Container(
color: Colors.green,
height: 100,
child: Center(child: Text('Flex Adaptativo')),
),
),
Expanded(
flex: 2.flexFor(context,
mobile: 2,
tablet: 2,
desktop: 1, // Menos espacio en desktop
),
child: Container(
color: Colors.orange,
height: 100,
child: Center(child: Text('Flex Secundario')),
),
),
],
),
],
);
}
}
🎯 Casos de Uso Prácticos #
class PracticalExamples extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: Icon(
Icons.menu,
size: 24.sizeFor(context, mobile: 20, tablet: 28),
),
actions: [
IconButton(
onPressed: () {},
icon: Icon(
Icons.search,
size: 22.size(context),
),
),
IconButton(
onPressed: () {},
icon: Icon(
Icons.more_vert,
size: 22.size(context),
),
),
],
),
body: Padding(
padding: EdgeInsets.all(16.size(context)),
child: Column(
children: [
// Card con esquinas y padding responsive
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.radius(context)),
),
child: Padding(
padding: EdgeInsets.all(20.size(context)),
child: Row(
children: [
Icon(
Icons.notifications,
size: 28.sizeFor(context,
mobile: 24,
tablet: 32,
desktop: 36,
),
color: Colors.blue,
),
SizedBox(width: 16.size(context)),
Expanded(
flex: 4.flexValue(context),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Notificación',
style: TextStyle(
fontSize: 3.sp(context),
fontWeight: FontWeight.bold,
),
),
Text(
'Descripción de la notificación',
style: TextStyle(fontSize: 2.5.sp(context)),
),
],
),
),
],
),
),
),
SizedBox(height: 20.size(context)),
// Botones con diferentes estilos responsive
Row(
children: [
Expanded(
flex: 2.flexFor(context, mobile: 1, tablet: 2),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.radius(context)),
),
padding: EdgeInsets.symmetric(
vertical: 12.size(context),
horizontal: 24.size(context),
),
),
onPressed: () {},
child: Text(
'Aceptar',
style: TextStyle(fontSize: 2.8.sp(context)),
),
),
),
SizedBox(width: 12.size(context)),
Expanded(
flex: 1.flexValue(context),
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.radius(context)),
),
padding: EdgeInsets.symmetric(
vertical: 12.size(context),
horizontal: 16.size(context),
),
),
onPressed: () {},
child: Text(
'Cancelar',
style: TextStyle(fontSize: 2.8.sp(context)),
),
),
),
],
),
],
),
),
);
}
}
🧪 Testing #
Para ejecutar las pruebas:
flutter test
📦 Desarrollo Local #
Estructura del proyecto #
lib/
├── leulit_flutter_fullresponsive.dart # Archivo principal
├── core/
│ └── screen_scaler_inherited_widget.dart # InheritedWidget
└── domain/
└── screen_info.dart # Modelo de datos
Ejecutar ejemplo de desarrollo #
flutter create example
cd example
# Agregar dependencia local en pubspec.yaml
flutter run
📋 Publicación en pub.dev #
Requisitos previos #
- Tener una cuenta en pub.dev
- Configurar las credenciales:
dart pub login
Proceso de publicación #
- Verificar que el paquete esté listo:
dart pub publish --dry-run
- Actualizar la versión en
pubspec.yaml:
version: 1.0.1 # Incrementar según semantic versioning
- Actualizar el CHANGELOG.md:
## [1.0.1] - 2024-10-14
### Added
- Nueva funcionalidad X
### Fixed
- Corrección del bug Y
- Publicar:
dart pub publish
Semantic Versioning #
- MAJOR (1.0.0 → 2.0.0): Cambios que rompen compatibilidad
- MINOR (1.0.0 → 1.1.0): Nuevas funcionalidades compatibles
- PATCH (1.0.0 → 1.0.1): Correcciones de bugs
Actualización de versiones #
- Hacer cambios en el código
- Ejecutar pruebas:
flutter test - Actualizar versión en
pubspec.yaml - Actualizar
CHANGELOG.md - Commit y push a git
- Publicar:
dart pub publish
⚠️ Troubleshooting #
Error: "Missing ScreenSizeInitializer" #
Problema: Las extensiones lanzan error sobre inicializador faltante.
Solución: Asegúrate de envolver tu MaterialApp con ScreenSizeInitializer:
ScreenSizeInitializer(
child: MaterialApp(...),
)
Comportamiento inesperado en tamaños #
Problema: Los porcentajes no se comportan como esperado.
Solución: Verifica que estés usando los valores correctos:
.w()y.h()esperan valores 0-100 (porcentajes).sp()funciona mejor con valores pequeños (1-6 típicamente)
🤝 Contribuciones #
Las contribuciones son bienvenidas. Por favor:
- Fork el repositorio
- Crea una rama para tu feature (
git checkout -b feature/nueva-funcionalidad) - Commit tus cambios (
git commit -am 'Agrega nueva funcionalidad') - Push a la rama (
git push origin feature/nueva-funcionalidad) - Crea un Pull Request
📄 Licencia #
Este proyecto está bajo la Licencia MIT - ver el archivo LICENSE para detalles.
👥 Autor #
Desarrollado por Leulit