gmana_flutter 0.0.8 copy "gmana_flutter: ^0.0.8" to clipboard
gmana_flutter: ^0.0.8 copied to clipboard

A comprehensive Flutter UI library providing curated widgets, form helpers, theme services, and extensions to accelerate and standardize development.

example/lib/main.dart

import 'package:example/views/color_ext_view.dart';
import 'package:flutter/material.dart';
import 'package:gmana_flutter/gmana_flutter.dart';

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

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

  @override
  State<ThemeModeExampleApp> createState() => _ThemeModeExampleAppState();
}

class _ThemeModeExampleAppState extends State<ThemeModeExampleApp> {
  ThemeMode _currentThemeMode = ThemeMode.system;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Gmana Flutter Showcase',
      theme: _buildShowcaseTheme(Brightness.light),
      darkTheme: _buildShowcaseTheme(Brightness.dark),
      themeMode: _currentThemeMode,
      home: ThemeModeHomePage(
        currentThemeMode: _currentThemeMode,
        onThemeChanged: _updateThemeMode,
      ),
    );
  }

  void _updateThemeMode(ThemeMode newMode) {
    setState(() {
      _currentThemeMode = newMode;
    });
  }
}

ThemeData _buildShowcaseTheme(Brightness brightness) {
  final baseTheme =
      brightness == Brightness.dark ? GColors.darkTheme : GColors.lightTheme;
  final colorScheme = baseTheme.colorScheme;
  final isDark = brightness == Brightness.dark;

  return baseTheme.copyWith(
    appBarTheme: AppBarTheme(
      backgroundColor: Colors.transparent,
      foregroundColor: colorScheme.onSurface,
      elevation: 0,
      centerTitle: false,
      scrolledUnderElevation: 0,
      surfaceTintColor: Colors.transparent,
    ),
    cardTheme: CardThemeData(
      color: colorScheme.surface.withAlpha(isDark ? 230 : 245),
      elevation: 0,
      margin: EdgeInsets.zero,
      surfaceTintColor: Colors.transparent,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(28),
        side: BorderSide(color: colorScheme.outline.withAlpha(60)),
      ),
    ),
    chipTheme: baseTheme.chipTheme.copyWith(
      padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
      side: BorderSide(color: colorScheme.outline.withAlpha(50)),
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
    ),
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ElevatedButton.styleFrom(
        backgroundColor: colorScheme.primary,
        foregroundColor: colorScheme.onPrimary,
        elevation: 0,
        padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 16),
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)),
        textStyle: const TextStyle(fontWeight: FontWeight.w700),
      ),
    ),
    outlinedButtonTheme: OutlinedButtonThemeData(
      style: OutlinedButton.styleFrom(
        foregroundColor: colorScheme.onSurface,
        side: BorderSide(color: colorScheme.outline.withAlpha(90)),
        padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 16),
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)),
        textStyle: const TextStyle(fontWeight: FontWeight.w600),
      ),
    ),
    scaffoldBackgroundColor:
        isDark ? const Color(0xFF101113) : const Color(0xFFF7F3EE),
    textTheme: baseTheme.textTheme.copyWith(
      displaySmall: baseTheme.textTheme.displaySmall?.copyWith(
        fontWeight: FontWeight.w800,
        height: 1.05,
      ),
      headlineSmall: baseTheme.textTheme.headlineSmall?.copyWith(
        fontWeight: FontWeight.w700,
      ),
      titleLarge: baseTheme.textTheme.titleLarge?.copyWith(
        fontWeight: FontWeight.w700,
      ),
      bodyLarge: baseTheme.textTheme.bodyLarge?.copyWith(height: 1.5),
      bodyMedium: baseTheme.textTheme.bodyMedium?.copyWith(height: 1.45),
    ),
  );
}

class ThemeModeHomePage extends StatelessWidget {
  const ThemeModeHomePage({
    super.key,
    required this.currentThemeMode,
    required this.onThemeChanged,
  });

  final ThemeMode currentThemeMode;
  final ValueChanged<ThemeMode> onThemeChanged;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;

    return Scaffold(
      body: Stack(
        children: [
          Positioned(
            top: -120,
            right: -40,
            child: _GlowOrb(
              color: colorScheme.primary.withAlpha(70),
              size: 260,
            ),
          ),
          Positioned(
            top: 280,
            left: -70,
            child: _GlowOrb(
              color: colorScheme.secondary.withAlpha(40),
              size: 220,
            ),
          ),
          SafeArea(
            child: LayoutBuilder(
              builder: (context, constraints) {
                final isWide = constraints.maxWidth >= 920;
                final horizontalPadding =
                    constraints.maxWidth >= 1100
                        ? 48.0
                        : constraints.maxWidth >= 720
                        ? 32.0
                        : 20.0;

                return SingleChildScrollView(
                  padding: EdgeInsets.fromLTRB(
                    horizontalPadding,
                    20,
                    horizontalPadding,
                    28,
                  ),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      _TopBar(
                        currentThemeMode: currentThemeMode,
                        onThemeChanged: onThemeChanged,
                      ),
                      const SizedBox(height: 24),
                      if (isWide)
                        Row(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Expanded(
                              flex: 3,
                              child: _HeroPanel(
                                currentThemeMode: currentThemeMode,
                                onExploreColorLab: () => _openColorLab(context),
                              ),
                            ),
                            const SizedBox(width: 20),
                            const Expanded(flex: 2, child: _SignalPanel()),
                          ],
                        )
                      else
                        Column(
                          children: [
                            _HeroPanel(
                              currentThemeMode: currentThemeMode,
                              onExploreColorLab: () => _openColorLab(context),
                            ),
                            const SizedBox(height: 16),
                            const _SignalPanel(),
                          ],
                        ),
                      const SizedBox(height: 28),
                      _SectionLabel(
                        eyebrow: 'Examples',
                        title: 'A cleaner way to present the package surface.',
                        subtitle:
                            'The demo now reads like a product showcase instead of a widget sandbox.',
                      ),
                      const SizedBox(height: 16),
                      Wrap(
                        spacing: 16,
                        runSpacing: 16,
                        children: [
                          _ShowcaseCard(
                            width:
                                isWide
                                    ? (constraints.maxWidth -
                                            horizontalPadding * 2 -
                                            16) /
                                        2
                                    : double.infinity,
                            icon: currentThemeMode.toIcon(),
                            title: 'Theme Mode Controls',
                            description:
                                'Preview light, dark, and system behavior with a clearer hierarchy and cleaner controls.',
                            metrics: const [
                              'Material 3',
                              'Adaptive',
                              'Focused',
                            ],
                            actionLabel: 'Review theme modes',
                            onPressed: () {},
                          ),
                          _ShowcaseCard(
                            width:
                                isWide
                                    ? (constraints.maxWidth -
                                            horizontalPadding * 2 -
                                            16) /
                                        2
                                    : double.infinity,
                            icon: Icons.palette_outlined,
                            title: 'Color Extension Lab',
                            description:
                                'Explore transformed colors, contrast output, and palette relationships in one place.',
                            metrics: const ['Contrast', 'Harmony', 'Swatches'],
                            actionLabel: 'Open color lab',
                            onPressed: () => _openColorLab(context),
                          ),
                        ],
                      ),
                      const SizedBox(height: 28),
                      _SectionLabel(
                        eyebrow: 'What Changed',
                        title: 'Sharper presentation, same package APIs.',
                        subtitle:
                            'This example emphasizes design clarity, information hierarchy, and stronger demo quality.',
                      ),
                      const SizedBox(height: 16),
                      const Wrap(
                        spacing: 14,
                        runSpacing: 14,
                        children: [
                          _BenefitTile(
                            icon: Icons.grid_view_rounded,
                            title: 'Structured Layout',
                            description:
                                'Clear sections, consistent spacing, and responsive grouping.',
                          ),
                          _BenefitTile(
                            icon: Icons.visibility_rounded,
                            title: 'Better Demo Value',
                            description:
                                'Each screen now teaches the package instead of just exposing controls.',
                          ),
                          _BenefitTile(
                            icon: Icons.brush_outlined,
                            title: 'Professional Finish',
                            description:
                                'Improved theme polish, card styling, and readable visual rhythm.',
                          ),
                        ],
                      ),
                    ],
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }

  void _openColorLab(BuildContext context) {
    Navigator.of(context).push(
      MaterialPageRoute<void>(builder: (_) => const ColorExtensionLabPage()),
    );
  }
}

class _TopBar extends StatelessWidget {
  const _TopBar({required this.currentThemeMode, required this.onThemeChanged});

  final ThemeMode currentThemeMode;
  final ValueChanged<ThemeMode> onThemeChanged;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;

    return Wrap(
      alignment: WrapAlignment.spaceBetween,
      crossAxisAlignment: WrapCrossAlignment.center,
      runSpacing: 12,
      spacing: 12,
      children: [
        Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            Container(
              width: 44,
              height: 44,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(14),
                gradient: const LinearGradient(
                  colors: [GColors.primary, GColors.primaryDark],
                  begin: Alignment.topLeft,
                  end: Alignment.bottomRight,
                ),
              ),
              child: const Icon(Icons.auto_awesome, color: Colors.white),
            ),
            const SizedBox(width: 12),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text('gmana_flutter', style: theme.textTheme.titleLarge),
                Text('Package showcase', style: theme.textTheme.bodySmall),
              ],
            ),
          ],
        ),
        Container(
          padding: const EdgeInsets.all(6),
          decoration: BoxDecoration(
            color: colorScheme.surface.withAlpha(220),
            borderRadius: BorderRadius.circular(18),
            border: Border.all(color: colorScheme.outline.withAlpha(60)),
          ),
          child: Wrap(
            spacing: 6,
            children:
                ThemeMode.values.map((mode) {
                  final isSelected = mode == currentThemeMode;
                  return ChoiceChip(
                    label: Text(mode.toLabel()),
                    avatar: Icon(mode.toIcon(), size: 18),
                    selected: isSelected,
                    onSelected: (_) => onThemeChanged(mode),
                  );
                }).toList(),
          ),
        ),
      ],
    );
  }
}

class _HeroPanel extends StatelessWidget {
  const _HeroPanel({
    required this.currentThemeMode,
    required this.onExploreColorLab,
  });

  final ThemeMode currentThemeMode;
  final VoidCallback onExploreColorLab;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;

    return Card(
      child: Container(
        padding: const EdgeInsets.all(28),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(28),
          gradient: LinearGradient(
            colors: [
              colorScheme.surface.withAlpha(240),
              colorScheme.primaryContainer.withAlpha(150),
            ],
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
          ),
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Container(
              padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
              decoration: BoxDecoration(
                color: colorScheme.onSurface.withAlpha(15),
                borderRadius: BorderRadius.circular(999),
              ),
              child: Text(
                'Flutter UI utilities and extensions',
                style: theme.textTheme.labelLarge?.copyWith(
                  color: colorScheme.onSurfaceVariant,
                ),
              ),
            ),
            const SizedBox(height: 18),
            Text(
              'Professional demos make a package feel trustworthy.',
              style: theme.textTheme.displaySmall,
            ),
            const SizedBox(height: 14),
            Text(
              'This refreshed example uses the package more deliberately, presents features with stronger hierarchy, and makes the demo easier to navigate.',
              style: theme.textTheme.bodyLarge?.copyWith(
                color: colorScheme.onSurfaceVariant,
              ),
            ),
            const SizedBox(height: 22),
            Wrap(
              spacing: 10,
              runSpacing: 10,
              children: const [
                _FeaturePill(label: 'Clean spacing'),
                _FeaturePill(label: 'Reusable sections'),
                _FeaturePill(label: 'Responsive layout'),
                _FeaturePill(label: 'Polished theme'),
              ],
            ),
            const SizedBox(height: 26),
            Wrap(
              spacing: 12,
              runSpacing: 12,
              crossAxisAlignment: WrapCrossAlignment.center,
              children: [
                ElevatedButton.icon(
                  onPressed: onExploreColorLab,
                  icon: const Icon(Icons.palette_outlined),
                  label: const Text('Explore color lab'),
                ),
                OutlinedButton.icon(
                  onPressed: () {},
                  icon: Icon(currentThemeMode.toIcon()),
                  label: Text(currentThemeMode.toLabel()),
                ),
                Row(
                  mainAxisSize: MainAxisSize.min,
                  children: const [
                    StarRatingBar(ratingValue: 4.8, starSize: 18),
                    SizedBox(width: 8),
                    Text('Demo polish'),
                  ],
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

class _SignalPanel extends StatelessWidget {
  const _SignalPanel();

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    return Card(
      child: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('Package signal', style: theme.textTheme.titleLarge),
            const SizedBox(height: 6),
            Text(
              'A clean example communicates maturity faster than documentation alone.',
              style: theme.textTheme.bodyMedium,
            ),
            const SizedBox(height: 22),
            const Wrap(
              spacing: 12,
              runSpacing: 12,
              children: [
                _MetricTile(value: '3', label: 'theme modes'),
                _MetricTile(value: '6+', label: 'showcase blocks'),
                _MetricTile(value: 'AA', label: 'contrast focus'),
                _MetricTile(value: 'M3', label: 'visual system'),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

class _SectionLabel extends StatelessWidget {
  const _SectionLabel({
    required this.eyebrow,
    required this.title,
    required this.subtitle,
  });

  final String eyebrow;
  final String title;
  final String subtitle;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          eyebrow.toUpperCase(),
          style: theme.textTheme.labelLarge?.copyWith(
            letterSpacing: 1.2,
            color: colorScheme.primary,
          ),
        ),
        const SizedBox(height: 6),
        Text(title, style: theme.textTheme.headlineSmall),
        const SizedBox(height: 6),
        Text(
          subtitle,
          style: theme.textTheme.bodyMedium?.copyWith(
            color: colorScheme.onSurfaceVariant,
          ),
        ),
      ],
    );
  }
}

class _ShowcaseCard extends StatelessWidget {
  const _ShowcaseCard({
    required this.width,
    required this.icon,
    required this.title,
    required this.description,
    required this.metrics,
    required this.actionLabel,
    required this.onPressed,
  });

  final double width;
  final IconData icon;
  final String title;
  final String description;
  final List<String> metrics;
  final String actionLabel;
  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;

    return SizedBox(
      width: width,
      child: Card(
        child: Padding(
          padding: const EdgeInsets.all(24),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Container(
                width: 48,
                height: 48,
                decoration: BoxDecoration(
                  color: colorScheme.primaryContainer,
                  borderRadius: BorderRadius.circular(16),
                ),
                child: Icon(icon, color: colorScheme.onPrimaryContainer),
              ),
              const SizedBox(height: 16),
              Text(title, style: theme.textTheme.titleLarge),
              const SizedBox(height: 8),
              Text(
                description,
                style: theme.textTheme.bodyMedium?.copyWith(
                  color: colorScheme.onSurfaceVariant,
                ),
              ),
              const SizedBox(height: 16),
              Wrap(
                spacing: 8,
                runSpacing: 8,
                children:
                    metrics
                        .map(
                          (metric) => Container(
                            padding: const EdgeInsets.symmetric(
                              horizontal: 10,
                              vertical: 8,
                            ),
                            decoration: BoxDecoration(
                              color: colorScheme.onSurface.withAlpha(12),
                              borderRadius: BorderRadius.circular(12),
                            ),
                            child: Text(metric),
                          ),
                        )
                        .toList(),
              ),
              const SizedBox(height: 18),
              TextButton.icon(
                onPressed: onPressed,
                icon: const Icon(Icons.arrow_forward_rounded),
                label: Text(actionLabel),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _BenefitTile extends StatelessWidget {
  const _BenefitTile({
    required this.icon,
    required this.title,
    required this.description,
  });

  final IconData icon;
  final String title;
  final String description;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;

    return SizedBox(
      width: 280,
      child: Card(
        child: Padding(
          padding: const EdgeInsets.all(22),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Icon(icon, color: colorScheme.primary),
              const SizedBox(height: 14),
              Text(title, style: theme.textTheme.titleMedium),
              const SizedBox(height: 8),
              Text(
                description,
                style: theme.textTheme.bodyMedium?.copyWith(
                  color: colorScheme.onSurfaceVariant,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _MetricTile extends StatelessWidget {
  const _MetricTile({required this.value, required this.label});

  final String value;
  final String label;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;

    return SizedBox(
      width: 120,
      child: Container(
        padding: const EdgeInsets.all(16),
        decoration: BoxDecoration(
          color: colorScheme.onSurface.withAlpha(10),
          borderRadius: BorderRadius.circular(18),
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              value,
              style: theme.textTheme.headlineSmall?.copyWith(
                fontWeight: FontWeight.w800,
              ),
            ),
            const SizedBox(height: 4),
            Text(label, style: theme.textTheme.bodySmall),
          ],
        ),
      ),
    );
  }
}

class _FeaturePill extends StatelessWidget {
  const _FeaturePill({required this.label});

  final String label;

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;

    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
      decoration: BoxDecoration(
        color: colorScheme.surface.withAlpha(190),
        borderRadius: BorderRadius.circular(999),
        border: Border.all(color: colorScheme.outline.withAlpha(45)),
      ),
      child: Text(label),
    );
  }
}

class _GlowOrb extends StatelessWidget {
  const _GlowOrb({required this.color, required this.size});

  final Color color;
  final double size;

  @override
  Widget build(BuildContext context) {
    return IgnorePointer(
      child: Container(
        width: size,
        height: size,
        decoration: BoxDecoration(
          shape: BoxShape.circle,
          gradient: RadialGradient(colors: [color, color.withAlpha(0)]),
        ),
      ),
    );
  }
}
1
likes
160
points
344
downloads

Documentation

API reference

Publisher

verified publishergmana.co

Weekly Downloads

A comprehensive Flutter UI library providing curated widgets, form helpers, theme services, and extensions to accelerate and standardize development.

Repository (GitHub)
View/report issues

Topics

#flutter #ui #widget #form #utilities

License

BSD-3-Clause (license)

Dependencies

flutter, gmana

More

Packages that depend on gmana_flutter