Exit

exit

Intercepts widget removal, plays the exit animation to completion, then unmounts.

Preview

Installation

Terminal
flutter pub add nib_motion

Usage

dart
NibPresence(
  children: isVisible
    ? [
        NibMotion(
          key: const ValueKey('modal'),
          initial: NibAnim(opacity: 0, scale: 0.94),
          animate: NibAnim(opacity: 1, scale: 1.0),
          exit:    NibAnim(opacity: 0, scale: 0.94),
          transition: NibTransition.springSnappy,
          child: MyModal(),
        ),
      ]
    : [],
)

Examples

Presence Toggle

NibPresence intercepts widget removal so the exit animation plays to completion before unmount.

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

  @override
  State<PresenceToggleExample> createState() => _PresenceToggleExampleState();
}

class _PresenceToggleExampleState extends State<PresenceToggleExample> {
  bool _visible = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFF0D1117),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            SizedBox(
              height: 140,
              child: Center(
                child: NibPresence(
                  children: _visible
                      ? [
                          NibMotion(
                            key: const ValueKey('card'),
                            initial: const NibAnim(opacity: 0, scale: 0.9),
                            animate: const NibAnim(opacity: 1, scale: 1.0),
                            exit: const NibAnim(opacity: 0, scale: 0.9),
                            transition: NibTransition.springSnappy,
                            child: Container(
                              width: 200,
                              height: 100,
                              decoration: BoxDecoration(
                                color: const Color(0xFF1C2537),
                                borderRadius: BorderRadius.circular(12),
                                border: Border.all(color: const Color(0xFF54C5F8).withAlpha(50)),
                              ),
                              child: const Center(
                                child: Text(
                                  'Toggle Me',
                                  style: TextStyle(
                                    color: Color(0xFFEDF2F7),
                                    fontSize: 16,
                                    fontWeight: FontWeight.w600,
                                  ),
                                ),
                              ),
                            ),
                          ),
                        ]
                      : [],
                ),
              ),
            ),
            const SizedBox(height: 16),
            FilledButton(
              onPressed: () => setState(() => _visible = !_visible),
              style: FilledButton.styleFrom(backgroundColor: const Color(0xFF1C2537)),
              child: Text(
                _visible ? 'Remove' : 'Show',
                style: const TextStyle(color: Color(0xFF54C5F8)),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Slide Exit

Widget slides off-screen on removal. Useful for toasts, drawers, and bottom sheets.

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

  @override
  State<SlideExitExample> createState() => _SlideExitExampleState();
}

class _SlideExitExampleState extends State<SlideExitExample> {
  bool _visible = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFF0D1117),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            SizedBox(
              height: 140,
              child: Center(
                child: NibPresence(
                  children: _visible
                      ? [
                          NibMotion(
                            key: const ValueKey('toast'),
                            initial: const NibAnim(x: 300, opacity: 0),
                            animate: const NibAnim(x: 0, opacity: 1),
                            exit: const NibAnim(x: 300, opacity: 0),
                            transition: NibTransition.springSnappy,
                            child: Container(
                              width: 200,
                              height: 60,
                              decoration: BoxDecoration(
                                color: const Color(0xFF1C2537),
                                borderRadius: BorderRadius.circular(12),
                                border: Border.all(color: const Color(0xFF54C5F8).withAlpha(50)),
                              ),
                              child: const Center(
                                child: Text(
                                  '→ Slide Exit',
                                  style: TextStyle(
                                    color: Color(0xFFEDF2F7),
                                    fontSize: 14,
                                    fontWeight: FontWeight.w600,
                                  ),
                                ),
                              ),
                            ),
                          ),
                        ]
                      : [],
                ),
              ),
            ),
            const SizedBox(height: 16),
            FilledButton(
              onPressed: () => setState(() => _visible = !_visible),
              style: FilledButton.styleFrom(backgroundColor: const Color(0xFF1C2537)),
              child: Text(
                _visible ? 'Dismiss' : 'Show',
                style: const TextStyle(color: Color(0xFF54C5F8)),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Scale + Fade Exit

Scales down and fades out on removal — natural for modals and dialogs.

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

  @override
  State<ScaleExitExample> createState() => _ScaleExitExampleState();
}

class _ScaleExitExampleState extends State<ScaleExitExample> {
  bool _visible = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFF0D1117),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            SizedBox(
              height: 160,
              child: Center(
                child: NibPresence(
                  children: _visible
                      ? [
                          NibMotion(
                            key: const ValueKey('modal'),
                            initial: const NibAnim(opacity: 0, scale: 0.85),
                            animate: const NibAnim(opacity: 1, scale: 1.0),
                            exit: const NibAnim(opacity: 0, scale: 0.85),
                            transition: NibTransition.springGentle,
                            child: Container(
                              width: 200,
                              height: 120,
                              decoration: BoxDecoration(
                                color: const Color(0xFF1C2537),
                                borderRadius: BorderRadius.circular(16),
                                border: Border.all(color: const Color(0xFF54C5F8).withAlpha(50)),
                              ),
                              child: const Center(
                                child: Text(
                                  'Scale Exit',
                                  style: TextStyle(
                                    color: Color(0xFFEDF2F7),
                                    fontSize: 16,
                                    fontWeight: FontWeight.w600,
                                  ),
                                ),
                              ),
                            ),
                          ),
                        ]
                      : [],
                ),
              ),
            ),
            const SizedBox(height: 16),
            FilledButton(
              onPressed: () => setState(() => _visible = !_visible),
              style: FilledButton.styleFrom(backgroundColor: const Color(0xFF1C2537)),
              child: Text(
                _visible ? 'Dismiss' : 'Show',
                style: const TextStyle(color: Color(0xFF54C5F8)),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

API Reference

API class

NibPresence

API Properties

children