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
27
widgetbook/lib/slider_m3e/INDEX-slider_m3e.md
Normal file
27
widgetbook/lib/slider_m3e/INDEX-slider_m3e.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# INDEX — slider_m3e
|
||||
|
||||
This index lists all Widgetbook use cases generated for the slider_m3e package.
|
||||
|
||||
## Components and Variants
|
||||
|
||||
- SliderM3E
|
||||
- default
|
||||
- discrete
|
||||
- disabled
|
||||
- extremes_0_to_100
|
||||
- negative_range
|
||||
- min_equals_max
|
||||
|
||||
- RangeSliderM3E
|
||||
- default
|
||||
- discrete
|
||||
- disabled
|
||||
- extremes_0_to_100
|
||||
- negative_range
|
||||
- min_equals_max
|
||||
|
||||
Notes
|
||||
- All variants follow plan/guide.md rules with @UseCase annotations and required method signatures.
|
||||
- Knobs are provided for critical parameters (values, min, max, divisions) and key visuals (size, emphasis, shapeFamily, density, value indicator).
|
||||
- Callbacks print helpful messages to the console.
|
||||
- Range labels are included where useful; complex configurations are kept simple with TODOs where appropriate.
|
||||
191
widgetbook/lib/slider_m3e/range_slider_m3e_usecases.dart
Normal file
191
widgetbook/lib/slider_m3e/range_slider_m3e_usecases.dart
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:slider_m3e/slider_m3e.dart';
|
||||
import 'package:widgetbook/widgetbook.dart';
|
||||
import 'package:widgetbook_annotation/widgetbook_annotation.dart';
|
||||
|
||||
Widget _buildRangeSliderM3EDemo(
|
||||
BuildContext context, {
|
||||
double? forcedMin,
|
||||
double? forcedMax,
|
||||
int? forcedDivisions,
|
||||
bool disabled = false,
|
||||
bool zeroRange = false,
|
||||
}) {
|
||||
// Critical params via knobs
|
||||
final minKnob = forcedMin ??
|
||||
context.knobs.double.slider(
|
||||
label: 'min',
|
||||
initialValue: 0,
|
||||
min: -100,
|
||||
max: 100,
|
||||
divisions: 40,
|
||||
);
|
||||
final maxKnob = forcedMax ??
|
||||
context.knobs.double.slider(
|
||||
label: 'max',
|
||||
initialValue: 100,
|
||||
min: -100,
|
||||
max: 200,
|
||||
divisions: 60,
|
||||
);
|
||||
|
||||
double min = minKnob;
|
||||
double max = maxKnob;
|
||||
|
||||
// Force min == max when zeroRange is requested
|
||||
if (zeroRange) {
|
||||
final fixed = context.knobs.double.slider(
|
||||
label: 'fixed value (min==max)',
|
||||
initialValue: 25,
|
||||
min: -100,
|
||||
max: 100,
|
||||
divisions: 40,
|
||||
);
|
||||
min = fixed;
|
||||
max = fixed;
|
||||
}
|
||||
|
||||
if (min >= max) {
|
||||
// Ensure valid range
|
||||
max = min + 1;
|
||||
}
|
||||
|
||||
// Pick start/end values within [min, max]
|
||||
final start = context.knobs.double.slider(
|
||||
label: 'start',
|
||||
initialValue: min + (max - min) * 0.25,
|
||||
min: min,
|
||||
max: max,
|
||||
divisions: 100,
|
||||
);
|
||||
final end = context.knobs.double.slider(
|
||||
label: 'end',
|
||||
initialValue: min + (max - min) * 0.75,
|
||||
min: min,
|
||||
max: max,
|
||||
divisions: 100,
|
||||
);
|
||||
|
||||
final divisions = forcedDivisions ??
|
||||
context.knobs.intOrNull.slider(
|
||||
label: 'divisions',
|
||||
initialValue: null,
|
||||
min: 1,
|
||||
max: 20,
|
||||
divisions: 19,
|
||||
);
|
||||
|
||||
// Visual params via knobs
|
||||
final size = context.knobs.object.dropdown<SliderM3ESize>(
|
||||
label: 'size',
|
||||
initialOption: SliderM3ESize.medium,
|
||||
options: SliderM3ESize.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final emphasis = context.knobs.object.dropdown<SliderM3EEmphasis>(
|
||||
label: 'emphasis',
|
||||
initialOption: SliderM3EEmphasis.primary,
|
||||
options: SliderM3EEmphasis.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final shape = context.knobs.object.dropdown<SliderM3EShapeFamily>(
|
||||
label: 'shapeFamily',
|
||||
initialOption: SliderM3EShapeFamily.round,
|
||||
options: SliderM3EShapeFamily.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final density = context.knobs.object.dropdown<SliderM3EDensity>(
|
||||
label: 'density',
|
||||
initialOption: SliderM3EDensity.regular,
|
||||
options: SliderM3EDensity.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
|
||||
final showValueIndicator = context.knobs.boolean(
|
||||
label: 'showValueIndicator',
|
||||
initialValue: false,
|
||||
);
|
||||
|
||||
// Labels & semantics
|
||||
final hasLabels = context.knobs.boolean(label: 'use RangeLabels', initialValue: false);
|
||||
final startLabel = hasLabels
|
||||
? context.knobs.string(label: 'start label', initialValue: 'Start')
|
||||
: null;
|
||||
final endLabel = hasLabels
|
||||
? context.knobs.string(label: 'end label', initialValue: 'End')
|
||||
: null;
|
||||
final labels = hasLabels && startLabel != null && endLabel != null
|
||||
? RangeLabels(startLabel, endLabel)
|
||||
: null;
|
||||
|
||||
final semanticLabel = zeroRange
|
||||
? null
|
||||
: context.knobs.stringOrNull(
|
||||
label: 'semanticLabel',
|
||||
initialValue: 'Progress',
|
||||
);
|
||||
|
||||
final onChanged = disabled
|
||||
? null
|
||||
: (RangeValues v) => print('RangeSliderM3E onChanged -> ${v.start} - ${v.end}');
|
||||
final onChangeStart = disabled
|
||||
? null
|
||||
: (RangeValues v) => print('RangeSliderM3E onChangeStart -> ${v.start} - ${v.end}');
|
||||
final onChangeEnd = disabled
|
||||
? null
|
||||
: (RangeValues v) => print('RangeSliderM3E onChangeEnd -> ${v.start} - ${v.end}');
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: RangeSliderM3E(
|
||||
values: RangeValues(
|
||||
start.clamp(min, max),
|
||||
end.clamp(min, max),
|
||||
),
|
||||
onChanged: onChanged,
|
||||
onChangeStart: onChangeStart,
|
||||
onChangeEnd: onChangeEnd,
|
||||
min: min,
|
||||
max: max,
|
||||
divisions: divisions,
|
||||
labels: labels,
|
||||
semanticLabel: semanticLabel,
|
||||
size: size,
|
||||
emphasis: emphasis,
|
||||
shapeFamily: shape,
|
||||
density: density,
|
||||
showValueIndicator: showValueIndicator,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@UseCase(name: 'default', type: RangeSliderM3E)
|
||||
Widget buildRangeSliderM3EDefaultUseCase(BuildContext context) {
|
||||
return _buildRangeSliderM3EDemo(context);
|
||||
}
|
||||
|
||||
@UseCase(name: 'discrete', type: RangeSliderM3E)
|
||||
Widget buildRangeSliderM3EDiscreteUseCase(BuildContext context) {
|
||||
return _buildRangeSliderM3EDemo(context, forcedDivisions: 6);
|
||||
}
|
||||
|
||||
@UseCase(name: 'disabled', type: RangeSliderM3E)
|
||||
Widget buildRangeSliderM3EDisabledUseCase(BuildContext context) {
|
||||
return _buildRangeSliderM3EDemo(context, disabled: true);
|
||||
}
|
||||
|
||||
@UseCase(name: 'extremes_0_to_100', type: RangeSliderM3E)
|
||||
Widget buildRangeSliderM3EExtremes0100UseCase(BuildContext context) {
|
||||
return _buildRangeSliderM3EDemo(context, forcedMin: 0, forcedMax: 100);
|
||||
}
|
||||
|
||||
@UseCase(name: 'negative_range', type: RangeSliderM3E)
|
||||
Widget buildRangeSliderM3ENegativeRangeUseCase(BuildContext context) {
|
||||
return _buildRangeSliderM3EDemo(context, forcedMin: -100, forcedMax: 0);
|
||||
}
|
||||
|
||||
@UseCase(name: 'min_equals_max', type: RangeSliderM3E)
|
||||
Widget buildRangeSliderM3EMinEqualsMaxUseCase(BuildContext context) {
|
||||
// Zero-range; interactions disabled to avoid semantic division by zero
|
||||
return _buildRangeSliderM3EDemo(context, zeroRange: true, disabled: true);
|
||||
}
|
||||
186
widgetbook/lib/slider_m3e/slider_m3e_usecases.dart
Normal file
186
widgetbook/lib/slider_m3e/slider_m3e_usecases.dart
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:slider_m3e/slider_m3e.dart';
|
||||
import 'package:widgetbook/widgetbook.dart';
|
||||
import 'package:widgetbook_annotation/widgetbook_annotation.dart';
|
||||
|
||||
Widget _buildSliderM3EDemo(
|
||||
BuildContext context, {
|
||||
double? forcedMin,
|
||||
double? forcedMax,
|
||||
int? forcedDivisions,
|
||||
bool disabled = false,
|
||||
bool zeroRange = false,
|
||||
}) {
|
||||
// Critical params via knobs
|
||||
final minKnob = forcedMin ??
|
||||
context.knobs.double.slider(
|
||||
label: 'min',
|
||||
initialValue: 0,
|
||||
min: -100,
|
||||
max: 100,
|
||||
divisions: 40,
|
||||
);
|
||||
final maxKnob = forcedMax ??
|
||||
context.knobs.double.slider(
|
||||
label: 'max',
|
||||
initialValue: 100,
|
||||
min: -100,
|
||||
max: 200,
|
||||
divisions: 60,
|
||||
);
|
||||
|
||||
double min = minKnob;
|
||||
double max = maxKnob;
|
||||
|
||||
// Special handling when zeroRange is requested
|
||||
if (zeroRange) {
|
||||
// Force min == max and disable interactions to avoid semantic division by zero
|
||||
final fixed = context.knobs.double.slider(
|
||||
label: 'fixed value (min==max)',
|
||||
initialValue: 50,
|
||||
min: -100,
|
||||
max: 100,
|
||||
divisions: 40,
|
||||
);
|
||||
min = fixed;
|
||||
max = fixed;
|
||||
}
|
||||
|
||||
if (min >= max) {
|
||||
// Guard against invalid ranges; ensure there is at least a small span
|
||||
max = min + 1;
|
||||
}
|
||||
|
||||
final value = context.knobs.double.slider(
|
||||
label: 'value',
|
||||
initialValue: (min + max) / 2,
|
||||
min: min,
|
||||
max: max,
|
||||
divisions: 100,
|
||||
);
|
||||
|
||||
final divisions = forcedDivisions ??
|
||||
context.knobs.intOrNull.slider(
|
||||
label: 'divisions',
|
||||
initialValue: null,
|
||||
min: 1,
|
||||
max: 20,
|
||||
divisions: 19,
|
||||
);
|
||||
|
||||
// Visual params via knobs
|
||||
final size = context.knobs.object.dropdown<SliderM3ESize>(
|
||||
label: 'size',
|
||||
initialOption: SliderM3ESize.medium,
|
||||
options: SliderM3ESize.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final emphasis = context.knobs.object.dropdown<SliderM3EEmphasis>(
|
||||
label: 'emphasis',
|
||||
initialOption: SliderM3EEmphasis.primary,
|
||||
options: SliderM3EEmphasis.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final shape = context.knobs.object.dropdown<SliderM3EShapeFamily>(
|
||||
label: 'shapeFamily',
|
||||
initialOption: SliderM3EShapeFamily.round,
|
||||
options: SliderM3EShapeFamily.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
final density = context.knobs.object.dropdown<SliderM3EDensity>(
|
||||
label: 'density',
|
||||
initialOption: SliderM3EDensity.regular,
|
||||
options: SliderM3EDensity.values,
|
||||
labelBuilder: (v) => v.name,
|
||||
);
|
||||
|
||||
final showValueIndicator = context.knobs.boolean(
|
||||
label: 'showValueIndicator',
|
||||
initialValue: false,
|
||||
);
|
||||
final withStartIcon = context.knobs.boolean(
|
||||
label: 'startIcon',
|
||||
initialValue: false,
|
||||
);
|
||||
final withEndIcon = context.knobs.boolean(
|
||||
label: 'endIcon',
|
||||
initialValue: false,
|
||||
);
|
||||
|
||||
// Content params
|
||||
final label = context.knobs.stringOrNull(
|
||||
label: 'label',
|
||||
initialValue: null,
|
||||
);
|
||||
// Avoid semantic callback when zeroRange to prevent divide-by-zero in tokens formatting
|
||||
final semanticLabel = zeroRange
|
||||
? null
|
||||
: context.knobs.stringOrNull(
|
||||
label: 'semanticLabel',
|
||||
initialValue: 'Progress',
|
||||
);
|
||||
|
||||
final onChanged = disabled
|
||||
? null
|
||||
: (double v) => print('SliderM3E onChanged -> $v');
|
||||
final onChangeStart = disabled
|
||||
? null
|
||||
: (double v) => print('SliderM3E onChangeStart -> $v');
|
||||
final onChangeEnd = disabled
|
||||
? null
|
||||
: (double v) => print('SliderM3E onChangeEnd -> $v');
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: SliderM3E(
|
||||
value: value,
|
||||
onChanged: onChanged,
|
||||
onChangeStart: onChangeStart,
|
||||
onChangeEnd: onChangeEnd,
|
||||
min: min,
|
||||
max: max,
|
||||
divisions: divisions,
|
||||
label: label,
|
||||
semanticLabel: semanticLabel,
|
||||
size: size,
|
||||
emphasis: emphasis,
|
||||
shapeFamily: shape,
|
||||
density: density,
|
||||
showValueIndicator: showValueIndicator,
|
||||
startIcon: withStartIcon ? const Icon(Icons.remove) : null,
|
||||
endIcon: withEndIcon ? const Icon(Icons.add) : null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@UseCase(name: 'default', type: SliderM3E)
|
||||
Widget buildSliderM3EDefaultUseCase(BuildContext context) {
|
||||
return _buildSliderM3EDemo(context);
|
||||
}
|
||||
|
||||
@UseCase(name: 'discrete', type: SliderM3E)
|
||||
Widget buildSliderM3EDiscreteUseCase(BuildContext context) {
|
||||
// Provide a default divisions count; still knob-customizable via 'divisions'
|
||||
return _buildSliderM3EDemo(context, forcedDivisions: 5);
|
||||
}
|
||||
|
||||
@UseCase(name: 'disabled', type: SliderM3E)
|
||||
Widget buildSliderM3EDisabledUseCase(BuildContext context) {
|
||||
return _buildSliderM3EDemo(context, disabled: true);
|
||||
}
|
||||
|
||||
@UseCase(name: 'extremes_0_to_100', type: SliderM3E)
|
||||
Widget buildSliderM3EExtremes0100UseCase(BuildContext context) {
|
||||
return _buildSliderM3EDemo(context, forcedMin: 0, forcedMax: 100);
|
||||
}
|
||||
|
||||
@UseCase(name: 'negative_range', type: SliderM3E)
|
||||
Widget buildSliderM3ENegativeRangeUseCase(BuildContext context) {
|
||||
return _buildSliderM3EDemo(context, forcedMin: -100, forcedMax: 0);
|
||||
}
|
||||
|
||||
@UseCase(name: 'min_equals_max', type: SliderM3E)
|
||||
Widget buildSliderM3EMinEqualsMaxUseCase(BuildContext context) {
|
||||
// Show a zero-range slider (min == max), interactions disabled to avoid semantic math
|
||||
return _buildSliderM3EDemo(context, zeroRange: true, disabled: true);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue