forked from mirrors/material_3_expressive
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
44
widgetbook/lib/fab_m3e/INDEX-fab_m3e.md
Normal file
44
widgetbook/lib/fab_m3e/INDEX-fab_m3e.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Widgetbook Index — fab_m3e
|
||||
|
||||
This index lists all Widgetbook use cases for the fab_m3e package.
|
||||
|
||||
## Components and Variants
|
||||
|
||||
### FabM3E
|
||||
- default
|
||||
- disabled
|
||||
- small
|
||||
- large
|
||||
- secondary
|
||||
- tertiary
|
||||
- surface
|
||||
- square_shape
|
||||
- compact_density
|
||||
- focused
|
||||
|
||||
### ExtendedFabM3E
|
||||
- default
|
||||
- with_icon
|
||||
- without_label
|
||||
- disabled
|
||||
- expand
|
||||
- long_text
|
||||
- secondary
|
||||
- square_shape
|
||||
- compact_density
|
||||
|
||||
### FabMenuM3E
|
||||
- default
|
||||
- initially_open
|
||||
- direction_down
|
||||
- direction_left
|
||||
- direction_right
|
||||
- overlay_off
|
||||
- spacing_tight
|
||||
- many_items
|
||||
- empty_items
|
||||
|
||||
Notes:
|
||||
- Each use case uses Widgetbook knobs for critical and visual parameters.
|
||||
- Callbacks print informative messages to the console.
|
||||
- Complex parameters are configured with meaningful defaults; TODOs to expand configuration may be added later as needed.
|
||||
126
widgetbook/lib/fab_m3e/extended_fab_m3e_usecases.dart
Normal file
126
widgetbook/lib/fab_m3e/extended_fab_m3e_usecases.dart
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:widgetbook/widgetbook.dart';
|
||||
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
|
||||
import 'package:fab_m3e/fab_m3e.dart';
|
||||
|
||||
Widget _buildExtendedFab(
|
||||
BuildContext context, {
|
||||
required FabM3EKind kind,
|
||||
Widget? icon,
|
||||
String labelText = 'Create',
|
||||
FabM3ESize size = FabM3ESize.regular,
|
||||
FabM3EShapeFamily shape = FabM3EShapeFamily.round,
|
||||
FabM3EDensity density = FabM3EDensity.regular,
|
||||
bool enabled = true,
|
||||
bool expand = false,
|
||||
double? elevation,
|
||||
String? tooltip,
|
||||
Object? heroTag,
|
||||
String? semanticLabel,
|
||||
}) {
|
||||
final selectedKind = context.knobs.object.dropdown<FabM3EKind>(
|
||||
label: 'kind',
|
||||
initialOption: kind,
|
||||
options: FabM3EKind.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final selectedSize = context.knobs.object.dropdown<FabM3ESize>(
|
||||
label: 'size',
|
||||
initialOption: size,
|
||||
options: FabM3ESize.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final selectedShape = context.knobs.object.dropdown<FabM3EShapeFamily>(
|
||||
label: 'shape',
|
||||
initialOption: shape,
|
||||
options: FabM3EShapeFamily.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final selectedDensity = context.knobs.object.dropdown<FabM3EDensity>(
|
||||
label: 'density',
|
||||
initialOption: density,
|
||||
options: FabM3EDensity.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
|
||||
final label = context.knobs.string(label: 'label', initialValue: labelText);
|
||||
final withIcon = context.knobs.boolean(label: 'with_icon', initialValue: icon != null);
|
||||
final isEnabled = context.knobs.boolean(label: 'enabled', initialValue: enabled);
|
||||
final shouldExpand = context.knobs.boolean(label: 'expand', initialValue: expand);
|
||||
final dblElevation = context.knobs.doubleOrNull.slider(
|
||||
label: 'elevation',
|
||||
initialValue: elevation,
|
||||
min: 0.0,
|
||||
max: 24.0,
|
||||
divisions: 24,
|
||||
);
|
||||
final tip = context.knobs.stringOrNull(label: 'tooltip', initialValue: tooltip);
|
||||
final semLabel = context.knobs.stringOrNull(label: 'semanticLabel', initialValue: semanticLabel);
|
||||
final useHero = context.knobs.boolean(label: 'wrap in Hero', initialValue: heroTag != null);
|
||||
|
||||
final Widget fab = ExtendedFabM3E(
|
||||
label: Text(label, overflow: TextOverflow.ellipsis),
|
||||
icon: withIcon ? const Icon(Icons.add) : null,
|
||||
onPressed: isEnabled ? () => print('ExtendedFabM3E pressed (kind=$selectedKind, size=$selectedSize)') : null,
|
||||
tooltip: tip,
|
||||
heroTag: useHero ? (heroTag ?? 'extended-fab-hero') : null,
|
||||
kind: selectedKind,
|
||||
size: selectedSize,
|
||||
shapeFamily: selectedShape,
|
||||
density: selectedDensity,
|
||||
expand: shouldExpand,
|
||||
elevation: dblElevation,
|
||||
semanticLabel: semLabel,
|
||||
);
|
||||
|
||||
return Center(
|
||||
child: SizedBox(width: shouldExpand ? 360 : null, child: fab),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'default', type: ExtendedFabM3E)
|
||||
Widget buildExtendedFabM3EUseCase(BuildContext context) {
|
||||
return _buildExtendedFab(context, kind: FabM3EKind.primary, icon: const Icon(Icons.add));
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'with_icon', type: ExtendedFabM3E)
|
||||
Widget buildExtendedFabM3EWithIconUseCase(BuildContext context) {
|
||||
return _buildExtendedFab(context, kind: FabM3EKind.primary, icon: const Icon(Icons.add));
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'without_label', type: ExtendedFabM3E)
|
||||
Widget buildExtendedFabM3EWithoutLabelUseCase(BuildContext context) {
|
||||
return _buildExtendedFab(context, kind: FabM3EKind.primary, labelText: '');
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'disabled', type: ExtendedFabM3E)
|
||||
Widget buildExtendedFabM3EDisabledUseCase(BuildContext context) {
|
||||
return _buildExtendedFab(context, kind: FabM3EKind.primary, icon: const Icon(Icons.add), enabled: false);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'expand', type: ExtendedFabM3E)
|
||||
Widget buildExtendedFabM3EExpandUseCase(BuildContext context) {
|
||||
return _buildExtendedFab(context, kind: FabM3EKind.primary, icon: const Icon(Icons.add), expand: true);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'long_text', type: ExtendedFabM3E)
|
||||
Widget buildExtendedFabM3ELongTextUseCase(BuildContext context) {
|
||||
return _buildExtendedFab(context, kind: FabM3EKind.primary, icon: const Icon(Icons.add), labelText: 'Compose a very long descriptive label that may overflow');
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'secondary', type: ExtendedFabM3E)
|
||||
Widget buildExtendedFabM3ESecondaryUseCase(BuildContext context) {
|
||||
return _buildExtendedFab(context, kind: FabM3EKind.secondary, icon: const Icon(Icons.edit));
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'square_shape', type: ExtendedFabM3E)
|
||||
Widget buildExtendedFabM3ESquareShapeUseCase(BuildContext context) {
|
||||
return _buildExtendedFab(context, kind: FabM3EKind.primary, icon: const Icon(Icons.add), shape: FabM3EShapeFamily.square);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'compact_density', type: ExtendedFabM3E)
|
||||
Widget buildExtendedFabM3ECompactDensityUseCase(BuildContext context) {
|
||||
return _buildExtendedFab(context, kind: FabM3EKind.primary, icon: const Icon(Icons.add), density: FabM3EDensity.compact);
|
||||
}
|
||||
131
widgetbook/lib/fab_m3e/fab_m3e_usecases.dart
Normal file
131
widgetbook/lib/fab_m3e/fab_m3e_usecases.dart
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
import 'package:fab_m3e/fab_m3e.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:widgetbook/widgetbook.dart';
|
||||
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
|
||||
|
||||
Widget _buildFab(
|
||||
BuildContext context, {
|
||||
required FabM3EKind kind,
|
||||
FabM3ESize size = FabM3ESize.regular,
|
||||
FabM3EShapeFamily shape = FabM3EShapeFamily.round,
|
||||
FabM3EDensity density = FabM3EDensity.regular,
|
||||
bool enabled = true,
|
||||
bool autofocus = false,
|
||||
double? elevation,
|
||||
String? tooltip,
|
||||
Object? heroTag,
|
||||
String? semanticLabel,
|
||||
}) {
|
||||
final selectedKind = context.knobs.object.dropdown<FabM3EKind>(
|
||||
label: 'kind',
|
||||
initialOption: kind,
|
||||
options: FabM3EKind.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final selectedSize = context.knobs.object.dropdown<FabM3ESize>(
|
||||
label: 'size',
|
||||
initialOption: size,
|
||||
options: FabM3ESize.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final selectedShape = context.knobs.object.dropdown<FabM3EShapeFamily>(
|
||||
label: 'shape',
|
||||
initialOption: shape,
|
||||
options: FabM3EShapeFamily.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final selectedDensity = context.knobs.object.dropdown<FabM3EDensity>(
|
||||
label: 'density',
|
||||
initialOption: density,
|
||||
options: FabM3EDensity.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final isEnabled =
|
||||
context.knobs.boolean(label: 'enabled', initialValue: enabled);
|
||||
final isAutofocus =
|
||||
context.knobs.boolean(label: 'autofocus', initialValue: autofocus);
|
||||
final dblElevation = context.knobs.doubleOrNull.slider(
|
||||
label: 'elevation',
|
||||
initialValue: elevation,
|
||||
min: 0.0,
|
||||
max: 24.0,
|
||||
divisions: 24,
|
||||
);
|
||||
final tip =
|
||||
context.knobs.stringOrNull(label: 'tooltip', initialValue: tooltip);
|
||||
final semLabel = context.knobs
|
||||
.stringOrNull(label: 'semanticLabel', initialValue: semanticLabel);
|
||||
final useHero = context.knobs
|
||||
.boolean(label: 'wrap in Hero', initialValue: heroTag != null);
|
||||
|
||||
final Widget fab = FabM3E(
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: isEnabled
|
||||
? () => print('FabM3E pressed (kind=$selectedKind, size=$selectedSize)')
|
||||
: null,
|
||||
tooltip: tip,
|
||||
heroTag: useHero ? (heroTag ?? 'fab-hero') : null,
|
||||
kind: selectedKind,
|
||||
size: selectedSize,
|
||||
shapeFamily: selectedShape,
|
||||
density: selectedDensity,
|
||||
elevation: dblElevation,
|
||||
autofocus: isAutofocus,
|
||||
semanticLabel: semLabel,
|
||||
);
|
||||
|
||||
return Center(child: fab);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'default', type: FabM3E)
|
||||
Widget buildFabM3EUseCase(BuildContext context) {
|
||||
return _buildFab(context, kind: FabM3EKind.primary);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'disabled', type: FabM3E)
|
||||
Widget buildFabM3EDisabledUseCase(BuildContext context) {
|
||||
return _buildFab(context, kind: FabM3EKind.primary, enabled: false);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'small', type: FabM3E)
|
||||
Widget buildFabM3ESmallUseCase(BuildContext context) {
|
||||
return _buildFab(context, kind: FabM3EKind.primary, size: FabM3ESize.small);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'large', type: FabM3E)
|
||||
Widget buildFabM3ELargeUseCase(BuildContext context) {
|
||||
return _buildFab(context, kind: FabM3EKind.primary, size: FabM3ESize.large);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'secondary', type: FabM3E)
|
||||
Widget buildFabM3ESecondaryUseCase(BuildContext context) {
|
||||
return _buildFab(context, kind: FabM3EKind.secondary);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'tertiary', type: FabM3E)
|
||||
Widget buildFabM3ETertiaryUseCase(BuildContext context) {
|
||||
return _buildFab(context, kind: FabM3EKind.tertiary);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'surface', type: FabM3E)
|
||||
Widget buildFabM3ESurfaceUseCase(BuildContext context) {
|
||||
return _buildFab(context, kind: FabM3EKind.surface);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'square_shape', type: FabM3E)
|
||||
Widget buildFabM3ESquareShapeUseCase(BuildContext context) {
|
||||
return _buildFab(context,
|
||||
kind: FabM3EKind.primary, shape: FabM3EShapeFamily.square);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'compact_density', type: FabM3E)
|
||||
Widget buildFabM3ECompactDensityUseCase(BuildContext context) {
|
||||
return _buildFab(context,
|
||||
kind: FabM3EKind.primary, density: FabM3EDensity.compact);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'focused', type: FabM3E)
|
||||
Widget buildFabM3EFocusedUseCase(BuildContext context) {
|
||||
// Emphasize focus by enabling autofocus
|
||||
return _buildFab(context, kind: FabM3EKind.primary, autofocus: true);
|
||||
}
|
||||
144
widgetbook/lib/fab_m3e/fab_menu_m3e_usecases.dart
Normal file
144
widgetbook/lib/fab_m3e/fab_menu_m3e_usecases.dart
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:widgetbook/widgetbook.dart';
|
||||
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
|
||||
import 'package:fab_m3e/fab_m3e.dart';
|
||||
|
||||
FabMenuM3E _buildMenu(
|
||||
BuildContext context, {
|
||||
required List<FabMenuItem> items,
|
||||
FabMenuDirection direction = FabMenuDirection.up,
|
||||
bool overlay = true,
|
||||
double? spacing,
|
||||
Alignment alignment = Alignment.bottomRight,
|
||||
bool popOnItemTap = true,
|
||||
Object? heroTag,
|
||||
bool initiallyOpen = false,
|
||||
}) {
|
||||
final selectedDirection = context.knobs.object.dropdown<FabMenuDirection>(
|
||||
label: 'direction',
|
||||
initialOption: direction,
|
||||
options: FabMenuDirection.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final showOverlay = context.knobs.boolean(label: 'overlay', initialValue: overlay);
|
||||
final gap = context.knobs.doubleOrNull.slider(
|
||||
label: 'spacing',
|
||||
initialValue: spacing,
|
||||
min: 0,
|
||||
max: 48,
|
||||
divisions: 24,
|
||||
);
|
||||
final alignOpt = context.knobs.object.dropdown<String>(
|
||||
label: 'alignment',
|
||||
initialOption: 'bottomRight',
|
||||
options: const ['bottomRight', 'bottomLeft', 'topRight', 'topLeft', 'center'],
|
||||
labelBuilder: (v) => v,
|
||||
);
|
||||
final Alignment align = switch (alignOpt) {
|
||||
'bottomLeft' => Alignment.bottomLeft,
|
||||
'topRight' => Alignment.topRight,
|
||||
'topLeft' => Alignment.topLeft,
|
||||
'center' => Alignment.center,
|
||||
_ => Alignment.bottomRight,
|
||||
};
|
||||
final popOnTap = context.knobs.boolean(label: 'popOnItemTap', initialValue: popOnItemTap);
|
||||
final useHero = context.knobs.boolean(label: 'wrap in Hero', initialValue: heroTag != null);
|
||||
|
||||
final controller = FabMenuController();
|
||||
if (initiallyOpen) controller.open();
|
||||
|
||||
return FabMenuM3E(
|
||||
primaryFab: FabM3E(
|
||||
icon: AnimatedRotation(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
turns: controller.isOpen ? 0.125 : 0,
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
onPressed: controller.toggle,
|
||||
heroTag: useHero ? (heroTag ?? 'fab-menu-hero') : null,
|
||||
),
|
||||
items: items,
|
||||
direction: selectedDirection,
|
||||
overlay: showOverlay,
|
||||
spacing: gap,
|
||||
controller: controller,
|
||||
alignment: align,
|
||||
popOnItemTap: popOnTap,
|
||||
);
|
||||
}
|
||||
|
||||
List<FabMenuItem> _makeItems(BuildContext context, int count) {
|
||||
return List.generate(count, (i) {
|
||||
return FabMenuItem(
|
||||
icon: Icon([Icons.share, Icons.edit, Icons.delete, Icons.copy][i % 4]),
|
||||
label: Text('Action ${i + 1}'),
|
||||
onPressed: () => print('Menu item ${i + 1} pressed'),
|
||||
semanticLabel: 'Menu action ${i + 1}',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'default', type: FabMenuM3E)
|
||||
Widget buildFabMenuM3EUseCase(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Container(),
|
||||
),
|
||||
_buildMenu(
|
||||
context,
|
||||
items: _makeItems(context, 3),
|
||||
direction: FabMenuDirection.up,
|
||||
overlay: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'initially_open', type: FabMenuM3E)
|
||||
Widget buildFabMenuM3EInitiallyOpenUseCase(BuildContext context) {
|
||||
return _buildMenu(
|
||||
context,
|
||||
items: _makeItems(context, 3),
|
||||
direction: FabMenuDirection.up,
|
||||
overlay: true,
|
||||
initiallyOpen: true,
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'direction_down', type: FabMenuM3E)
|
||||
Widget buildFabMenuM3EDirectionDownUseCase(BuildContext context) {
|
||||
return _buildMenu(context, items: _makeItems(context, 3), direction: FabMenuDirection.down);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'direction_left', type: FabMenuM3E)
|
||||
Widget buildFabMenuM3EDirectionLeftUseCase(BuildContext context) {
|
||||
return _buildMenu(context, items: _makeItems(context, 3), direction: FabMenuDirection.left);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'direction_right', type: FabMenuM3E)
|
||||
Widget buildFabMenuM3EDirectionRightUseCase(BuildContext context) {
|
||||
return _buildMenu(context, items: _makeItems(context, 3), direction: FabMenuDirection.right);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'overlay_off', type: FabMenuM3E)
|
||||
Widget buildFabMenuM3EOverlayOffUseCase(BuildContext context) {
|
||||
return _buildMenu(context, items: _makeItems(context, 3), overlay: false);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'spacing_tight', type: FabMenuM3E)
|
||||
Widget buildFabMenuM3ESpacingTightUseCase(BuildContext context) {
|
||||
return _buildMenu(context, items: _makeItems(context, 3), spacing: 4);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'many_items', type: FabMenuM3E)
|
||||
Widget buildFabMenuM3EManyItemsUseCase(BuildContext context) {
|
||||
return _buildMenu(context, items: _makeItems(context, 8));
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'empty_items', type: FabMenuM3E)
|
||||
Widget buildFabMenuM3EEmptyItemsUseCase(BuildContext context) {
|
||||
return _buildMenu(context, items: const []);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue