feat(ui): fix, rename, rewrite some helpers

This commit is contained in:
Hugo Pointcheval 2023-04-26 18:14:00 +02:00
parent cef73aa62d
commit 3fb4020594
Signed by: hugo
GPG Key ID: 3AAC487E131E00BC
11 changed files with 169 additions and 96 deletions

View File

@ -16,6 +16,5 @@
export 'enums/enums.dart'; export 'enums/enums.dart';
export 'extensions/build_context_extensions.dart'; export 'extensions/build_context_extensions.dart';
export 'extensions/string_extension.dart';
export 'mixins/copy_with_mixin.dart'; export 'mixins/copy_with_mixin.dart';
export 'utils/utils.dart'; export 'utils/utils.dart';

View File

@ -1,25 +0,0 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/widgets.dart';
import 'package:wyatt_ui_components/wyatt_ui_components.dart';
extension StringExtension on String? {
TextWrapper? wrap({TextStyle? style, MultiColor? gradientColors}) =>
this != null
? TextWrapper(this!, style: style, gradientColors: gradientColors)
: null;
}

View File

@ -0,0 +1,38 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:wyatt_ui_components/wyatt_ui_components.dart';
abstract class GradientHelper {
static LinearGradient? linearFromNullableColors(List<Color>? colors) =>
colors != null ? LinearGradient(colors: colors) : null;
static LinearGradient? linearFromMultiColor(MultiColor multiColor) =>
multiColor.isGradient ? LinearGradient(colors: multiColor.colors) : null;
static RadialGradient? radialFromNullableColors(List<Color>? colors) =>
colors != null ? RadialGradient(colors: colors) : null;
static RadialGradient? radialFromMultiColor(MultiColor multiColor) =>
multiColor.isGradient ? RadialGradient(colors: multiColor.colors) : null;
static SweepGradient? sweepFromNullableColors(List<Color>? colors) =>
colors != null ? SweepGradient(colors: colors) : null;
static SweepGradient? sweepFromMultiColor(MultiColor multiColor) =>
multiColor.isGradient ? SweepGradient(colors: multiColor.colors) : null;
}

View File

@ -36,15 +36,28 @@ class TextWrapper {
}); });
/// Creates a [TextWrapper] from a [Text] widget. /// Creates a [TextWrapper] from a [Text] widget.
const TextWrapper.text(this.data) TextWrapper.text(Text text)
: style = null, : data = text.data!,
style = text.style,
gradientColors = null, gradientColors = null,
textAlign = null, textAlign = text.textAlign,
textDirection = null, textDirection = text.textDirection,
softWrap = null, softWrap = text.softWrap,
overflow = null, overflow = text.overflow,
maxLines = null, maxLines = text.maxLines,
selectionColor = null; selectionColor = text.selectionColor;
/// Creates a [TextWrapper] from a [RichText] widget.
TextWrapper.rich(RichText richText)
: data = richText.text.toPlainText(),
style = richText.text.style,
gradientColors = null,
textAlign = richText.textAlign,
textDirection = richText.textDirection,
softWrap = richText.softWrap,
overflow = richText.overflow,
maxLines = richText.maxLines,
selectionColor = richText.selectionColor;
/// Text to be displayed /// Text to be displayed
final String data; final String data;

View File

