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.

This commit is contained in:
Emily Pauli 2025-10-24 09:28:07 +02:00
commit a255494ec1
28 changed files with 280 additions and 48 deletions

View file

@ -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)"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -20,6 +20,18 @@
<excludeFolder url="file://$MODULE_DIR$/example/macos/Flutter" />
<excludeFolder url="file://$MODULE_DIR$/example/macos/Pods" />
<excludeFolder url="file://$MODULE_DIR$/example/macos/.symlinks" />
<excludeFolder url="file://$MODULE_DIR$/example/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/build" />
<excludeFolder url="file://$MODULE_DIR$/example/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/windows/flutter/ephemeral/.plugin_symlinks/dynamic_color/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/windows/flutter/ephemeral/.plugin_symlinks/dynamic_color/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/windows/flutter/ephemeral/.plugin_symlinks/dynamic_color/build" />
<excludeFolder url="file://$MODULE_DIR$/example/windows/flutter/ephemeral/.plugin_symlinks/dynamic_color/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/windows/flutter/ephemeral/.plugin_symlinks/dynamic_color/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/windows/flutter/ephemeral/.plugin_symlinks/dynamic_color/example/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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<DemoApp> createState() => _DemoAppState();

View file

@ -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';

View file

@ -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<NavigationRailM3E> createState() => _NavigationRailM3EState();
}
@ -73,15 +91,37 @@ class _NavigationRailM3EState extends State<NavigationRailM3E>
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<NavigationRailM3E>
});
}
// 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<NavigationRailM3E>
_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<NavigationRailM3E>
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<NavigationRailM3E>
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<NavigationRailM3E>
);
}
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<Widget> _buildChildren(BuildContext context,
{required bool showLabels}) {
final theme = Theme.of(context).extension<NavigationRailM3ETheme>() ??
@ -364,6 +445,11 @@ class _NavigationRailM3EState extends State<NavigationRailM3E>
));
}
}
// 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<NavigationRailM3E>
child: LayoutBuilder(
builder: (ctx, constraints) {
final showLabels = _isExpanded && constraints.maxWidth >= 180;
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: _buildChildren(ctx, showLabels: showLabels),
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,
);
}
},
),
);

View file

@ -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.

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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<NavigationRailM3EDestination> destinations;
/// Optional header widget displayed above the destinations.
final Widget? header;
}

View file

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
/// Theme extension for NavigationRailM3E token values.
class NavigationRailM3ETheme extends ThemeExtension<NavigationRailM3ETheme> {
/// 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<NavigationRailM3ETheme> {
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

View file

@ -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;

View file

@ -3,12 +3,22 @@ enum NavigationRailM3EType {
/// Slim 96dp rail.
collapsed,
/// Slim 96dp rail with no button to expand.
alwaysCollapse,
/// Wide 220360dp rail that replaces the drawer.
expanded,
/// Wide 220360dp 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,
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -8,9 +8,10 @@ void main(List<String> 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=<semver>');
stderr.writeln(
'Usage: dart run tool/update_versions.dart -- version=<semver>');
exit(64);
}
@ -41,22 +42,28 @@ void main(List<String> 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';