diff --git a/melos.yaml b/melos.yaml
index 2d194fd..d7ac43f 100644
--- a/melos.yaml
+++ b/melos.yaml
@@ -11,6 +11,8 @@ scripts:
bootstrap: melos bootstrap
clean: melos exec -- flutter clean
get: melos exec -- flutter pub get
+ upgrade: melos exec -- flutter pub upgrade
+ upgrade-major: melos exec -- flutter pub upgrade --major-versions
format: melos exec -- dart format --set-exit-if-changed .
analyze: melos exec -- dart analyze --fatal-infos --fatal-warnings
test: melos exec -- flutter test --coverage
@@ -31,6 +33,3 @@ scripts:
noPrivate: true
dirExists:
- lib
- set-version:
- run: dart run tool/update_versions.dart
- description: "Set version for all packages (usage: melos run set-version -- version=1.2.3)"
diff --git a/packages/app_bar_m3e/pubspec.yaml b/packages/app_bar_m3e/pubspec.yaml
index d70a699..a32b786 100644
--- a/packages/app_bar_m3e/pubspec.yaml
+++ b/packages/app_bar_m3e/pubspec.yaml
@@ -1,6 +1,6 @@
name: app_bar_m3e
description: Expressive App Bar (Material 3 Expressive) with small/medium/large variants and Sliver integration.
-version: 0.1.0
+version: 0.3.0
repository: https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/app_bar_m3e
issue_tracker: https://github.com/EmilyMonestone/material_3_expressive/issues
diff --git a/packages/button_group_m3e/pubspec.yaml b/packages/button_group_m3e/pubspec.yaml
index 3a11425..863021b 100644
--- a/packages/button_group_m3e/pubspec.yaml
+++ b/packages/button_group_m3e/pubspec.yaml
@@ -1,6 +1,6 @@
name: button_group_m3e
description: Wrapper-only Button Group for Material 3 Expressive (layout, shape, size propagation).
-version: 0.1.0
+version: 0.3.0
repository: https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/button_group_m3e
issue_tracker: https://github.com/EmilyMonestone/material_3_expressive/issues
diff --git a/packages/button_m3e/pubspec.yaml b/packages/button_m3e/pubspec.yaml
index 6ff7681..f4faa16 100644
--- a/packages/button_m3e/pubspec.yaml
+++ b/packages/button_m3e/pubspec.yaml
@@ -1,7 +1,7 @@
name: button_m3e
description: Material 3 Expressive Buttons for Flutter with 5 styles, 5 sizes, round/square shapes, and toggle selection.
-version: 0.1.0
+version: 0.3.0
repository: https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/button_m3e
issue_tracker: https://github.com/EmilyMonestone/material_3_expressive/issues
diff --git a/packages/fab_m3e/pubspec.yaml b/packages/fab_m3e/pubspec.yaml
index ad2e369..9d7dcd9 100644
--- a/packages/fab_m3e/pubspec.yaml
+++ b/packages/fab_m3e/pubspec.yaml
@@ -1,6 +1,6 @@
name: fab_m3e
description: Material 3 Expressive Floating Action Button (FAB), Extended FAB, and FAB Menu for Flutter using M3E tokens.
-version: 0.1.0
+version: 0.3.0
repository: https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/fab_m3e
issue_tracker: https://github.com/EmilyMonestone/material_3_expressive/issues
diff --git a/packages/icon_button_m3e/melos_icon_button_m3e.iml b/packages/icon_button_m3e/melos_icon_button_m3e.iml
index 9fc8ce7..44e881d 100644
--- a/packages/icon_button_m3e/melos_icon_button_m3e.iml
+++ b/packages/icon_button_m3e/melos_icon_button_m3e.iml
@@ -20,10 +20,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/packages/icon_button_m3e/pubspec.yaml b/packages/icon_button_m3e/pubspec.yaml
index 406dfd8..686c644 100644
--- a/packages/icon_button_m3e/pubspec.yaml
+++ b/packages/icon_button_m3e/pubspec.yaml
@@ -1,6 +1,6 @@
name: icon_button_m3e
description: "Material 3 Expressive IconButton with sizes, variants, shapes, toggle, and accessible hit targets."
-version: 0.2.0
+version: 0.3.0
repository: https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/icon_button_m3e
issue_tracker: https://github.com/EmilyMonestone/material_3_expressive/issues
@@ -14,7 +14,7 @@ dependencies:
m3e_design: ^0.1.0
dev_dependencies:
- flutter_lints: ^4.0.0
+ flutter_lints: ^6.0.0
flutter_test:
sdk: flutter
diff --git a/packages/loading_indicator_m3e/pubspec.yaml b/packages/loading_indicator_m3e/pubspec.yaml
index 9fb4cae..47d2889 100644
--- a/packages/loading_indicator_m3e/pubspec.yaml
+++ b/packages/loading_indicator_m3e/pubspec.yaml
@@ -1,6 +1,6 @@
name: loading_indicator_m3e
description: Material 3 Expressive Loading Indicator (morphing polygons) for Flutter, with Default and Contained variants.
-version: 0.1.0
+version: 0.3.0
repository: https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/loading_indicator_m3e
issue_tracker: https://github.com/EmilyMonestone/material_3_expressive/issues
diff --git a/packages/m3e_collection/pubspec.yaml b/packages/m3e_collection/pubspec.yaml
index 86fdefa..43c6165 100644
--- a/packages/m3e_collection/pubspec.yaml
+++ b/packages/m3e_collection/pubspec.yaml
@@ -1,6 +1,6 @@
name: m3e_collection
description: Aggregated exports of all Material 3 Expressive components for Flutter.
-version: 0.1.0
+version: 0.3.2
repository: https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/m3e_collection
issue_tracker: https://github.com/EmilyMonestone/material_3_expressive/issues
@@ -20,7 +20,7 @@ dependencies:
m3e_design: ^0.1.0
material_new_shapes: ^1.0.0
navigation_bar_m3e: ^0.1.0
- navigation_rail_m3e: ^0.1.1
+ navigation_rail_m3e: ^0.3.2
progress_indicator_m3e: ^0.1.0
slider_m3e: ^0.1.0
split_button_m3e: ^0.2.0
diff --git a/packages/m3e_design/pubspec.yaml b/packages/m3e_design/pubspec.yaml
index 4ff7003..1c5f7e8 100644
--- a/packages/m3e_design/pubspec.yaml
+++ b/packages/m3e_design/pubspec.yaml
@@ -1,6 +1,6 @@
name: m3e_design
description: Material 3 Expressive design language for Flutter (tokens, ThemeExtension, motion).
-version: 0.1.0
+version: 0.3.0
repository: https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/m3e_design
issue_tracker: https://github.com/EmilyMonestone/material_3_expressive/issues
diff --git a/packages/navigation_bar_m3e/pubspec.yaml b/packages/navigation_bar_m3e/pubspec.yaml
index 79f822f..ae6daa2 100644
--- a/packages/navigation_bar_m3e/pubspec.yaml
+++ b/packages/navigation_bar_m3e/pubspec.yaml
@@ -1,6 +1,6 @@
name: navigation_bar_m3e
description: Material 3 Expressive Navigation Bar for Flutter with token-driven colors, shapes, and badges.
-version: 0.1.0
+version: 0.3.0
repository: https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/navigation_bar_m3e
issue_tracker: https://github.com/EmilyMonestone/material_3_expressive/issues
diff --git a/packages/navigation_rail_m3e/example/lib/main.dart b/packages/navigation_rail_m3e/example/lib/main.dart
index 9477cdf..0efb7c6 100644
--- a/packages/navigation_rail_m3e/example/lib/main.dart
+++ b/packages/navigation_rail_m3e/example/lib/main.dart
@@ -5,7 +5,9 @@ void main() {
runApp(const DemoApp());
}
+/// Simple demo application for NavigationRailM3E.
class DemoApp extends StatefulWidget {
+ /// Creates the demo app widget.
const DemoApp({super.key});
@override
State createState() => _DemoAppState();
diff --git a/packages/navigation_rail_m3e/lib/navigation_rail_m3e.dart b/packages/navigation_rail_m3e/lib/navigation_rail_m3e.dart
index 250492a..0792100 100644
--- a/packages/navigation_rail_m3e/lib/navigation_rail_m3e.dart
+++ b/packages/navigation_rail_m3e/lib/navigation_rail_m3e.dart
@@ -1,6 +1,6 @@
// ignore_for_file: public_member_api_docs
-library navigation_rail_m3e;
+library;
export 'src/modality.dart';
export 'src/navigation_rail_m3e_widget.dart';
diff --git a/packages/navigation_rail_m3e/lib/src/navigation_rail_m3e_widget.dart b/packages/navigation_rail_m3e/lib/src/navigation_rail_m3e_widget.dart
index 8faee19..90694d2 100644
--- a/packages/navigation_rail_m3e/lib/src/navigation_rail_m3e_widget.dart
+++ b/packages/navigation_rail_m3e/lib/src/navigation_rail_m3e_widget.dart
@@ -16,7 +16,7 @@ class NavigationRailM3E extends StatefulWidget {
/// Creates a Material 3 Expressive navigation rail.
const NavigationRailM3E({
super.key,
- required this.type,
+ this.type = NavigationRailM3EType.expanded,
this.modality = NavigationRailM3EModality.standard,
required this.sections,
required this.selectedIndex,
@@ -27,9 +27,12 @@ class NavigationRailM3E extends StatefulWidget {
this.onDismissModal,
this.onTypeChanged,
this.labelBehavior = NavigationRailM3ELabelBehavior.alwaysShow,
+ this.scrollable = true,
+ this.trailing,
+ this.trailingAtBottom = true,
});
- /// Presentation type for the rail (collapsed or expanded).
+ /// Presentation type for the rail (collapsed or expanded or alwaysCollapsed or alwaysExpanded).
final NavigationRailM3EType type;
/// How the rail is shown (standard or modal overlay).
@@ -62,6 +65,21 @@ class NavigationRailM3E extends StatefulWidget {
/// Controls how labels are shown when the rail is expanded.
final NavigationRailM3ELabelBehavior labelBehavior;
+ /// Whether the rail's main content area should be scrollable.
+ /// Defaults to true to match the current behavior.
+ final bool scrollable;
+
+ /// Optional trailing widget, always conceptually placed after the sections.
+ /// When [trailingAtBottom] is true, it's pinned to the bottom of the rail,
+ /// leaving flexible space between the sections and the trailing.
+ /// When false, it's inserted immediately after the sections within the content.
+ final Widget? trailing;
+
+ /// Controls where the trailing is placed relative to the rail's bottom.
+ /// If true, [trailing] is pinned to the bottom with space to the sections.
+ /// If false, [trailing] appears directly after the sections.
+ final bool trailingAtBottom;
+
@override
State createState() => _NavigationRailM3EState();
}
@@ -73,15 +91,37 @@ class _NavigationRailM3EState extends State
final LayerLink _anchor = LayerLink();
bool _suppressInk = false;
- bool get _isExpanded => widget.type == NavigationRailM3EType.expanded;
+ // Internal expanded/collapsed state. Initialized from widget.type and then
+ // controlled internally by the rail's menu button.
+ bool _expanded = false;
+
+ bool get _isExpanded => _expanded;
bool get _isModal => widget.modality == NavigationRailM3EModality.modal;
bool get _needsOverlay => _isModal && _isExpanded;
bool get _needsCollapsedPeek =>
- !_isExpanded && !_isModal && widget.hideWhenCollapsed;
+ !_isExpanded && !_isModal && widget.hideWhenCollapsed && _canToggle;
+
+ bool get _canToggle =>
+ widget.type == NavigationRailM3EType.collapsed ||
+ widget.type == NavigationRailM3EType.expanded;
+
+ NavigationRailM3EType get _notifiedType => _expanded
+ ? NavigationRailM3EType.expanded
+ : NavigationRailM3EType.collapsed;
@override
void initState() {
super.initState();
+ // Initialize internal expanded state from the provided type.
+ // When toggleable (collapsed/expanded), mirror the provided type.
+ // When locked (alwaysCollapse/alwaysExpand), infer via enum name.
+ if (_canToggle) {
+ _expanded = widget.type == NavigationRailM3EType.expanded;
+ } else {
+ final name = widget.type.toString();
+ final isAlwaysCollapse = name.contains('alwaysCollapse');
+ _expanded = !isAlwaysCollapse; // alwaysExpand => true
+ }
WidgetsBinding.instance.addPostFrameCallback((_) => _syncOverlay());
}
@@ -97,6 +137,27 @@ class _NavigationRailM3EState extends State
});
}
+ // Keep internal state in sync with locking semantics of `always*` types.
+ final bool oldCanToggle =
+ oldWidget.type == NavigationRailM3EType.collapsed ||
+ oldWidget.type == NavigationRailM3EType.expanded;
+ final bool newCanToggle = _canToggle;
+
+ if (!newCanToggle) {
+ // Force the locked state.
+ final name = widget.type.toString();
+ final bool lockExpanded = !name.contains('alwaysCollapse');
+ if (_expanded != lockExpanded) {
+ setState(() => _expanded = lockExpanded);
+ }
+ } else if (!oldCanToggle && newCanToggle) {
+ // Transition from locked to toggleable: seed from the new default.
+ final bool startExpanded = widget.type == NavigationRailM3EType.expanded;
+ if (_expanded != startExpanded) {
+ setState(() => _expanded = startExpanded);
+ }
+ }
+
WidgetsBinding.instance.addPostFrameCallback((_) => _syncOverlay());
}
@@ -156,6 +217,19 @@ class _NavigationRailM3EState extends State
_collapsedPeekEntry = null;
}
+ void _setExpanded(bool value) {
+ if (_expanded == value) return;
+ setState(() {
+ _expanded = value;
+ _suppressInk = true;
+ });
+ Future.delayed(const Duration(milliseconds: 320), () {
+ if (mounted) setState(() => _suppressInk = false);
+ });
+ // Notify listeners (if any) for backward compatibility.
+ widget.onTypeChanged?.call(_notifiedType);
+ }
+
Widget _buildModalOverlay(BuildContext context) {
return Stack(
children: [
@@ -192,9 +266,7 @@ class _NavigationRailM3EState extends State
Widget btn = IconButtonM3E(
icon: const Icon(Icons.menu),
tooltip: 'Expand',
- onPressed: widget.onTypeChanged == null
- ? null
- : () => widget.onTypeChanged!(NavigationRailM3EType.expanded),
+ onPressed: _canToggle ? () => _setExpanded(true) : null,
);
if (_suppressInk) {
final t = Theme.of(context);
@@ -232,17 +304,13 @@ class _NavigationRailM3EState extends State
Widget _buildMenuButton(BuildContext context,
{required Alignment alignment}) {
+ if (!_canToggle) return const SizedBox.shrink();
+
final isExpanded = _isExpanded;
Widget button = IconButtonM3E(
icon: Icon(isExpanded ? Icons.menu_open : Icons.menu),
tooltip: isExpanded ? 'Collapse' : 'Expand',
- onPressed: widget.onTypeChanged == null
- ? null
- : () => widget.onTypeChanged!(
- isExpanded
- ? NavigationRailM3EType.collapsed
- : NavigationRailM3EType.expanded,
- ),
+ onPressed: () => _setExpanded(!isExpanded),
);
if (_suppressInk) {
@@ -301,6 +369,19 @@ class _NavigationRailM3EState extends State
);
}
+ Widget? _buildTrailing(BuildContext context) {
+ final tr = widget.trailing;
+ if (tr == null) return null;
+ final isExpanded = _isExpanded;
+ return Padding(
+ padding: const EdgeInsetsDirectional.only(start: 16, end: 16, bottom: 12),
+ child: Align(
+ alignment: isExpanded ? Alignment.centerLeft : Alignment.center,
+ child: tr,
+ ),
+ );
+ }
+
List _buildChildren(BuildContext context,
{required bool showLabels}) {
final theme = Theme.of(context).extension() ??
@@ -364,6 +445,11 @@ class _NavigationRailM3EState extends State
));
}
}
+ // Place trailing after sections when not bottom-pinned.
+ if (widget.trailing != null && !widget.trailingAtBottom) {
+ final trailingWidget = _buildTrailing(context);
+ if (trailingWidget != null) children.add(trailingWidget);
+ }
return children;
}
@@ -378,10 +464,49 @@ class _NavigationRailM3EState extends State
child: LayoutBuilder(
builder: (ctx, constraints) {
final showLabels = _isExpanded && constraints.maxWidth >= 180;
- return ListView(
- padding: EdgeInsets.zero,
- children: _buildChildren(ctx, showLabels: showLabels),
- );
+ final children = _buildChildren(ctx, showLabels: showLabels);
+ final bottomTrailing =
+ (widget.trailing != null && widget.trailingAtBottom)
+ ? _buildTrailing(ctx)
+ : null;
+
+ if (widget.scrollable) {
+ if (bottomTrailing != null) {
+ return Column(
+ children: [
+ Expanded(
+ child: ListView(
+ padding: EdgeInsets.zero,
+ children: children,
+ ),
+ ),
+ bottomTrailing,
+ ],
+ );
+ }
+ return ListView(
+ padding: EdgeInsets.zero,
+ children: children,
+ );
+ } else {
+ if (bottomTrailing != null) {
+ return Column(
+ children: [
+ Expanded(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: children,
+ ),
+ ),
+ bottomTrailing,
+ ],
+ );
+ }
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: children,
+ );
+ }
},
),
);
diff --git a/packages/navigation_rail_m3e/lib/src/rail_destination_m3e.dart b/packages/navigation_rail_m3e/lib/src/rail_destination_m3e.dart
index 430c540..37f3868 100644
--- a/packages/navigation_rail_m3e/lib/src/rail_destination_m3e.dart
+++ b/packages/navigation_rail_m3e/lib/src/rail_destination_m3e.dart
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
/// Model for a navigation destination. One class per file.
class NavigationRailM3EDestination {
+ /// Creates a [NavigationRailM3EDestination].
const NavigationRailM3EDestination({
required this.icon,
this.selectedIcon,
@@ -11,10 +12,19 @@ class NavigationRailM3EDestination {
this.short = false,
});
+ /// Icon shown for the destination.
final Widget icon;
+
+ /// Optional icon when selected; falls back to [icon].
final Widget? selectedIcon;
+
+ /// Text label for the destination.
final String label;
+
+ /// Optional badge count to show.
final int? badgeCount;
+
+ /// Optional semantic label for accessibility.
final String? semanticLabel;
/// If true, uses short item height (56dp) instead of 64dp.
diff --git a/packages/navigation_rail_m3e/lib/src/rail_fab_slot.dart b/packages/navigation_rail_m3e/lib/src/rail_fab_slot.dart
index 7b28b69..dfe9fc2 100644
--- a/packages/navigation_rail_m3e/lib/src/rail_fab_slot.dart
+++ b/packages/navigation_rail_m3e/lib/src/rail_fab_slot.dart
@@ -11,6 +11,7 @@ import 'package:flutter/material.dart';
/// Consumers provide values (icon, label, onPressed, etc.) instead of a widget.
@immutable
class NavigationRailM3EFabSlot {
+ /// Creates a [NavigationRailM3EFabSlot].
const NavigationRailM3EFabSlot({
required this.icon,
required this.label,
diff --git a/packages/navigation_rail_m3e/lib/src/rail_item_button_m3e.dart b/packages/navigation_rail_m3e/lib/src/rail_item_button_m3e.dart
index b421285..e91142d 100644
--- a/packages/navigation_rail_m3e/lib/src/rail_item_button_m3e.dart
+++ b/packages/navigation_rail_m3e/lib/src/rail_item_button_m3e.dart
@@ -7,6 +7,7 @@ import 'package:navigation_rail_m3e/navigation_rail_m3e.dart';
/// switching widget types. This avoids animation hitches when the
/// rail animates between collapsed and expanded.
class RailItemButtonM3E extends StatelessWidget {
+ /// Creates a [RailItemButtonM3E].
const RailItemButtonM3E({
super.key,
required this.icon,
@@ -21,15 +22,34 @@ class RailItemButtonM3E extends StatelessWidget {
this.badgeCount,
});
+ /// Icon to display.
final Widget icon;
+
+ /// Optional icon to display when [isSelected] is true; falls back to [icon].
final Widget? selectedIcon;
+
+ /// Whether this destination is currently selected.
final bool isSelected;
+
+ /// Callback when the button is tapped.
final VoidCallback onPressed;
+
+ /// Whether the rail is in expanded layout.
final bool expanded;
+
+ /// Controls when the text label is visible in collapsed mode.
final NavigationRailM3ELabelBehavior labelBehavior;
+
+ /// Text label for the destination.
final String label;
+
+ /// Semantic label used for accessibility (and tooltip when collapsed).
final String? semanticLabel;
+
+ /// If true, suppresses Ink splash/hover effects.
final bool suppressInk;
+
+ /// Optional numeric badge value to show.
final int? badgeCount;
@override
diff --git a/packages/navigation_rail_m3e/lib/src/rail_menu_slot.dart b/packages/navigation_rail_m3e/lib/src/rail_menu_slot.dart
index 8194635..b355a0f 100644
--- a/packages/navigation_rail_m3e/lib/src/rail_menu_slot.dart
+++ b/packages/navigation_rail_m3e/lib/src/rail_menu_slot.dart
@@ -2,7 +2,10 @@ import 'package:flutter/material.dart';
/// Menu slot at the top of the rail (non-selectable). One class per file.
class NavigationRailM3EMenu extends StatelessWidget {
+ /// Creates a [NavigationRailM3EMenu].
const NavigationRailM3EMenu({super.key, required this.child});
+
+ /// Content widget placed in the menu slot.
final Widget child;
@override
diff --git a/packages/navigation_rail_m3e/lib/src/rail_section_m3e.dart b/packages/navigation_rail_m3e/lib/src/rail_section_m3e.dart
index 4dff379..7da5ad9 100644
--- a/packages/navigation_rail_m3e/lib/src/rail_section_m3e.dart
+++ b/packages/navigation_rail_m3e/lib/src/rail_section_m3e.dart
@@ -1,13 +1,18 @@
import 'package:flutter/material.dart';
+
import 'rail_destination_m3e.dart';
/// Section groups a header and a list of destinations. One class per file.
class NavigationRailM3ESection {
+ /// Creates a [NavigationRailM3ESection].
const NavigationRailM3ESection({
required this.destinations,
this.header,
});
+ /// Destinations shown in this section.
final List destinations;
+
+ /// Optional header widget displayed above the destinations.
final Widget? header;
-}
\ No newline at end of file
+}
diff --git a/packages/navigation_rail_m3e/lib/src/rail_theme.dart b/packages/navigation_rail_m3e/lib/src/rail_theme.dart
index d82b5a0..6539f92 100644
--- a/packages/navigation_rail_m3e/lib/src/rail_theme.dart
+++ b/packages/navigation_rail_m3e/lib/src/rail_theme.dart
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
/// Theme extension for NavigationRailM3E token values.
class NavigationRailM3ETheme extends ThemeExtension {
+ /// Creates a [NavigationRailM3ETheme] with default token values.
const NavigationRailM3ETheme({
this.collapsedWidth = 96.0,
this.expandedMinWidth = 220.0,
@@ -20,18 +21,43 @@ class NavigationRailM3ETheme extends ThemeExtension {
this.sectionHeaderSpacingBottom = 8.0,
});
+ /// Width of the rail when collapsed.
final double collapsedWidth;
+
+ /// Minimum width of the rail when expanded.
final double expandedMinWidth;
+
+ /// Maximum width of the rail when expanded.
final double expandedMaxWidth;
+
+ /// Default height of an item.
final double itemHeight;
+
+ /// Short item height variant.
final double itemShortHeight;
+
+ /// Default icon size used for items.
final double iconSize;
+
+ /// Leading inset for the active indicator in expanded mode.
final double indicatorLeading;
+
+ /// Trailing inset for the active indicator in expanded mode.
final double indicatorTrailing;
+
+ /// Gap between icon and label.
final double iconLabelGap;
+
+ /// Vertical gap between items.
final double itemVerticalGap;
+
+ /// Minimum spacing between the top and the first header.
final double headerMinSpace;
+
+ /// Top spacing around a section header.
final double sectionHeaderSpacingTop;
+
+ /// Bottom spacing around a section header.
final double sectionHeaderSpacingBottom;
@override
diff --git a/packages/navigation_rail_m3e/lib/src/rail_tokens_adapter.dart b/packages/navigation_rail_m3e/lib/src/rail_tokens_adapter.dart
index ba08e5e..9a91609 100644
--- a/packages/navigation_rail_m3e/lib/src/rail_tokens_adapter.dart
+++ b/packages/navigation_rail_m3e/lib/src/rail_tokens_adapter.dart
@@ -3,33 +3,40 @@ import 'package:m3e_design/m3e_design.dart' as m3e;
/// Provides colors & shapes from `m3e_design` with safe fallbacks to Theme.of(context).
class NavigationRailTokensAdapter {
+ /// Creates a [NavigationRailTokensAdapter].
const NavigationRailTokensAdapter(this.context);
+ /// Source context for resolving Theme and m3e tokens.
final BuildContext context;
ColorScheme get _cs => Theme.of(context).colorScheme;
// Colors per spec
+ /// Background color of the rail container.
Color get containerColor {
// Use surface container token if present, else fallback.
return _maybe(() => context.m3e.colors.surfaceContainer) ??
_cs.surfaceContainer;
}
+ /// Background color of the active item indicator.
Color get activeIndicatorColor {
return _maybe(() => context.m3e.colors.secondaryContainer) ??
_cs.secondaryContainer;
}
+ /// Color for the icon and label when the item is active.
Color get activeIconAndLabel {
return _maybe(() => context.m3e.colors.secondary) ?? _cs.secondary;
}
+ /// Color for the icon and label when the item is inactive.
Color get inactiveIconAndLabel {
return _maybe(() => context.m3e.colors.onSurfaceVariant) ??
_cs.onSurfaceVariant;
}
+ /// Foreground color used for the menu (top) slot.
Color get menuColor {
return _maybe(() => context.m3e.colors.onSecondaryContainer) ??
_cs.onSecondaryContainer;
diff --git a/packages/navigation_rail_m3e/lib/src/type.dart b/packages/navigation_rail_m3e/lib/src/type.dart
index 58e7202..4a09504 100644
--- a/packages/navigation_rail_m3e/lib/src/type.dart
+++ b/packages/navigation_rail_m3e/lib/src/type.dart
@@ -3,12 +3,22 @@ enum NavigationRailM3EType {
/// Slim 96dp rail.
collapsed,
+ /// Slim 96dp rail with no button to expand.
+ alwaysCollapse,
+
/// Wide 220–360dp rail that replaces the drawer.
expanded,
+
+ /// Wide 220–360dp rail that replaces the drawer with no button to collapse.
+ alwaysExpand,
}
+/// Convenience extension for checking the current rail type.
extension NavigationRailM3ETypeX on NavigationRailM3EType {
+ /// Whether this type equals [NavigationRailM3EType.collapsed].
bool get isCollapsed => this == NavigationRailM3EType.collapsed;
+
+ /// Whether this type equals [NavigationRailM3EType.expanded].
bool get isExpanded => this == NavigationRailM3EType.expanded;
}
@@ -18,7 +28,12 @@ extension NavigationRailM3ETypeX on NavigationRailM3EType {
/// - onlySelected: show the label only for the selected destination.
/// - alwaysHide: never show labels even when expanded.
enum NavigationRailM3ELabelBehavior {
+ /// Always show labels (subject to width constraints).
alwaysShow,
+
+ /// Show the label only for the selected destination.
onlySelected,
+
+ /// Never show labels even when expanded.
alwaysHide,
}
diff --git a/packages/navigation_rail_m3e/pubspec.yaml b/packages/navigation_rail_m3e/pubspec.yaml
index cb5d813..f88f71c 100644
--- a/packages/navigation_rail_m3e/pubspec.yaml
+++ b/packages/navigation_rail_m3e/pubspec.yaml
@@ -1,6 +1,6 @@
name: navigation_rail_m3e
description: Material 3 Expressive navigation rail (collapsed & expanded) with modal/standard modes, badges, sections, and m3e_design token integration.
-version: 0.1.1
+version: 0.3.2
homepage: https://github.com/EmilyMonestone/material_3_expressive
repository: https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/navigation_rail_m3e
issue_tracker: https://github.com/EmilyMonestone/material_3_expressive/issues
@@ -20,7 +20,7 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
- flutter_lints: ^4.0.0
+ flutter_lints: ^6.0.0
flutter:
uses-material-design: true
\ No newline at end of file
diff --git a/packages/progress_indicator_m3e/pubspec.yaml b/packages/progress_indicator_m3e/pubspec.yaml
index 652253c..4b9727f 100644
--- a/packages/progress_indicator_m3e/pubspec.yaml
+++ b/packages/progress_indicator_m3e/pubspec.yaml
@@ -1,6 +1,6 @@
name: progress_indicator_m3e
description: "Material 3 Expressive progress indicators."
-version: 0.1.0
+version: 0.3.0
repository: https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/progress_indicator_m3e
issue_tracker: https://github.com/EmilyMonestone/material_3_expressive/issues
diff --git a/packages/slider_m3e/pubspec.yaml b/packages/slider_m3e/pubspec.yaml
index 029358c..3ed5a96 100644
--- a/packages/slider_m3e/pubspec.yaml
+++ b/packages/slider_m3e/pubspec.yaml
@@ -1,6 +1,6 @@
name: slider_m3e
description: Material 3 Expressive Sliders (single & range) for Flutter, powered by M3E tokens.
-version: 0.1.0
+version: 0.3.0
repository: https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/slider_m3e
issue_tracker: https://github.com/EmilyMonestone/material_3_expressive/issues
diff --git a/packages/split_button_m3e/pubspec.yaml b/packages/split_button_m3e/pubspec.yaml
index f699122..406999d 100644
--- a/packages/split_button_m3e/pubspec.yaml
+++ b/packages/split_button_m3e/pubspec.yaml
@@ -1,6 +1,6 @@
name: split_button_m3e
description: "Material 3 Expressive Split Button with sizes, variants, shapes, a11y, and menu."
-version: 0.2.0
+version: 0.3.0
repository: https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/split_button_m3e
issue_tracker: https://github.com/EmilyMonestone/material_3_expressive/issues
diff --git a/packages/toolbar_m3e/pubspec.yaml b/packages/toolbar_m3e/pubspec.yaml
index 78d5d5e..e4eed55 100644
--- a/packages/toolbar_m3e/pubspec.yaml
+++ b/packages/toolbar_m3e/pubspec.yaml
@@ -1,6 +1,6 @@
name: toolbar_m3e
description: Material 3 Expressive Toolbars for Flutter with token-driven colors, shapes, density, and overflow handling.
-version: 0.1.0
+version: 0.3.0
repository: https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/toolbar_m3e
issue_tracker: https://github.com/EmilyMonestone/material_3_expressive/issues
diff --git a/tool/update_versions.dart b/tool/update_versions.dart
index 336963e..2cab9ea 100644
--- a/tool/update_versions.dart
+++ b/tool/update_versions.dart
@@ -8,9 +8,10 @@ void main(List args) {
(a) => a.startsWith('version='),
orElse: () => 'version=',
);
- final version = versionArg.split('=', 2).last.trim();
+ final version = versionArg.split('=').last.trim();
if (version.isEmpty) {
- stderr.writeln('Usage: dart run tool/update_versions.dart -- version=');
+ stderr.writeln(
+ 'Usage: dart run tool/update_versions.dart -- version=');
exit(64);
}
@@ -41,22 +42,28 @@ void main(List args) {
// Skip if it's clearly an app (heuristic): has 'flutter:' and 'uses-material-design:' at top-level
// but the requirement is to update all packages under packages/*. We will proceed regardless.
- final nameMatch = RegExp(r'^name:\s*(.+)$', multiLine: true).firstMatch(content);
- final pkgName = nameMatch != null ? nameMatch.group(1)!.trim() : file.parent.path.split('\\').last;
+ final nameMatch =
+ RegExp(r'^name:\s*(.+)$', multiLine: true).firstMatch(content);
+ final pkgName = nameMatch != null
+ ? nameMatch.group(1)!.trim()
+ : file.parent.path.split('\\').last;
String updatedContent;
final versionRe = RegExp(r'^version:\s*.*$', multiLine: true);
if (versionRe.hasMatch(content)) {
- updatedContent = content.replaceFirstMapped(versionRe, (_) => 'version: $version');
+ updatedContent =
+ content.replaceFirstMapped(versionRe, (_) => 'version: $version');
} else {
// Insert after description if present, otherwise after name.
final descRe = RegExp(r'^(description:.*)$', multiLine: true);
if (descRe.hasMatch(content)) {
- updatedContent = content.replaceFirstMapped(descRe, (m) => '${m.group(1)}\nversion: $version');
+ updatedContent = content.replaceFirstMapped(
+ descRe, (m) => '${m.group(1)}\nversion: $version');
} else {
final nameRe = RegExp(r'^(name:.*)$', multiLine: true);
if (nameRe.hasMatch(content)) {
- updatedContent = content.replaceFirstMapped(nameRe, (m) => '${m.group(1)}\nversion: $version');
+ updatedContent = content.replaceFirstMapped(
+ nameRe, (m) => '${m.group(1)}\nversion: $version');
} else {
// If neither present, prepend version at top.
updatedContent = 'version: $version\n$content';