From a255494ec15d348050a246f9bd7f53ab5e61147f Mon Sep 17 00:00:00 2001 From: Emily Pauli Date: Fri, 24 Oct 2025 09:28:07 +0200 Subject: [PATCH] Bump package versions to 0.3.0 across most packages; update `navigation_rail_m3e` to 0.3.2, add new features, enhancements, and documentation to `navigation_rail_m3e`. Update `melos.yaml` with new scripts. Refactor `update_versions.dart` script. --- melos.yaml | 5 +- packages/app_bar_m3e/pubspec.yaml | 2 +- packages/button_group_m3e/pubspec.yaml | 2 +- packages/button_m3e/pubspec.yaml | 2 +- packages/fab_m3e/pubspec.yaml | 2 +- .../icon_button_m3e/melos_icon_button_m3e.iml | 14 +- packages/icon_button_m3e/pubspec.yaml | 4 +- packages/loading_indicator_m3e/pubspec.yaml | 2 +- packages/m3e_collection/pubspec.yaml | 4 +- packages/m3e_design/pubspec.yaml | 2 +- packages/navigation_bar_m3e/pubspec.yaml | 2 +- .../navigation_rail_m3e/example/lib/main.dart | 2 + .../lib/navigation_rail_m3e.dart | 2 +- .../lib/src/navigation_rail_m3e_widget.dart | 161 ++++++++++++++++-- .../lib/src/rail_destination_m3e.dart | 10 ++ .../lib/src/rail_fab_slot.dart | 1 + .../lib/src/rail_item_button_m3e.dart | 20 +++ .../lib/src/rail_menu_slot.dart | 3 + .../lib/src/rail_section_m3e.dart | 7 +- .../lib/src/rail_theme.dart | 26 +++ .../lib/src/rail_tokens_adapter.dart | 7 + .../navigation_rail_m3e/lib/src/type.dart | 15 ++ packages/navigation_rail_m3e/pubspec.yaml | 4 +- packages/progress_indicator_m3e/pubspec.yaml | 2 +- packages/slider_m3e/pubspec.yaml | 2 +- packages/split_button_m3e/pubspec.yaml | 2 +- packages/toolbar_m3e/pubspec.yaml | 2 +- tool/update_versions.dart | 21 ++- 28 files changed, 280 insertions(+), 48 deletions(-) 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';