@ -16,33 +16,76 @@
/// A helper class for getting theme elements. /// A helper class for getting theme elements.
abstract class ThemeHelper { abstract class ThemeHelper {
/// Gets a theme element from a list of styles. /// Gets a nullable theme element from a list of styles.
/// {@template getElement}
/// Styles are checked in order, and the first one that passes the /// Styles are checked in order, and the first one that passes the
/// [valueValidator] is returned. /// [valueValidator] is returned.
/// Style elements are transformed using the [transform] function. /// Style elements are transformed using the [transform] function.
/// ///
/// [styles]: A list of styles that need to be checked. /// - [styles] : A list of styles that need to be checked.
/// [transform]: A function that transforms each style element ///
/// to a [T] type. /// - [transform] : An optional function that transforms each style element
/// [valueValidator]: An optional validation function that /// to a [T] type after it passes the [valueValidator]. *(default: returns
/// determines if a style element is valid. /// element as is)*
/// [combine]: A function that combines two [P] type objects to create ///
/// a new object. /// - [valueValidator] : An optional validation function that
static T? getThemeElement<P, T>( /// determines if a style element is valid. *(default: checks if element
/// is not null)*
///
/// - [combine] : A function that combines two [P] type objects to create
/// a new object. *(default: returns the first element)*
///
/// So, if you only pass a [styles] list, the first valid style element
/// will be returned as is.
/// If you pass a [transform] function, the first valid style element
/// will be transformed to a [T] type.
/// {@endtemplate}
static T? maybeGetElement<P, T>(
List<P?>? styles, { List<P?>? styles, {
required T? Function(P?)? transform, T? Function(P?)? transform,
bool? Function(P?)? valueValidator, bool? Function(P?)? valueValidator,
P? Function(P?, P?)? combine, P? Function(P?, P?)? combine,
}) { }) {
// List of valid styles
final Iterable<P?>? validStyles = styles?.where( final Iterable<P?>? validStyles = styles?.where(
(element) => valueValidator?.call(element) ?? (element != null), (element) => valueValidator?.call(element) ?? (element != null),
); );
// tranformation function
final transformation = transform ?? (element) => element as T?;
return (validStyles?.isNotEmpty ?? false) return (validStyles?.isNotEmpty ?? false)
? transform?.call( ? transformation.call(
validStyles?.reduce( validStyles?.reduce(
(value, element) => combine?.call(value, element) ?? value, (value, element) => combine?.call(value, element) ?? value,
), ),
) )
: null; : null;
} }
/// Gets a theme element from a list of styles. Throws an exception if no
/// valid style is found.
///
/// See [maybeGetElement] for more details.
///
/// {@macro getElement}
static T getElement<P, T>(
List<P?>? styles, {
T? Function(P?)? transform,
bool? Function(P?)? valueValidator,
P? Function(P?, P?)? combine,
}) {
final result = maybeGetElement<P, T>(
styles,
transform: transform,
valueValidator: valueValidator,
combine: combine,
);
if (result == null) {
throw Exception('No valid style found');
}
return result;
}
} }

View File

@ -15,12 +15,25 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:wyatt_ui_components/wyatt_ui_components.dart';
class LinearGradientHelper { abstract class ThemeImporter {
static LinearGradient? fromNullableColors(List<Color>? colors) => /// Imports a [ThemeData] from either a [BuildContext] or a [ThemeData].
colors != null ? LinearGradient(colors: colors) : null; ///
/// Throws an [ArgumentError] if the type of [from] is not a [BuildContext]
/// or a [ThemeData].
static ThemeData importFrom<T>(T from) {
ThemeData theme;
static LinearGradient? fromMultiColor(MultiColor multiColor) => if (from is BuildContext) {
multiColor.isGradient ? LinearGradient(colors: multiColor.colors) : null; theme = Theme.of(from);
} else if (from is ThemeData) {
theme = from;
} else {
throw ArgumentError(
'from must be either a BuildContext or a ThemeData',
);
}
return theme;
}
} }

View File

