Add Widgetbook setup with theme configuration, analysis options, and initial component use cases
This commit is contained in:
parent
d3ad4b9c9d
commit
80ca8f665a
46 changed files with 6031 additions and 2 deletions
|
|
@ -0,0 +1,35 @@
|
|||
# INDEX — navigation_rail_m3e
|
||||
|
||||
This index summarizes Widgetbook use cases for the `navigation_rail_m3e` package.
|
||||
|
||||
## Components and Variants
|
||||
|
||||
- NavigationRailM3E
|
||||
- default
|
||||
- collapsed
|
||||
- always_collapsed
|
||||
- always_expanded
|
||||
- modal
|
||||
- labels_only_selected
|
||||
- labels_always_hide
|
||||
- three_destinations
|
||||
- five_destinations_with_badges
|
||||
- with_fab_slot
|
||||
- with_trailing
|
||||
|
||||
- RailItemButtonM3E
|
||||
- default
|
||||
- expanded
|
||||
- selected
|
||||
- with_badge
|
||||
|
||||
- RailBadgeM3E
|
||||
- default
|
||||
- dot
|
||||
- overflow_999+
|
||||
- dense
|
||||
|
||||
Notes
|
||||
- All use cases are placed under: `packages/navigation_rail_m3e/lib/src/widgetbook/`.
|
||||
- Use cases follow plan/guide.md: `@UseCase(name: '...', type: ComponentType)` and method names `build[Component][Variant]UseCase`.
|
||||
- Critical parameters are exposed via knobs; callbacks print useful messages.
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:navigation_rail_m3e/navigation_rail_m3e.dart';
|
||||
import 'package:widgetbook/widgetbook.dart';
|
||||
import 'package:widgetbook_annotation/widgetbook_annotation.dart';
|
||||
|
||||
List<NavigationRailM3ESection> _buildSections(
|
||||
BuildContext context, {
|
||||
required int sectionCount,
|
||||
required int itemsPerSection,
|
||||
required bool withBadges,
|
||||
required bool useShortItems,
|
||||
}) {
|
||||
final icons = <IconData>[
|
||||
Icons.inbox_outlined,
|
||||
Icons.send_outlined,
|
||||
Icons.star_outline,
|
||||
Icons.archive_outlined,
|
||||
Icons.delete_outline,
|
||||
Icons.settings_outlined,
|
||||
];
|
||||
|
||||
return List.generate(sectionCount, (s) {
|
||||
final destinations = List.generate(itemsPerSection, (i) {
|
||||
final idx = (s * itemsPerSection + i) % icons.length;
|
||||
return NavigationRailM3EDestination(
|
||||
icon: Icon(icons[idx]),
|
||||
selectedIcon: Icon(
|
||||
icons[idx].codePoint == Icons.inbox_outlined.codePoint
|
||||
? Icons.inbox
|
||||
: icons[idx]),
|
||||
label: 'Item ${s + 1}.${i + 1}',
|
||||
semanticLabel: 'Item ${s + 1}.${i + 1}',
|
||||
badgeCount: withBadges
|
||||
? ((i % 3 == 0)
|
||||
? 0
|
||||
: (i % 4 == 0)
|
||||
? 1001
|
||||
: (i + 1) * (s + 1))
|
||||
: null,
|
||||
short: useShortItems && (i % 2 == 0),
|
||||
);
|
||||
});
|
||||
return NavigationRailM3ESection(
|
||||
header: Text('Section ${s + 1}'),
|
||||
destinations: destinations,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildRailDemo(
|
||||
BuildContext context, {
|
||||
NavigationRailM3EType? forcedType,
|
||||
NavigationRailM3EModality? forcedModality,
|
||||
}) {
|
||||
// Content knobs
|
||||
final sectionsCount = context.knobs.int.slider(
|
||||
label: 'sections',
|
||||
initialValue: 2,
|
||||
min: 1,
|
||||
max: 3,
|
||||
);
|
||||
final itemsPerSection = context.knobs.int.slider(
|
||||
label: 'items per section',
|
||||
initialValue: 3,
|
||||
min: 1,
|
||||
max: 6,
|
||||
);
|
||||
final withBadges =
|
||||
context.knobs.boolean(label: 'with badges', initialValue: true);
|
||||
final useShortItems =
|
||||
context.knobs.boolean(label: 'use short items', initialValue: false);
|
||||
|
||||
final sections = _buildSections(
|
||||
context,
|
||||
sectionCount: sectionsCount,
|
||||
itemsPerSection: itemsPerSection,
|
||||
withBadges: withBadges,
|
||||
useShortItems: useShortItems,
|
||||
);
|
||||
final totalItems =
|
||||
sections.fold<int>(0, (sum, s) => sum + s.destinations.length);
|
||||
|
||||
// Behavior knobs
|
||||
final type = forcedType ??
|
||||
context.knobs.object.dropdown<NavigationRailM3EType>(
|
||||
label: 'type',
|
||||
options: NavigationRailM3EType.values,
|
||||
initialOption: NavigationRailM3EType.expanded,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final modality = forcedModality ??
|
||||
context.knobs.object.dropdown<NavigationRailM3EModality>(
|
||||
label: 'modality',
|
||||
options: NavigationRailM3EModality.values,
|
||||
initialOption: NavigationRailM3EModality.standard,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final labelBehavior =
|
||||
context.knobs.object.dropdown<NavigationRailM3ELabelBehavior>(
|
||||
label: 'labelBehavior',
|
||||
options: NavigationRailM3ELabelBehavior.values,
|
||||
initialOption: NavigationRailM3ELabelBehavior.alwaysShow,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final hideWhenCollapsed = context.knobs.boolean(
|
||||
label: 'hideWhenCollapsed',
|
||||
initialValue: false,
|
||||
);
|
||||
final scrollable = context.knobs.boolean(
|
||||
label: 'scrollable',
|
||||
initialValue: true,
|
||||
);
|
||||
final expandedWidth = context.knobs.double.slider(
|
||||
label: 'expandedWidth',
|
||||
initialValue: 280,
|
||||
min: 220,
|
||||
max: 360,
|
||||
divisions: 14,
|
||||
);
|
||||
final selectedIndex = context.knobs.int.slider(
|
||||
label: 'selectedIndex',
|
||||
initialValue: 0,
|
||||
min: 0,
|
||||
max: (totalItems == 0 ? 0 : totalItems - 1),
|
||||
);
|
||||
|
||||
// Slots knobs
|
||||
final withFab = context.knobs.boolean(label: 'with FAB', initialValue: true);
|
||||
final withTrailing =
|
||||
context.knobs.boolean(label: 'with trailing', initialValue: false);
|
||||
final trailingAtBottom =
|
||||
context.knobs.boolean(label: 'trailingAtBottom', initialValue: true);
|
||||
|
||||
final fab = withFab
|
||||
? NavigationRailM3EFabSlot(
|
||||
icon: const Icon(Icons.add),
|
||||
label: 'Create',
|
||||
onPressed: () => print('Rail FAB pressed'),
|
||||
tooltip: 'Create',
|
||||
)
|
||||
: null;
|
||||
|
||||
final trailing = withTrailing
|
||||
? IconButton(
|
||||
tooltip: 'Settings',
|
||||
onPressed: () => print('Trailing pressed'),
|
||||
icon: const Icon(Icons.settings_outlined),
|
||||
)
|
||||
: null;
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
NavigationRailM3E(
|
||||
type: type,
|
||||
modality: modality,
|
||||
sections: sections,
|
||||
selectedIndex: selectedIndex.clamp(0, (totalItems - 1).clamp(0, 9999)),
|
||||
onDestinationSelected: (i) => print('Selected index: $i'),
|
||||
fab: fab,
|
||||
hideWhenCollapsed: hideWhenCollapsed,
|
||||
expandedWidth: expandedWidth,
|
||||
onDismissModal: () => print('Dismiss modal'),
|
||||
onTypeChanged: (t) => print('Type changed -> ${t.name}'),
|
||||
labelBehavior: labelBehavior,
|
||||
scrollable: scrollable,
|
||||
trailing: trailing,
|
||||
trailingAtBottom: trailingAtBottom,
|
||||
),
|
||||
// Fake content area to the right
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: double.infinity,
|
||||
color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.2),
|
||||
alignment: Alignment.center,
|
||||
child: const Text('Content area'),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@UseCase(name: 'default', type: NavigationRailM3E)
|
||||
Widget buildNavigationRailM3EDefaultUseCase(BuildContext context) {
|
||||
return _buildRailDemo(context);
|
||||
}
|
||||
|
||||
@UseCase(name: 'collapsed_standard', type: NavigationRailM3E)
|
||||
Widget buildNavigationRailM3ECollapsedStandardUseCase(BuildContext context) {
|
||||
return _buildRailDemo(
|
||||
context,
|
||||
forcedType: NavigationRailM3EType.collapsed,
|
||||
forcedModality: NavigationRailM3EModality.standard,
|
||||
);
|
||||
}
|
||||
|
||||
@UseCase(name: 'expanded_modal', type: NavigationRailM3E)
|
||||
Widget buildNavigationRailM3EExpandedModalUseCase(BuildContext context) {
|
||||
return _buildRailDemo(
|
||||
context,
|
||||
forcedType: NavigationRailM3EType.expanded,
|
||||
forcedModality: NavigationRailM3EModality.modal,
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:widgetbook/widgetbook.dart';
|
||||
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
|
||||
import 'package:navigation_rail_m3e/navigation_rail_m3e.dart';
|
||||
|
||||
// Note: RailBadgeM3E shows nothing when count is null; dot when 0; count otherwise.
|
||||
|
||||
@widgetbook.UseCase(name: 'default', type: RailBadgeM3E)
|
||||
Widget buildRailBadgeM3EUseCase(BuildContext context) {
|
||||
final count = context.knobs.intOrNull.slider(
|
||||
label: 'count',
|
||||
initialValue: 7,
|
||||
min: 0,
|
||||
max: 1200,
|
||||
divisions: 120,
|
||||
);
|
||||
final maxDigits = context.knobs.int.slider(
|
||||
label: 'maxDigits',
|
||||
initialValue: 3,
|
||||
min: 1,
|
||||
max: 4,
|
||||
);
|
||||
final dense = context.knobs.boolean(label: 'dense', initialValue: false);
|
||||
return Center(
|
||||
child: RailBadgeM3E(count: count, maxDigits: maxDigits, dense: dense),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'dot', type: RailBadgeM3E)
|
||||
Widget buildRailBadgeM3EDotUseCase(BuildContext context) {
|
||||
return const Center(child: RailBadgeM3E(count: 0));
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'overflow_999+', type: RailBadgeM3E)
|
||||
Widget buildRailBadgeM3EOverflowUseCase(BuildContext context) {
|
||||
return const Center(child: RailBadgeM3E(count: 1200, maxDigits: 3));
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'dense', type: RailBadgeM3E)
|
||||
Widget buildRailBadgeM3EDenseUseCase(BuildContext context) {
|
||||
return const Center(child: RailBadgeM3E(count: 42, dense: true));
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:navigation_rail_m3e/navigation_rail_m3e.dart';
|
||||
import 'package:widgetbook/widgetbook.dart';
|
||||
import 'package:widgetbook_annotation/widgetbook_annotation.dart';
|
||||
|
||||
Widget _buildRailItemButtonDemo(
|
||||
BuildContext context, {
|
||||
required bool expanded,
|
||||
}) {
|
||||
final isSelected =
|
||||
context.knobs.boolean(label: 'isSelected', initialValue: false);
|
||||
final label = context.knobs
|
||||
.string(label: 'label', initialValue: expanded ? 'Inbox' : 'Inbox');
|
||||
final semantic =
|
||||
context.knobs.stringOrNull(label: 'semanticLabel', initialValue: 'Inbox');
|
||||
final labelBehavior =
|
||||
context.knobs.object.dropdown<NavigationRailM3ELabelBehavior>(
|
||||
label: 'labelBehavior',
|
||||
options: NavigationRailM3ELabelBehavior.values,
|
||||
initialOption: NavigationRailM3ELabelBehavior.alwaysShow,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final badge = context.knobs.intOrNull.slider(
|
||||
label: 'badgeCount',
|
||||
initialValue: null,
|
||||
min: 0,
|
||||
max: 200,
|
||||
divisions: 20,
|
||||
);
|
||||
final suppressInk =
|
||||
context.knobs.boolean(label: 'suppressInk', initialValue: false);
|
||||
final useAltIcon =
|
||||
context.knobs.boolean(label: 'useAltIcon', initialValue: false);
|
||||
|
||||
final icon = Icon(useAltIcon ? Icons.star : Icons.inbox_outlined);
|
||||
final selectedIcon = Icon(useAltIcon ? Icons.star : Icons.inbox);
|
||||
|
||||
return Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 300),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey.withValues(alpha: 0.3)),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: RailItemButtonM3E(
|
||||
icon: icon,
|
||||
selectedIcon: selectedIcon,
|
||||
isSelected: isSelected,
|
||||
onPressed: () => print('RailItemButtonM3E pressed'),
|
||||
expanded: expanded,
|
||||
labelBehavior: labelBehavior,
|
||||
label: label,
|
||||
semanticLabel: semantic,
|
||||
suppressInk: suppressInk,
|
||||
badgeCount: badge,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@UseCase(name: 'collapsed', type: RailItemButtonM3E)
|
||||
Widget buildRailItemButtonM3ECollapsedUseCase(BuildContext context) {
|
||||
return _buildRailItemButtonDemo(context, expanded: false);
|
||||
}
|
||||
|
||||
@UseCase(name: 'expanded', type: RailItemButtonM3E)
|
||||
Widget buildRailItemButtonM3EExpandedUseCase(BuildContext context) {
|
||||
return _buildRailItemButtonDemo(context, expanded: true);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue