singgih_counter_widget 1.0.6 copy "singgih_counter_widget: ^1.0.6" to clipboard
singgih_counter_widget: ^1.0.6 copied to clipboard

PlatformAndroid

A Flutter package providing a customizable counter widget and a live chat widget powered by WebView.

example/lib/main.dart

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

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Kouventa Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MainShell(),
    );
  }
}

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

  @override
  State<MainShell> createState() => _MainShellState();
}

class _MainShellState extends State<MainShell> {
  int _currentIndex = 0;

  final _pages = const [
    _HomePage(),
    _ShopPage(),
    _CounterDemoPage(),
    _ProfilePage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Scaffold(
          body: _pages[_currentIndex],
          bottomNavigationBar: NavigationBar(
            selectedIndex: _currentIndex,
            onDestinationSelected: (i) => setState(() => _currentIndex = i),
            destinations: const [
              NavigationDestination(icon: Icon(Icons.home_outlined), selectedIcon: Icon(Icons.home), label: 'Home'),
              NavigationDestination(icon: Icon(Icons.store_outlined), selectedIcon: Icon(Icons.store), label: 'Shop'),
              NavigationDestination(icon: Icon(Icons.add_circle_outline), selectedIcon: Icon(Icons.add_circle), label: 'Counter'),
              NavigationDestination(icon: Icon(Icons.person_outline), selectedIcon: Icon(Icons.person), label: 'Profile'),
            ],
          ),
        ),
        LiveChatOverlayButton(config: LivechatModel(id: '63db685f-9ae6-49f1-9a4a-b9ffb3215256')),
      ],
    );
  }
}

// ──────────────────────────────────────────────
// Home Page
// ──────────────────────────────────────────────
class _HomePage extends StatelessWidget {
  const _HomePage();

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Scaffold(
      appBar: AppBar(
        title: const Text('Kouventa'),
        actions: [
          IconButton(icon: const Icon(Icons.notifications_outlined), onPressed: () {}),
          IconButton(icon: const Icon(Icons.search), onPressed: () {}),
        ],
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          // Banner
          Container(
            height: 160,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(16),
              gradient: LinearGradient(
                colors: [theme.colorScheme.primary, theme.colorScheme.secondary],
              ),
            ),
            child: const Center(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text('Welcome to Kouventa', style: TextStyle(color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold)),
                  SizedBox(height: 8),
                  Text('Your all-in-one solution', style: TextStyle(color: Colors.white70)),
                ],
              ),
            ),
          ),
          const SizedBox(height: 24),
          Text('Quick Actions', style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold)),
          const SizedBox(height: 12),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              _QuickAction(icon: Icons.flash_on, label: 'Flash Sale'),
              _QuickAction(icon: Icons.category, label: 'Categories'),
              _QuickAction(icon: Icons.local_offer, label: 'Vouchers'),
              _QuickAction(icon: Icons.history, label: 'History'),
            ],
          ),
          const SizedBox(height: 24),
          Text('Featured Products', style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold)),
          const SizedBox(height: 12),
          GridView.builder(
            shrinkWrap: true,
            physics: const NeverScrollableScrollPhysics(),
            itemCount: 4,
            gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
              mainAxisSpacing: 12,
              crossAxisSpacing: 12,
              childAspectRatio: 0.75,
            ),
            itemBuilder: (_, i) => _ProductCard(index: i),
          ),
          const SizedBox(height: 24),
          Text('Latest Articles', style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold)),
          const SizedBox(height: 12),
          ...List.generate(3, (i) => _ArticleCard(index: i)),
        ],
      ),
    );
  }
}

class _QuickAction extends StatelessWidget {
  final IconData icon;
  final String label;
  const _QuickAction({required this.icon, required this.label});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        CircleAvatar(
          radius: 28,
          backgroundColor: Theme.of(context).colorScheme.primaryContainer,
          child: Icon(icon, color: Theme.of(context).colorScheme.primary),
        ),
        const SizedBox(height: 6),
        Text(label, style: const TextStyle(fontSize: 12)),
      ],
    );
  }
}

class _ProductCard extends StatelessWidget {
  final int index;
  const _ProductCard({required this.index});

  static const _names = ['Wireless Earbuds', 'Smart Watch', 'Mechanical Keyboard', 'USB-C Hub'];
  static const _prices = ['Rp 299.000', 'Rp 899.000', 'Rp 1.250.000', 'Rp 450.000'];
  static const _ratings = ['4.8', '4.6', '4.9', '4.7'];
  static const _colors = [Colors.blue, Colors.green, Colors.orange, Colors.purple];

  @override
  Widget build(BuildContext context) {
    return Card(
      clipBehavior: Clip.antiAlias,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Expanded(
            child: Container(
              color: _colors[index % _colors.length].withAlpha(40),
              child: Center(child: Icon(Icons.shopping_bag_outlined, size: 48, color: _colors[index % _colors.length])),
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(_names[index % _names.length], style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 13), maxLines: 1, overflow: TextOverflow.ellipsis),
                const SizedBox(height: 2),
                Text(_prices[index % _prices.length], style: TextStyle(color: Theme.of(context).colorScheme.primary, fontWeight: FontWeight.bold, fontSize: 13)),
                Row(
                  children: [
                    const Icon(Icons.star, size: 12, color: Colors.amber),
                    const SizedBox(width: 2),
                    Text(_ratings[index % _ratings.length], style: const TextStyle(fontSize: 11)),
                  ],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class _ArticleCard extends StatelessWidget {
  final int index;
  const _ArticleCard({required this.index});

  static const _titles = [
    'Top 10 Gadgets You Need in 2025',
    'How to Choose the Right Smartwatch',
    'Wireless Audio: A Buyer\'s Guide',
  ];
  static const _subtitles = [
    'Technology is evolving fast — here are the must-haves.',
    'Battery life, health tracking, and more explained.',
    'Bluetooth codecs, latency, and what actually matters.',
  ];

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.only(bottom: 10),
      child: ListTile(
        leading: CircleAvatar(
          backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
          child: const Icon(Icons.article_outlined),
        ),
        title: Text(_titles[index % _titles.length], style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 13)),
        subtitle: Text(_subtitles[index % _subtitles.length], maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 12)),
        trailing: const Icon(Icons.chevron_right),
      ),
    );
  }
}

// ──────────────────────────────────────────────
// Shop Page
// ──────────────────────────────────────────────
class _ShopPage extends StatelessWidget {
  const _ShopPage();

  static const _categories = ['All', 'Electronics', 'Fashion', 'Home', 'Sports', 'Beauty'];

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: _categories.length,
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Shop'),
          bottom: TabBar(
            isScrollable: true,
            tabs: _categories.map((c) => Tab(text: c)).toList(),
          ),
        ),
        body: TabBarView(
          children: _categories.map((_) => _ShopGrid()).toList(),
        ),
      ),
    );
  }
}

class _ShopGrid extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      padding: const EdgeInsets.all(12),
      itemCount: 8,
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
        mainAxisSpacing: 12,
        crossAxisSpacing: 12,
        childAspectRatio: 0.7,
      ),
      itemBuilder: (_, i) => _ProductCard(index: i),
    );
  }
}

// ──────────────────────────────────────────────
// Counter Demo Page
// ──────────────────────────────────────────────
class _CounterDemoPage extends StatelessWidget {
  const _CounterDemoPage();

  @override
  Widget build(BuildContext context) {
    return const CounterPage(title: 'Counter Demo');
  }
}

// ──────────────────────────────────────────────
// Profile Page
// ──────────────────────────────────────────────
class _ProfilePage extends StatelessWidget {
  const _ProfilePage();

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Scaffold(
      appBar: AppBar(title: const Text('Profile')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          Center(
            child: Column(
              children: [
                CircleAvatar(radius: 48, backgroundColor: theme.colorScheme.primaryContainer, child: const Icon(Icons.person, size: 48)),
                const SizedBox(height: 12),
                const Text('Singgih Rama', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
                const Text('singgih@example.com', style: TextStyle(color: Colors.grey)),
              ],
            ),
          ),
          const SizedBox(height: 24),
          Card(
            child: Column(
              children: [
                _ProfileTile(icon: Icons.shopping_bag_outlined, label: 'My Orders'),
                const Divider(height: 0),
                _ProfileTile(icon: Icons.favorite_outline, label: 'Wishlist'),
                const Divider(height: 0),
                _ProfileTile(icon: Icons.location_on_outlined, label: 'Addresses'),
                const Divider(height: 0),
                _ProfileTile(icon: Icons.payment_outlined, label: 'Payment Methods'),
              ],
            ),
          ),
          const SizedBox(height: 12),
          Card(
            child: Column(
              children: [
                _ProfileTile(icon: Icons.notifications_outlined, label: 'Notifications'),
                const Divider(height: 0),
                _ProfileTile(icon: Icons.help_outline, label: 'Help & Support'),
                const Divider(height: 0),
                _ProfileTile(icon: Icons.info_outline, label: 'About'),
              ],
            ),
          ),
          const SizedBox(height: 12),
          OutlinedButton.icon(
            onPressed: () {},
            icon: const Icon(Icons.logout),
            label: const Text('Sign Out'),
            style: OutlinedButton.styleFrom(foregroundColor: Colors.red),
          ),
        ],
      ),
    );
  }
}

class _ProfileTile extends StatelessWidget {
  final IconData icon;
  final String label;
  const _ProfileTile({required this.icon, required this.label});

  @override
  Widget build(BuildContext context) {
    return ListTile(
      leading: Icon(icon, color: Theme.of(context).colorScheme.primary),
      title: Text(label),
      trailing: const Icon(Icons.chevron_right, size: 18),
      onTap: () {},
    );
  }
}
1
likes
160
points
21
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter package providing a customizable counter widget and a live chat widget powered by WebView.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

connectivity_plus, flutter, webview_flutter, webview_flutter_android

More

Packages that depend on singgih_counter_widget