material_palette 1.2.0 copy "material_palette: ^1.2.0" to clipboard
material_palette: ^1.2.0 copied to clipboard

A collection of procedural material shader widgets for Flutter.

example/lib/main.dart

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

import 'gallery_page.dart';
import 'shader_cards.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Material Palette',
      theme: ThemeData(
        colorScheme: const ColorScheme(
          brightness: Brightness.dark,
          primary: Color(0xFF8C8CEF),
          onPrimary: Color(0xFF09090B),
          primaryContainer: Color(0xFF4C52D1),
          onPrimaryContainer: Color(0xFFF0F0F0),
          secondary: Color(0xFFD19A66),
          onSecondary: Color(0xFF09090B),
          secondaryContainer: Color(0xFF5A3E1F),
          onSecondaryContainer: Color(0xFFF0F0F0),
          tertiary: Color(0xFF81B88B),
          onTertiary: Color(0xFF09090B),
          tertiaryContainer: Color(0xFF2E5435),
          onTertiaryContainer: Color(0xFFF0F0F0),
          error: Color(0xFFE06C75),
          onError: Color(0xFF09090B),
          errorContainer: Color(0xFF5A222A),
          onErrorContainer: Color(0xFFF0F0F0),
          surface: Color(0xFF202329),
          onSurface: Color(0xFFF0F0F0),
          onSurfaceVariant: Color(0xFFABB2BF),
          outline: Color(0xFF373A44),
          outlineVariant: Color(0xFF2C2F38),
          shadow: Color(0xFF000000),
          scrim: Color(0xFF000000),
          inverseSurface: Color(0xFFF0F0F0),
          onInverseSurface: Color(0xFF202329),
          inversePrimary: Color(0xFF4C52D1),
          surfaceContainerHighest: Color(0xFF282C34),
        ),
      ),
      home: const ShaderCarouselPage(),
    );
  }
}

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

  @override
  State<ShaderCarouselPage> createState() => _ShaderCarouselPageState();
}

class _ShaderCarouselPageState extends State<ShaderCarouselPage> {
  PageController? _pageController;
  int _currentPage = 0;
  double _lastViewportFraction = 0;

  @override
  void dispose() {
    _pageController?.dispose();
    super.dispose();
  }
  
  double _getViewportFraction(double screenWidth) {
    // On smaller screens, show more of the current card
    if (screenWidth < 400) return 0.85;
    if (screenWidth < 600) return 0.75;
    if (screenWidth < 900) return 0.65;
    return 0.55;
  }
  
  void _updatePageController(double viewportFraction) {
    if (_lastViewportFraction != viewportFraction) {
      _lastViewportFraction = viewportFraction;
      _pageController?.dispose();
      _pageController = PageController(
        viewportFraction: viewportFraction,
        initialPage: _currentPage,
      );
    }
  }

  void _goToPage(int page) {
    _pageController?.animateToPage(
      page,
      duration: const Duration(milliseconds: 300),
      curve: Curves.easeInOut,
    );
  }

  void _previousPage() {
    if (_currentPage > 0) {
      _pageController?.previousPage(
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeInOut,
      );
    }
  }

  void _nextPage() {
    if (_currentPage < allShaders.length - 1) {
      _pageController?.nextPage(
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeInOut,
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;
    final viewportFraction = _getViewportFraction(screenWidth);
    _updatePageController(viewportFraction);
    
    return Scaffold(
      backgroundColor: backgroundColor,
      appBar: AppBar(
        title: const Text('Material Palette'),
        backgroundColor: Colors.transparent,
        elevation: 0,
        centerTitle: true,
        actions: [
          IconButton(
            icon: const Icon(Icons.grid_view),
            tooltip: 'Preset Gallery',
            onPressed: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const GalleryPage()),
            ),
          ),
        ],
      ),
      body: Column(
        children: [
          const SizedBox(height: 12),
          // Navigation bar with dropdown and arrows
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                // Previous button
                IconButton(
                  onPressed: _currentPage > 0 ? _previousPage : null,
                  icon: const Icon(Icons.chevron_left, size: 32),
                  color: Colors.white,
                  disabledColor: Colors.grey.shade700,
                ),
                const SizedBox(width: 8),
                // Shader picker dropdown
                Container(
                  padding: const EdgeInsets.symmetric(horizontal: 12),
                  decoration: BoxDecoration(
                    color: Colors.grey.shade900,
                    borderRadius: BorderRadius.circular(8),
                    border: Border.all(color: Colors.grey.shade700),
                  ),
                  child: DropdownButtonHideUnderline(
                    child: DropdownButton<int>(
                      value: _currentPage,
                      dropdownColor: Colors.grey.shade900,
                      style: const TextStyle(color: Colors.white, fontSize: 16),
                      icon: const Icon(Icons.arrow_drop_down, color: Colors.white),
                      items: List.generate(
                        allShaders.length,
                        (index) => DropdownMenuItem(
                          value: index,
                          child: SizedBox(
                            width: 140,
                            child: Text(
                              allShaders[index].title,
                              overflow: TextOverflow.ellipsis,
                            ),
                          ),
                        ),
                      ),
                      onChanged: (value) {
                        if (value != null) _goToPage(value);
                      },
                    ),
                  ),
                ),
                const SizedBox(width: 8),
                // Next button
                IconButton(
                  onPressed: _currentPage < allShaders.length - 1 ? _nextPage : null,
                  icon: const Icon(Icons.chevron_right, size: 32),
                  color: Colors.white,
                  disabledColor: Colors.grey.shade700,
                ),
              ],
            ),
          ),
          // Page counter
          Padding(
            padding: const EdgeInsets.only(top: 8),
            child: Text(
              '${_currentPage + 1} / ${allShaders.length}',
              style: TextStyle(
                color: Colors.grey.shade500,
                fontSize: 12,
              ),
            ),
          ),
          const SizedBox(height: 12),
          // Carousel
          Expanded(
            child: PageView.builder(
              controller: _pageController,
              onPageChanged: (index) {
                setState(() {
                  _currentPage = index;
                });
              },
              itemCount: allShaders.length,
              itemBuilder: (context, index) {
                return ShaderCard(data: allShaders[index]);
              },
            ),
          ),
          const SizedBox(height: 20),
        ],
      ),
    );
  }
}
1
likes
160
points
1.26k
downloads

Publisher

verified publisherflutterflow.io

Weekly Downloads

A collection of procedural material shader widgets for Flutter.

Homepage
Repository (GitHub)
View/report issues

Topics

#shader #material #gpu #glsl #animation

Documentation

API reference

License

MIT (license)

Dependencies

flutter, flutter_shaders

More

Packages that depend on material_palette