morphix_button 1.0.2 copy "morphix_button: ^1.0.2" to clipboard
morphix_button: ^1.0.2 copied to clipboard

A premium animated async button for Flutter with spring physics, particle burst, neon glow, and gradient styles.

example/lib/main.dart

import 'dart:async';

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

void main() => runApp(const MorphixExampleApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Morphix Button',
      debugShowCheckedModeBanner: false,
      themeMode: ThemeMode.light,
      theme: ThemeData.light(useMaterial3: true).copyWith(
        scaffoldBackgroundColor: const Color(0xFFFAFAF9),
        colorScheme: const ColorScheme.light(
          primary: Color(0xFF18181B),
          surface: Color(0xFFFAFAF9),
        ),
      ),
      home: const ExampleScreen(),
    );
  }
}

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

  @override
  State<ExampleScreen> createState() => _ExampleScreenState();
}

class _ExampleScreenState extends State<ExampleScreen> {
  // Controller for manual progress driving (upload/download flows).
  // Always dispose in State.dispose().
  final _progressController = MorphixController();
  Timer? _progressTimer;

  @override
  void dispose() {
    _progressController.dispose();
    _progressTimer?.cancel();
    super.dispose();
  }

  // Returns normally → morphix transitions to success automatically.
  Future<void> _success() async =>
      Future.delayed(const Duration(seconds: 2));

  // Throws → morphix catches it, transitions to error + shake.
  // You never catch this yourself. Just throw.
  Future<void> _error() async {
    await Future.delayed(const Duration(seconds: 2));
    throw Exception('Something went wrong');
  }

  // Drives progress 0.0 → 1.0 via controller.
  // In production: replace with your upload stream or dio onSendProgress.
  Future<void> _driveProgress() async {
    double p = 0.0;
    _progressTimer?.cancel();
    _progressTimer = Timer.periodic(const Duration(milliseconds: 80), (t) {
      p += 0.02;
      if (p >= 1.0) {
        t.cancel();
        _progressController.success();
      } else {
        _progressController.setProgress(p);
      }
    });
  }

