promotion_card 1.0.0 copy "promotion_card: ^1.0.0" to clipboard
promotion_card: ^1.0.0 copied to clipboard

A flexible Flutter widget for promotion cards with built-in variants, RTL-aware overlay positioning, and Material 3 theme extension support.

example/lib/main.dart

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Promotion Card Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorSchemeSeed: Colors.deepOrange,
        useMaterial3: true,
        extensions: [_buildPromotionCardTheme()],
      ),
      home: const DemoScreen(),
    );
  }
}

/// App-wide theme defaults for all four variants, mirroring the pattern used
/// in a real app: gradient/image backgrounds, shared overlay position,
/// and per-variant arrow colors.
PromotionCardThemeData _buildPromotionCardTheme() {
  const overlayPosition = PromotionCardOverlayPosition(
    top: 8,
    end: 16,
    width: 120,
    height: 120,
  );

  const sharedLeading = PromotionCardStyle(
    leadingWidth: 40,
    leadingHeight: 40,
    leadingEndPadding: 8,
  );

  return PromotionCardThemeData(
    variants: {
      PromotionCardVariant.primary: PromotionCardVariantData(
        style: PromotionCardStyle(
          gradient: const LinearGradient(
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
            colors: [Color(0xFFFF7043), Color(0xFFBF360C)],
          ),
          borderRadius: const BorderRadius.only(
            topLeft: Radius.circular(16),
            topRight: Radius.circular(16),
          ),
          padding: const EdgeInsetsDirectional.fromSTEB(16, 14, 16, 14),
          arrowColor: Colors.white,
          showOverlay: false,
        ).merge(sharedLeading),
      ),
      PromotionCardVariant.soft: PromotionCardVariantData(
        style: PromotionCardStyle(
          gradient: const LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [Color(0xFFFFF3E0), Color(0xFFFFE0B2)],
          ),
          showOverlay: true,
          overlayPosition: overlayPosition,
          padding: const EdgeInsetsDirectional.fromSTEB(16, 14, 16, 14),
          arrowColor: Colors.deepOrange,
          borderRadius: BorderRadius.zero,
        ).merge(sharedLeading),
      ),
      PromotionCardVariant.imageHero: PromotionCardVariantData(
        style: PromotionCardStyle(
          gradient: const LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [
              Color(0xB3FCFCFC),
              Color(0xE2FFE2D4),
              Color(0xFFFFE2D4),
              Color(0xB3FCFCFC),
            ],
            stops: [0.0, 0.32, 0.75, 1.0],
          ),
          showOverlay: true,
          overlayPosition: overlayPosition,
          padding: const EdgeInsetsDirectional.fromSTEB(16, 14, 16, 14),
          arrowColor: Colors.deepOrange,
          borderRadius: BorderRadius.zero,
        ).merge(sharedLeading),
      ),
      PromotionCardVariant.minimal: PromotionCardVariantData(
        style: PromotionCardStyle(
          backgroundColor: const Color(0xFFF5F5F5),
          showOverlay: true,
          overlayPosition: overlayPosition,
          padding: const EdgeInsetsDirectional.fromSTEB(16, 14, 16, 14),
          arrowColor: Colors.deepOrange,
          borderRadius: BorderRadius.zero,
        ).merge(sharedLeading),
      ),
    },
  );
}

// ── Demo screen ───────────────────────────────────────────────────────────────

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Promotion Card Demo')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _label(context, 'primary variant'),
          const SizedBox(height: 8),
          const _RamadanCard(),
          const SizedBox(height: 24),
          _label(context, 'soft variant'),
          const SizedBox(height: 8),
          const _EidCard(),
          const SizedBox(height: 24),
          _label(context, 'imageHero variant'),
          const SizedBox(height: 8),
          const _SummerCard(),
          const SizedBox(height: 24),
          _label(context, 'minimal variant'),
          const SizedBox(height: 8),
          const _BackToSchoolCard(),
          const SizedBox(height: 24),
          _label(context, 'custom style override'),
          const SizedBox(height: 8),
          const _CustomCard(),
          const SizedBox(height: 32),
        ],
      ),
    );
  }

  Widget _label(BuildContext context, String text) => Text(
        text,
        style: Theme.of(context).textTheme.labelLarge?.copyWith(
              color: Theme.of(context).colorScheme.onSurfaceVariant,
            ),
      );
}

// ── Variant cards ─────────────────────────────────────────────────────────────

class _RamadanCard extends StatelessWidget {
  const _RamadanCard();

  @override
  Widget build(BuildContext context) {
    return PromotionCard(
      variant: PromotionCardVariant.primary,
      leading: const Icon(Icons.nights_stay, color: Colors.white),
      title: const Text(
        'Ramadan Flash',
        style: TextStyle(
          color: Colors.white,
          fontSize: 22,
          fontWeight: FontWeight.bold,
        ),
      ),
      description: const Text(
        'Bulk discounts — save more on your store essentials.',
        style: TextStyle(color: Colors.white70),
      ),
      onTap: () {},
      body: const _ProductRow(color: Color(0xFFFBE9E7)),
    );
  }
}

class _EidCard extends StatelessWidget {
  const _EidCard();

  @override
  Widget build(BuildContext context) {
    return PromotionCard(
      variant: PromotionCardVariant.soft,
      leading: const Icon(Icons.star, color: Color(0xFFD383E1)),
      overlay: const Icon(Icons.star, size: 120, color: Color(0x22D383E1)),
      title: const Text(
        'Eid Essentials',
        style: TextStyle(
          color: Colors.deepOrange,
          fontSize: 22,
          fontWeight: FontWeight.bold,
        ),
      ),
      description: const Text(
        'Special wholesale pricing on Eid sweets and drinks.',
        style: TextStyle(color: Colors.brown),
      ),
      onTap: () {},
      body: const _ProductRow(color: Color(0xFFFFF8E1)),
    );
  }
}

class _SummerCard extends StatelessWidget {
  const _SummerCard();

  @override
  Widget build(BuildContext context) {
    return PromotionCard(
      variant: PromotionCardVariant.imageHero,
      leading: const Icon(Icons.wb_sunny, color: Color(0xFF4898A2)),
      overlay: const Icon(Icons.wb_sunny, size: 120, color: Color(0x224898A2)),
      title: const Text(
        'Summer Refresh',
        style: TextStyle(
          color: Color(0xFF4898A2),
          fontSize: 22,
          fontWeight: FontWeight.bold,
        ),
      ),
      description: const Text(
        'Cool down with seasonal picks for your shelves.',
        style: TextStyle(color: Colors.blueGrey),
      ),
      onTap: () {},
      body: const _ProductRow(color: Color(0xFFE0F2F1)),
    );
  }
}

class _BackToSchoolCard extends StatelessWidget {
  const _BackToSchoolCard();

  @override
  Widget build(BuildContext context) {
    return PromotionCard(
      variant: PromotionCardVariant.minimal,
      leading: const Icon(Icons.school, color: Color(0xFFFABC51)),
      overlay: const Icon(Icons.school, size: 120, color: Color(0x22FABC51)),
      title: const Text(
        'Back to School',
        style: TextStyle(
          color: Colors.deepOrange,
          fontSize: 22,
          fontWeight: FontWeight.bold,
        ),
      ),
      description: const Text(
        'Stock up on classroom and office must-haves.',
        style: TextStyle(color: Colors.brown),
      ),
      onTap: () {},
      body: const _ProductRow(color: Color(0xFFFFF8E1)),
    );
  }
}

/// Demonstrates a per-widget [PromotionCardStyle] override — no variant or
/// theme involvement.
class _CustomCard extends StatelessWidget {
  const _CustomCard();

  @override
  Widget build(BuildContext context) {
    return PromotionCard(
      leading: const Icon(Icons.palette, color: Colors.white),
      title: const Text(
        'Custom Style',
        style: TextStyle(
          color: Colors.white,
          fontSize: 22,
          fontWeight: FontWeight.bold,
        ),
      ),
      description: const Text(
        'Gradient and border radius applied via PromotionCardStyle.',
        style: TextStyle(color: Colors.white70),
      ),
      onTap: () {},
      style: PromotionCardStyle(
        gradient: const LinearGradient(
          colors: [Color(0xFF6A1B9A), Color(0xFF283593)],
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
        ),
        borderRadius: BorderRadius.circular(20),
        arrowColor: Colors.white,
        padding: const EdgeInsetsDirectional.all(20),
      ),
      body: const _ProductRow(color: Color(0xFFEDE7F6)),
    );
  }
}

// ── Shared placeholder ────────────────────────────────────────────────────────

class _ProductRow extends StatelessWidget {
  const _ProductRow({required this.color});

  final Color color;

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 100,
      color: color,
      child: Row(
        children: List.generate(
          4,
          (i) => Expanded(
            child: Container(
              margin: const EdgeInsets.all(8),
              decoration: BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.circular(8),
                boxShadow: [
                  BoxShadow(
                    color: Colors.black.withValues(alpha: 0.06),
                    blurRadius: 4,
                    offset: const Offset(0, 2),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}
2
likes
160
points
10
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A flexible Flutter widget for promotion cards with built-in variants, RTL-aware overlay positioning, and Material 3 theme extension support.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on promotion_card