InheritedWidget es un widget especial en Flutter que pasa datos hacia abajo por el árbol de widgets sin pasarlos explícitamente a través de constructores. Los widgets hijos acceden a los datos mediante BuildContext y se suscriben automáticamente a las actualizaciones. Cuando los datos en InheritedWidget cambian, todos los widgets dependientes se reconstruyen. Según Flutter API Reference, 2025, InheritedWidget es la base de Theme, MediaQuery, Localizations y la mayoría de las bibliotecas de gestión de estado.
Puntos clave
InheritedWidget es un widget que hace que sus datos estén disponibles para todos los descendientes en el Widget Tree. A diferencia de un widget normal que solo pasa datos a través de constructores a los elementos hijos, InheritedWidget permite que cualquier widget en el subárbol acceda a los datos sin una cadena de parámetros. Esto resuelve el problema de “prop drilling” — pasar datos a través de muchos widgets intermedios que no usan estos datos por sí mismos.
Flutter incluye varios InheritedWidget incorporados: Theme (esquema de colores y estilos), MediaQuery (tamaño de pantalla, orientación, densidad de píxeles), Localizations (cadenas localizadas), Directionality (dirección del texto), DefaultTextStyle (estilo de texto predeterminado). Estos widgets son establecidos por widgets raíz como MaterialApp y están disponibles en toda la aplicación.
InheritedWidget no tiene estado propio — almacena los datos pasados a través del constructor. Cuando el padre de InheritedWidget se reconstruye con nuevos datos, se llama al método updateShouldNotify para comparar los datos antiguos y nuevos. Si el método devuelve true, todos los widgets dependientes se marcan para reconstrucción. Este es un mecanismo de actualización reactiva simple pero efectivo.
El mecanismo de paso de datos a través de InheritedWidget se basa en el Element Tree. Cuando un widget llama a dependOnInheritedWidgetOfExactType, el elemento correspondiente registra una dependencia en InheritedElement. Cuando InheritedWidget cambia, InheritedElement notifica a todos los elementos dependientes, que se reconstruyen en el siguiente fotograma.
El método dependOnInheritedWidgetOfExactType no solo encuentra InheritedWidget en el árbol — suscribe el elemento actual a las notificaciones. Si usaras findAncestorWidgetOfExactType en lugar de dependOn, el widget obtendría los datos pero no se reconstruiría cuando cambien. Esta es una diferencia importante: dependOn es una suscripción, findAncestor es una búsqueda única.
Cuando un widget solicita un InheritedWidget, Flutter asciende por el Element Tree desde el elemento actual hasta la raíz, verificando cada InheritedElement para encontrar una coincidencia de tipo. Se devuelve el primer InheritedElement que coincide. Esto significa que el InheritedWidget más cercano en el árbol tiene prioridad — puedes sobrescribir datos en un nivel específico colocando InheritedWidget más cerca de los descendientes.
class ThemeData {
final Color primaryColor;
final TextTheme textTheme;
const ThemeData({required this.primaryColor, required this.textTheme});
}
class MyTheme extends InheritedWidget {
final ThemeData data;
const MyTheme({required this.data, required Widget child}) : super(child: child);
static MyTheme of(BuildContext context) {
final widget = context.dependOnInheritedWidgetOfExactType<MyTheme>();
assert(widget != null, "MyTheme not found in tree");
return widget!;
}
@override
bool updateShouldNotify(MyTheme oldWidget) => oldWidget.data != data;
}
En este ejemplo, MyTheme usa un método estático of para proporcionar datos a los descendientes. El método dependOnInheritedWidgetOfExactType registra una dependencia, y updateShouldNotify compara los datos antiguos y nuevos para determinar si los widgets dependientes necesitan reconstruirse.
Crear un InheritedWidget personalizado consta de dos pasos: definir una clase que extienda InheritedWidget e implementar un método estático of para el acceso desde los descendientes. Los datos se pasan a través del constructor, y el método updateShouldNotify determina cuándo deben reconstruirse los widgets dependientes.
La clase debe extender InheritedWidget y aceptar datos a través de un constructor con un parámetro child obligatorio. Los datos pueden ser de cualquier tipo: primitivos, objetos, funciones. La regla principal es que los datos deben ser inmutables para poder comparar de forma fiable los valores antiguos y nuevos.
El método estático of toma BuildContext y devuelve los datos de InheritedWidget. Internamente, llama a dependOnInheritedWidgetOfExactType, que encuentra el InheritedWidget más cercano del tipo especificado en el árbol. Si no se encuentra InheritedWidget, el método lanza una excepción o devuelve un valor predeterminado según la implementación.
Para acceder a los datos, el widget llama a MyWidget.of(context) dentro del método build. Flutter suscribe automáticamente el widget a las actualizaciones. Si los datos cambian, el widget se reconstruye en el siguiente fotograma. Esto permite un código limpio y declarativo sin parámetros innecesarios.
class UserPreferences extends InheritedWidget {
final String languageCode;
final bool darkMode;
const UserPreferences({
required this.languageCode,
required this.darkMode,
required Widget child,
}) : super(child: child);
static UserPreferences of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<UserPreferences>()!;
}
@override
bool updateShouldNotify(UserPreferences oldWidget) =>
oldWidget.languageCode != languageCode || oldWidget.darkMode != darkMode;
}
En este ejemplo, UserPreferences almacena la configuración del usuario. El método updateShouldNotify compara cada campo individualmente, lo que evita reconstrucciones innecesarias cuando solo cambia un parámetro. Usa un enfoque similar para tus propios InheritedWidget con múltiples campos.
updateShouldNotify es el método clave de InheritedWidget que determina si es necesario notificar a los widgets dependientes sobre cambios en los datos. Si el método devuelve false, los widgets dependientes no se reconstruyen, incluso si el propio InheritedWidget recibió una nueva instancia con los mismos datos. Esto es críticamente importante para el rendimiento.
Compara solo aquellos campos que realmente han cambiado y afectan la visualización. Si InheritedWidget contiene 10 campos pero solo uno afecta la UI, verifica solo ese campo. Para colecciones, usa comparación profunda o estructuras de datos inmutables. No uses == para List o Map, ya que comparan por referencia.
El error más común es devolver true sin comparación. Esto hace que todos los widgets dependientes se reconstruyan en cada actualización del padre, incluso si los datos no han cambiado. El segundo error es devolver false cuando los datos han cambiado, lo que lleva a una UI desactualizada. El tercero es una comparación compleja que se ejecuta cada fotograma y ralentiza el rendimiento.
InheritedWidget y los callbacks (pasar funciones a través de constructores) resuelven diferentes problemas. InheritedWidget es adecuado para datos que necesitan muchos widgets en diferentes niveles del árbol. Los callbacks son convenientes para el paso unidireccional de eventos del padre a un hijo específico o viceversa. La elección depende de la arquitectura de la aplicación y la frecuencia de las actualizaciones.
Usa InheritedWidget cuando los datos sean necesarios para muchos widgets en diferentes niveles de anidamiento: tema de la aplicación, configuración del usuario, información del dispositivo, datos de la sesión actual. InheritedWidget es especialmente efectivo para datos “globales” que rara vez cambian pero se necesitan en diferentes partes de la UI.
Los callbacks (funciones de devolución de llamada) son adecuados para pasar eventos de un widget hijo a un padre: pulsación de botón, selección de elemento de lista, envío de formulario. Los callbacks indican explícitamente qué acciones puede realizar un hijo y no crean dependencias ocultas. Para pasar datos hacia abajo por el árbol en un número reducido de niveles, también es más simple usar parámetros del constructor.
| Criterio | InheritedWidget | Callbacks |
|---|---|---|
| Dirección | De arriba abajo (padre → descendientes) | De abajo arriba (hijo → padre) o directa |
| Ámbito | Todo el subárbol | Widget específico |
| Reconstrucción | Automática al cambiar datos | Requiere setState manual |
| Complejidad | Media (requiere clase InheritedWidget) | Baja (solo una función) |
Provider y Riverpod son bibliotecas populares de gestión de estado en Flutter construidas sobre InheritedWidget. Amplían sus capacidades: añaden soporte para ChangeNotifier, eliminación automática al desmontar, inicialización diferida y sintaxis simplificada con genéricos.
Provider usa InheritedWidget para pasar un objeto de cualquier tipo hacia abajo por el árbol. ChangeNotifierProvider rastrea los cambios a través de ChangeNotifier y llama a updateShouldNotify cuando se invoca notifyListeners. Esto libera al desarrollador de crear manualmente InheritedWidget e implementar updateShouldNotify.
El InheritedWidget directo ofrece más control y no requiere dependencias externas. Provider proporciona infraestructura lista: Consumer, Selector, MultiProvider, ProxyProvider. La elección depende de la complejidad de la aplicación. Para proyectos simples, el InheritedWidget directo es suficiente; para grandes, Provider o Riverpod reducen el código repetitivo.
// InheritedWidget directo
class UserProvider extends InheritedWidget {
final UserData userData;
const UserProvider({required this.userData, required Widget child}) : super(child: child);
static UserData of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<UserProvider>()!.userData;
@override
bool updateShouldNotify(UserProvider old) => old.userData != userData;
}
// Equivalente de Provider
return ChangeNotifierProvider<UserData>(
create: (_) => UserData(),
child: MyApp(),
);
Ambos enfoques en el ejemplo resuelven el mismo problema — pasar UserData hacia abajo por el árbol. Provider reduce el volumen de código pero oculta la mecánica de InheritedWidget. El InheritedWidget directo ofrece control total y comprensión de lo que sucede, lo que es especialmente importante al aprender Flutter y depurar problemas complejos de reconstrucción.
Preguntas frecuentes
InheritedWidget hace que los datos estén disponibles para todos los descendientes a través de BuildContext, mientras que un widget normal solo pasa datos a través del constructor. InheritedWidget también suscribe a los descendientes a las actualizaciones de datos.
Los widgets dependientes solo se reconstruyen cuando updateShouldNotify devuelve true. Si el método se implementa correctamente, la reconstrucción solo ocurre cuando los datos realmente cambian, no en cada reconstrucción del padre.
Sí, puedes usar cualquier cantidad de InheritedWidget en el mismo árbol. Cada uno proporciona datos de un tipo específico, y los widgets pueden obtener datos de varios InheritedWidget simultáneamente.
dependOn suscribe el widget a las actualizaciones — cuando los datos cambian, el widget se reconstruye. findAncestor realiza una búsqueda única sin suscripción, y el widget no se enterará de los cambios en los datos.
Para estado simple (tema, configuración) InheritedWidget es suficiente. Para estado complejo con lógica de negocio, usa Provider, Riverpod o BLoC — están construidos sobre InheritedWidget y añaden la infraestructura necesaria.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también