RectRevealButton class

Botón animado con efecto reveal rectangular que alterna entre dos estados.

Genera una transición rectangular que se expande desde un punto (click, izquierda o derecha) hasta cubrir el área del botón, creando una transición suave entre dos contenidos y dos esquemas de color.

Ideal para: toggles horizontales, switches deslizantes, botones de activación/desactivación, y cualquier control que requiera feedback visual direccional.

Cómo funciona

  • El fondo alterna entre selectedBackgroundColor y unselectedBackgroundColor.
  • El reveal dibuja un rectángulo del color correspondiente al estado destino.
  • El contenido alterna entre selectedChild (seleccionado) y unselectedChild (no seleccionado).
  • La dirección puede ser desde el click, desde la izquierda o desde la derecha.

Anatomía del efecto

Estado inicial: NO SELECCIONADO
┌─────────────────────┐
│  unselectedChild    │  ← Fondo: unselectedBackgroundColor
└─────────────────────┘

Al tocar (fromClick):
┌─────────────────────┐
│    ▌████████▐        │  ← Rectángulo expandiéndose (selectedRippleColor)
└─────────────────────┘

Al tocar (fromLeft):
┌─────────────────────┐
│███████              │  ← Rectángulo desde izquierda
└─────────────────────┘

Estado final: SELECCIONADO
┌─────────────────────┐
│   selectedChild     │  ← Fondo: selectedBackgroundColor
└─────────────────────┘

Buenas prácticas

  • Usa colores contrastantes entre fondo y reveal para maximizar el impacto visual.
  • Asegúrate de que selectedChild y unselectedChild sean visualmente distintos.
  • Para esquinas suaves, incrementa borderRadius (mínimo 2.0).
  • Si controlas el estado externamente, usa isSelected y actualízalo con setState.
  • Usa RevealDirection.fromLeft o fromRight para indicar dirección de activación/desactivación.

Ejemplos

Ejemplo básico (toggle ON/OFF)

RectRevealButton(
  selectedChild: const Text(
    'ON',
    style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
  ),
  unselectedChild: const Text(
    'OFF',
    style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
  ),
  selectedBackgroundColor: Colors.black,
  unselectedBackgroundColor: Colors.white,
  selectedRippleColor: Colors.black,
  unselectedRippleColor: Colors.white,
  onPressed: () => debugPrint('¡Alternado!'),
)

Ejemplo con iconos y dirección desde la izquierda

RectRevealButton(
  selectedChild: const Icon(Icons.favorite, color: Colors.white),
  unselectedChild: const Icon(Icons.favorite_border, color: Colors.red),
  selectedBackgroundColor: Colors.red,
  unselectedBackgroundColor: Colors.grey.shade100,
  selectedRippleColor: Colors.red,
  unselectedRippleColor: Colors.grey.shade300,
  revealDirection: RevealDirection.fromLeft,
  height: 56,
  width: 56,
  borderRadius: 28,
  onPressed: () => print('Favorito alternado'),
)

Ejemplo con control externo

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

  @override
  State<MyToggle> createState() => _MyToggleState();
}

class _MyToggleState extends State<MyToggle> {
  bool _isActive = false;

  @override
  Widget build(BuildContext context) {
    return RectRevealButton(
      isSelected: _isActive,
      selectedChild: const Text('Activo'),
      unselectedChild: const Text('Inactivo'),
      selectedBackgroundColor: Colors.blue,
      unselectedBackgroundColor: Colors.grey.shade200,
      selectedRippleColor: Colors.blue,
      unselectedRippleColor: Colors.grey,
      revealDirection: RevealDirection.fromLeft,
      onPressed: () => setState(() => _isActive = !_isActive),
    );
  }
}

Ejemplo con borde decorativo y dirección desde la derecha

RectRevealButton(
  selectedChild: const Text(
    'Premium',
    style: TextStyle(color: Colors.amber, fontWeight: FontWeight.bold),
  ),
  unselectedChild: const Text('Básico'),
  selectedBackgroundColor: Colors.black,
  unselectedBackgroundColor: Colors.white,
  selectedRippleColor: Colors.amber,
  unselectedRippleColor: Colors.black,
  revealDirection: RevealDirection.fromRight,
  border: Border.all(color: Colors.amber, width: 2),
  borderRadius: 16,
  height: 56,
  onPressed: () => print('Plan cambiado'),
)

Notas técnicas

  • El cursor se establece automáticamente a click en plataformas desktop.
  • El reveal puede expanderse desde: el punto de tap, la izquierda o la derecha.
  • animationDuration controla la velocidad de la expansión rectangular.
  • El widget es completamente responsivo y se adapta al tamaño disponible.
Inheritance

Constructors

RectRevealButton({Key? key, required Widget selectedChild, Widget? unselectedChild, required VoidCallback onPressed, Color? selectedBackgroundColor, Color? unselectedBackgroundColor, Color? selectedRippleColor, Color? unselectedRippleColor, double width = double.infinity, double height = 48, double? borderRadius, Duration? animationDuration, bool? isSelected, BoxBorder? border, EdgeInsetsGeometry padding = const EdgeInsets.symmetric(horizontal: 16), AlignmentGeometry alignment = Alignment.center, RevealDirection revealDirection = RevealDirection.fromClick, @Deprecated('Use selectedChild instead. Will be removed in v2.0.0') Widget? widgetA, @Deprecated('Use unselectedChild instead. Will be removed in v2.0.0') Widget? widgetB, @Deprecated('Use selectedBackgroundColor instead. Will be removed in v2.0.0') Color? backgroundColorA, @Deprecated('Use unselectedBackgroundColor instead. Will be removed in v2.0.0') Color? backgroundColorB, @Deprecated('Use selectedRippleColor instead. Will be removed in v2.0.0') Color? rippleColorA, @Deprecated('Use unselectedRippleColor instead. Will be removed in v2.0.0') Color? rippleColorB, @Deprecated('Use borderRadius instead. Will be removed in v2.0.0') double? radius, @Deprecated('Use animationDuration instead. Will be removed in v2.0.0') Duration? duration, @Deprecated('Use isSelected instead. Will be removed in v2.0.0') bool? selected})
Crea un RectRevealButton con efecto de reveal rectangular.
const

Properties

alignment → AlignmentGeometry
Alineación del contenido dentro del botón.
final
animationDuration → Duration
Duración de la animación del efecto reveal.
final
border → BoxBorder?
Borde decorativo opcional aplicado al contorno del botón.
final
borderRadius → double
Radio de redondeo de las esquinas del botón.
final
hashCode → int
The hash code for this object.
no setterinherited
height → double
Alto del botón en píxeles lógicos.
final
isSelected → bool?
Controla el estado del botón externamente (opcional).
final
key → Key?
Controls how one widget replaces another widget in the tree.
finalinherited
onPressed → VoidCallback
Callback ejecutado al presionar el botón, antes de cambiar el estado.
final
padding → EdgeInsetsGeometry
Espaciado interno entre el borde del botón y su contenido.
final
revealDirection → RevealDirection
Dirección desde donde se expande el efecto reveal.
final
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited
selectedBackgroundColor → Color
Color de fondo cuando el botón está seleccionado.
final
selectedChild → Widget
Widget mostrado cuando el botón está en estado seleccionado.
final
selectedRippleColor → Color
Color del rectángulo de reveal al transicionar hacia el estado seleccionado.
final
unselectedBackgroundColor → Color
Color de fondo cuando el botón está no seleccionado.
final
unselectedChild → Widget?
Widget mostrado cuando el botón está en estado no seleccionado.
final
unselectedRippleColor → Color
Color del rectángulo de reveal al transicionar hacia el estado no seleccionado.
final
width → double
Ancho del botón en píxeles lógicos.
final

Methods

createElement() → StatefulElement
Creates a StatefulElement to manage this widget's location in the tree.
inherited
createState() → State<RectRevealButton>
Creates the mutable state for this widget at a given location in the tree.
override
debugDescribeChildren() → List<DiagnosticsNode>
Returns a list of DiagnosticsNode objects describing this node's children.
inherited
debugFillProperties(DiagnosticPropertiesBuilder properties) → void
Add additional properties associated with the node.
inherited
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toDiagnosticsNode({String? name, DiagnosticsTreeStyle? style}) → DiagnosticsNode
Returns a debug representation of the object that is used by debugging tools and by DiagnosticsNode.toStringDeep.
inherited
toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) → String
A string representation of this object.
inherited
toStringDeep({String prefixLineOne = '', String? prefixOtherLines, DiagnosticLevel minLevel = DiagnosticLevel.debug, int wrapWidth = 65}) → String
Returns a string representation of this node and its descendants.
inherited
toStringShallow({String joiner = ', ', DiagnosticLevel minLevel = DiagnosticLevel.debug}) → String
Returns a one-line detailed description of the object.
inherited
toStringShort() → String
A short, textual description of this widget.
inherited

Operators

operator ==(Object other) → bool
The equality operator.
inherited