@ -26,47 +26,52 @@ import 'package:wyatt_ui_components/src/domain/entities/theme_style.dart';
/// 1) Pass the "radius" into the constructor, `Component(radius: 12)`. /// 1) Pass the "radius" into the constructor, `Component(radius: 12)`.
/// 2) Set up a theme extension `ComponentThemeExtension(radius: 15)`. /// 2) Set up a theme extension `ComponentThemeExtension(radius: 15)`.
/// 3) Let `wyatt_ui_kit` "negotiate" and try to find a suitable style in the /// 3) Let `wyatt_ui_kit` "negotiate" and try to find a suitable style in the
/// flutter theme. /// flutter theme, or use a hardcoded value.
/// ///
/// If this negotiation phase fails, then: /// If a negotiation phase fails, it will fallback to the next one.
/// - If the value is mandatory: a hardcoded value in "wyatt_ui_kit" is chosen. ///
/// - If not, the style is simply not applied. /// This resolver uses [ThemeHelper] to negotiate and merge styles.
/// {@endtemplate} /// {@endtemplate}
abstract class ThemeResolver<S extends ThemeStyle<S>, T, E> { abstract class ThemeResolver<Style extends ThemeStyle<Style>, Extension,
Extra> {
/// {@macro theme_resolver} /// {@macro theme_resolver}
const ThemeResolver(); const ThemeResolver();
S? Function(BuildContext context, {E? extra}) get customStyleFn;
/// Compute default value from Flutter Theme or with hardcoded values. /// Compute default value from Flutter Theme or with hardcoded values.
S computeDefaultValue( Style computeDefaultValue(
BuildContext context, { BuildContext context, {
E? extra, Extra? extra,
}); });
/// Compute custom style from context.
Style? Function(BuildContext context, {Extra? extra}) get customStyleFn;
/// Compute extension value from custom component extension. /// Compute extension value from custom component extension.
S? computeExtensionValueFn( Style? computeExtensionValueFn(
BuildContext context, BuildContext context,
T? themeExtension, { Extension? themeExtension, {
E? extra, Extra? extra,
}); });
/// Choose most suitable style for a given context. /// Choose most suitable style for a given context.
S negotiate(BuildContext context, {E? extra}) { Style negotiate(BuildContext context, {Extra? extra}) {
// 1) Custom style passed in constructor (cannot be null)
final style = computeDefaultValue(context, extra: extra); final style = computeDefaultValue(context, extra: extra);
return ThemeHelper.getThemeElement<S, S>( return ThemeHelper.getElement<Style, Style>(
[ [
style, // 1) Custom style passed in constructor
computeExtensionValueFn( customStyleFn(context, extra: extra),
context, // 2) Theme extension
Theme.of(context).extension<T>(), computeExtensionValueFn(
extra: extra, context,
), Theme.of(context).extension<Extension>(),
customStyleFn(context, extra: extra) extra: extra,
], ),
transform: (value) => value, // 3) Default
combine: (value, element) => value?.mergeWith(element), style,
) ?? ],
style; transform: (value) => value,
combine: (value, element) => value?.mergeWith(element),
);
} }
} }

View File

@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'gradient_helper.dart';
export 'multi_color.dart'; export 'multi_color.dart';
export 'text_wrapper.dart'; export 'text_wrapper.dart';
export 'theme_helper.dart'; export 'theme_helper.dart';

View File

@ -15,4 +15,3 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
export './extensions/extensions.dart'; export './extensions/extensions.dart';
export './helpers/helpers.dart';

View File

@ -1,17 +0,0 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export './linear_gradient_helper.dart';

View File

@ -17,7 +17,11 @@
import 'package:wyatt_ui_components/wyatt_ui_components.dart'; import 'package:wyatt_ui_components/wyatt_ui_components.dart';
import 'package:wyatt_ui_kit/wyatt_ui_kit.dart'; import 'package:wyatt_ui_kit/wyatt_ui_kit.dart';
/// {@template wyatt_component_theme_data}
/// A class that holds the theme data for the Wyatt UI Kit.
/// {@endtemplate}
class WyattComponentThemeData { class WyattComponentThemeData {
/// {@macro wyatt_component_theme_data}
static ComponentThemeData get wyattComponentThemeData => ComponentThemeData( static ComponentThemeData get wyattComponentThemeData => ComponentThemeData(
appBar: const TopAppBar(), appBar: const TopAppBar(),
topNavigationBarComponent: const TopNavigationBar(), topNavigationBarComponent: const TopNavigationBar(),