Add initial configuration, tokens, and widgets for M3E components
- Introduced `.gitignore` and `.metadata` for apps and examples. - Added Flutter/Dart analysis configurations (`analysis_options.yaml`). - Implemented foundational tokens and themes for M3E (colors, shapes). - Created base implementations for `IconButtonM3E` and `SplitButtonM3E`. - Set up CI workflow (`ci.yaml`) to automate testing and analysis.
This commit is contained in:
parent
2c0f2df0b8
commit
62ecb86b76
184 changed files with 9872 additions and 0 deletions
21
packages/navigation_bar_m3e/LICENSE
Normal file
21
packages/navigation_bar_m3e/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) ...
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
77
packages/navigation_bar_m3e/README.md
Normal file
77
packages/navigation_bar_m3e/README.md
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
# navigation_bar_m3e
|
||||
|
||||
Material 3 **Expressive** Navigation Bar for Flutter with badges, pill/underline indicators, and token-driven styling.
|
||||
|
||||
- `NavigationBarM3E` — wrapper around Flutter's `NavigationBar` with M3E tokens
|
||||
- `NavigationDestinationM3E` — destination data (icon, selectedIcon, label, badge)
|
||||
- `NavBadgeM3E` — small badge/dot utility for icons
|
||||
|
||||
All styling is driven by the `m3e_design` ThemeExtension (**M3ETheme**).
|
||||
|
||||
## Monorepo Layout
|
||||
|
||||
```
|
||||
packages/
|
||||
m3e_design/
|
||||
navigation_bar_m3e/
|
||||
```
|
||||
|
||||
`pubspec.yaml` references `../m3e_design`.
|
||||
|
||||
## Usage
|
||||
|
||||
```dart
|
||||
import 'package:navigation_bar_m3e/navigation_bar_m3e.dart';
|
||||
|
||||
final items = [
|
||||
const NavigationDestinationM3E(
|
||||
icon: Icon(Icons.home_outlined),
|
||||
selectedIcon: Icon(Icons.home),
|
||||
label: 'Home',
|
||||
),
|
||||
const NavigationDestinationM3E(
|
||||
icon: Icon(Icons.search),
|
||||
label: 'Search',
|
||||
badgeCount: 3,
|
||||
),
|
||||
const NavigationDestinationM3E(
|
||||
icon: Icon(Icons.person),
|
||||
label: 'Profile',
|
||||
badgeDot: true,
|
||||
),
|
||||
];
|
||||
|
||||
NavigationBarM3E(
|
||||
destinations: items,
|
||||
selectedIndex: 0,
|
||||
onDestinationSelected: (i) {},
|
||||
labelBehavior: NavBarM3ELabelBehavior.onlySelected,
|
||||
indicatorStyle: NavBarM3EIndicatorStyle.pill, // pill | underline | none
|
||||
size: NavBarM3ESize.medium,
|
||||
density: NavBarM3EDensity.regular,
|
||||
shapeFamily: NavBarM3EShapeFamily.round,
|
||||
);
|
||||
```
|
||||
|
||||
## Tokens mapping
|
||||
|
||||
- **Container**: `surfaceContainerHigh`
|
||||
- **Indicator**: `secondaryContainer` (color), pill shape by default; `underline` style uses a bottom border
|
||||
- **Selected**: `onSecondaryContainer` (icon/label)
|
||||
- **Unselected**: `onSurfaceVariant`
|
||||
- **Label style**: `labelMedium`
|
||||
- **Heights**: `small ≈64dp`, `medium ≈80dp`
|
||||
- **Icon size**: `24dp`
|
||||
|
||||
## Badges
|
||||
|
||||
Use `badgeCount` for numeric badges or `badgeDot: true` for a small dot. Colors default to `errorContainer / onErrorContainer` and can be overridden via `NavBadgeM3E`.
|
||||
|
||||
## Accessibility
|
||||
|
||||
- Provide `semanticLabel` per destination (used as tooltip) or on the bar.
|
||||
- Label behavior options: **alwaysShow**, **onlySelected**, or **alwaysHide**.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
7
packages/navigation_bar_m3e/lib/navigation_bar_m3e.dart
Normal file
7
packages/navigation_bar_m3e/lib/navigation_bar_m3e.dart
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
library navigation_bar_m3e;
|
||||
|
||||
export 'src/enums.dart';
|
||||
export 'src/nav_tokens_adapter.dart' show NavTokensAdapter;
|
||||
export 'src/navigation_bar_m3e.dart';
|
||||
export 'src/nav_badge_m3e.dart';
|
||||
export 'src/nav_destination_m3e.dart';
|
||||
5
packages/navigation_bar_m3e/lib/src/enums.dart
Normal file
5
packages/navigation_bar_m3e/lib/src/enums.dart
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
enum NavBarM3ELabelBehavior { alwaysShow, onlySelected, alwaysHide }
|
||||
enum NavBarM3ESize { small, medium }
|
||||
enum NavBarM3EShapeFamily { round, square }
|
||||
enum NavBarM3EDensity { regular, compact }
|
||||
enum NavBarM3EIndicatorStyle { pill, underline, none }
|
||||
78
packages/navigation_bar_m3e/lib/src/nav_badge_m3e.dart
Normal file
78
packages/navigation_bar_m3e/lib/src/nav_badge_m3e.dart
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3e_design/m3e_design.dart';
|
||||
|
||||
class NavBadgeM3E extends StatelessWidget {
|
||||
const NavBadgeM3E({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.count,
|
||||
this.showDot = false,
|
||||
this.maxCount = 99,
|
||||
this.backgroundColor,
|
||||
this.foregroundColor,
|
||||
this.semanticLabel,
|
||||
this.offset = const Offset(8, -6),
|
||||
}) : assert(count == null || count >= 0);
|
||||
|
||||
final Widget child;
|
||||
final int? count;
|
||||
final bool showDot;
|
||||
final int maxCount;
|
||||
final Color? backgroundColor;
|
||||
final Color? foregroundColor;
|
||||
final String? semanticLabel;
|
||||
final Offset offset;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final t = Theme.of(context);
|
||||
final m3e = t.extension<M3ETheme>() ?? M3ETheme.defaults(t.colorScheme);
|
||||
final bg = backgroundColor ?? m3e.colors.errorContainer;
|
||||
final fg = foregroundColor ?? m3e.colors.onErrorContainer;
|
||||
|
||||
final badge = showDot
|
||||
? _dot(bg)
|
||||
: _label(bg, fg, count == null ? '' : _format(count!, maxCount));
|
||||
|
||||
final stack = Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
child,
|
||||
Positioned(
|
||||
right: offset.dx,
|
||||
top: offset.dy,
|
||||
child: Semantics(
|
||||
label: semanticLabel ?? (count != null ? 'Notifications: ${count!}' : 'Notifications'),
|
||||
child: badge,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
Widget _dot(Color bg) {
|
||||
return Container(
|
||||
width: 8, height: 8,
|
||||
decoration: BoxDecoration(color: bg, shape: BoxShape.circle),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _label(Color bg, Color fg, String text) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: bg,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
constraints: const BoxConstraints(minWidth: 18, minHeight: 18),
|
||||
child: DefaultTextStyle(
|
||||
style: const TextStyle(fontSize: 10, fontWeight: FontWeight.w600),
|
||||
child: Text(text, textAlign: TextAlign.center, style: TextStyle(color: fg)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _format(int c, int max) => (c > max) ? '$max+' : '$c';
|
||||
}
|
||||
38
packages/navigation_bar_m3e/lib/src/nav_destination_m3e.dart
Normal file
38
packages/navigation_bar_m3e/lib/src/nav_destination_m3e.dart
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'nav_badge_m3e.dart';
|
||||
|
||||
class NavigationDestinationM3E {
|
||||
const NavigationDestinationM3E({
|
||||
required this.icon,
|
||||
required this.label,
|
||||
this.selectedIcon,
|
||||
this.badgeCount,
|
||||
this.badgeDot = false,
|
||||
this.semanticLabel,
|
||||
});
|
||||
|
||||
final Widget icon;
|
||||
final Widget? selectedIcon;
|
||||
final String label;
|
||||
|
||||
/// Optional badge counter
|
||||
final int? badgeCount;
|
||||
|
||||
/// If true, show a small dot instead of a counter.
|
||||
final bool badgeDot;
|
||||
|
||||
final String? semanticLabel;
|
||||
|
||||
Widget buildIcon([bool selected = false]) {
|
||||
final base = selected && selectedIcon != null ? selectedIcon! : icon;
|
||||
if (badgeCount != null || badgeDot) {
|
||||
return NavBadgeM3E(
|
||||
child: base,
|
||||
count: badgeCount,
|
||||
showDot: badgeDot,
|
||||
semanticLabel: semanticLabel,
|
||||
);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
}
|
||||
84
packages/navigation_bar_m3e/lib/src/nav_tokens_adapter.dart
Normal file
84
packages/navigation_bar_m3e/lib/src/nav_tokens_adapter.dart
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3e_design/m3e_design.dart';
|
||||
import 'enums.dart';
|
||||
|
||||
@immutable
|
||||
class _NavMetrics {
|
||||
final double heightSmall;
|
||||
final double heightMedium;
|
||||
final double iconSize;
|
||||
final EdgeInsetsGeometry padding;
|
||||
final double indicatorThickness; // for underline
|
||||
const _NavMetrics({
|
||||
required this.heightSmall,
|
||||
required this.heightMedium,
|
||||
required this.iconSize,
|
||||
required this.padding,
|
||||
required this.indicatorThickness,
|
||||
});
|
||||
}
|
||||
|
||||
_NavMetrics _metricsFor(BuildContext context, NavBarM3EDensity density) {
|
||||
final theme = Theme.of(context);
|
||||
final m3e = theme.extension<M3ETheme>() ?? M3ETheme.defaults(theme.colorScheme);
|
||||
final sp = m3e.spacing;
|
||||
|
||||
double hSmall = 64; // compact/phone-tight
|
||||
double hMedium = 80; // default M3 nav bar height
|
||||
double icon = 24;
|
||||
double underline = 3;
|
||||
|
||||
if (density == NavBarM3EDensity.compact) {
|
||||
hSmall -= 4; hMedium -= 4; underline -= 1;
|
||||
}
|
||||
|
||||
return _NavMetrics(
|
||||
heightSmall: hSmall,
|
||||
heightMedium: hMedium,
|
||||
iconSize: icon,
|
||||
padding: EdgeInsets.symmetric(horizontal: sp.md),
|
||||
indicatorThickness: underline,
|
||||
);
|
||||
}
|
||||
|
||||
class NavTokensAdapter {
|
||||
NavTokensAdapter(this.context);
|
||||
final BuildContext context;
|
||||
|
||||
M3ETheme get _m3e {
|
||||
final t = Theme.of(context);
|
||||
return t.extension<M3ETheme>() ?? M3ETheme.defaults(t.colorScheme);
|
||||
}
|
||||
|
||||
_NavMetrics metrics(NavBarM3EDensity density) => _metricsFor(context, density);
|
||||
|
||||
// Container/background
|
||||
Color containerColor() => _m3e.colors.surfaceContainerHigh;
|
||||
|
||||
// Indicator
|
||||
Color indicatorColor() => _m3e.colors.secondaryContainer;
|
||||
|
||||
// Icon/label colors
|
||||
Color selectedColor() => _m3e.colors.onSecondaryContainer;
|
||||
Color unselectedColor() => _m3e.colors.onSurfaceVariant;
|
||||
|
||||
// Typography
|
||||
TextStyle labelStyle() => _m3e.type.labelMedium;
|
||||
|
||||
// Shapes
|
||||
ShapeBorder containerShape(NavBarM3EShapeFamily family) {
|
||||
final set = family == NavBarM3EShapeFamily.round ? _m3e.shapes.round : _m3e.shapes.square;
|
||||
return RoundedRectangleBorder(borderRadius: set.lg);
|
||||
}
|
||||
|
||||
ShapeBorder indicatorShapePill() => const StadiumBorder();
|
||||
|
||||
// Underline decoration for selected.
|
||||
BoxDecoration underlineDecoration(Color color, double thickness) {
|
||||
return BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(color: color, width: thickness),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
144
packages/navigation_bar_m3e/lib/src/navigation_bar_m3e.dart
Normal file
144
packages/navigation_bar_m3e/lib/src/navigation_bar_m3e.dart
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3e_design/m3e_design.dart';
|
||||
import 'enums.dart';
|
||||
import 'nav_tokens_adapter.dart';
|
||||
import 'nav_destination_m3e.dart';
|
||||
|
||||
class NavigationBarM3E extends StatelessWidget {
|
||||
const NavigationBarM3E({
|
||||
super.key,
|
||||
required this.destinations,
|
||||
this.selectedIndex = 0,
|
||||
this.onDestinationSelected,
|
||||
this.labelBehavior = NavBarM3ELabelBehavior.onlySelected,
|
||||
this.size = NavBarM3ESize.medium,
|
||||
this.shapeFamily = NavBarM3EShapeFamily.round,
|
||||
this.density = NavBarM3EDensity.regular,
|
||||
this.backgroundColor,
|
||||
this.elevation,
|
||||
this.indicatorStyle = NavBarM3EIndicatorStyle.pill,
|
||||
this.indicatorColor,
|
||||
this.padding,
|
||||
this.safeArea = true,
|
||||
this.semanticLabel,
|
||||
});
|
||||
|
||||
final List<NavigationDestinationM3E> destinations;
|
||||
final int selectedIndex;
|
||||
final ValueChanged<int>? onDestinationSelected;
|
||||
|
||||
final NavBarM3ELabelBehavior labelBehavior;
|
||||
final NavBarM3ESize size;
|
||||
final NavBarM3EShapeFamily shapeFamily;
|
||||
final NavBarM3EDensity density;
|
||||
|
||||
final Color? backgroundColor;
|
||||
final double? elevation;
|
||||
|
||||
final NavBarM3EIndicatorStyle indicatorStyle;
|
||||
final Color? indicatorColor;
|
||||
|
||||
final EdgeInsetsGeometry? padding;
|
||||
final bool safeArea;
|
||||
|
||||
final String? semanticLabel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(destinations.isNotEmpty, 'Provide at least one destination');
|
||||
|
||||
final tokens = NavTokensAdapter(context);
|
||||
final metrics = tokens.metrics(density);
|
||||
final m3e = Theme.of(context).extension<M3ETheme>() ?? M3ETheme.defaults(Theme.of(context).colorScheme);
|
||||
|
||||
final height = size == NavBarM3ESize.small ? metrics.heightSmall : metrics.heightMedium;
|
||||
final bg = backgroundColor ?? tokens.containerColor();
|
||||
final shape = tokens.containerShape(shapeFamily);
|
||||
|
||||
final nav = Material(
|
||||
color: bg,
|
||||
elevation: elevation ?? 0,
|
||||
shape: shape,
|
||||
child: SizedBox(
|
||||
height: height,
|
||||
child: NavigationBar(
|
||||
height: height,
|
||||
elevation: elevation ?? 0,
|
||||
indicatorColor: indicatorStyle == NavBarM3EIndicatorStyle.none
|
||||
? Colors.transparent
|
||||
: (indicatorColor ?? tokens.indicatorColor()),
|
||||
indicatorShape: switch (indicatorStyle) {
|
||||
NavBarM3EIndicatorStyle.pill => tokens.indicatorShapePill(),
|
||||
NavBarM3EIndicatorStyle.underline => const StadiumBorder(), // we'll fake underline via decoration below
|
||||
NavBarM3EIndicatorStyle.none => const StadiumBorder(),
|
||||
},
|
||||
backgroundColor: Colors.transparent, // outer Material supplies bg + shape
|
||||
labelBehavior: switch (labelBehavior) {
|
||||
NavBarM3ELabelBehavior.alwaysShow => NavigationDestinationLabelBehavior.alwaysShow,
|
||||
NavBarM3ELabelBehavior.onlySelected => NavigationDestinationLabelBehavior.onlyShowSelected,
|
||||
NavBarM3ELabelBehavior.alwaysHide => NavigationDestinationLabelBehavior.alwaysHide,
|
||||
},
|
||||
selectedIndex: selectedIndex,
|
||||
destinations: List.generate(destinations.length, (i) {
|
||||
final d = destinations[i];
|
||||
return NavigationDestination(
|
||||
icon: _icon(context, false, d, metrics.iconSize),
|
||||
selectedIcon: _selectedIcon(context, true, d, metrics.iconSize, tokens, indicatorStyle),
|
||||
label: d.label,
|
||||
tooltip: d.semanticLabel,
|
||||
);
|
||||
}),
|
||||
onDestinationSelected: onDestinationSelected,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final padded = Padding(
|
||||
padding: padding ?? EdgeInsets.zero,
|
||||
child: nav,
|
||||
);
|
||||
|
||||
final content = DefaultTextStyle.merge(
|
||||
style: tokens.labelStyle().copyWith(
|
||||
color: m3e.colors.onSurfaceVariant,
|
||||
),
|
||||
child: IconTheme.merge(
|
||||
data: IconThemeData(size: metrics.iconSize, color: m3e.colors.onSurfaceVariant),
|
||||
child: padded,
|
||||
),
|
||||
);
|
||||
|
||||
if (!safeArea && semanticLabel == null) return content;
|
||||
final wrapped = SafeArea(top: false, left: false, right: false, bottom: safeArea, child: content);
|
||||
|
||||
if (semanticLabel == null) return wrapped;
|
||||
return Semantics(container: true, label: semanticLabel!, child: wrapped);
|
||||
}
|
||||
|
||||
Widget _icon(BuildContext context, bool selected, NavigationDestinationM3E d, double iconSize) {
|
||||
return SizedBox(
|
||||
width: iconSize + 8, // give a little space for underline
|
||||
height: iconSize + 8,
|
||||
child: Center(child: d.buildIcon(selected)),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _selectedIcon(
|
||||
BuildContext context,
|
||||
bool selected,
|
||||
NavigationDestinationM3E d,
|
||||
double iconSize,
|
||||
NavTokensAdapter tokens,
|
||||
NavBarM3EIndicatorStyle style,
|
||||
) {
|
||||
final w = _icon(context, selected, d, iconSize);
|
||||
if (style != NavBarM3EIndicatorStyle.underline) return w;
|
||||
|
||||
final metrics = tokens.metrics(density);
|
||||
final deco = tokens.underlineDecoration(tokens.indicatorColor(), metrics.indicatorThickness);
|
||||
return DecoratedBox(
|
||||
decoration: deco,
|
||||
child: w,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3e_design/m3e_design.dart';
|
||||
|
||||
class NavigationBarM3EWidget extends StatelessWidget {
|
||||
const NavigationBarM3EWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final m3e = context.m3e;
|
||||
return Container(
|
||||
padding: EdgeInsets.all(m3e.spacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: m3e.colors.surfaceStrong,
|
||||
borderRadius: m3e.shapes.square.md,
|
||||
),
|
||||
child: Text('NavigationBar placeholder', style: m3e.typography.base.titleMedium),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _pascal(String s) => s.split('_').map((p) => p.isEmpty ? '' : (p[0].toUpperCase() + p.substring(1))).join();
|
||||
29
packages/navigation_bar_m3e/melos_navigation_bar_m3e.iml
Normal file
29
packages/navigation_bar_m3e/melos_navigation_bar_m3e.iml
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.idea" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/.idea" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/android/.gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/android/.idea" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/ios/Pods" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/macos/Flutter" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/macos/Pods" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/macos/.symlinks" />
|
||||
</content>
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||
<orderEntry type="library" name="Flutter Plugins" level="project" />
|
||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
18
packages/navigation_bar_m3e/pubspec.yaml
Normal file
18
packages/navigation_bar_m3e/pubspec.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
name: navigation_bar_m3e
|
||||
description: Material 3 Expressive Navigation Bar for Flutter with token-driven colors, shapes, and badges.
|
||||
version: 0.1.0
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=3.5.0 <4.0.0"
|
||||
flutter: ">=3.22.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
m3e_design:
|
||||
path: ../m3e_design
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
4
packages/navigation_bar_m3e/pubspec_overrides.yaml
Normal file
4
packages/navigation_bar_m3e/pubspec_overrides.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# melos_managed_dependency_overrides: m3e_design
|
||||
dependency_overrides:
|
||||
m3e_design:
|
||||
path: ..\\m3e_design
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
test('placeholder', () {
|
||||
expect(2 * 3, 6);
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue