Skip to content

Commit e4c233e

Browse files
committed
refactor: Clean up and documentation
1 parent 0d45b40 commit e4c233e

File tree

9 files changed

+83
-83
lines changed

9 files changed

+83
-83
lines changed

meta/preview.png

16.2 KB
Loading

packages/storybook_flutter/README.md

Lines changed: 21 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
A cross-platform storybook for showcasing widgets. It should work in all platforms supported by Flutter.
66

77
- [Demo version](https://ookami-kb.github.io/storybook_flutter/)
8-
- [Documentation](https://ookamikb.gitbook.io/flutter-storybook/)
98

109
![](https://github.com/ookami-kb/storybook_flutter/raw/master/meta/preview.png)
1110

@@ -16,42 +15,19 @@ class MyApp extends StatelessWidget {
1615
@override
1716
Widget build(BuildContext context) =>
1817
Storybook(
19-
children: [
18+
stories: [
2019
Story(
21-
name: 'Flat button',
22-
padding: EdgeInsets.all(5), // optional padding customization
23-
background: Colors.red, // optional background color customization
24-
builder: (_, k) =>
25-
MaterialButton(
26-
onPressed: k.boolean(label: 'Enabled', initial: true) ? () {} : null,
27-
child: Text(k.text(label: 'Text', initial: 'Flat button')),
28-
),
20+
name: 'Screens/Counter',
21+
description: 'Demo Counter app with about dialog.',
22+
builder: (context) => CounterPage(
23+
title: context.knobs.text(label: 'Title', initial: 'Counter'),
24+
enabled: context.knobs.boolean(label: 'Enabled', initial: true),
25+
),
2926
),
3027
Story(
31-
name: 'Raised button',
32-
builder: (_, k) =>
33-
RaisedButton(
34-
onPressed: k.boolean(label: 'Enabled', initial: true) ? () {} : null,
35-
color: k.options(
36-
label: 'Color',
37-
initial: Colors.deepOrange,
38-
options: const [
39-
Option('Red', Colors.deepOrange),
40-
Option('Green', Colors.teal),
41-
],
42-
),
43-
textColor: Colors.white,
44-
child: Text(k.text(label: 'Text', initial: 'Raised button')),
45-
),
46-
),
47-
Story.simple(
48-
name: 'Input field',
49-
child: const TextField(
50-
decoration: InputDecoration(
51-
border: OutlineInputBorder(),
52-
labelText: 'Input field',
53-
),
54-
),
28+
name: 'Widgets/Text',
29+
description: 'Simple text widget.',
30+
builder: (context) => const Center(child: Text('Simple text')),
5531
),
5632
],
5733
);
@@ -60,30 +36,21 @@ class MyApp extends StatelessWidget {
6036

6137
## Customization
6238

63-
By default, each story is wrapped into:
64-
65-
```
66-
Container(
67-
color: story.background,
68-
padding: story.padding,
69-
child: Center(child: child),
70-
)
71-
```
72-
73-
You can override this behavior by providing either `wrapperBuilder` to the `Story` or `storyWrapperBuilder` to
74-
the `Storybook`. In the latest case this wrapper will be applied to each story (of course, you can still override this
75-
behavior by providing another `wrapperBuilder` to individual stories).
39+
By default, each story is wrapped into `MaterialApp`.
7640

77-
## CustomStorybook
41+
You can override this behavior by providing either `wrapperBuilder` to the
42+
`Storybook`. You can either use one of the default ones: `materialWrapper` or
43+
`cupertinoWrapper`, or provide a fully custom wrapper. In the latest case,
44+
make sure to use `child` widget that will contain the story.
7845

79-
If you need even more customization, you can use `CustomStorybook`. You have to provide `builder` parameter to it where
80-
you can define the custom layout. In this case you're responsible for rendering the story, contents and knobs panel.
46+
## Plugins
8147

82-
You can use `CurrentStory`, `Contents` and `KnobPanel` widgets that will render the corresponding data automatically.
48+
Almost all the functionality is provided by plugins, even contents and
49+
knobs are plugins (although, they are first-party plugins).
8350

84-
As an example of full customization, take a look
85-
at [storybook_device_preview](https://pub.dev/packages/storybook_device_preview) package that allows to embed storybook
86-
into [device_preview](https://pub.dev/packages/device_preview) package with knobs and contents rendered as plugins.
51+
Plugins documentation is TBD, but you can take a look at the existing
52+
first-party plugins: `ContentsPlugin`, `DeviceFramePlugin`, `KnobsPlugin`,
53+
`ThemModePlugin`.
8754

8855
## Features and bugs
8956

packages/storybook_flutter/lib/src/plugins/contents.dart

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import '../story.dart';
66
import '../storybook.dart';
77
import 'plugin.dart';
88

9+
/// Plugin that adds content as expandable list of stories.
10+
///
11+
/// If [sidePanel] is true, the stories are shown in a left side panel,
12+
/// otherwise as a popup.
913
class ContentsPlugin extends Plugin {
1014
const ContentsPlugin({bool sidePanel = false})
1115
: super(
@@ -17,7 +21,7 @@ class ContentsPlugin extends Plugin {
1721

1822
Widget _buildIcon(BuildContext _) => const Icon(Icons.list);
1923

20-
Widget _buildPanel(BuildContext context) => const Contents();
24+
Widget _buildPanel(BuildContext context) => const _Contents();
2125

2226
Widget _buildWrapper(BuildContext context, Widget? child) => Directionality(
2327
textDirection: TextDirection.ltr,
@@ -30,7 +34,7 @@ Widget _buildWrapper(BuildContext context, Widget? child) => Directionality(
3034
right: BorderSide(color: Colors.black12),
3135
),
3236
),
33-
child: const SizedBox(width: 250, child: Contents()),
37+
child: const SizedBox(width: 250, child: _Contents()),
3438
),
3539
),
3640
Expanded(
@@ -40,27 +44,8 @@ Widget _buildWrapper(BuildContext context, Widget? child) => Directionality(
4044
),
4145
);
4246

43-
class Contents extends StatelessWidget {
44-
const Contents({
45-
Key? key,
46-
this.onStorySelected,
47-
}) : super(key: key);
48-
49-
final ValueSetter<Story>? onStorySelected;
50-
51-
@override
52-
Widget build(BuildContext context) => _Contents(
53-
onStorySelected: (story) {
54-
context.read<StoryNotifier>().value = story;
55-
onStorySelected?.call(story);
56-
},
57-
);
58-
}
59-
6047
class _Contents extends StatefulWidget {
61-
const _Contents({Key? key, required this.onStorySelected}) : super(key: key);
62-
63-
final ValueSetter<Story> onStorySelected;
48+
const _Contents({Key? key}) : super(key: key);
6449

6550
@override
6651
_ContentsState createState() => _ContentsState();
@@ -93,10 +78,7 @@ class _ContentsState extends State<_Contents> {
9378
selected: story == context.watch<StoryNotifier>().value,
9479
title: Text(story.title),
9580
subtitle: story.description == null ? null : Text(story.description!),
96-
onTap: () {
97-
final onStorySelected = widget.onStorySelected;
98-
onStorySelected(story);
99-
},
81+
onTap: () => context.read<StoryNotifier>().value = story,
10082
);
10183

10284
Widget _buildSection(String title, Iterable<Story> stories) => ExpansionTile(

packages/storybook_flutter/lib/src/plugins/device_frame.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'plugin.dart';
88

99
part 'device_frame.freezed.dart';
1010

11+
/// Plugin that allows wrapping each story into a device frame.
1112
class DeviceFramePlugin extends Plugin {
1213
DeviceFramePlugin({DeviceFrameData initialData = const DeviceFrameData()})
1314
: super(

packages/storybook_flutter/lib/src/plugins/knobs.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import '../knobs/string_knob.dart';
99
import '../story.dart';
1010
import 'plugin.dart';
1111

12+
/// Plugin that adds story customization knobs.
13+
///
14+
/// If [sidePanel] is true, the knobs will be displayed in the right side panel.
1215
class KnobsPlugin extends Plugin {
1316
KnobsPlugin({bool sidePanel = false})
1417
: super(

packages/storybook_flutter/lib/src/plugins/plugin.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export 'device_frame.dart';
1010
export 'knobs.dart';
1111
export 'theme_mode.dart';
1212

13+
/// Use this method to initialize and customize built-in plugins.
1314
List<Plugin> initializePlugins({
1415
bool enableContents = true,
1516
bool enableKnobs = true,
@@ -38,9 +39,28 @@ class Plugin {
3839
this.onPressed,
3940
});
4041

42+
/// Optional wrapper that will be inserted above the whole storybook content,
43+
/// including panel.
44+
///
45+
/// E.g. `ContentsPlugin` uses this builder to add side panel.
4146
final TransitionBuilder? wrapperBuilder;
47+
48+
/// Optional builder that will be used to display panel popup. It appears when
49+
/// user clicks on the [icon].
50+
///
51+
/// For it to be used, [icon] must be provided.
4252
final WidgetBuilder? panelBuilder;
53+
54+
/// Optional wrapper that will be inserted above each story.
55+
///
56+
/// E.g. `DeviceFramePlugin` uses this builder to display device frame.
4357
final TransitionBuilder? storyBuilder;
58+
59+
/// Optional icon that will be displayed on the bottom panel.
4460
final WidgetBuilder? icon;
61+
62+
/// Optional callback that will be called when user clicks on the [icon].
63+
///
64+
/// For it to be used, [icon] must be provided.
4565
final OnPluginButtonPressed? onPressed;
4666
}

packages/storybook_flutter/lib/src/plugins/theme_mode.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ Widget _buildWrapper(BuildContext context, Widget? child) =>
6262
),
6363
);
6464

65+
/// Use this notifier to get the current theme mode.
66+
///
67+
/// [ThemeModePlugin] should be added to plugins for this to work.
6568
class ThemeModeNotifier extends ValueNotifier<ThemeMode> {
6669
ThemeModeNotifier(ThemeMode value) : super(value);
6770
}

packages/storybook_flutter/lib/src/story.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,18 @@ class Story {
77
required this.builder,
88
});
99

10+
/// Unique name of the story.
11+
///
12+
/// Use `/` to group stories in sections, e.g. `Buttons/FlatButton`
13+
/// will create a section `Buttons` with a story `FlatButton` in it.
1014
final String name;
15+
16+
/// Optional description of the story.
17+
///
18+
/// It will be used in the contents as a secondary text.
1119
final String? description;
20+
21+
/// Story builder.
1222
final WidgetBuilder builder;
1323

1424
String get section {
@@ -29,6 +39,7 @@ class Story {
2939
}
3040
}
3141

42+
/// Use this notifier to get the current story.
3243
class StoryNotifier extends ValueNotifier<Story?> {
3344
StoryNotifier(Story? value) : super(value);
3445
}

packages/storybook_flutter/lib/src/storybook.dart

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:collection/collection.dart';
12
import 'package:flutter/cupertino.dart';
23
import 'package:flutter/material.dart';
34
import 'package:nested/nested.dart';
@@ -7,6 +8,7 @@ import 'plugins/plugin.dart';
78
import 'plugins/plugin_panel.dart';
89
import 'story.dart';
910

11+
/// Use this wrapper to wrap each story into a [MaterialApp] widget.
1012
Widget materialWrapper(BuildContext context, Widget? child) => MaterialApp(
1113
theme: ThemeData.light(),
1214
darkTheme: ThemeData.dark(),
@@ -15,6 +17,7 @@ Widget materialWrapper(BuildContext context, Widget? child) => MaterialApp(
1517
home: Scaffold(body: Center(child: child)),
1618
);
1719

20+
/// Use this wrapper to wrap each story into a [CupertinoApp] widget.
1821
Widget cupertinoWrapper(BuildContext context, Widget? child) => CupertinoApp(
1922
debugShowCheckedModeBanner: false,
2023
useInheritedMediaQuery: true,
@@ -26,18 +29,28 @@ final _defaultPlugins = initializePlugins();
2629
class Storybook extends StatefulWidget {
2730
Storybook({
2831
Key? key,
29-
required this.stories,
30-
List<Plugin>? plugins,
32+
required Iterable<Story> stories,
33+
Iterable<Plugin>? plugins,
3134
this.initialStory,
3235
this.wrapperBuilder = materialWrapper,
3336
this.showPanel = true,
34-
}) : plugins = plugins ?? _defaultPlugins,
37+
}) : plugins = UnmodifiableListView(plugins ?? _defaultPlugins),
38+
stories = UnmodifiableListView(stories),
3539
super(key: key);
3640

41+
/// All enabled plugins.
3742
final List<Plugin> plugins;
43+
44+
/// All available stories.
3845
final List<Story> stories;
46+
47+
/// Optional initial story.
3948
final String? initialStory;
49+
50+
/// Each story will be wrapped into a widget returned by this builder.
4051
final TransitionBuilder wrapperBuilder;
52+
53+
/// Whether to show the plugin panel at the bottom.
4154
final bool showPanel;
4255

4356
@override

0 commit comments

Comments
 (0)