material_expressive 0.2.0 copy "material_expressive: ^0.2.0" to clipboard
material_expressive: ^0.2.0 copied to clipboard

A complete expressive design system for Flutter — tokens, motion, shapes, theme generation, adaptive layouts, loading indicators, animated icons, and screen templates.

example/main.dart

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

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

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

  @override
  State<ExpressiveExampleApp> createState() => _ExpressiveExampleAppState();
}

class _ExpressiveExampleAppState extends State<ExpressiveExampleApp> {
  Color _seedColor = Colors.deepOrange;
  var _shapeType = ExpressiveShapeType.organic;
  var _isDark = false;

  @override
  Widget build(BuildContext context) {
    final theme = ExpressiveTheme.fromSeed(
      seedColor: _seedColor,
      shapeType: _shapeType,
    );

    return ExpressiveThemeProvider(
      themeData: theme,
      child: MaterialApp(
        title: 'Material Expressive Demo',
        debugShowCheckedModeBanner: false,
        theme: ExpressiveComponentThemes.applyExpressive(theme.materialTheme),
        darkTheme: ExpressiveComponentThemes.applyExpressive(
          theme.materialDarkTheme,
        ),
        themeMode: _isDark ? ThemeMode.dark : ThemeMode.light,
        home: _HomePage(
          seedColor: _seedColor,
          shapeType: _shapeType,
          onSeedChanged: (c) => setState(() => _seedColor = c),
          onShapeChanged: (s) => setState(() => _shapeType = s),
          onToggleDark: () => setState(() => _isDark = !_isDark),
        ),
      ),
    );
  }
}

class _HomePage extends StatefulWidget {
  const _HomePage({
    required this.seedColor,
    required this.shapeType,
    required this.onSeedChanged,
    required this.onShapeChanged,
    required this.onToggleDark,
  });

  final Color seedColor;
  final ExpressiveShapeType shapeType;
  final ValueChanged<Color> onSeedChanged;
  final ValueChanged<ExpressiveShapeType> onShapeChanged;
  final VoidCallback onToggleDark;

  @override
  State<_HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<_HomePage> {
  var _isMenuOpen = false;
  var _isLiked = false;
  var _isPlaying = false;
  var _selectedButton = 0;
  var _currentShapeIndex = 0;
  var _springActive = false;

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

    return Scaffold(
      appBar: AppBar(
        title: const Text('Material Expressive'),
        actions: [
          IconButton(
            icon: const Icon(Icons.dark_mode),
            onPressed: widget.onToggleDark,
          ),
        ],
      ),
      endDrawer: const TokenInspector(),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        child: const Icon(Icons.add),
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          // -- Seed color --
          Text('Seed Color', style: theme.textTheme.titleMedium),
          const SizedBox(height: 8),
          Wrap(
            spacing: 8,
            runSpacing: 8,
            children:
                [
                      Colors.deepOrange,
                      Colors.blue,
                      Colors.teal,
                      Colors.purple,
                      Colors.green,
                      Colors.pink,
                    ]
                    .map(
                      (c) => GestureDetector(
                        onTap: () => widget.onSeedChanged(c),
                        child: CircleAvatar(
                          radius: 20,
                          backgroundColor: c,
                          child: widget.seedColor == c
                              ? const Icon(
                                  Icons.check,
                                  color: Colors.white,
                                  size: 16,
                                )
                              : null,
                        ),
                      ),
                    )
                    .toList(),
          ),
          const SizedBox(height: 24),

          // -- Shape type --
          Text('Shape Type', style: theme.textTheme.titleMedium),
          const SizedBox(height: 8),
          SegmentedButton<ExpressiveShapeType>(
            segments: const [
              ButtonSegment(
                value: ExpressiveShapeType.rounded,
                label: Text('Rounded'),
              ),
              ButtonSegment(
                value: ExpressiveShapeType.squircle,
                label: Text('Squircle'),
              ),
              ButtonSegment(
                value: ExpressiveShapeType.organic,
                label: Text('Organic'),
              ),
            ],
            selected: {widget.shapeType},
            onSelectionChanged: (s) => widget.onShapeChanged(s.first),
          ),
          const SizedBox(height: 32),

          // -- Shape Morphing --
          Text('Shape Morphing', style: theme.textTheme.titleLarge),
          const SizedBox(height: 4),
          Text('Tap to morph between shapes', style: theme.textTheme.bodySmall),
          const SizedBox(height: 12),
          Center(
            child: GestureDetector(
              onTap: () => setState(() {
                _currentShapeIndex =
                    (_currentShapeIndex + 1) % MaterialShapes.allNames.length;
              }),
              child: Column(
                children: [
                  ShapeMorph(
                    shape: MaterialShapes.byName(
                      MaterialShapes.allNames[_currentShapeIndex],
                    ),
                    size: 120,
                    color: scheme.primary,
                    duration: const Duration(milliseconds: 600),
                    curve: Curves.easeInOut,
                  ),
                  const SizedBox(height: 8),
                  Text(
                    MaterialShapes.allNames[_currentShapeIndex],
                    style: theme.textTheme.labelLarge,
                  ),
                ],
              ),
            ),
          ),
          const SizedBox(height: 16),
          // Static shape grid
          Wrap(
            spacing: 10,
            runSpacing: 10,
            children:
                [
                      'heart',
                      'flower',
                      'clover4Leaf',
                      'boom',
                      'sunny',
                      'gem',
                      'cookie6Sided',
                      'diamond',
                    ]
                    .map(
                      (name) => Container(
                        width: 48,
                        height: 48,
                        decoration: ShapeDecoration(
                          color: scheme.tertiaryContainer,
                          shape: MorphableShapeBorder(
                            points: MaterialShapes.byName(name),
                          ),
                        ),
                      ),
                    )
                    .toList(),
          ),
          const SizedBox(height: 32),

          // -- Spring Physics --
          Text('Spring Physics', style: theme.textTheme.titleLarge),
          const SizedBox(height: 12),
          Center(
            child: GestureDetector(
              onTap: () => setState(() => _springActive = !_springActive),
              child: SpringTransition(
                isActive: _springActive,
                spring: MotionScheme.expressive.defaultSpatial,
                scaleActive: 1.3,
                scaleInactive: 1.0,
                child: Container(
                  width: 80,
                  height: 80,
                  decoration: BoxDecoration(
                    color: scheme.secondaryContainer,
                    borderRadius: BorderRadius.circular(20),
                  ),
                  child: Center(
                    child: Text(
                      _springActive ? '🎉' : '👆',
                      style: const TextStyle(fontSize: 32),
                    ),
                  ),
                ),
              ),
            ),
          ),
          const SizedBox(height: 8),
          Center(
            child: Text(
              'Tap for spring bounce',
              style: theme.textTheme.bodySmall,
            ),
          ),
          const SizedBox(height: 32),

          // -- Button Group --
          Text('Button Group', style: theme.textTheme.titleLarge),
          const SizedBox(height: 12),
          ExpressiveButtonGroup(
            buttons: const [
              ExpressiveButtonGroupItem(label: 'Day', icon: Icons.today),
              ExpressiveButtonGroupItem(label: 'Week', icon: Icons.view_week),
              ExpressiveButtonGroupItem(
                label: 'Month',
                icon: Icons.calendar_month,
              ),
            ],
            selectedIndex: _selectedButton,
            onSelected: (i) => setState(() => _selectedButton = i),
          ),
          const SizedBox(height: 32),

          // -- Split Button --
          Text('Split Button', style: theme.textTheme.titleLarge),
          const SizedBox(height: 12),
          ExpressiveSplitButton(
            label: 'Save',
            icon: Icons.save,
            onPressed: () {},
            onDropdown: () {},
          ),
          const SizedBox(height: 32),

          // -- Floating Toolbar --
          Text('Floating Toolbar', style: theme.textTheme.titleLarge),
          const SizedBox(height: 12),
          Center(
            child: ExpressiveFloatingToolbar(
              items: [
                FloatingToolbarItem(
                  icon: Icons.format_bold,
                  isSelected: true,
                  onPressed: () {},
                ),
                FloatingToolbarItem(
                  icon: Icons.format_italic,
                  onPressed: () {},
                ),
                FloatingToolbarItem(
                  icon: Icons.format_underline,
                  onPressed: () {},
                ),
                const FloatingToolbarItem.divider(),
                FloatingToolbarItem(icon: Icons.link, onPressed: () {}),
                FloatingToolbarItem(icon: Icons.image, onPressed: () {}),
              ],
            ),
          ),
          const SizedBox(height: 32),

          // -- Loading Indicators --
          Text('Loading Indicators', style: theme.textTheme.titleLarge),
          const SizedBox(height: 12),
          const Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              Column(
                children: [
                  ContainedLoadingIndicator(segments: 4),
                  SizedBox(height: 8),
                  Text('Contained'),
                ],
              ),
              Column(
                children: [
                  OrganicLoadingIndicator(),
                  SizedBox(height: 8),
                  Text('Organic'),
                ],
              ),
              Column(
                children: [
                  PulsingDotsIndicator(),
                  SizedBox(height: 8),
                  Text('Pulsing'),
                ],
              ),
              Column(
                children: [
                  MorphingShapeIndicator(),
                  SizedBox(height: 8),
                  Text('Morphing'),
                ],
              ),
            ],
          ),
          const SizedBox(height: 32),

          // -- Animated Icons --
          Text('Animated Icons', style: theme.textTheme.titleLarge),
          const SizedBox(height: 12),
          Card(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  Column(
                    children: [
                      ExpressiveAnimatedIcon(
                        icon: AnimatedIcons.menu_close,
                        isActive: _isMenuOpen,
                        onPressed: () =>
                            setState(() => _isMenuOpen = !_isMenuOpen),
                      ),
                      const Text('Menu'),
                    ],
                  ),
                  Column(
                    children: [
                      BouncingIcon(
                        icon: Icons.favorite_border,
                        activeIcon: Icons.favorite,
                        isActive: _isLiked,
                        activeColor: Colors.red,
                        onPressed: () => setState(() => _isLiked = !_isLiked),
                      ),
                      const Text('Like'),
                    ],
                  ),
                  Column(
                    children: [
                      MorphingIcon(
                        icon: Icons.play_arrow,
                        activeIcon: Icons.pause,
                        isActive: _isPlaying,
                        onPressed: () =>
                            setState(() => _isPlaying = !_isPlaying),
                      ),
                      const Text('Play'),
                    ],
                  ),
                  Column(
                    children: [
                      HoverIcon(
                        icon: Icons.settings,
                        tooltip: 'Settings',
                        onPressed: () {},
                      ),
                      const Text('Hover'),
                    ],
                  ),
                ],
              ),
            ),
          ),
          const SizedBox(height: 32),

          // -- Surfaces --
          Text('Surfaces', style: theme.textTheme.titleLarge),
          const SizedBox(height: 12),
          const Wrap(
            spacing: 10,
            runSpacing: 10,
            children: [
              _SurfaceChip('Primary', ExpressiveSurfaceType.primaryTinted),
              _SurfaceChip('Secondary', ExpressiveSurfaceType.secondaryTinted),
              _SurfaceChip('Tertiary', ExpressiveSurfaceType.tertiaryTinted),
              _SurfaceChip('Container', ExpressiveSurfaceType.container),
              _SurfaceChip('High', ExpressiveSurfaceType.containerHigh),
            ],
          ),
          const SizedBox(height: 32),

          // -- State Layer --
          Text('State Layer', style: theme.textTheme.titleLarge),
          const SizedBox(height: 12),
          ExpressiveStateLayer(
            onTap: () {},
            child: Card(
              child: Padding(
                padding: const EdgeInsets.all(24),
                child: Center(
                  child: Text(
                    'Press for scale animation',
                    style: theme.textTheme.bodyLarge,
                  ),
                ),
              ),
            ),
          ),
          const SizedBox(height: 80),
        ],
      ),
    );
  }
}

class _SurfaceChip extends StatelessWidget {
  const _SurfaceChip(this.label, this.type);
  final String label;
  final ExpressiveSurfaceType type;

  @override
  Widget build(BuildContext context) {
    return ExpressiveSurface(
      type: type,
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      borderRadius: BorderRadius.circular(12),
      child: Text(label, style: Theme.of(context).textTheme.labelMedium),
    );
  }
}
0
likes
0
points
440
downloads

Publisher

verified publishervenx.site

Weekly Downloads

A complete expressive design system for Flutter — tokens, motion, shapes, theme generation, adaptive layouts, loading indicators, animated icons, and screen templates.

Repository (GitHub)
View/report issues

Topics

#design-system #material-design #theming #animation #ui

License

unknown (license)

Dependencies

animations, flutter

More

Packages that depend on material_expressive