material_design 0.30.0-dev
material_design: ^0.30.0-dev copied to clipboard
The fastest path to consistent Material Design UIs in Flutter. Build beautiful apps aligned with official metrics and guidelines using a powerful set of ready-to-use design tokens and helper widgets.
Material Design 3 (2025) for Flutter #
The fastest path to consistent Material Design UIs in Flutter. Build beautiful apps aligned with official metrics and guidelines using a powerful set of ready-to-use design tokens and helper widgets.
This package provides a comprehensive toolkit for implementing Material Design 3 in Flutter. It includes a complete design token system and utilities that align with the official Material Design 3 specification, making it easier to build beautiful and consistent user interfaces.
🚀 Features #
- Design Token System: A complete implementation of the Material Design 3 token hierarchy, including reference, system, and component tokens.
- Adaptive Design: Utilities for creating adaptive layouts that respond to different screen sizes and platforms.
- Material Design 3 Compliance: Built to align with the official Material Design 3 guidelines.
📦 Getting Started #
To use this package, add material_design
as a dependency in your pubspec.yaml
file.
dependencies:
material_design: ^0.30.0-dev
Then, import the library in your Dart code:
import 'package:material_design/material_design.dart';
🎨 Usage #
Design Tokens #
The library provides a flexible system for defining and using design tokens.
// Create reference tokens
const primaryRef = ReferenceToken<Color>(
Color(0xFF6750A4),
'purple.40'
);
// Create system tokens
const primaryColor = SystemToken<Color>(
Color(0xFF6750A4),
'color.primary',
referenceToken: primaryRef,
);
// Create component tokens
const buttonBackground = ComponentToken<Color>(
Color(0xFF6750A4),
'button.filled.background',
systemToken: primaryColor,
component: 'Button',
);
How to Use AdaptiveConfig
#
Here is a basic example of how to use AdaptiveConfig
within a Flutter widget. This StatelessWidget
gets the screen dimensions and uses the configuration to display a different layout based on the current WindowSizeClass
.
Example Code
import 'package.flutter/material.dart';
// Certifique-se de importar sua biblioteca. O caminho pode variar.
import 'package:your_project_name/material_adaptive.dart';
/// Uma tela que combina uma estrutura clara com lógica adaptativa avançada.
class BestResponsiveScreen extends StatelessWidget {
const BestResponsiveScreen({super.key});
@override
Widget build(BuildContext context) {
// 1. Obter a configuração adaptativa a partir do tamanho da tela.
final Size screenSize = MediaQuery.of(context).size;
final AdaptiveConfig config = AdaptiveConfig.fromDimensions(
width: screenSize.width,
height: screenSize.height,
);
// 2. Usar a estrutura limpa do 'switch' para lidar com os layouts.
// Esta é a principal vantagem do segundo código: clareza e escalabilidade.
switch (config.sizeClass) {
case WindowSizeClass.compact:
return _buildCompactLayout(config);
case WindowSizeClass.medium:
return _buildMediumLayout(config);
case WindowSizeClass.expanded:
case WindowSizeClass.large:
case WindowSizeClass.extraLarge:
// 3. Chamar um método de layout 'expanded' que agora possui a lógica
// inteligente do primeiro código.
return _buildSmartExpandedLayout(config);
}
}
/// Layout para telas compactas: simples e direto.
Widget _buildCompactLayout(AdaptiveConfig config) {
return Scaffold(
appBar: AppBar(title: const Text('Compact Layout')),
body: Center(
child: Text('Painel único para celular\nLargura: ${config.width.toInt()}px'),
),
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(icon: Icon(Icons.article), label: 'Feed'),
BottomNavigationBarItem(icon: Icon(Icons.chat), label: 'Chat'),
],
),
);
}
/// Layout para telas médias: usa Navigation Rail para melhor uso do espaço.
Widget _buildMediumLayout(AdaptiveConfig config) {
return Scaffold(
appBar: AppBar(title: const Text('Medium Layout')),
body: Row(
children: [
NavigationRail(
selectedIndex: 0,
labelType: NavigationRailLabelType.all,
destinations: const [
NavigationRailDestination(icon: Icon(Icons.article), label: Text('Feed')),
NavigationRailDestination(icon: Icon(Icons.chat), label: Text('Chat')),
],
),
const VerticalDivider(thickness: 1, width: 1),
Expanded(
child: Center(
child: Text(
'Layout de dois painéis (List-Detail)\nLargura: ${config.width.toInt()}px',
),
),
),
],
),
);
}
/// O "melhor dos dois mundos" para telas grandes.
/// Usa a lógica detalhada do primeiro código dentro da estrutura limpa do segundo.
Widget _buildSmartExpandedLayout(AdaptiveConfig config) {
return Scaffold(
body: Row(
children: [
// LÓGICA DO CÓDIGO 1: Escolhe dinamicamente entre Rail e Drawer.
// Para telas 'Large' ou maiores, usa um Drawer permanente.
// Para 'Expanded', pode usar um Rail para economizar espaço.
if (AdaptiveUtils.shouldUsePermanentDrawer(config.sizeClass))
NavigationDrawer(
selectedIndex: 0,
children: const [
Padding(padding: EdgeInsets.all(28), child: Text('Header')),
NavigationDrawerDestination(icon: Icon(Icons.article), label: Text('Feed')),
NavigationDrawerDestination(icon: Icon(Icons.chat), label: Text('Chat')),
],
)
else
NavigationRail(
selectedIndex: 0,
labelType: NavigationRailLabelType.all,
destinations: const [
NavigationRailDestination(icon: Icon(Icons.article), label: Text('Feed')),
NavigationRailDestination(icon: Icon(Icons.chat), label: Text('Chat')),
],
),
const VerticalDivider(thickness: 1, width: 1),
// LÓGICA DO CÓDIGO 1: Centraliza e limita a largura do conteúdo principal
// para melhor legibilidade em telas muito largas.
Expanded(
child: Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: config.maxContentWidth),
child: Container(
color: Colors.blue.shade50,
child: Center(
child: Text(
'Conteúdo Principal\n(Largura máxima: ${config.maxContentWidth.toInt()}px)',
textAlign: TextAlign.center,
),
),
),
),
),
),
// LÓGICA DO CÓDIGO 1: Adiciona um painel de apoio (terceiro painel)
// apenas se houver espaço suficiente (telas Large/ExtraLarge).
if (config.supportsThreePane) ...[
const VerticalDivider(thickness: 1, width: 1),
SizedBox(
width: AdaptiveUtils.getSupportingPaneWidth(
totalWidth: config.width,
sizeClass: config.sizeClass,
),
child: Container(
color: Colors.green.shade50,
child: const Center(child: Text('Painel de Apoio')),
),
),
]
],
),
);
}
}
// Stubs para os widgets de navegação para o código compilar.
// No seu projeto real, você usaria seus próprios widgets.
class NavigationDrawer extends StatelessWidget {
final int selectedIndex;
final List<Widget> children;
const NavigationDrawer({super.key, required this.selectedIndex, required this.children});
@override
Widget build(BuildContext context) => SizedBox(width: 250, child: Column(children: children));
}
🙏 Contributing #
Contributions are welcome! Please see the CONTRIBUTING.md file for more information.
📄 License #
This project is licensed under the MIT License - see the LICENSE file for details.