  void _toast(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(message),
        duration: const Duration(seconds: 2),
        behavior: SnackBarBehavior.floating,
        backgroundColor: const Color(0xFF18181B),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: SingleChildScrollView(
          padding: const EdgeInsets.symmetric(horizontal: 24),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              const SizedBox(height: 36),
              _header(),
              const SizedBox(height: 52),

              // Filled — primary CTA.
              // Throw inside onTap to trigger error state automatically.
              _section('FILLED'),
              const SizedBox(height: 12),
              Morphix(
                label: 'Pay \$49.00',
                onTap: _success,
                style: MorphixStyle.filled,
                color: const Color(0xFF2563EB),
                successColor: const Color(0xFF0D9488),
              ),
              const SizedBox(height: 10),
              Morphix(
                label: 'Delete Account',
                onTap: _error,
                style: MorphixStyle.filled,
                color: const Color(0xFFDC2626),
                successColor: const Color(0xFF16A34A),
                errorColor: const Color(0xFFD97706),
                onError: (e) => _toast('$e'),
              ),
              const SizedBox(height: 44),

              // Outlined — secondary action.
              _section('OUTLINED'),
              const SizedBox(height: 12),
              Morphix(
                label: 'Save Draft',
                onTap: _success,
                style: MorphixStyle.outlined,
                color: const Color(0xFF2563EB),
                successColor: const Color(0xFF16A34A),
              ),
              const SizedBox(height: 44),

              // Neon — hero CTA. Glow breathes on idle automatically.
              _section('NEON'),
              const SizedBox(height: 12),
              Morphix(
                label: 'Start Free Trial',
                onTap: _success,
                style: MorphixStyle.neon,
                color: const Color(0xFF2563EB),
                successColor: const Color(0xFF16A34A),
              ),
              const SizedBox(height: 10),
              Morphix(
                label: 'End Session',
                onTap: _error,
                style: MorphixStyle.neon,
                color: const Color(0xFFDC2626),
                errorColor: const Color(0xFFD97706),
                onError: (e) => _toast('$e'),
              ),
              const SizedBox(height: 44),

              // Gradient — premium tier. Arc rotates during loading.
              // Keep both colors in the same temperature family.
              _section('GRADIENT'),
              const SizedBox(height: 12),
              Morphix(
                label: 'Upgrade to Pro',
                onTap: _success,
                style: MorphixStyle.gradient,
                color: const Color(0xFF2563EB),
                gradientColors: const [
                  Color(0xFF2563EB),
                  Color(0xFF0D9488),
                ],
                successColor: const Color(0xFF15803D),
              ),
              const SizedBox(height: 44),

              // Icon — pass any IconData, hides during loading automatically.
              _section('WITH ICON'),
              const SizedBox(height: 12),
              Morphix(
                label: 'Send Message',
                icon: Icons.send_rounded,
                iconPosition: IconPosition.right,
                onTap: _success,
                style: MorphixStyle.filled,
                color: const Color(0xFF18181B),
                successColor: const Color(0xFF16A34A),
              ),
              const SizedBox(height: 44),

              // Custom child — overrides label and icon entirely.
              // All animations still apply.
              _section('CUSTOM CHILD'),
              const SizedBox(height: 12),
              Morphix(
                onTap: _success,
                style: MorphixStyle.filled,
                color: const Color(0xFF18181B),
                successColor: const Color(0xFF16A34A),
                child: const Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Icon(Icons.apple, color: Colors.white, size: 20),
                    SizedBox(width: 8),
                    Text(
                      'Sign in with Apple',
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 16,
                        fontWeight: FontWeight.w600,
                      ),
                    ),
                  ],
                ),
              ),
              const SizedBox(height: 44),

              // Progress mode — drive from your upload stream via controller.
              // ctrl.setProgress(0.0–1.0) → ctrl.success() or ctrl.error()
              _section('PROGRESS MODE'),
              const SizedBox(height: 12),
              Morphix(
                label: 'Upload Video',
                onTap: _driveProgress,
                controller: _progressController,
                style: MorphixStyle.gradient,
                color: const Color(0xFF2563EB),
                gradientColors: const [
                  Color(0xFF2563EB),
                  Color(0xFF0D9488),
                ],
                successColor: const Color(0xFF16A34A),
              ),
              const SizedBox(height: 44),

              // Spring presets — override collapse physics.
              // widthSpring drives collapse, radiusSpring chases it.
              _section('SPRING PRESETS'),
              const SizedBox(height: 12),
              Morphix(
                label: 'Snappy',
                onTap: _success,
                style: MorphixStyle.filled,
                color: const Color(0xFF18181B),
                successColor: const Color(0xFF16A34A),
                widthSpring: MorphixSprings.snappy,
                radiusSpring: MorphixSprings.snappy,
              ),
              const SizedBox(height: 10),
              Morphix(
                label: 'Bouncy',
                onTap: _success,
                style: MorphixStyle.neon,
                color: const Color(0xFF2563EB),
                successColor: const Color(0xFF16A34A),
                widthSpring: MorphixSprings.bouncy,
                radiusSpring: MorphixSprings.follow,
              ),
              const SizedBox(height: 10),
              Morphix(
                label: 'Cinematic',
                onTap: _success,
                style: MorphixStyle.gradient,
                color: const Color(0xFF16A34A),
                gradientColors: const [
                  Color(0xFF16A34A),
                  Color(0xFF0D9488),
                ],
                successColor: const Color(0xFF15803D),
                widthSpring: MorphixSprings.cinematic,
                radiusSpring: MorphixSprings.cinematic,
              ),
              const SizedBox(height: 60),
            ],
          ),
        ),
      ),
    );
  }

  Widget _header() {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text(
          'morphix_button',
          style: TextStyle(
            fontSize: 26,
            fontWeight: FontWeight.w700,
            letterSpacing: -0.5,
            color: Color(0xFF18181B),
          ),
        ),
        const SizedBox(height: 4),
        const Text(
          'Animated async button for Flutter',
          style: TextStyle(
            fontSize: 13,
            color: Color(0xFF71717A),
          ),
        ),
      ],
    );
  }

  Widget _section(String title) => Padding(
        padding: const EdgeInsets.only(bottom: 2),
        child: Text(
          title,
          style: const TextStyle(
            fontSize: 10,
            fontWeight: FontWeight.w600,
            color: Color(0xFFA1A1AA),
            letterSpacing: 1.4,
          ),
        ),
      );
}
2
likes
150
points
77
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A premium animated async button for Flutter with spring physics, particle burst, neon glow, and gradient styles.

Repository (GitHub)
View/report issues

Topics

#button #animation #async #ui #widget

License

MIT (license)

Dependencies

flutter

More

Packages that depend on morphix_button