Skip to content

Commit

Permalink
refactor(widgetbook): ♻️ AddOn API
Browse files Browse the repository at this point in the history
fix stuff
  • Loading branch information
jenshor committed Feb 10, 2023
1 parent ae30cd8 commit 31734f9
Show file tree
Hide file tree
Showing 71 changed files with 1,327 additions and 2,293 deletions.
101 changes: 76 additions & 25 deletions packages/widgetbook/lib/src/addons/addon.dart
Original file line number Diff line number Diff line change
@@ -1,37 +1,88 @@
import 'package:flutter/widgets.dart';
import 'package:nested/nested.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:widgetbook/src/navigation/router.dart';
import 'package:widgetbook/widgetbook.dart';

class WidgetbookAddOn {
const WidgetbookAddOn({
abstract class WidgetbookAddOnModel {
const WidgetbookAddOnModel();

/// Required to allow proper deep linking including AddOn property selection
///
/// Defaults to an empty Map, which means no query parameters are set for the
/// route
Map<String, String> toQueryParameter() {
return {};
}
}

/// A class that can be used to extend the selection of Widgetbook properties.
///
/// See also:
///
/// * [ThemeAddon], a generic implementation of a [WidgetbookAddOn].
/// * [MaterialThemeAddon], an [WidgetbookAddOn] to change the active
/// [ThemeData] of the [WidgetbookUseCase].
/// * [FrameAddon], an [WidgetbookAddOn] to change the active [Frame] that
/// allows to view the [WidgetbookUseCase] on different screens.
///
/// You must not have multiple [WidgetbookAddOn]s that are of the same generic
/// type
abstract class WidgetbookAddOn<T extends WidgetbookAddOnModel> {
WidgetbookAddOn({
required this.name,
required this.wrapperBuilder,
required this.builder,
required this.providerBuilder,
required this.getQueryParameter,
});
required this.setting,
}) : provider = ValueNotifier<T>(setting);

final String name;
final T setting;
late ValueNotifier<T> provider;

final Widget Function(
BuildContext context,
Map<String, dynamic> routerData,
Widget child,
) wrapperBuilder;
/// Allows for parsing of [queryParameters] by using information from the
/// router and from the initially provided [setting].
///
/// If no [queryParameters] are available, return [setting].
/// If [queryParameters] are avaialbe return a propert `Setting` object.
///
/// If not overriden, returns the initially provided [setting].
T settingFromQueryParameters({
required Map<String, String> queryParameters,
required T setting,
}) {
return setting;
}

final Widget Function(
BuildContext context,
) builder;
T get value => provider.value;

void onChanged(BuildContext context, T value) {
provider.value = value;
context.goTo(queryParams: value.toQueryParameter());
}

final SingleChildWidget Function(
Widget buildProvider(
BuildContext context,
) providerBuilder;
Map<String, String> queryParameters,
Widget child,
) {
final initialData = settingFromQueryParameters(
queryParameters: queryParameters,
setting: setting,
);
provider = ValueNotifier<T>(initialData);

final Map<String, String> Function(BuildContext context) getQueryParameter;
return ChangeNotifierProvider.value(
key: ValueKey(initialData),
value: provider,
child: child,
);
}

@override
bool operator ==(Object other) =>
other is WidgetbookAddOn && name == other.name;
Widget build(
BuildContext context,
);
}

@override
int get hashCode => name.hashCode;
extension AddonExtension on BuildContext {
T? getAddonValue<T extends WidgetbookAddOnModel>() {
return read<ValueNotifier<T>?>()?.value;
}
}
22 changes: 11 additions & 11 deletions packages/widgetbook/lib/src/addons/addon_injector_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@ class AddonInjectorWidget extends StatelessWidget {

final List<WidgetbookAddOn> addons;
final Widget child;
final Map<String, dynamic> routerData;
final Map<String, String> routerData;

@override
Widget build(BuildContext context) {
return Nested(
key: ValueKey(routerData),
children: addons
.map(
(e) => SingleChildBuilder(
builder: (context, child) => e.wrapperBuilder(
context,
routerData,
child!,
),
children: [
...addons.map(
(e) => SingleChildBuilder(
builder: (context, child) => e.buildProvider(
context,
routerData,
child!,
),
)
.toList(),
),
),
],
child: child,
);
}
Expand Down
4 changes: 0 additions & 4 deletions packages/widgetbook/lib/src/addons/addon_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,4 @@ import 'package:widgetbook/src/addons/addon.dart';

class AddOnProvider extends ValueNotifier<List<WidgetbookAddOn>> {
AddOnProvider(super.value);

void update() {
notifyListeners();
}
}
2 changes: 1 addition & 1 deletion packages/widgetbook/lib/src/addons/addons.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:widgetbook/src/addons/addon.dart';
import 'package:widgetbook/widgetbook.dart';

export './addon.dart';
export './cupertino_theme_addon/addon.dart';
export './cupertino_theme_addon/cupertino_theme_addon.dart';
export './custom_theme_addon/addon.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
import 'package:widgetbook/src/addons/theme_addon/theme_provider.dart';
import 'package:widgetbook/widgetbook.dart';

class CustomThemeAddon<T> extends ThemeAddon<T> {
Expand All @@ -10,5 +8,5 @@ class CustomThemeAddon<T> extends ThemeAddon<T> {
}

extension CustomThemeExtension on BuildContext {
T? theme<T>() => watch<ThemeProvider<T>?>()?.value.data;
T? theme<T>() => getAddonValue<ThemeSetting<T>>()?.activeTheme.data;
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export './device_addon/device_addon.dart';
export './no_frame_addon/no_frame_addon.dart';
export 'device/device_addon.dart';
export 'no_frame/no_frame_addon.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import 'package:flutter/material.dart';
import 'package:widgetbook/src/navigation/router.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_core/widgetbook_core.dart';

export './device_setting.dart';

class DeviceAddon extends WidgetbookAddOn<DeviceSetting> {
DeviceAddon({
required super.setting,
}) : super(
name: 'Device',
);

@override
DeviceSetting settingFromQueryParameters({
required Map<String, String> queryParameters,
required DeviceSetting setting,
}) {
final activeDevice = parseQueryParameters(
name: 'device',
queryParameters: queryParameters,
mappedData: {for (var e in setting.devices) e.name: e},
) ??
setting.activeDevice;
final activeOrientation = parseQueryParameters(
name: 'orientation',
queryParameters: queryParameters,
mappedData: {
Orientation.portrait.name: Orientation.portrait,
Orientation.landscape.name: Orientation.landscape,
},
) ??
setting.orientation;

return setting.copyWith(
activeDevice: activeDevice,
orientation: activeOrientation,
);
}

@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
SubSetting(
name: '$Device',
child: DropdownSetting<Device>(
options: value.devices,
optionValueBuilder: (device) => device.name,
initialSelection: value.activeDevice,
onSelected: (newActiveDevice) {
onChanged(context, value.copyWith(activeDevice: newActiveDevice));
},
),
),
SubSetting(
name: '$Orientation',
child: DropdownSetting<Orientation>(
options: const [
Orientation.portrait,
Orientation.landscape,
],
optionValueBuilder: (orientation) => orientation.name,
initialSelection: value.orientation,
onSelected: (orientation) {
onChanged(context, value.copyWith(orientation: orientation));
},
),
),
],
);
}
}

extension DeviceExtension on BuildContext {
Device get device => getAddonValue<DeviceSetting>()!.activeDevice;
}

extension OrientationExtension on BuildContext {
Orientation get orientation => getAddonValue<DeviceSetting>()!.orientation;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import 'package:widgetbook/widgetbook.dart';
part 'device_setting.freezed.dart';

@freezed
class DeviceSetting with _$DeviceSetting {
class DeviceSetting extends WidgetbookAddOnModel with _$DeviceSetting {
factory DeviceSetting({
required List<Device> devices,
required Device activeDevice,
Expand All @@ -24,4 +24,14 @@ class DeviceSetting with _$DeviceSetting {
devices: devices,
);
}

const DeviceSetting._();

@override
Map<String, String> toQueryParameter() {
return {
'orientation': orientation.name,
'device': activeDevice.name,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,13 @@ class __$$_DeviceSettingCopyWithImpl<$Res>

/// @nodoc
class _$_DeviceSetting implements _DeviceSetting {
class _$_DeviceSetting extends _DeviceSetting {
_$_DeviceSetting(
{required final List<Device> devices,
required this.activeDevice,
this.orientation = Orientation.portrait})
: _devices = devices;
: _devices = devices,
super._();

final List<Device> _devices;
@override
Expand Down Expand Up @@ -177,11 +178,12 @@ class _$_DeviceSetting implements _DeviceSetting {
__$$_DeviceSettingCopyWithImpl<_$_DeviceSetting>(this, _$identity);
}

abstract class _DeviceSetting implements DeviceSetting {
abstract class _DeviceSetting extends DeviceSetting {
factory _DeviceSetting(
{required final List<Device> devices,
required final Device activeDevice,
final Orientation orientation}) = _$_DeviceSetting;
_DeviceSetting._() : super._();

@override
List<Device> get devices;
Expand Down
Loading

0 comments on commit 31734f9

Please sign in to comment.