flutter_minimal_design 1.0.1
flutter_minimal_design: ^1.0.1 copied to clipboard
A minimal and elegant design system for Flutter applications
Flutter Minimal Design 🎨 #
A minimal, production-ready design system for Flutter applications. Build beautiful, consistent UIs with pre-defined spacing, typography, colors, and components - following industry best practices.
✨ Features #
- 🎯 Consistent Design Tokens - Spacing, sizing, colors, typography
- 📱 Fully Responsive - Built with flutter_screenutil for perfect scaling
- 🧩 Pre-built Components - AppBar, Buttons, Form Fields, Text Widget, Screen Wrapper
- 📝 CustomAppText - Simplified text widget with automatic DSTextStyles integration
- 🎨 Easy Customization - Full copyWith support on all components
- ♿ Accessible - Follows WCAG guidelines (44×44 touch targets)
- 🌍 RTL Support - Works seamlessly with Arabic and other RTL languages
- 🎨 Theme-Aware Fonts - Automatic font detection from MaterialApp theme
- 📦 Zero Configuration - Works out of the box
- 🔧 Type-Safe - Leverages Dart's type system
🎬 Preview #
CustomScreen(
title: 'Home',
body: Column(
children: [
CustomAppText('Welcome Back!', style: DSTextStyles.pageTitle),
DSVSpace.xlg,
DSButton.primary(label: 'Get Started', onPressed: () {}),
],
),
)
📦 Installation #
Add this to your package's pubspec.yaml file:
dependencies:
flutter_minimal_design: ^1.0.1
flutter_screenutil: ^5.9.0
flutter_svg: ^2.0.9
Then run:
flutter pub get
🚀 Quick Start #
1. Initialize ScreenUtil #
In your main.dart:
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_minimal_design/flutter_minimal_design.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(375, 812), // iPhone 11 Pro
minTextAdapt: true,
splitScreenMode: true,
builder: (context, child) {
return MaterialApp(
title: 'My App',
home: HomeScreen(),
);
},
);
}
}
2. Use CustomScreen Wrapper #
import 'package:flutter_minimal_design/flutter_minimal_design.dart';
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CustomScreen(
title: 'Home',
body: Column(
children: [
CustomAppText('Welcome!', style: DSTextStyles.pageTitle),
DSVSpace.xlg,
DSButton.primary(
label: 'Get Started',
onPressed: () {},
),
],
),
);
}
}
📚 Core Components #
🖼️ CustomScreen - Universal Screen Wrapper #
The heart of the design system - provides consistent layout for all screens:
// Basic screen
CustomScreen(
title: 'Home',
body: YourContent(),
)
// Form screen (keyboard aware)
DSScreen.form(
title: 'Login',
body: LoginForm(),
)
// Loading state
CustomScreen(
title: 'Data',
isLoading: true,
body: Content(),
)
// Error state with retry
CustomScreen(
title: 'Data',
hasError: true,
onRetry: () => _loadData(),
body: Content(),
)
// Custom padding
CustomScreen(
title: 'Custom',
body: Content(),
padding: EdgeInsets.all(16.w),
)
Features:
- ✅ Automatic AppBar integration
- ✅ Built-in loading & error states
- ✅ Safe area handling
- ✅ Responsive padding (20×20 default)
- ✅ Auto-scroll with bounce physics
- ✅ Keyboard awareness
- ✅ Complete copyWith support
🔼 AppBar Component #
CustomScreen(
title: 'Settings',
hideBackButton: false,
appBarEndWidget: IconButton(
icon: Icon(Icons.more_vert),
onPressed: () {},
),
body: Content(),
)
📝 CustomAppText Widget #
Simplified text widget with automatic DSTextStyles integration:
// Basic usage (defaults to DSTextStyles.body)
CustomAppText('Hello World')
// With specific text style
CustomAppText('Title', style: DSTextStyles.pageTitle)
// With style overrides
CustomAppText(
'Custom Text',
color: Colors.blue,
fontSize: 18,
fontWeight: FontWeight.bold,
)
// All standard Text properties
CustomAppText(
'Long text example',
style: DSTextStyles.body,
maxLines: 2,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
)
Features:
- ✅ Defaults to DSTextStyles.body
- ✅ Quick style overrides without copyWith
- ✅ Full Text widget compatibility
- ✅ Automatic theme font integration
- ✅ Responsive font sizes
🔘 Buttons #
// Primary button
DSButton.primary(
label: 'Continue',
onPressed: () {},
)
// Secondary button
DSButton.secondary(
label: 'Cancel',
onPressed: () {},
)
// Danger button
DSButton.danger(
label: 'Delete',
onPressed: () {},
)
// Success button
DSButton.success(
label: 'Confirm',
onPressed: () {},
)
// Text button
DSButton.text(
label: 'Skip',
onPressed: () {},
)
// Custom button
CustomButton(
label: 'Custom',
onPressed: () {},
backgroundColor: Colors.purple,
isIconButton: true,
iconPath: 'assets/icons/icon.svg',
)
📝 Form Fields #
// Email field
DSFormField.email(
controller: emailController,
)
// Password field
DSFormField.password(
controller: passwordController,
)
// Phone field with country code
DSFormField.phone(
countryCode: '966',
showCountryCode: true,
)
// Search field
DSFormField.search(
onChanged: (value) => _search(value),
)
// Text area
DSFormField.textArea(
labelText: 'Description',
maxLength: 500,
)
// Custom field
CustomAppTextFormField(
labelText: 'Name',
hintText: 'Enter your name',
validator: (value) => value!.isEmpty ? 'Required' : null,
)
🎨 Design Tokens #
Spacing #
DSSpacing.xsm // 4 - Extra small
DSSpacing.sm // 8 - Small
DSSpacing.md // 12 - Medium (default)
DSSpacing.lg // 16 - Large
DSSpacing.xlg // 20 - Extra large
DSSpacing.xxlg // 24 - Double XL
DSSpacing.xxxlg // 32 - Triple XL
// Usage
DSVSpace.xlg // 20.h vertical gap
DSHSpace.md // 12.w horizontal gap
Sizes #
// Buttons
DSSize.buttonHeight // 48.h
DSSize.smallButtonHeight // 32.h
DSSize.minTouchTarget // 44.h (accessibility)
// Icons
DSSize.iconXs // 16.sp
DSSize.iconSm // 20.sp
DSSize.iconMd // 24.sp
DSSize.iconLg // 32.sp
DSSize.iconXl // 40.sp
// Avatars
DSSize.avatarSm // 32.w
DSSize.avatarMd // 48.w
DSSize.avatarLg // 64.w
DSSize.avatarXl // 80.w
// Border Radius
DSSize.radiusSm // 8.r
DSSize.radiusMd // 12.r
DSSize.radiusLg // 16.r
DSSize.radiusXl // 20.r
DSSize.radiusPill // 24.r
Typography #
DSTextStyles.pageTitle // 20.sp, w700
DSTextStyles.sectionHeader // 16.sp, w700
DSTextStyles.subheader // 14.sp, w600
DSTextStyles.body // 14.sp, w400
DSTextStyles.label // 12.sp, w500
DSTextStyles.caption // 12.sp, w400
DSTextStyles.button // 16.sp, w600
CustomAppText - Simplified text widget with automatic DSTextStyles integration:
// Basic usage (uses DSTextStyles.body by default)
CustomAppText('Hello World')
// With custom style
CustomAppText('Title', style: DSTextStyles.pageTitle)
// With direct overrides
CustomAppText(
'Custom Text',
color: Colors.blue,
fontWeight: FontWeight.bold,
fontSize: 18,
)
// All Text widget properties supported
CustomAppText(
'Long text that wraps',
style: DSTextStyles.body,
maxLines: 2,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
)
Initialize Theme-Aware Fonts (Optional):
// In your MaterialApp
MaterialApp(
theme: ThemeData(
fontFamily: 'Cairo', // Your custom font
),
home: Builder(
builder: (context) {
DSTextStyles.initialize(context);
return HomeScreen();
},
),
)
Padding Presets #
DSEdgeInsets.screen // 20×20 (full screen)
DSEdgeInsets.container // 12×8
DSEdgeInsets.button // 16×12
DSEdgeInsets.card // 16×12
DSEdgeInsets.formField // 16×14
DSEdgeInsets.listItem // 12×12
📱 Complete Examples #
Login Screen #
class LoginScreen extends StatefulWidget {
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
bool _isLoading = false;
@override
Widget build(BuildContext context) {
return CustomScreen(
title: 'Login',
hideBackButton: true,
isLoading: _isLoading,
body: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomAppText('Welcome Back', style: DSTextStyles.pageTitle),
DSVSpace.xlg,
DSFormField.email(
controller: _emailController,
),
DSVSpace.lg,
DSFormField.password(
controller: _passwordController,
),
DSVSpace.xxxlg,
DSButton.primary(
label: 'Login',
onPressed: _handleLogin,
),
DSVSpace.md,
DSButton.text(
label: 'Forgot Password?',
onPressed: () {},
),
],
),
),
);
}
void _handleLogin() async {
if (_formKey.currentState!.validate()) {
setState(() => _isLoading = true);
// Login logic
await Future.delayed(Duration(seconds: 2));
setState(() => _isLoading = false);
}
}
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
}
List Screen #
class ProductsScreen extends StatefulWidget {
@override
State<ProductsScreen> createState() => _ProductsScreenState();
}
class _ProductsScreenState extends State<ProductsScreen> {
bool _isLoading = true;
bool _hasError = false;
List<Product> _products = [];
@override
void initState() {
super.initState();
_loadProducts();
}
@override
Widget build(BuildContext context) {
return CustomScreen(
title: 'Products',
isLoading: _isLoading,
hasError: _hasError,
onRetry: _loadProducts,
appBarEndWidget: IconButton(
icon: Icon(Icons.filter_list),
onPressed: _showFilters,
),
body: Column(
children: [
DSFormField.search(
onChanged: (value) => _searchProducts(value),
),
DSVSpace.lg,
..._products.map((product) => Column(
children: [
ProductCard(product: product),
DSVSpace.md,
],
)),
],
),
);
}
Future<void> _loadProducts() async {
setState(() {
_isLoading = true;
_hasError = false;
});
try {
final products = await ProductService.fetchProducts();
setState(() {
_products = products;
_isLoading = false;
});
} catch (e) {
setState(() {
_hasError = true;
_isLoading = false;
});
}
}
}
Settings Screen #
class SettingsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CustomScreen(
title: 'Settings',
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomAppText('Account', style: DSTextStyles.sectionHeader),
DSVSpace.md,
SettingTile(
icon: Icons.person,
title: 'Edit Profile',
onTap: () {},
),
DSVSpace.sm,
SettingTile(
icon: Icons.lock,
title: 'Change Password',
onTap: () {},
),
DSVSpace.xlg,
CustomAppText('Preferences', style: DSTextStyles.sectionHeader),
DSVSpace.md,
SettingTile(
icon: Icons.notifications,
title: 'Notifications',
trailing: Switch(value: true, onChanged: (v) {}),
),
DSVSpace.xlg,
DSButton.danger(
label: 'Logout',
onPressed: () => _handleLogout(),
),
],
),
);
}
}
🎨 Pre-configured Screen Variants #
// Basic screen
DSScreen.basic(
title: 'Home',
body: Content(),
)
// Form screen (keyboard aware, no scroll)
DSScreen.form(
title: 'Register',
body: RegistrationForm(),
)
// Details screen (scrollable)
DSScreen.details(
title: 'Article',
body: ArticleContent(),
)
// No AppBar
DSScreen.noAppBar(
body: OnboardingContent(),
)
// Full screen (no padding/safe area)
DSScreen.fullScreen(
body: SplashScreen(),
)
// Loading screen
DSScreen.loading(
title: 'Loading',
)
// Error screen
DSScreen.error(
title: 'Error',
onRetry: () => _reload(),
)
// With bottom navigation
DSScreen.withBottomNav(
title: 'Home',
body: Content(),
bottomNavigationBar: BottomNav(),
)
// With FAB
DSScreen.withFAB(
title: 'Messages',
body: MessagesList(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {},
),
)
🎯 Best Practices #
1. Always Use Design Tokens #
// ❌ Bad
padding: EdgeInsets.all(12.w)
// ✅ Good
padding: DSEdgeInsets.container
2. Use Pre-configured Variants #
// ❌ Verbose
CustomButton(
label: 'Submit',
backgroundColor: Color(0xFF1A4220),
textColor: Colors.white,
// ... many parameters
)
// ✅ Simple
DSButton.primary(
label: 'Submit',
onPressed: () {},
)
3. Consistent Spacing #
// ✅ Good
Column(
children: [
Text('Title'),
DSVSpace.xlg, // 20.h between sections
Text('Content'),
DSVSpace.md, // 12.h between items
DSButton.primary(...),
],
)
4. Use CustomScreen for All Screens #
// ✅ Consistent layout
CustomScreen(
title: 'My Screen',
body: Content(),
)
// Automatically provides:
// - AppBar integration
// - Safe area handling
// - Responsive padding (20×20)
// - Loading/error states
// - Keyboard awareness
5. Respect Touch Targets (44×44 minimum) #
// ❌ Bad - Too small
Container(
width: 30.w,
height: 30.h,
child: IconButton(...),
)
// ✅ Good - Accessible
Container(
width: DSSize.minTouchTarget, // 44×44
height: DSSize.minTouchTarget,
child: IconButton(...),
)
📊 Design System Values #
| Token | Value | Usage |
|---|---|---|
DSEdgeInsets.screen |
20×20 | Screen padding |
DSVSpace.xlg |
20.h | Section gaps |
DSVSpace.md |
12.h | Item gaps |
DSSize.buttonHeight |
48.h | Button height |
DSSize.iconMd |
24.sp | Standard icons |
DSRadius.md |
12.r | Border radius |
DSTextStyles.pageTitle |
20sp w700 | Page headers |
DSTextStyles.body |
14sp w400 | Body text |
🎨 Customization #
Using copyWith #
// Create base screen
final baseScreen = CustomScreen(
title: 'Base',
body: Content(),
);
// Create variants
final darkScreen = baseScreen.copyWith(
appBarBackgroundColor: Colors.black87,
appBarTitleColor: Colors.white,
backgroundColor: Colors.grey.shade900,
);
final customButton = CustomButton(
label: 'Base',
onPressed: () {},
).copyWith(
backgroundColor: Colors.purple,
borderRadius: 20,
);
Extend Design System #
// Add your own spacing
class MySpacing {
static double get custom => 40.h;
}
// Add your own text styles
class MyTextStyles {
static TextStyle get custom => TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w600,
);
}
🔧 Configuration #
Change Design Size #
ScreenUtilInit(
designSize: const Size(390, 844), // iPhone 14 Pro
builder: (context, child) => MaterialApp(...),
)
Customize Default Colors #
// In your app's theme
ThemeData(
primaryColor: Color(0xFF1A4220),
// Components will use this color
)
🐛 Troubleshooting #
Issue: Sizes not responsive #
Solution: Make sure you initialized ScreenUtil:
ScreenUtilInit(
designSize: const Size(375, 812),
builder: (context, child) => MaterialApp(...),
)
Issue: SVG icons not showing #
Solution: Ensure flutter_svg is added and assets are in your app:
# In your app's pubspec.yaml
dependencies:
flutter_svg: ^2.0.9
flutter:
assets:
- assets/icons/
Issue: CustomScreen not found #
Solution: Import the package:
import 'package:flutter_minimal_design/flutter_minimal_design.dart';
📦 What's Included #
- ✅ Foundation: Spacing, Sizing, Colors, Typography, Border Radius
- ✅ Components: CustomScreen, AppBar, Buttons, Form Fields, CustomAppText
- ✅ Utilities: Spacing widgets, Padding presets, Extensions
- ✅ Pre-configured Variants: 6 button types, 6 form field types, 9 screen types
- ✅ Complete copyWith: All components support full customization
- ✅ Theme Integration: Automatic font detection and initialization API
🎯 Package Size #
Lightweight and minimal:
- Core package: ~50KB
- With dependencies: ~2MB (flutter_screenutil + flutter_svg)
📄 License #
MIT License - see LICENSE file for details
🤝 Contributing #
Contributions are welcome! Please read CONTRIBUTING.md for details.
📝 Changelog #
See CHANGELOG.md for version history.
👥 Authors #
- Your Name - GitHub
🙏 Acknowledgments #
- Built with Flutter and flutter_screenutil
- Follows Material Design and WCAG accessibility guidelines
- Inspired by modern design systems (Tailwind, Chakra UI, Material Design)