Refactor button component and add new sections for loading indicators, icons, and navigation; update enums and pubspec description
This commit is contained in:
parent
62ecb86b76
commit
020db0ac38
23 changed files with 1033 additions and 828 deletions
|
|
@ -1,5 +1,15 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3e_collection/m3e_collection.dart';
|
||||
import 'package:m3e_gallery/sections/button_section.dart';
|
||||
import 'package:m3e_gallery/sections/fab_section.dart';
|
||||
import 'package:m3e_gallery/sections/icon_button_section.dart';
|
||||
import 'package:m3e_gallery/sections/loading_indicator_section.dart';
|
||||
import 'package:m3e_gallery/sections/navigation_section.dart';
|
||||
import 'package:m3e_gallery/sections/progress_section.dart';
|
||||
import 'package:m3e_gallery/sections/section_card.dart';
|
||||
import 'package:m3e_gallery/sections/slider_section.dart';
|
||||
import 'package:m3e_gallery/sections/split_button_section.dart';
|
||||
import 'package:m3e_gallery/sections/toolbar_section.dart';
|
||||
|
||||
void main() => runApp(const GalleryApp());
|
||||
|
||||
|
|
@ -26,19 +36,8 @@ class GalleryHome extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _GalleryHomeState extends State<GalleryHome> {
|
||||
// Navigation examples
|
||||
int _navBarIndex = 0;
|
||||
int _railIndex = 0;
|
||||
|
||||
// Slider examples
|
||||
double _sliderValue = 0.4;
|
||||
RangeValues _rangeValues = const RangeValues(0.25, 0.75);
|
||||
|
||||
// Progress examples
|
||||
double _progressValue = 0.6;
|
||||
|
||||
void onPressed() {
|
||||
// Placeholder function for button onPressed
|
||||
// Placeholder function onPressed
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -55,478 +54,7 @@ class _GalleryHomeState extends State<GalleryHome> {
|
|||
Icon(Icons.more_vert),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
// Icon buttons
|
||||
Text('IconButtonM3E', style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
IconButtonM3E(
|
||||
icon: Icon(Icons.favorite),
|
||||
variant: IconButtonM3EVariant.filled,
|
||||
onPressed: onPressed),
|
||||
IconButtonM3E(
|
||||
icon: Icon(Icons.favorite),
|
||||
variant: IconButtonM3EVariant.tonal,
|
||||
onPressed: onPressed),
|
||||
IconButtonM3E(
|
||||
icon: Icon(Icons.favorite),
|
||||
variant: IconButtonM3EVariant.outlined,
|
||||
onPressed: onPressed),
|
||||
IconButtonM3E(
|
||||
icon: Icon(Icons.favorite),
|
||||
variant: IconButtonM3EVariant.standard,
|
||||
onPressed: onPressed),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
// Split Button
|
||||
Text('SplitButtonM3E', style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
SplitButtonM3E<String>(
|
||||
label: 'Primary',
|
||||
onPressed: () {},
|
||||
items: const [
|
||||
SplitButtonM3EItem<String>(value: 'one', child: 'One'),
|
||||
SplitButtonM3EItem<String>(value: 'two', child: 'Two'),
|
||||
],
|
||||
),
|
||||
SplitButtonM3E<String>(
|
||||
label: 'Tonal',
|
||||
emphasis: SplitButtonM3EEmphasis.tonal,
|
||||
onPressed: () {},
|
||||
items: const [
|
||||
SplitButtonM3EItem<String>(value: 'one', child: 'One'),
|
||||
SplitButtonM3EItem<String>(value: 'two', child: 'Two'),
|
||||
],
|
||||
),
|
||||
SplitButtonM3E<String>(
|
||||
label: 'Outlined',
|
||||
emphasis: SplitButtonM3EEmphasis.outlined,
|
||||
onPressed: () {},
|
||||
items: const [
|
||||
SplitButtonM3EItem<String>(value: 'one', child: 'One'),
|
||||
SplitButtonM3EItem<String>(value: 'two', child: 'Two'),
|
||||
],
|
||||
),
|
||||
SplitButtonM3E<String>(
|
||||
label: 'Text',
|
||||
emphasis: SplitButtonM3EEmphasis.text,
|
||||
onPressed: () {},
|
||||
items: const [
|
||||
SplitButtonM3EItem<String>(value: 'one', child: 'One'),
|
||||
SplitButtonM3EItem<String>(value: 'two', child: 'Two'),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
// Buttons
|
||||
Text('ButtonM3E', style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(spacing: 12, runSpacing: 12, children: [
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
ButtonM3E(
|
||||
labelText: 'Filled',
|
||||
variant: ButtonM3EVariant.filled,
|
||||
onPressed: onPressed),
|
||||
ButtonM3E(
|
||||
labelText: 'Tonal',
|
||||
variant: ButtonM3EVariant.tonal,
|
||||
onPressed: onPressed),
|
||||
ButtonM3E(
|
||||
labelText: 'Outlined',
|
||||
variant: ButtonM3EVariant.outlined,
|
||||
onPressed: onPressed),
|
||||
ButtonM3E(
|
||||
labelText: 'Text',
|
||||
variant: ButtonM3EVariant.text,
|
||||
onPressed: onPressed),
|
||||
ButtonM3E(
|
||||
labelText: 'Elevated',
|
||||
variant: ButtonM3EVariant.elevated,
|
||||
onPressed: onPressed),
|
||||
],
|
||||
),
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
ButtonM3E(
|
||||
labelText: 'Filled',
|
||||
variant: ButtonM3EVariant.filled,
|
||||
shapeFamily: ButtonM3EShapeFamily.square,
|
||||
onPressed: onPressed),
|
||||
ButtonM3E(
|
||||
labelText: 'Tonal',
|
||||
variant: ButtonM3EVariant.tonal,
|
||||
shapeFamily: ButtonM3EShapeFamily.square,
|
||||
onPressed: onPressed),
|
||||
ButtonM3E(
|
||||
labelText: 'Outlined',
|
||||
variant: ButtonM3EVariant.outlined,
|
||||
shapeFamily: ButtonM3EShapeFamily.square,
|
||||
onPressed: onPressed),
|
||||
ButtonM3E(
|
||||
labelText: 'Text',
|
||||
variant: ButtonM3EVariant.text,
|
||||
shapeFamily: ButtonM3EShapeFamily.square,
|
||||
onPressed: onPressed),
|
||||
ButtonM3E(
|
||||
labelText: 'Elevated',
|
||||
variant: ButtonM3EVariant.elevated,
|
||||
shapeFamily: ButtonM3EShapeFamily.square,
|
||||
onPressed: onPressed),
|
||||
],
|
||||
),
|
||||
]),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
// Button groups
|
||||
Text('ButtonGroupM3E', style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 12),
|
||||
// Standard group, round, md
|
||||
ButtonGroupM3E(
|
||||
type: ButtonGroupM3EType.standard,
|
||||
shape: ButtonGroupM3EShape.round,
|
||||
size: ButtonGroupM3ESize.md,
|
||||
children: const [
|
||||
IconButtonM3E(icon: Icon(Icons.skip_previous)),
|
||||
IconButtonM3E(icon: Icon(Icons.play_arrow)),
|
||||
IconButtonM3E(icon: Icon(Icons.skip_next)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// Connected group with divider and clipping for outer corners
|
||||
ButtonGroupM3E(
|
||||
type: ButtonGroupM3EType.connected,
|
||||
shape: ButtonGroupM3EShape.round,
|
||||
size: ButtonGroupM3ESize.lg,
|
||||
showDividers: true,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
equalizeWidths: true,
|
||||
semanticLabel: 'Actions',
|
||||
children: [
|
||||
SplitButtonM3E<String>(
|
||||
label: 'Primary',
|
||||
onPressed: () {},
|
||||
items: const [
|
||||
SplitButtonM3EItem<String>(value: 'one', child: 'One'),
|
||||
SplitButtonM3EItem<String>(value: 'two', child: 'Two'),
|
||||
],
|
||||
),
|
||||
SplitButtonM3E<String>(
|
||||
label: 'Tonal',
|
||||
emphasis: SplitButtonM3EEmphasis.tonal,
|
||||
onPressed: () {},
|
||||
items: const [
|
||||
SplitButtonM3EItem<String>(value: 'one', child: 'One'),
|
||||
SplitButtonM3EItem<String>(value: 'two', child: 'Two'),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// Square, compact, vertical wrap
|
||||
ButtonGroupM3E(
|
||||
type: ButtonGroupM3EType.standard,
|
||||
shape: ButtonGroupM3EShape.square,
|
||||
size: ButtonGroupM3ESize.sm,
|
||||
density: ButtonGroupM3EDensity.compact,
|
||||
direction: Axis.vertical,
|
||||
children: const [
|
||||
IconButtonM3E(icon: Icon(Icons.view_agenda_outlined)),
|
||||
IconButtonM3E(icon: Icon(Icons.table_rows_outlined)),
|
||||
IconButtonM3E(icon: Icon(Icons.grid_view_outlined)),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
// Toolbar
|
||||
Text('ToolbarM3E', style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 12),
|
||||
ToolbarM3E(
|
||||
titleText: 'Toolbar Title',
|
||||
subtitleText: 'Subtitle',
|
||||
actions: [
|
||||
ToolbarActionM3E(icon: Icons.search, onPressed: () {}),
|
||||
ToolbarActionM3E(icon: Icons.share, onPressed: () {}),
|
||||
ToolbarActionM3E(
|
||||
icon: Icons.delete,
|
||||
onPressed: () {},
|
||||
isDestructive: true,
|
||||
label: 'Delete'),
|
||||
ToolbarActionM3E(
|
||||
icon: Icons.settings, onPressed: () {}, label: 'Settings'),
|
||||
],
|
||||
maxInlineActions: 2,
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
// FABs
|
||||
Text('FabM3E', style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: const [
|
||||
FabM3E(icon: Icon(Icons.add)),
|
||||
FabM3E(icon: Icon(Icons.edit), kind: FabM3EKind.secondary),
|
||||
FabM3E(icon: Icon(Icons.share), kind: FabM3EKind.tertiary),
|
||||
FabM3E(icon: Icon(Icons.more_horiz), kind: FabM3EKind.surface),
|
||||
FabM3E(icon: Icon(Icons.add), size: FabM3ESize.small),
|
||||
FabM3E(icon: Icon(Icons.add), size: FabM3ESize.large),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: const [
|
||||
ExtendedFabM3E(label: Text('Create'), icon: Icon(Icons.add)),
|
||||
ExtendedFabM3E(
|
||||
label: Text('Edit'),
|
||||
icon: Icon(Icons.edit),
|
||||
kind: FabM3EKind.secondary),
|
||||
ExtendedFabM3E(
|
||||
label: Text('Share'),
|
||||
icon: Icon(Icons.share),
|
||||
kind: FabM3EKind.tertiary),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
SizedBox(
|
||||
height: 180,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
borderRadius: m3e.shapes.round.lg,
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: FabMenuM3E(
|
||||
primaryFab: const FabM3E(icon: Icon(Icons.menu)),
|
||||
items: [
|
||||
FabMenuItem(
|
||||
icon: const Icon(Icons.image),
|
||||
label: const Text('Image'),
|
||||
onPressed: () {}),
|
||||
FabMenuItem(
|
||||
icon: const Icon(Icons.camera_alt),
|
||||
label: const Text('Camera'),
|
||||
onPressed: () {}),
|
||||
FabMenuItem(
|
||||
icon: const Icon(Icons.file_upload),
|
||||
label: const Text('Upload'),
|
||||
onPressed: () {}),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
// Loading indicators
|
||||
Text('LoadingIndicatorM3E',
|
||||
style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 16,
|
||||
runSpacing: 16,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: const [
|
||||
LoadingIndicatorM3E(
|
||||
variant: LoadingIndicatorM3EVariant.defaultStyle),
|
||||
LoadingIndicatorM3E(
|
||||
variant: LoadingIndicatorM3EVariant.contained),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
// Progress indicators
|
||||
Text('ProgressIndicatorM3E',
|
||||
style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 16,
|
||||
runSpacing: 16,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
const CircularProgressM3E(),
|
||||
const CircularProgressM3E(size: ProgressM3ESize.small),
|
||||
CircularProgressM3E(
|
||||
size: ProgressM3ESize.large,
|
||||
value: _progressValue,
|
||||
showCenterLabel: true),
|
||||
const LinearProgressM3E(minWidth: 200),
|
||||
LinearProgressM3E(minWidth: 200, value: _progressValue),
|
||||
const LinearProgressM3E(
|
||||
minWidth: 200,
|
||||
variant: LinearProgressM3EVariant.indeterminate),
|
||||
const LinearProgressM3E(
|
||||
minWidth: 200, variant: LinearProgressM3EVariant.query),
|
||||
const LinearProgressM3E(
|
||||
minWidth: 200,
|
||||
variant: LinearProgressM3EVariant.buffer,
|
||||
bufferValue: 0.8,
|
||||
value: 0.4),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
// Sliders
|
||||
Text('SliderM3E', style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 12),
|
||||
SliderM3E(
|
||||
value: _sliderValue,
|
||||
onChanged: (v) => setState(() => _sliderValue = v),
|
||||
min: 0,
|
||||
max: 100,
|
||||
label: '${_sliderValue.toStringAsFixed(0)}',
|
||||
startIcon: const Icon(Icons.volume_mute),
|
||||
endIcon: const Icon(Icons.volume_up),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
RangeSliderM3E(
|
||||
values: _rangeValues,
|
||||
onChanged: (v) => setState(() => _rangeValues = v),
|
||||
min: 0,
|
||||
max: 100,
|
||||
labels: RangeLabels(
|
||||
_rangeValues.start.toStringAsFixed(0),
|
||||
_rangeValues.end.toStringAsFixed(0),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
// Navigation Bar
|
||||
Text('NavigationBarM3E',
|
||||
style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 12),
|
||||
NavigationBarM3E(
|
||||
selectedIndex: _navBarIndex,
|
||||
onDestinationSelected: (i) => setState(() => _navBarIndex = i),
|
||||
indicatorStyle: NavBarM3EIndicatorStyle.pill,
|
||||
destinations: const [
|
||||
NavigationDestinationM3E(
|
||||
icon: Icon(Icons.home_outlined),
|
||||
selectedIcon: Icon(Icons.home),
|
||||
label: 'Home'),
|
||||
NavigationDestinationM3E(
|
||||
icon: Icon(Icons.search_outlined),
|
||||
selectedIcon: Icon(Icons.search),
|
||||
label: 'Search',
|
||||
badgeDot: true),
|
||||
NavigationDestinationM3E(
|
||||
icon: Icon(Icons.favorite_outline),
|
||||
selectedIcon: Icon(Icons.favorite),
|
||||
label: 'Favorites',
|
||||
badgeCount: 3),
|
||||
NavigationDestinationM3E(
|
||||
icon: Icon(Icons.person_outline),
|
||||
selectedIcon: Icon(Icons.person),
|
||||
label: 'Profile'),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
// Navigation Rail
|
||||
Text('NavigationRailM3E',
|
||||
style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
borderRadius: m3e.shapes.round.lg,
|
||||
),
|
||||
height: 180,
|
||||
child: Row(
|
||||
children: [
|
||||
NavigationRailM3E(
|
||||
selectedIndex: _railIndex,
|
||||
onDestinationSelected: (i) => setState(() => _railIndex = i),
|
||||
indicatorStyle: RailIndicatorStyle.pill,
|
||||
destinations: const [
|
||||
RailDestinationM3E(
|
||||
icon: Icon(Icons.dashboard_outlined),
|
||||
selectedIcon: Icon(Icons.dashboard),
|
||||
label: 'Dashboard'),
|
||||
RailDestinationM3E(
|
||||
icon: Icon(Icons.analytics_outlined),
|
||||
selectedIcon: Icon(Icons.analytics),
|
||||
label: 'Reports'),
|
||||
RailDestinationM3E(
|
||||
icon: Icon(Icons.settings_outlined),
|
||||
selectedIcon: Icon(Icons.settings),
|
||||
label: 'Settings'),
|
||||
],
|
||||
),
|
||||
const VerticalDivider(width: 1),
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Text('Selected: $_railIndex'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
// Sliver App Bar demo route
|
||||
Text('App bars', style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
children: [
|
||||
const ButtonM3E(labelText: 'Open SliverAppBarM3E Demo'),
|
||||
// Use GestureDetector to navigate when tapping the button label area
|
||||
]
|
||||
.map((w) => GestureDetector(
|
||||
onTap: () => Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => const _SliverAppBarDemoPage()),
|
||||
),
|
||||
child: w,
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
|
||||
const SizedBox(height: 48),
|
||||
// Theming surface example remains
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: m3e.colors.surfaceStrong,
|
||||
borderRadius: m3e.shapes.square.lg,
|
||||
),
|
||||
child: Text('Surface strong example',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium
|
||||
?.copyWith(color: cs.onSurface)),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: const SectionedGallery(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -558,3 +86,38 @@ class _SliverAppBarDemoPage extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SectionedGallery extends StatelessWidget {
|
||||
const SectionedGallery({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
const IconButtonSection(),
|
||||
const SplitButtonSection(),
|
||||
const ButtonSection(),
|
||||
const ToolbarSection(),
|
||||
const FabSection(),
|
||||
const LoadingIndicatorSection(),
|
||||
const ProgressSection(),
|
||||
const SliderSection(),
|
||||
const NavigationSection(),
|
||||
SectionCard(
|
||||
title: 'App bars',
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: ButtonM3E(
|
||||
label: Text('Open SliverAppBarM3E Demo'),
|
||||
onPressed: () => Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => const _SliverAppBarDemoPage()),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
50
apps/gallery/lib/sections/button_section.dart
Normal file
50
apps/gallery/lib/sections/button_section.dart
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3e_collection/m3e_collection.dart';
|
||||
|
||||
import 'section_card.dart';
|
||||
|
||||
class ButtonSection extends StatelessWidget {
|
||||
const ButtonSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SectionCard(
|
||||
title: 'ButtonM3E',
|
||||
subtitle:
|
||||
'Generated from enums: variant × size; grouped by shape family.',
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
for (final shape in ButtonM3EShape.values) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text('${shape.name} shape',
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
for (final size in ButtonM3ESize.values) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8, bottom: 4),
|
||||
child: Text('size: ${size.name}',
|
||||
style: Theme.of(context).textTheme.labelLarge),
|
||||
),
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
for (final variant in ButtonM3EStyle.values)
|
||||
ButtonM3E(
|
||||
label: Text(variant.name),
|
||||
style: variant,
|
||||
size: size,
|
||||
shape: shape,
|
||||
onPressed: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
60
apps/gallery/lib/sections/fab_section.dart
Normal file
60
apps/gallery/lib/sections/fab_section.dart
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3e_collection/m3e_collection.dart';
|
||||
|
||||
import 'section_card.dart';
|
||||
|
||||
class FabSection extends StatelessWidget {
|
||||
const FabSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
void onPressed() {
|
||||
// Placeholder function onPressed
|
||||
}
|
||||
|
||||
return SectionCard(
|
||||
title: 'FabM3E',
|
||||
subtitle:
|
||||
'Generated from enums: kind × size. Includes Extended FAB examples.',
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
for (final kind in FabM3EKind.values)
|
||||
for (final size in FabM3ESize.values)
|
||||
FabM3E(
|
||||
icon: const Icon(Icons.add),
|
||||
kind: kind,
|
||||
size: size,
|
||||
onPressed: onPressed),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
ExtendedFabM3E(
|
||||
label: Text('Create'),
|
||||
icon: Icon(Icons.add),
|
||||
onPressed: onPressed),
|
||||
ExtendedFabM3E(
|
||||
label: Text('Edit'),
|
||||
icon: Icon(Icons.edit),
|
||||
kind: FabM3EKind.secondary,
|
||||
onPressed: onPressed),
|
||||
ExtendedFabM3E(
|
||||
label: Text('Share'),
|
||||
icon: Icon(Icons.share),
|
||||
kind: FabM3EKind.tertiary,
|
||||
onPressed: onPressed),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
40
apps/gallery/lib/sections/icon_button_section.dart
Normal file
40
apps/gallery/lib/sections/icon_button_section.dart
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3e_collection/m3e_collection.dart';
|
||||
|
||||
import 'section_card.dart';
|
||||
|
||||
class IconButtonSection extends StatelessWidget {
|
||||
const IconButtonSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SectionCard(
|
||||
title: 'IconButtonM3E',
|
||||
subtitle: 'Generated from enums: variant × size (round shape, default width).',
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
for (final variant in IconButtonM3EVariant.values) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text(variant.name, style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
for (final size in IconButtonM3ESize.values)
|
||||
IconButtonM3E(
|
||||
icon: const Icon(Icons.favorite),
|
||||
variant: variant,
|
||||
size: size,
|
||||
onPressed: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
25
apps/gallery/lib/sections/loading_indicator_section.dart
Normal file
25
apps/gallery/lib/sections/loading_indicator_section.dart
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3e_collection/m3e_collection.dart';
|
||||
|
||||
import 'section_card.dart';
|
||||
|
||||
class LoadingIndicatorSection extends StatelessWidget {
|
||||
const LoadingIndicatorSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SectionCard(
|
||||
title: 'LoadingIndicatorM3E',
|
||||
subtitle: 'Generated from enums: variant values.',
|
||||
child: Wrap(
|
||||
spacing: 16,
|
||||
runSpacing: 16,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
for (final v in LoadingIndicatorM3EVariant.values)
|
||||
LoadingIndicatorM3E(variant: v),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
95
apps/gallery/lib/sections/navigation_section.dart
Normal file
95
apps/gallery/lib/sections/navigation_section.dart
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3e_collection/m3e_collection.dart';
|
||||
|
||||
import 'section_card.dart';
|
||||
|
||||
class NavigationSection extends StatefulWidget {
|
||||
const NavigationSection({super.key});
|
||||
|
||||
@override
|
||||
State<NavigationSection> createState() => _NavigationSectionState();
|
||||
}
|
||||
|
||||
class _NavigationSectionState extends State<NavigationSection> {
|
||||
int _barIndex = 0;
|
||||
int _railIndex = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final m3e = theme.extension<M3ETheme>() ?? M3ETheme.defaults(theme.colorScheme);
|
||||
|
||||
return SectionCard(
|
||||
title: 'Navigation',
|
||||
subtitle: 'Generated from enums: NavBar indicator styles and Rail indicator styles.',
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text('Navigation Bar', style: theme.textTheme.titleMedium),
|
||||
),
|
||||
Wrap(
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
for (final style in NavBarM3EIndicatorStyle.values)
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 4),
|
||||
child: Text('indicator: ${style.name}', style: theme.textTheme.labelLarge),
|
||||
),
|
||||
NavigationBarM3E(
|
||||
selectedIndex: _barIndex,
|
||||
onDestinationSelected: (i) => setState(() => _barIndex = i),
|
||||
indicatorStyle: style,
|
||||
destinations: const [
|
||||
NavigationDestinationM3E(icon: Icon(Icons.home_outlined), selectedIcon: Icon(Icons.home), label: 'Home'),
|
||||
NavigationDestinationM3E(icon: Icon(Icons.search_outlined), selectedIcon: Icon(Icons.search), label: 'Search', badgeDot: true),
|
||||
NavigationDestinationM3E(icon: Icon(Icons.favorite_outline), selectedIcon: Icon(Icons.favorite), label: 'Favorites', badgeCount: 2),
|
||||
NavigationDestinationM3E(icon: Icon(Icons.person_outline), selectedIcon: Icon(Icons.person), label: 'Profile'),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text('Navigation Rail', style: theme.textTheme.titleMedium),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainerHighest,
|
||||
borderRadius: m3e.shapes.round.lg,
|
||||
),
|
||||
height: 220,
|
||||
child: Row(
|
||||
children: [
|
||||
for (final style in RailIndicatorStyle.values) ...[
|
||||
NavigationRailM3E(
|
||||
selectedIndex: _railIndex,
|
||||
onDestinationSelected: (i) => setState(() => _railIndex = i),
|
||||
indicatorStyle: style,
|
||||
destinations: const [
|
||||
RailDestinationM3E(icon: Icon(Icons.dashboard_outlined), selectedIcon: Icon(Icons.dashboard), label: 'Dash'),
|
||||
RailDestinationM3E(icon: Icon(Icons.analytics_outlined), selectedIcon: Icon(Icons.analytics), label: 'Reports'),
|
||||
RailDestinationM3E(icon: Icon(Icons.settings_outlined), selectedIcon: Icon(Icons.settings), label: 'Settings'),
|
||||
],
|
||||
),
|
||||
const VerticalDivider(width: 1),
|
||||
],
|
||||
Expanded(
|
||||
child: Center(child: Text('Selected: $_railIndex')),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
107
apps/gallery/lib/sections/progress_section.dart
Normal file
107
apps/gallery/lib/sections/progress_section.dart
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3e_collection/m3e_collection.dart';
|
||||
|
||||
import 'section_card.dart';
|
||||
|
||||
class ProgressSection extends StatelessWidget {
|
||||
const ProgressSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SectionCard(
|
||||
title: 'ProgressIndicatorM3E',
|
||||
subtitle: 'Generated from enums: circular sizes and linear variants.',
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text('Circular - Wavy',
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
Wrap(
|
||||
spacing: 16,
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
for (final s in ProgressM3ESize.values) ...[
|
||||
CircularProgressM3E(
|
||||
size: s,
|
||||
value: 0.4,
|
||||
),
|
||||
CircularProgressM3E(
|
||||
size: s,
|
||||
value: 0.6,
|
||||
showCenterLabel: s != ProgressM3ESize.small),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text('Circular - Flat',
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
Wrap(
|
||||
spacing: 16,
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
for (final s in ProgressM3ESize.values) ...[
|
||||
CircularProgressM3E(
|
||||
size: s,
|
||||
value: 0.4,
|
||||
shape: CircularBarShapeM3E.flat,
|
||||
),
|
||||
CircularProgressM3E(
|
||||
size: s,
|
||||
shape: CircularBarShapeM3E.flat,
|
||||
value: 0.6,
|
||||
showCenterLabel: s != ProgressM3ESize.small),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text('Linear - Wavy',
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
Wrap(
|
||||
spacing: 16,
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
for (final v in LinearProgressM3EVariant.values)
|
||||
LinearProgressM3E(
|
||||
minWidth: 220,
|
||||
variant: v,
|
||||
value: v == LinearProgressM3EVariant.determinate ? 0.6 : null,
|
||||
bufferValue:
|
||||
v == LinearProgressM3EVariant.buffer ? 0.8 : null,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text('Linear - Flat',
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
Wrap(
|
||||
spacing: 16,
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
for (final v in LinearProgressM3EVariant.values)
|
||||
LinearProgressM3E(
|
||||
minWidth: 220,
|
||||
variant: v,
|
||||
shape: LinearBarShapeM3E.flat,
|
||||
value: v == LinearProgressM3EVariant.determinate ? 0.6 : null,
|
||||
bufferValue:
|
||||
v == LinearProgressM3EVariant.buffer ? 0.8 : null,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
40
apps/gallery/lib/sections/section_card.dart
Normal file
40
apps/gallery/lib/sections/section_card.dart
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3e_collection/m3e_collection.dart';
|
||||
|
||||
class SectionCard extends StatelessWidget {
|
||||
const SectionCard(
|
||||
{super.key, required this.title, this.subtitle, required this.child});
|
||||
|
||||
final String title;
|
||||
final String? subtitle;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final m3e =
|
||||
theme.extension<M3ETheme>() ?? M3ETheme.defaults(theme.colorScheme);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(title, style: theme.textTheme.titleLarge),
|
||||
subtitle: subtitle == null ? null : Text(subtitle!),
|
||||
),
|
||||
Material(
|
||||
color: theme.colorScheme.surfaceContainerLow,
|
||||
borderRadius: m3e.shapes.round.lg,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
73
apps/gallery/lib/sections/slider_section.dart
Normal file
73
apps/gallery/lib/sections/slider_section.dart
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3e_collection/m3e_collection.dart';
|
||||
|
||||
import 'section_card.dart';
|
||||
|
||||
class SliderSection extends StatefulWidget {
|
||||
const SliderSection({super.key});
|
||||
|
||||
@override
|
||||
State<SliderSection> createState() => _SliderSectionState();
|
||||
}
|
||||
|
||||
class _SliderSectionState extends State<SliderSection> {
|
||||
double _value = 40;
|
||||
RangeValues _range = const RangeValues(25, 75);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SectionCard(
|
||||
title: 'SliderM3E',
|
||||
subtitle: 'Generated from enums: size × emphasis (round shape).',
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
for (final size in SliderM3ESize.values) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text('size: ${size.name}', style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
Wrap(
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
for (final emp in SliderM3EEmphasis.values)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('emphasis: ${emp.name}', style: Theme.of(context).textTheme.labelLarge),
|
||||
SliderM3E(
|
||||
value: _value,
|
||||
onChanged: (v) => setState(() => _value = v),
|
||||
min: 0,
|
||||
max: 100,
|
||||
label: _value.toStringAsFixed(0),
|
||||
size: size,
|
||||
emphasis: emp,
|
||||
startIcon: const Icon(Icons.volume_mute),
|
||||
endIcon: const Icon(Icons.volume_up),
|
||||
),
|
||||
RangeSliderM3E(
|
||||
values: _range,
|
||||
onChanged: (v) => setState(() => _range = v),
|
||||
min: 0,
|
||||
max: 100,
|
||||
size: size,
|
||||
emphasis: emp,
|
||||
labels: RangeLabels(
|
||||
_range.start.toStringAsFixed(0),
|
||||
_range.end.toStringAsFixed(0),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
47
apps/gallery/lib/sections/split_button_section.dart
Normal file
47
apps/gallery/lib/sections/split_button_section.dart
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3e_collection/m3e_collection.dart';
|
||||
|
||||
import 'section_card.dart';
|
||||
|
||||
class SplitButtonSection extends StatelessWidget {
|
||||
const SplitButtonSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final items = const [
|
||||
SplitButtonM3EItem<String>(value: 'one', child: 'One'),
|
||||
SplitButtonM3EItem<String>(value: 'two', child: 'Two'),
|
||||
SplitButtonM3EItem<String>(value: 'three', child: 'Three'),
|
||||
];
|
||||
|
||||
return SectionCard(
|
||||
title: 'SplitButtonM3E',
|
||||
subtitle: 'Generated from enums: emphasis × size (round shape).',
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
for (final emphasis in SplitButtonM3EEmphasis.values) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text(emphasis.name, style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
for (final size in SplitButtonM3ESize.values)
|
||||
SplitButtonM3E<String>(
|
||||
label: emphasis.name,
|
||||
size: size,
|
||||
emphasis: emphasis,
|
||||
onPressed: () {},
|
||||
items: items,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
48
apps/gallery/lib/sections/toolbar_section.dart
Normal file
48
apps/gallery/lib/sections/toolbar_section.dart
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3e_collection/m3e_collection.dart';
|
||||
|
||||
import 'section_card.dart';
|
||||
|
||||
class ToolbarSection extends StatelessWidget {
|
||||
const ToolbarSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final actions = [
|
||||
ToolbarActionM3E(icon: Icons.search, onPressed: () {}),
|
||||
ToolbarActionM3E(icon: Icons.share, onPressed: () {}),
|
||||
ToolbarActionM3E(icon: Icons.delete, onPressed: () {}, isDestructive: true, label: 'Delete'),
|
||||
ToolbarActionM3E(icon: Icons.settings, onPressed: () {}, label: 'Settings'),
|
||||
];
|
||||
|
||||
return SectionCard(
|
||||
title: 'ToolbarM3E',
|
||||
subtitle: 'Generated from enums: variant × size (round shape).',
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
for (final variant in ToolbarM3EVariant.values) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text(variant.name, style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
Wrap(
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
for (final size in ToolbarM3ESize.values)
|
||||
ToolbarM3E(
|
||||
titleText: 'Toolbar',
|
||||
subtitleText: 'size: ${size.name}',
|
||||
actions: actions,
|
||||
maxInlineActions: 2,
|
||||
variant: variant,
|
||||
size: size,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue