├── doc └── assets │ ├── logo.png │ └── simple_demo.png ├── example ├── assets │ ├── button.jpg │ ├── toggle.gif │ ├── code_demo.png │ ├── demo_app.gif │ ├── example_1.jpg │ ├── styled_widget.jpg │ └── japan-style-example.gif ├── README.md ├── pubspec.yaml └── pubspec.lock ├── analysis_options.yaml ├── .metadata ├── pubspec.yaml ├── lib ├── styled_widget.dart └── src │ ├── extensions │ ├── icon_extension.dart │ ├── list_extension.dart │ ├── text_span_extension.dart │ ├── text_extension.dart │ └── widget_extension.dart │ ├── Styled.dart │ ├── animated_icon.dart │ ├── animated_text.dart │ └── animated_widget.dart ├── .github └── FUNDING.yml ├── LICENSE ├── .gitignore ├── CHANGELOG.md └── README.md /doc/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReinBentdal/styled_widget/HEAD/doc/assets/logo.png -------------------------------------------------------------------------------- /doc/assets/simple_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReinBentdal/styled_widget/HEAD/doc/assets/simple_demo.png -------------------------------------------------------------------------------- /example/assets/button.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReinBentdal/styled_widget/HEAD/example/assets/button.jpg -------------------------------------------------------------------------------- /example/assets/toggle.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReinBentdal/styled_widget/HEAD/example/assets/toggle.gif -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | **Examples available in the wiki [here](https://github.com/ReinBentdal/styled_widget/wiki/Examples)** -------------------------------------------------------------------------------- /example/assets/code_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReinBentdal/styled_widget/HEAD/example/assets/code_demo.png -------------------------------------------------------------------------------- /example/assets/demo_app.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReinBentdal/styled_widget/HEAD/example/assets/demo_app.gif -------------------------------------------------------------------------------- /example/assets/example_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReinBentdal/styled_widget/HEAD/example/assets/example_1.jpg -------------------------------------------------------------------------------- /example/assets/styled_widget.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReinBentdal/styled_widget/HEAD/example/assets/styled_widget.jpg -------------------------------------------------------------------------------- /example/assets/japan-style-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReinBentdal/styled_widget/HEAD/example/assets/japan-style-example.gif -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: styled_widget_example 2 | 3 | environment: 4 | sdk: ">=2.12.0 <3.0.0" 5 | 6 | dependencies: 7 | styled_widget: 8 | path: ../ 9 | flutter: 10 | sdk: flutter 11 | 12 | dev_dependencies: 13 | flutter_test: 14 | sdk: flutter 15 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:lint/strict.yaml 2 | 3 | linter: 4 | rules: 5 | 6 | analyzer: 7 | strong-mode: 8 | implicit-casts: false 9 | implicit-dynamic: false 10 | 11 | exclude: 12 | - "android/**" 13 | - "ios/**" 14 | - "build/**" 15 | - "vendors/**" 16 | - "resources/**" 17 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: cf37c2cd07a1d3ba296efff2dc75e19ba65e1665 8 | channel: dev 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: styled_widget 2 | description: Simplifying your widget tree structure by defining widget using methods. Taking ispiration from CSS and SwiftUI 3 | version: 0.4.1 4 | homepage: https://github.com/ReinBentdal/styled_widget 5 | repository: https://github.com/ReinBentdal/styled_widget 6 | 7 | environment: 8 | sdk: ">=2.12.0 <3.0.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | dev_dependencies: 15 | flutter_test: 16 | sdk: flutter 17 | 18 | lint: ^2.0.0 19 | -------------------------------------------------------------------------------- /lib/styled_widget.dart: -------------------------------------------------------------------------------- 1 | library styled_widget; 2 | 3 | import 'dart:math'; 4 | import 'dart:ui'; 5 | 6 | import 'package:flutter/foundation.dart'; 7 | import 'package:flutter/gestures.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter/rendering.dart'; 10 | 11 | part 'src/Styled.dart'; 12 | part 'src/animated_icon.dart'; 13 | part 'src/animated_text.dart'; 14 | part 'src/animated_widget.dart'; 15 | part 'src/extensions/icon_extension.dart'; 16 | part 'src/extensions/list_extension.dart'; 17 | part 'src/extensions/text_extension.dart'; 18 | part 'src/extensions/text_span_extension.dart'; 19 | part 'src/extensions/widget_extension.dart'; 20 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ReinBentdal 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['https://www.paypal.me/reinbentdal'] 13 | -------------------------------------------------------------------------------- /lib/src/extensions/icon_extension.dart: -------------------------------------------------------------------------------- 1 | part of '../../styled_widget.dart'; 2 | 3 | extension StyledIcon on T { 4 | T copyWith({ 5 | double? size, 6 | Color? color, 7 | String? semanticLabel, 8 | TextDirection? textDirection, 9 | }) => 10 | (this is _StyledAnimatedIconContainer 11 | ? _StyledAnimatedIconContainer( 12 | icon, 13 | color: color ?? this.color, 14 | size: size ?? this.size, 15 | semanticLabel: semanticLabel ?? this.semanticLabel, 16 | textDirection: textDirection ?? this.textDirection, 17 | ) 18 | : Icon( 19 | icon, 20 | color: color ?? this.color, 21 | size: size ?? this.size, 22 | semanticLabel: semanticLabel ?? this.semanticLabel, 23 | textDirection: textDirection ?? this.textDirection, 24 | )) as T; 25 | 26 | T iconSize(double size) => this.copyWith(size: size); 27 | 28 | T iconColor(Color color) => this.copyWith(color: color); 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Rein Gundersen Bentdal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | build/ 32 | 33 | # Android related 34 | **/android/**/gradle-wrapper.jar 35 | **/android/.gradle 36 | **/android/captures/ 37 | **/android/gradlew 38 | **/android/gradlew.bat 39 | **/android/local.properties 40 | **/android/**/GeneratedPluginRegistrant.java 41 | 42 | # iOS/XCode related 43 | **/ios/**/*.mode1v3 44 | **/ios/**/*.mode2v3 45 | **/ios/**/*.moved-aside 46 | **/ios/**/*.pbxuser 47 | **/ios/**/*.perspectivev3 48 | **/ios/**/*sync/ 49 | **/ios/**/.sconsign.dblite 50 | **/ios/**/.tags* 51 | **/ios/**/.vagrant/ 52 | **/ios/**/DerivedData/ 53 | **/ios/**/Icon? 54 | **/ios/**/Pods/ 55 | **/ios/**/.symlinks/ 56 | **/ios/**/profile 57 | **/ios/**/xcuserdata 58 | **/ios/.generated/ 59 | **/ios/Flutter/App.framework 60 | **/ios/Flutter/Flutter.framework 61 | **/ios/Flutter/Flutter.podspec 62 | **/ios/Flutter/Generated.xcconfig 63 | **/ios/Flutter/app.flx 64 | **/ios/Flutter/app.zip 65 | **/ios/Flutter/flutter_assets/ 66 | **/ios/Flutter/flutter_export_environment.sh 67 | **/ios/ServiceDefinitions.json 68 | **/ios/Runner/GeneratedPluginRegistrant.* 69 | 70 | # Exceptions to above rules. 71 | !**/ios/**/default.mode1v3 72 | !**/ios/**/default.mode2v3 73 | !**/ios/**/default.pbxuser 74 | !**/ios/**/default.perspectivev3 75 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 76 | 77 | # Since this is an library 78 | /pubspec.lock 79 | -------------------------------------------------------------------------------- /lib/src/Styled.dart: -------------------------------------------------------------------------------- 1 | part of '../styled_widget.dart'; 2 | 3 | class Styled { 4 | static Widget builder({ 5 | required Widget Function(BuildContext context, Widget child) builder, 6 | required Widget child, 7 | }) => 8 | Builder( 9 | builder: (context) => builder( 10 | context, 11 | child, 12 | ), 13 | ); 14 | 15 | static Widget widget({Widget? child}) => 16 | child ?? 17 | LimitedBox( 18 | maxWidth: 0.0, 19 | maxHeight: 0.0, 20 | child: ConstrainedBox( 21 | constraints: const BoxConstraints.expand(), 22 | ), 23 | ); 24 | 25 | static Text text( 26 | String data, { 27 | TextStyle? style, 28 | StrutStyle? strutStyle, 29 | TextAlign? textAlign, 30 | TextDirection? textDirection, 31 | Locale? locale, 32 | bool? softWrap, 33 | TextOverflow? overflow, 34 | double? textScaleFactor, 35 | int? maxLines, 36 | String? semanticsLabel, 37 | TextWidthBasis? textWidthBasis, 38 | bool animate = false, 39 | }) => 40 | animate 41 | ? _StyledAnimatedTextContainer( 42 | data, 43 | locale: locale, 44 | maxLines: maxLines, 45 | overflow: overflow, 46 | semanticsLabel: semanticsLabel, 47 | softWrap: softWrap, 48 | strutStyle: strutStyle, 49 | style: style, 50 | textAlign: textAlign, 51 | textDirection: textDirection, 52 | textScaleFactor: textScaleFactor, 53 | textWidthBasis: textWidthBasis, 54 | ) 55 | : Text( 56 | data, 57 | locale: locale, 58 | maxLines: maxLines, 59 | overflow: overflow, 60 | semanticsLabel: semanticsLabel, 61 | softWrap: softWrap, 62 | strutStyle: strutStyle, 63 | style: style, 64 | textAlign: textAlign, 65 | textDirection: textDirection, 66 | textScaleFactor: textScaleFactor, 67 | textWidthBasis: textWidthBasis, 68 | ); 69 | 70 | static Icon icon( 71 | IconData icon, { 72 | Key? key, 73 | double? size, 74 | Color? color, 75 | String? semanticLabel, 76 | TextDirection? textDirection, 77 | bool animate = false, 78 | }) => 79 | animate 80 | ? _StyledAnimatedIconContainer( 81 | icon, 82 | color: color, 83 | size: size, 84 | semanticLabel: semanticLabel, 85 | textDirection: textDirection, 86 | ) 87 | : Icon( 88 | icon, 89 | color: color, 90 | size: size, 91 | semanticLabel: semanticLabel, 92 | textDirection: textDirection, 93 | ); 94 | } 95 | -------------------------------------------------------------------------------- /lib/src/animated_icon.dart: -------------------------------------------------------------------------------- 1 | part of '../styled_widget.dart'; 2 | 3 | // TODO: why extend icon 4 | class _StyledAnimatedIconContainer extends Icon { 5 | @override 6 | final IconData? icon; 7 | @override 8 | final double? size; 9 | @override 10 | final Color? color; 11 | @override 12 | final String? semanticLabel; 13 | @override 14 | final TextDirection? textDirection; 15 | 16 | const _StyledAnimatedIconContainer( 17 | this.icon, { 18 | this.color, 19 | this.semanticLabel, 20 | this.size, 21 | this.textDirection, 22 | }) : super( 23 | icon, 24 | color: color, 25 | semanticLabel: semanticLabel, 26 | size: size, 27 | textDirection: textDirection, 28 | ); 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | final _StyledAnimatedModel? animation = 33 | _StyledInheritedAnimation.of(context)?.animation; 34 | if (animation == null) { 35 | return super.build(context); 36 | } 37 | // assert( 38 | // animation != null, 'You can`t animate without specifying an animation'); 39 | return _AnimatedIcon( 40 | icon, 41 | duration: animation.duration, 42 | curve: animation.curve, 43 | color: color, 44 | semanticLabel: semanticLabel, 45 | size: size, 46 | textDirection: textDirection, 47 | ); 48 | } 49 | } 50 | 51 | class _AnimatedIcon extends ImplicitlyAnimatedWidget { 52 | /// Creates a container that animates its parameters implicitly. 53 | /// 54 | /// The [curve] and [duration] arguments must not be null. 55 | const _AnimatedIcon( 56 | this.icon, { 57 | Key? key, 58 | this.color, 59 | this.semanticLabel, 60 | this.size, 61 | this.textDirection, 62 | Curve curve = Curves.linear, 63 | required Duration duration, 64 | }) : super( 65 | key: key, 66 | curve: curve, 67 | duration: duration, 68 | ); 69 | 70 | final IconData? icon; 71 | final double? size; 72 | final Color? color; 73 | final String? semanticLabel; 74 | final TextDirection? textDirection; 75 | 76 | @override 77 | _AnimatedIconState createState() => _AnimatedIconState(); 78 | } 79 | 80 | class _AnimatedIconState extends AnimatedWidgetBaseState<_AnimatedIcon> { 81 | ColorTween? _color; 82 | Tween? _size; 83 | 84 | @override 85 | void forEachTween(TweenVisitor visitor) { 86 | _color = visitor( 87 | _color, 88 | widget.color, 89 | (dynamic value) => ColorTween(begin: value as Color), 90 | ) as ColorTween?; 91 | _size = visitor( 92 | _size, 93 | widget.size, 94 | (dynamic value) => Tween(begin: value as double), 95 | ) as Tween?; 96 | } 97 | 98 | @override 99 | Widget build(BuildContext context) { 100 | return Icon( 101 | widget.icon, 102 | semanticLabel: widget.semanticLabel, 103 | textDirection: widget.textDirection, 104 | color: _color?.evaluate(animation), 105 | size: _size?.evaluate(animation), 106 | ); 107 | } 108 | 109 | @override 110 | void debugFillProperties(DiagnosticPropertiesBuilder description) { 111 | super.debugFillProperties(description); 112 | // TODO: debug 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /lib/src/extensions/list_extension.dart: -------------------------------------------------------------------------------- 1 | part of '../../styled_widget.dart'; 2 | 3 | extension StyledList on List { 4 | Widget toColumn({ 5 | Key? key, 6 | MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, 7 | MainAxisSize mainAxisSize = MainAxisSize.max, 8 | CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, 9 | TextDirection? textDirection, 10 | VerticalDirection verticalDirection = VerticalDirection.down, 11 | TextBaseline? textBaseline, 12 | Widget? separator, 13 | }) => 14 | Column( 15 | key: key, 16 | mainAxisAlignment: mainAxisAlignment, 17 | mainAxisSize: mainAxisSize, 18 | crossAxisAlignment: crossAxisAlignment, 19 | textDirection: textDirection, 20 | verticalDirection: verticalDirection, 21 | textBaseline: textBaseline, 22 | children: separator != null && isNotEmpty 23 | ? (expand((child) => [child, separator]).toList() 24 | ..removeLast()) 25 | : this, 26 | ); 27 | 28 | Widget toRow({ 29 | Key? key, 30 | MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, 31 | MainAxisSize mainAxisSize = MainAxisSize.max, 32 | CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, 33 | TextDirection? textDirection, 34 | VerticalDirection verticalDirection = VerticalDirection.down, 35 | TextBaseline? textBaseline, 36 | Widget? separator, 37 | }) => 38 | Row( 39 | key: key, 40 | mainAxisAlignment: mainAxisAlignment, 41 | mainAxisSize: mainAxisSize, 42 | crossAxisAlignment: crossAxisAlignment, 43 | textDirection: textDirection, 44 | verticalDirection: verticalDirection, 45 | textBaseline: textBaseline, 46 | children: separator != null && isNotEmpty 47 | ? (expand((child) => [child, separator]).toList() 48 | ..removeLast()) 49 | : this, 50 | ); 51 | 52 | Widget toStack({ 53 | Key? key, 54 | AlignmentGeometry alignment = AlignmentDirectional.topStart, 55 | TextDirection? textDirection, 56 | StackFit fit = StackFit.loose, 57 | Clip clipBehavior = Clip.hardEdge, 58 | List children = const [], 59 | }) => 60 | Stack( 61 | key: key, 62 | alignment: alignment, 63 | textDirection: textDirection, 64 | fit: fit, 65 | clipBehavior: clipBehavior, 66 | children: this, 67 | ); 68 | 69 | Widget toWrap({ 70 | Key? key, 71 | Axis direction = Axis.horizontal, 72 | WrapAlignment alignment = WrapAlignment.start, 73 | double spacing = 0.0, 74 | WrapAlignment runAlignment = WrapAlignment.start, 75 | double runSpacing = 0.0, 76 | WrapCrossAlignment crossAxisAlignment = WrapCrossAlignment.start, 77 | TextDirection? textDirection, 78 | VerticalDirection verticalDirection = VerticalDirection.down, 79 | Clip clipBehavior = Clip.none, 80 | List children = const [], 81 | }) => 82 | Wrap( 83 | key: key, 84 | direction: direction, 85 | alignment: alignment, 86 | spacing: spacing, 87 | runAlignment: runAlignment, 88 | runSpacing: runSpacing, 89 | crossAxisAlignment: crossAxisAlignment, 90 | textDirection: textDirection, 91 | verticalDirection: verticalDirection, 92 | clipBehavior: clipBehavior, 93 | children: this, 94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.4.1 2 | * Added `toWrap` 3 | * Added padding parameter to `scrollable` 4 | 5 | ## 0.4.0 6 | * Added key parameter to all widget methods 7 | * Added `Styled.builder()` method 8 | 9 | ## 0.3.1+2 10 | * Text extensions nullable 11 | 12 | ## 0.3.1+1 13 | * Animation null-exeption fix 14 | 15 | ## 0.3.1 16 | * Added `paddingDirectional`, `positionedDirectional` and `borderRadiusDirectional` 17 | 18 | ## 0.3.0 19 | * Null safety 20 | * Minor fixes 21 | 22 | ## 0.2.2 23 | * Added `offstage` as `Widget` extension method 24 | * Added `neumorphism` widget as `Widget` extension method 25 | * [Breaking] Changed `scale` method to accept directional scaling 26 | * [Breaking] Renamed `stack` overflow parameter to clipBehavior 27 | 28 | ## 0.2.1 29 | * Added `aspectRatio`, `center`, `fittedBox`, `fractionallySizedBox`, `card` 30 | * Added `seperator` to `toRow` and `toColumn` list methods 31 | 32 | ## 0.2.0 33 | * Major code refactoring 34 | * Added `parent` as `Widget` extension method 35 | * Added animations for `overflow` 36 | * [Breaking] Similar widgets no longer try to merge 37 | * [Breaking] Changes `elevation` formula to match the material spec 38 | * [Breaking] Renamed `constraints` to `constrained` to avoid nameclash 39 | * [Breaking] Renamed `decoration` to `decorated` to avoid nameclash 40 | 41 | ## 0.1.3 42 | * Added `flexible` as `Widget` extension method 43 | * Fixed bug with `textStyle` not working correctly 44 | 45 | ## 0.1.2 46 | * Added `expanded` as `Widget` extension method 47 | * Added `positioned` as `Widget` extension method 48 | 49 | ## 0.1.1 50 | * Added `textStyle` method to `Text` and `TextSpan` 51 | * `TextSpan` methods updated to match `Text` methods 52 | * Added `clipRect` 53 | * Added `clipper` and `clipBehavior` to `clipRRect` 54 | 55 | ## 0.1.0+2 56 | * Fixed gestures not working with `ripple` 57 | 58 | ## 0.1.0 59 | * Added `Styled.icon` to be able to animate `Icon` 60 | * Added `Styled.text` to be able to animate `Text` 61 | * Changed `elevation` formula 62 | * Changed `animate` parameters from named to "unnamed" and required 63 | 64 | ## 0.0.8 65 | * Added `animation` for `backgroundColor`, `backgroundImage`, `backgroundGradient`, `backgroundLinearGradient`, `backgroundRadialGradient`, `backgroundSweepGradient`, `backgroundBlendMode`, `border`, `boxShadow`, `width` and `height` 66 | * [fix] `transform` animation crashes app 67 | 68 | ## 0.0.7 69 | * Added `animate` 70 | * Added `border` 71 | * Added more parameters to `ripple` 72 | * Added `toColumn`, `toRow` and `toStack` to [List] 73 | * Renamed `isTap` to `onTapChange` 74 | * Changed `elevation` behaviour to match material design standard 75 | * [Breaking] Removed `duration` and `curve` parameters from all methods in favor of `animate` method 76 | 77 | ## 0.0.6 78 | * Added `animatedText` to implicitly animate text when property value changes 79 | * Animated `borderRadius` and `backgroundBlur` 80 | * Multiple `boxShadow` methods will now merge together instead of replacing 81 | * Added `textWidthBasis` method to [Text] 82 | * Added `textElevation` 83 | 84 | ## 0.0.5 85 | * Added styling methods for [TextSpan] 86 | 87 | ## 0.0.4 88 | * Added `linearGradient`, `radialGradient`, `sweepGradient`, `backgroundBlendMode`, `backgroundImage`, `scrollable` 89 | * Merges similar widgets together 90 | * Minor improvements and fixes 91 | 92 | ## 0.0.3 93 | * Added animations for all [widget] methods supporting animations 94 | * Added more methods 95 | 96 | ## 0.0.2 97 | * Added more basic widgets 98 | * Added some animated widgets 99 | 100 | ## 0.0.1 101 | * Initial consept 102 | -------------------------------------------------------------------------------- /lib/src/extensions/text_span_extension.dart: -------------------------------------------------------------------------------- 1 | part of '../../styled_widget.dart'; 2 | 3 | extension StyledTextSpan on T { 4 | T copyWith({ 5 | TextStyle? style, 6 | GestureRecognizer? recognizer, 7 | String? semanticsLabel, 8 | }) => 9 | TextSpan( 10 | text: text, 11 | children: children, 12 | style: style ?? this.style, 13 | recognizer: recognizer ?? this.recognizer, 14 | semanticsLabel: semanticsLabel ?? this.semanticsLabel, 15 | ) as T; 16 | 17 | T textStyle(TextStyle style) => this.copyWith( 18 | style: this.style?.copyWith( 19 | background: style.background, 20 | backgroundColor: style.backgroundColor, 21 | color: style.color, 22 | debugLabel: style.debugLabel, 23 | decoration: style.decoration, 24 | decorationColor: style.decorationColor, 25 | decorationStyle: style.decorationStyle, 26 | decorationThickness: style.decorationThickness, 27 | fontFamily: style.fontFamily, 28 | fontFamilyFallback: style.fontFamilyFallback, 29 | fontFeatures: style.fontFeatures, 30 | fontSize: style.fontSize, 31 | fontStyle: style.fontStyle, 32 | fontWeight: style.fontWeight, 33 | foreground: style.foreground, 34 | height: style.height, 35 | inherit: style.inherit, 36 | letterSpacing: style.letterSpacing, 37 | locale: style.locale, 38 | shadows: style.shadows, 39 | textBaseline: style.textBaseline, 40 | wordSpacing: style.wordSpacing, 41 | ), 42 | ); 43 | 44 | T bold() => this.copyWith( 45 | style: (style ?? const TextStyle()).copyWith( 46 | fontWeight: FontWeight.bold, 47 | ), 48 | ); 49 | 50 | T italic() => this.copyWith( 51 | style: (style ?? const TextStyle()).copyWith( 52 | fontStyle: FontStyle.italic, 53 | ), 54 | ); 55 | 56 | T fontWeight(FontWeight fontWeight) => this.copyWith( 57 | style: (style ?? const TextStyle()).copyWith( 58 | fontWeight: fontWeight, 59 | ), 60 | ); 61 | 62 | T fontSize(double size) => this.copyWith( 63 | style: (style ?? const TextStyle()).copyWith( 64 | fontSize: size, 65 | ), 66 | ); 67 | 68 | T fontFamily(String font) => this.copyWith( 69 | style: (style ?? const TextStyle()).copyWith( 70 | fontFamily: font, 71 | ), 72 | ); 73 | 74 | T letterSpacing(double space) => this.copyWith( 75 | style: (style ?? const TextStyle()).copyWith( 76 | letterSpacing: space, 77 | ), 78 | ); 79 | 80 | T wordSpacing(double space) => this.copyWith( 81 | style: (style ?? const TextStyle()).copyWith( 82 | wordSpacing: space, 83 | ), 84 | ); 85 | 86 | T textShadow({ 87 | Color color = const Color(0x33000000), 88 | double blurRadius = 0.0, 89 | Offset offset = Offset.zero, 90 | }) => 91 | this.copyWith( 92 | style: (style ?? const TextStyle()).copyWith( 93 | shadows: [ 94 | Shadow( 95 | color: color, 96 | blurRadius: blurRadius, 97 | offset: offset, 98 | ), 99 | ], 100 | ), 101 | ); 102 | 103 | double _elevationOpacityCurve(double x) => 104 | pow(x, 1 / 16) / sqrt(pow(x, 2) + 2) + 0.2; 105 | 106 | T textElevation( 107 | double elevation, { 108 | double angle = 0.0, 109 | Color color = const Color(0x33000000), 110 | double opacityRatio = 1.0, 111 | }) { 112 | final double calculatedOpacity = 113 | _elevationOpacityCurve(elevation) * opacityRatio; 114 | 115 | final Shadow shadow = Shadow( 116 | color: color.withOpacity(calculatedOpacity), 117 | blurRadius: elevation, 118 | offset: Offset(sin(angle) * elevation, cos(angle) * elevation), 119 | ); 120 | return this.copyWith( 121 | style: (style ?? const TextStyle()).copyWith( 122 | shadows: [ 123 | shadow, 124 | ], 125 | ), 126 | ); 127 | } 128 | 129 | T textColor(Color color) => this.copyWith( 130 | style: (style ?? const TextStyle()).copyWith( 131 | color: color, 132 | ), 133 | ); 134 | 135 | T textBaseline(TextBaseline textBaseline) => this.copyWith( 136 | style: (style ?? const TextStyle()).copyWith( 137 | textBaseline: textBaseline, 138 | ), 139 | ); 140 | } 141 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.10.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.1.1" 20 | characters: 21 | dependency: transitive 22 | description: 23 | name: characters 24 | sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "1.2.1" 28 | clock: 29 | dependency: transitive 30 | description: 31 | name: clock 32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.1.1" 36 | collection: 37 | dependency: transitive 38 | description: 39 | name: collection 40 | sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.17.0" 44 | fake_async: 45 | dependency: transitive 46 | description: 47 | name: fake_async 48 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.3.1" 52 | flutter: 53 | dependency: "direct main" 54 | description: flutter 55 | source: sdk 56 | version: "0.0.0" 57 | flutter_test: 58 | dependency: "direct dev" 59 | description: flutter 60 | source: sdk 61 | version: "0.0.0" 62 | js: 63 | dependency: transitive 64 | description: 65 | name: js 66 | sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" 67 | url: "https://pub.dev" 68 | source: hosted 69 | version: "0.6.5" 70 | matcher: 71 | dependency: transitive 72 | description: 73 | name: matcher 74 | sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" 75 | url: "https://pub.dev" 76 | source: hosted 77 | version: "0.12.13" 78 | material_color_utilities: 79 | dependency: transitive 80 | description: 81 | name: material_color_utilities 82 | sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 83 | url: "https://pub.dev" 84 | source: hosted 85 | version: "0.2.0" 86 | meta: 87 | dependency: transitive 88 | description: 89 | name: meta 90 | sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" 91 | url: "https://pub.dev" 92 | source: hosted 93 | version: "1.8.0" 94 | path: 95 | dependency: transitive 96 | description: 97 | name: path 98 | sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b 99 | url: "https://pub.dev" 100 | source: hosted 101 | version: "1.8.2" 102 | sky_engine: 103 | dependency: transitive 104 | description: flutter 105 | source: sdk 106 | version: "0.0.99" 107 | source_span: 108 | dependency: transitive 109 | description: 110 | name: source_span 111 | sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 112 | url: "https://pub.dev" 113 | source: hosted 114 | version: "1.9.1" 115 | stack_trace: 116 | dependency: transitive 117 | description: 118 | name: stack_trace 119 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 120 | url: "https://pub.dev" 121 | source: hosted 122 | version: "1.11.0" 123 | stream_channel: 124 | dependency: transitive 125 | description: 126 | name: stream_channel 127 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" 128 | url: "https://pub.dev" 129 | source: hosted 130 | version: "2.1.1" 131 | string_scanner: 132 | dependency: transitive 133 | description: 134 | name: string_scanner 135 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 136 | url: "https://pub.dev" 137 | source: hosted 138 | version: "1.2.0" 139 | styled_widget: 140 | dependency: "direct main" 141 | description: 142 | path: ".." 143 | relative: true 144 | source: path 145 | version: "0.4.1" 146 | term_glyph: 147 | dependency: transitive 148 | description: 149 | name: term_glyph 150 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 151 | url: "https://pub.dev" 152 | source: hosted 153 | version: "1.2.1" 154 | test_api: 155 | dependency: transitive 156 | description: 157 | name: test_api 158 | sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 159 | url: "https://pub.dev" 160 | source: hosted 161 | version: "0.4.16" 162 | vector_math: 163 | dependency: transitive 164 | description: 165 | name: vector_math 166 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 167 | url: "https://pub.dev" 168 | source: hosted 169 | version: "2.1.4" 170 | sdks: 171 | dart: ">=2.18.0 <3.0.0" 172 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 |

8 | Simplifying your widget tree structure by defining widgets using methods. 9 |
10 |
11 | 12 | 13 | License: MIT 14 |
15 |
16 | Buy Me A Coffee 17 |

18 | 19 |
20 | 21 | Thanks to the introduction of [extension methods](https://dart.dev/guides/language/extension-methods) in Dart 2.7.0, `styled_widget` makes it possible to build widget tree\`s more readable and efficient. 22 | 23 | `styled_widget` is build as a tool to enhance your Flutter development experience and to seamlessly integrate with your codebase. 24 | 25 | ### Showcase 26 | | [Design](https://dribbble.com/shots/6459693-Creative-layout-design), [Code](https://github.com/ReinBentdal/styled_widget/wiki/demo_app) | [Design](https://dribbble.com/shots/4514354-Sign-up), [Code](https://github.com/ReinBentdal/styled_widget/wiki/japan-style-example) | [Design](https://no.pinterest.com/pin/403283341630104104/), [Code](https://github.com/ReinBentdal/styled_widget/wiki/toggle) | 27 | |-|-|-| 28 | |||| 29 | 30 | ### Basic example 31 | `styled_widget` has a **bottom up** approach to building widget\`s. This means you start with the inner most element and layer widget\`s on top. The following example is structured as follows: 32 | Icon -> blue circle -> light blue circle -> card -> background 33 | ```dart 34 | Icon(OMIcons.home, color: Colors.white) 35 | .padding(all: 10) 36 | .decorated(color: Color(0xff7AC1E7), shape: BoxShape.circle) 37 | .padding(all: 15) 38 | .decorated(color: Color(0xffE8F2F7), shape: BoxShape.circle) 39 | .padding(all: 20) 40 | .card( 41 | elevation: 10, 42 | shape: RoundedRectangleBorder( 43 | borderRadius: BorderRadius.circular(20), 44 | ), 45 | ) 46 | .alignment(Alignment.center) 47 | .backgroundColor(Color(0xffEBECF1)); 48 | ``` 49 |
50 | Raw Flutter (click to show) 51 |
 52 | 
 53 | ```dart
 54 | DecoratedBox(
 55 |   decoration: BoxDecoration(
 56 |     color: Color(0xffEBECF1),
 57 |   ),
 58 |   child: Align(
 59 |     alignment: Alignment.center,
 60 |     child: Card(
 61 |       elevation: 10,
 62 |       shape: RoundedRectangleBorder(
 63 |         borderRadius: BorderRadius.circular(20),
 64 |       ),
 65 |       child: Padding(
 66 |         padding: EdgeInsets.all(20),
 67 |         child: DecoratedBox(
 68 |           decoration: BoxDecoration(
 69 |             color: Color(0xffE8F2F7),
 70 |             shape: BoxShape.circle,
 71 |           ),
 72 |           child: Padding(
 73 |             padding: EdgeInsets.all(15),
 74 |             child: DecoratedBox(
 75 |               decoration: BoxDecoration(
 76 |                 color: Color(0xff7AC1E7),
 77 |                 shape: BoxShape.circle,
 78 |               ),
 79 |               child: Padding(
 80 |                 padding: EdgeInsets.all(10),
 81 |                 child: Icon(
 82 |                   OMIcons.home,
 83 |                   color: Colors.white,
 84 |                 ),
 85 |               ),
 86 |             ),
 87 |           ),
 88 |         ),
 89 |       ),
 90 |     ),
 91 |   ),
 92 | );
 93 | ```
 94 | 
95 |
96 | 97 | 98 | ### Docs 99 | See the documentation at [styled_widget/wiki](https://github.com/ReinBentdal/styled_widget/wiki) for more information about using `styled_widget`! 100 | 101 | Quicklinks 102 | * [Home](https://github.com/ReinBentdal/styled_widget/wiki) 103 | * [Core concept](https://github.com/ReinBentdal/styled_widget/wiki/Core-concept) 104 | * [Widgets](https://github.com/ReinBentdal/styled_widget/wiki/Widgets) 105 | * [Animations](https://github.com/ReinBentdal/styled_widget/wiki/Animations) 106 | * [List of methods](https://github.com/ReinBentdal/styled_widget/wiki/List-of-methods) 107 | * [Examples](https://github.com/ReinBentdal/styled_widget/wiki/Examples) 108 | * [demo app](https://github.com/ReinBentdal/styled_widget/wiki/demo_app) 109 | * [japan style](https://github.com/ReinBentdal/styled_widget/wiki/japan-style-example) 110 | * [toggle animation](https://github.com/ReinBentdal/styled_widget/wiki/toggle) 111 | * [styled_widget logo](https://github.com/ReinBentdal/styled_widget/wiki/styled_widget-logo) 112 | * [Basic text example](https://github.com/ReinBentdal/styled_widget/wiki/basic-text-example) 113 | * [Contributing](https://github.com/ReinBentdal/styled_widget/wiki/Contributing) 114 | -------------------------------------------------------------------------------- /lib/src/extensions/text_extension.dart: -------------------------------------------------------------------------------- 1 | part of '../../styled_widget.dart'; 2 | 3 | extension StyledText on T { 4 | T copyWith({ 5 | String? data, 6 | TextStyle? style, 7 | StrutStyle? strutStyle, 8 | TextAlign? textAlign, 9 | TextDirection? textDirection, 10 | Locale? locale, 11 | bool? softWrap, 12 | TextOverflow? overflow, 13 | double? textScaleFactor, 14 | int? maxLines, 15 | String? semanticsLabel, 16 | TextWidthBasis? textWidthBasis, 17 | }) => 18 | (this is _StyledAnimatedTextContainer 19 | ? _StyledAnimatedTextContainer( 20 | data ?? this.data ?? "", 21 | style: style ?? this.style, 22 | strutStyle: strutStyle ?? this.strutStyle, 23 | textAlign: textAlign ?? this.textAlign, 24 | locale: locale ?? this.locale, 25 | maxLines: maxLines ?? this.maxLines, 26 | overflow: overflow ?? this.overflow, 27 | semanticsLabel: semanticsLabel ?? this.semanticsLabel, 28 | softWrap: softWrap ?? this.softWrap, 29 | textDirection: textDirection ?? this.textDirection, 30 | textScaleFactor: textScaleFactor ?? this.textScaleFactor, 31 | textWidthBasis: textWidthBasis ?? this.textWidthBasis, 32 | ) 33 | : Text( 34 | data ?? this.data ?? "", 35 | style: style ?? this.style, 36 | strutStyle: strutStyle ?? this.strutStyle, 37 | textAlign: textAlign ?? this.textAlign, 38 | locale: locale ?? this.locale, 39 | maxLines: maxLines ?? this.maxLines, 40 | overflow: overflow ?? this.overflow, 41 | semanticsLabel: semanticsLabel ?? this.semanticsLabel, 42 | softWrap: softWrap ?? this.softWrap, 43 | textDirection: textDirection ?? this.textDirection, 44 | textScaleFactor: textScaleFactor ?? this.textScaleFactor, 45 | textWidthBasis: textWidthBasis ?? this.textWidthBasis, 46 | )) as T; 47 | 48 | T textStyle(TextStyle style) => this.copyWith( 49 | style: (this.style ?? const TextStyle()).copyWith( 50 | background: style.background, 51 | backgroundColor: style.backgroundColor, 52 | color: style.color, 53 | debugLabel: style.debugLabel, 54 | decoration: style.decoration, 55 | decorationColor: style.decorationColor, 56 | decorationStyle: style.decorationStyle, 57 | decorationThickness: style.decorationThickness, 58 | fontFamily: style.fontFamily, 59 | fontFamilyFallback: style.fontFamilyFallback, 60 | fontFeatures: style.fontFeatures, 61 | fontSize: style.fontSize, 62 | fontStyle: style.fontStyle, 63 | fontWeight: style.fontWeight, 64 | foreground: style.foreground, 65 | height: style.height, 66 | inherit: style.inherit, 67 | letterSpacing: style.letterSpacing, 68 | locale: style.locale, 69 | shadows: style.shadows, 70 | textBaseline: style.textBaseline, 71 | wordSpacing: style.wordSpacing, 72 | ), 73 | ); 74 | 75 | T textScale(double scaleFactor) => 76 | this.copyWith(textScaleFactor: scaleFactor); 77 | 78 | T bold() => this.copyWith( 79 | style: (style ?? const TextStyle()).copyWith( 80 | fontWeight: FontWeight.bold, 81 | ), 82 | ); 83 | 84 | T italic() => this.copyWith( 85 | style: (style ?? const TextStyle()).copyWith( 86 | fontStyle: FontStyle.italic, 87 | ), 88 | ); 89 | 90 | T fontWeight(FontWeight? fontWeight) => this.copyWith( 91 | style: (style ?? const TextStyle()).copyWith( 92 | fontWeight: fontWeight, 93 | ), 94 | ); 95 | 96 | T fontSize(double? size) => this.copyWith( 97 | style: (style ?? const TextStyle()).copyWith( 98 | fontSize: size, 99 | ), 100 | ); 101 | 102 | T fontFamily(String? font) => this.copyWith( 103 | style: (style ?? const TextStyle()).copyWith( 104 | fontFamily: font, 105 | ), 106 | ); 107 | 108 | T letterSpacing(double? space) => this.copyWith( 109 | style: (style ?? const TextStyle()).copyWith( 110 | letterSpacing: space, 111 | ), 112 | ); 113 | 114 | T wordSpacing(double? space) => this.copyWith( 115 | style: (style ?? const TextStyle()).copyWith( 116 | wordSpacing: space, 117 | ), 118 | ); 119 | 120 | T textShadow({ 121 | Color color = const Color(0x33000000), 122 | double blurRadius = 0.0, 123 | Offset offset = Offset.zero, 124 | }) => 125 | this.copyWith( 126 | style: (style ?? const TextStyle()).copyWith( 127 | shadows: [ 128 | Shadow( 129 | color: color, 130 | blurRadius: blurRadius, 131 | offset: offset, 132 | ), 133 | ], 134 | ), 135 | ); 136 | 137 | T textElevation( 138 | double elevation, { 139 | double angle = 0.0, 140 | Color color = const Color(0x33000000), 141 | double opacityRatio = 1.0, 142 | }) { 143 | final double calculatedOpacity = 144 | _elevationOpacityCurve(elevation) * opacityRatio; 145 | 146 | final Shadow shadow = Shadow( 147 | color: color.withOpacity(calculatedOpacity), 148 | blurRadius: elevation, 149 | offset: Offset(sin(angle) * elevation, cos(angle) * elevation), 150 | ); 151 | return this.copyWith( 152 | style: (style ?? const TextStyle()).copyWith( 153 | shadows: [ 154 | shadow, 155 | ], 156 | ), 157 | ); 158 | } 159 | 160 | T textColor(Color? color) => this.copyWith( 161 | style: (style ?? const TextStyle()).copyWith( 162 | color: color, 163 | ), 164 | ); 165 | 166 | T textAlignment(TextAlign? align) => this.copyWith(textAlign: align); 167 | 168 | T textDirection(TextDirection? direction) => 169 | this.copyWith(textDirection: direction); 170 | 171 | T textBaseline(TextBaseline? textBaseline) => this.copyWith( 172 | style: (style ?? const TextStyle()).copyWith( 173 | textBaseline: textBaseline, 174 | ), 175 | ); 176 | 177 | T textWidthBasis(TextWidthBasis? textWidthBasis) => 178 | this.copyWith(textWidthBasis: textWidthBasis); 179 | } 180 | -------------------------------------------------------------------------------- /lib/src/animated_text.dart: -------------------------------------------------------------------------------- 1 | part of '../styled_widget.dart'; 2 | 3 | // TODO: why extend text 4 | class _StyledAnimatedTextContainer extends Text { 5 | @override 6 | final String data; 7 | @override 8 | final TextStyle? style; 9 | @override 10 | final StrutStyle? strutStyle; 11 | @override 12 | final TextAlign? textAlign; 13 | @override 14 | final TextDirection? textDirection; 15 | @override 16 | final Locale? locale; 17 | @override 18 | final bool? softWrap; 19 | @override 20 | final TextOverflow? overflow; 21 | @override 22 | final double? textScaleFactor; 23 | @override 24 | final int? maxLines; 25 | @override 26 | final String? semanticsLabel; 27 | @override 28 | final TextWidthBasis? textWidthBasis; 29 | 30 | const _StyledAnimatedTextContainer( 31 | this.data, { 32 | this.locale, 33 | this.maxLines, 34 | this.overflow, 35 | this.semanticsLabel, 36 | this.softWrap, 37 | this.strutStyle, 38 | this.style, 39 | this.textAlign, 40 | this.textDirection, 41 | this.textScaleFactor, 42 | this.textWidthBasis, 43 | }) : super( 44 | data, 45 | locale: locale, 46 | maxLines: maxLines, 47 | overflow: overflow, 48 | semanticsLabel: semanticsLabel, 49 | softWrap: softWrap, 50 | strutStyle: strutStyle, 51 | style: style, 52 | textAlign: textAlign, 53 | textDirection: textDirection, 54 | textScaleFactor: textScaleFactor, 55 | textWidthBasis: textWidthBasis, 56 | ); 57 | 58 | @override 59 | Widget build(BuildContext context) { 60 | final _StyledAnimatedModel? animation = 61 | _StyledInheritedAnimation.of(context)?.animation; 62 | if (animation == null) { 63 | return super.build(context); 64 | } 65 | // assert( 66 | // animation != null, 'You can`t animate without specifying an animation'); 67 | return _AnimatedText( 68 | data, 69 | duration: animation.duration, 70 | curve: animation.curve, 71 | locale: locale, 72 | maxLines: maxLines, 73 | overflow: overflow, 74 | semanticsLabel: semanticsLabel, 75 | softWrap: softWrap, 76 | strutStyle: strutStyle, 77 | style: style, 78 | textAlign: textAlign, 79 | textDirection: textDirection, 80 | textScaleFactor: textScaleFactor, 81 | textWidthBasis: textWidthBasis, 82 | ); 83 | } 84 | } 85 | 86 | class _AnimatedText extends ImplicitlyAnimatedWidget { 87 | /// Creates a container that animates its parameters implicitly. 88 | /// 89 | /// The [curve] and [duration] arguments must not be null. 90 | const _AnimatedText( 91 | this.data, { 92 | Key? key, 93 | this.locale, 94 | this.maxLines, 95 | this.overflow, 96 | this.semanticsLabel, 97 | this.softWrap, 98 | this.strutStyle, 99 | this.style, 100 | this.textAlign, 101 | this.textDirection, 102 | this.textScaleFactor, 103 | this.textWidthBasis, 104 | Curve curve = Curves.linear, 105 | required Duration duration, 106 | VoidCallback? onEnd, 107 | }) : super(key: key, curve: curve, duration: duration, onEnd: onEnd); 108 | 109 | final String data; 110 | final TextStyle? style; 111 | final StrutStyle? strutStyle; 112 | final TextAlign? textAlign; 113 | final TextDirection? textDirection; 114 | final Locale? locale; 115 | final bool? softWrap; 116 | final TextOverflow? overflow; 117 | final double? textScaleFactor; 118 | final int? maxLines; 119 | final String? semanticsLabel; 120 | final TextWidthBasis? textWidthBasis; 121 | 122 | @override 123 | _AnimatedTextState createState() => _AnimatedTextState(); 124 | } 125 | 126 | class _AnimatedTextState extends AnimatedWidgetBaseState<_AnimatedText> { 127 | Tween? _textScaleFactor; 128 | Tween? _fontSize; 129 | Tween? _letterSpacing; 130 | Tween? _wordSpacing; 131 | Tween? _height; 132 | Tween? _decorationThickness; 133 | IntTween? _maxLines; 134 | ColorTween? _color; 135 | ColorTween? _decorationColor; 136 | // TODO: animate background and foreground? 137 | 138 | @override 139 | void forEachTween(TweenVisitor visitor) { 140 | _textScaleFactor = visitor( 141 | _textScaleFactor, 142 | widget.textScaleFactor, 143 | (dynamic value) => Tween(begin: value as double), 144 | ) as Tween?; 145 | _fontSize = visitor( 146 | _fontSize, 147 | widget.style?.fontSize, 148 | (dynamic value) => Tween(begin: value as double), 149 | ) as Tween?; 150 | _letterSpacing = visitor( 151 | _letterSpacing, 152 | widget.style?.letterSpacing, 153 | (dynamic value) => Tween(begin: value as double), 154 | ) as Tween?; 155 | _wordSpacing = visitor( 156 | _wordSpacing, 157 | widget.style?.wordSpacing, 158 | (dynamic value) => Tween(begin: value as double), 159 | ) as Tween?; 160 | _height = visitor( 161 | _height, 162 | widget.style?.height, 163 | (dynamic value) => Tween(begin: value as double), 164 | ) as Tween?; 165 | _decorationThickness = visitor( 166 | _decorationThickness, 167 | widget.style?.decorationThickness, 168 | (dynamic value) => Tween(begin: value as double), 169 | ) as Tween?; 170 | _maxLines = visitor( 171 | _maxLines, 172 | widget.maxLines, 173 | (dynamic value) => IntTween(begin: value as int), 174 | ) as IntTween?; 175 | _color = visitor( 176 | _color, 177 | widget.style?.color, 178 | (dynamic value) => ColorTween(begin: value as Color), 179 | ) as ColorTween?; 180 | _decorationColor = visitor( 181 | _decorationColor, 182 | widget.style?.decorationColor, 183 | (dynamic value) => ColorTween(begin: value as Color), 184 | ) as ColorTween?; 185 | } 186 | 187 | @override 188 | Widget build(BuildContext context) => Text( 189 | widget.data, 190 | style: widget.style?.copyWith( 191 | fontSize: _fontSize?.evaluate(animation), 192 | letterSpacing: _letterSpacing?.evaluate(animation), 193 | wordSpacing: _wordSpacing?.evaluate(animation), 194 | height: _height?.evaluate(animation), 195 | decorationThickness: _decorationThickness?.evaluate(animation), 196 | color: _color?.evaluate(animation), 197 | decorationColor: _decorationColor?.evaluate(animation), 198 | ), 199 | strutStyle: widget.strutStyle, 200 | textAlign: widget.textAlign, 201 | textDirection: widget.textDirection, 202 | locale: widget.locale, 203 | softWrap: widget.softWrap, 204 | overflow: widget.overflow, 205 | textScaleFactor: _textScaleFactor?.evaluate(animation), 206 | maxLines: _maxLines?.evaluate(animation), 207 | semanticsLabel: widget.semanticsLabel, 208 | textWidthBasis: widget.textWidthBasis, 209 | ); 210 | 211 | @override 212 | void debugFillProperties(DiagnosticPropertiesBuilder description) { 213 | super.debugFillProperties(description); 214 | // TODO: debug variables 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /lib/src/animated_widget.dart: -------------------------------------------------------------------------------- 1 | part of '../styled_widget.dart'; 2 | 3 | class _StyledAnimatedModel { 4 | final Duration duration; 5 | final Curve curve; 6 | _StyledAnimatedModel({ 7 | required this.duration, 8 | this.curve = Curves.linear, 9 | }); 10 | } 11 | 12 | class _StyledInheritedAnimation extends InheritedWidget { 13 | final _StyledAnimatedModel? animation; 14 | 15 | const _StyledInheritedAnimation({ 16 | Key? key, 17 | this.animation, 18 | required Widget child, 19 | }) : super(key: key, child: child); 20 | 21 | @override 22 | bool updateShouldNotify(_StyledInheritedAnimation oldAnimation) => 23 | !(oldAnimation.animation?.duration == animation?.duration && 24 | oldAnimation.animation?.curve == animation?.curve); 25 | 26 | static _StyledInheritedAnimation? of(BuildContext context) => 27 | context.dependOnInheritedWidgetOfExactType<_StyledInheritedAnimation>(); 28 | } 29 | 30 | class _StyledAnimatedBuilder extends StatelessWidget { 31 | const _StyledAnimatedBuilder({Key? key, required this.builder}) 32 | : super(key: key); 33 | 34 | final Widget Function(_StyledAnimatedModel) builder; 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | final _StyledAnimatedModel? animation = 39 | _StyledInheritedAnimation.of(context)?.animation; 40 | assert( 41 | animation != null, 42 | '[styled_widget]: Tried to animate a widget without an animation specified. Define your animation using .animate() as an ancestor of the widget you are trying to animate', 43 | ); 44 | return builder(animation!); 45 | } 46 | } 47 | 48 | class _AnimatedDecorationBox extends ImplicitlyAnimatedWidget { 49 | /// The [curve] and [duration] arguments must not be null. 50 | _AnimatedDecorationBox({ 51 | Key? key, 52 | this.decoration, 53 | this.position = DecorationPosition.background, 54 | this.child, 55 | Curve curve = Curves.linear, 56 | required Duration duration, 57 | VoidCallback? onEnd, 58 | }) : assert(decoration == null || decoration.debugAssertIsValid()), 59 | super( 60 | key: key, 61 | curve: curve, 62 | duration: duration, 63 | onEnd: onEnd, 64 | ); 65 | 66 | /// The [child] contained by the container. 67 | /// 68 | /// If null, and if the [constraints] are unbounded or also null, the 69 | /// container will expand to fill all available space in its parent, unless 70 | /// the parent provides unbounded constraints, in which case the container 71 | /// will attempt to be as small as possible. 72 | /// 73 | /// {@macro flutter.widgets.child} 74 | final Widget? child; 75 | 76 | /// The decoration to paint behind the [child]. 77 | /// 78 | /// A shorthand for specifying just a solid color is available in the 79 | /// constructor: set the `color` argument instead of the `decoration` 80 | /// argument. 81 | final Decoration? decoration; 82 | 83 | final DecorationPosition? position; 84 | 85 | @override 86 | _AnimatedDecorationBoxState createState() => _AnimatedDecorationBoxState(); 87 | 88 | @override 89 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 90 | super.debugFillProperties(properties); 91 | properties.add( 92 | DiagnosticsProperty('bg', decoration, defaultValue: null), 93 | ); 94 | // TODO: debug [position]? 95 | } 96 | } 97 | 98 | class _AnimatedDecorationBoxState 99 | extends AnimatedWidgetBaseState<_AnimatedDecorationBox> { 100 | DecorationTween? _decoration; 101 | 102 | @override 103 | void forEachTween(TweenVisitor visitor) { 104 | _decoration = visitor( 105 | _decoration, 106 | widget.decoration, 107 | (dynamic value) => DecorationTween(begin: value as Decoration), 108 | ) as DecorationTween?; 109 | } 110 | 111 | @override 112 | Widget build(BuildContext context) { 113 | return DecoratedBox( 114 | decoration: _decoration?.evaluate(animation) ?? const BoxDecoration(), 115 | position: widget.position ?? DecorationPosition.background, 116 | child: widget.child, 117 | ); 118 | } 119 | 120 | @override 121 | void debugFillProperties(DiagnosticPropertiesBuilder description) { 122 | super.debugFillProperties(description); 123 | description.add( 124 | DiagnosticsProperty( 125 | 'bg', 126 | _decoration, 127 | defaultValue: null, 128 | ), 129 | ); 130 | } 131 | } 132 | 133 | class _AnimatedConstrainedBox extends ImplicitlyAnimatedWidget { 134 | /// The [curve] and [duration] arguments must not be null. 135 | _AnimatedConstrainedBox({ 136 | Key? key, 137 | this.constraints, 138 | this.child, 139 | Curve curve = Curves.linear, 140 | required Duration duration, 141 | VoidCallback? onEnd, 142 | }) : assert(constraints == null || constraints.debugAssertIsValid()), 143 | super( 144 | key: key, 145 | curve: curve, 146 | duration: duration, 147 | onEnd: onEnd, 148 | ); 149 | 150 | /// The [child] contained by the container. 151 | /// 152 | /// If null, and if the [constraints] are unbounded or also null, the 153 | /// container will expand to fill all available space in its parent, unless 154 | /// the parent provides unbounded constraints, in which case the container 155 | /// will attempt to be as small as possible. 156 | /// 157 | /// {@macro flutter.widgets.child} 158 | final Widget? child; 159 | 160 | /// Additional constraints to apply to the child. 161 | /// 162 | /// The constructor `width` and `height` arguments are combined with the 163 | /// `constraints` argument to set this property. 164 | /// 165 | /// The [padding] goes inside the constraints. 166 | final BoxConstraints? constraints; 167 | 168 | @override 169 | _AnimatedConstrainedBoxState createState() => _AnimatedConstrainedBoxState(); 170 | 171 | @override 172 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 173 | super.debugFillProperties(properties); 174 | properties.add( 175 | DiagnosticsProperty( 176 | 'constraints', 177 | constraints, 178 | defaultValue: null, 179 | showName: false, 180 | ), 181 | ); 182 | } 183 | } 184 | 185 | class _AnimatedConstrainedBoxState 186 | extends AnimatedWidgetBaseState<_AnimatedConstrainedBox> { 187 | BoxConstraintsTween? _constraints; 188 | 189 | @override 190 | void forEachTween(TweenVisitor visitor) { 191 | _constraints = visitor( 192 | _constraints, 193 | widget.constraints, 194 | (dynamic value) => BoxConstraintsTween(begin: value as BoxConstraints), 195 | ) as BoxConstraintsTween?; 196 | } 197 | 198 | @override 199 | Widget build(BuildContext context) { 200 | return ConstrainedBox( 201 | constraints: _constraints?.evaluate(animation) ?? const BoxConstraints(), 202 | child: widget.child, 203 | ); 204 | } 205 | 206 | @override 207 | void debugFillProperties(DiagnosticPropertiesBuilder description) { 208 | super.debugFillProperties(description); 209 | description.add( 210 | DiagnosticsProperty( 211 | 'constraints', 212 | _constraints, 213 | showName: false, 214 | defaultValue: null, 215 | ), 216 | ); 217 | } 218 | } 219 | 220 | class _AnimatedTransform extends ImplicitlyAnimatedWidget { 221 | /// Creates a container that animates its parameters implicitly. 222 | /// 223 | /// The [curve] and [duration] arguments must not be null. 224 | const _AnimatedTransform({ 225 | Key? key, 226 | this.transform, 227 | this.origin, 228 | this.alignment, 229 | this.transformHitTests = true, 230 | this.child, 231 | Curve curve = Curves.linear, 232 | required Duration duration, 233 | }) : super( 234 | key: key, 235 | curve: curve, 236 | duration: duration, 237 | ); 238 | 239 | /// The [child] contained by the container. 240 | /// 241 | /// If null, and if the [constraints] are unbounded or also null, the 242 | /// container will expand to fill all available space in its parent, unless 243 | /// the parent provides unbounded constraints, in which case the container 244 | /// will attempt to be as small as possible. 245 | /// 246 | /// {@macro flutter.widgets.child} 247 | final Widget? child; 248 | 249 | final Offset? origin; 250 | 251 | final AlignmentGeometry? alignment; 252 | 253 | final bool? transformHitTests; 254 | 255 | /// The transformation matrix to apply before painting the container. 256 | final Matrix4? transform; 257 | 258 | @override 259 | _AnimatedTransformState createState() => _AnimatedTransformState(); 260 | 261 | @override 262 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 263 | super.debugFillProperties(properties); 264 | properties.add( 265 | DiagnosticsProperty( 266 | 'alignment', 267 | alignment, 268 | showName: false, 269 | defaultValue: null, 270 | ), 271 | ); 272 | properties.add(ObjectFlagProperty.has('transform', transform)); 273 | // TODO: debug [origin], [transformHitTest]? 274 | } 275 | } 276 | 277 | class _AnimatedTransformState 278 | extends AnimatedWidgetBaseState<_AnimatedTransform> { 279 | AlignmentGeometryTween? _alignment; 280 | Matrix4Tween? _transform; 281 | 282 | @override 283 | void forEachTween(TweenVisitor visitor) { 284 | _alignment = visitor( 285 | _alignment, 286 | widget.alignment, 287 | (dynamic value) => AlignmentGeometryTween( 288 | begin: value as AlignmentGeometry?, 289 | ), 290 | ) as AlignmentGeometryTween?; 291 | _transform = visitor( 292 | _transform, 293 | widget.transform, 294 | (dynamic value) => Matrix4Tween( 295 | begin: value as Matrix4?, 296 | ), 297 | ) as Matrix4Tween?; 298 | } 299 | 300 | @override 301 | Widget build(BuildContext context) { 302 | return Transform( 303 | transform: _transform?.evaluate(animation) ?? Matrix4.zero(), 304 | alignment: _alignment?.evaluate(animation), 305 | origin: widget.origin, 306 | transformHitTests: widget.transformHitTests ?? true, 307 | child: widget.child, 308 | ); 309 | } 310 | 311 | @override 312 | void debugFillProperties(DiagnosticPropertiesBuilder description) { 313 | super.debugFillProperties(description); 314 | description.add( 315 | DiagnosticsProperty( 316 | 'alignment', 317 | _alignment, 318 | showName: false, 319 | defaultValue: null, 320 | ), 321 | ); 322 | description 323 | .add(ObjectFlagProperty.has('transform', _transform)); 324 | } 325 | } 326 | 327 | class _AnimatedClipRRect extends ImplicitlyAnimatedWidget { 328 | /// The [curve] and [duration] arguments must not be null. 329 | const _AnimatedClipRRect({ 330 | Key? key, 331 | this.topLeft, 332 | this.topRight, 333 | this.bottomLeft, 334 | this.bottomRight, 335 | this.clipper, 336 | this.clipBehavior, 337 | this.child, 338 | Curve curve = Curves.linear, 339 | required Duration duration, 340 | VoidCallback? onEnd, 341 | }) : super( 342 | key: key, 343 | curve: curve, 344 | duration: duration, 345 | onEnd: onEnd, 346 | ); 347 | 348 | /// The [child] contained by the container. 349 | /// 350 | /// If null, and if the [constraints] are unbounded or also null, the 351 | /// container will expand to fill all available space in its parent, unless 352 | /// the parent provides unbounded constraints, in which case the container 353 | /// will attempt to be as small as possible. 354 | /// 355 | /// {@macro flutter.widgets.child} 356 | final Widget? child; 357 | 358 | final double? topLeft; 359 | final double? topRight; 360 | final double? bottomLeft; 361 | final double? bottomRight; 362 | final CustomClipper? clipper; 363 | final Clip? clipBehavior; 364 | 365 | @override 366 | _AnimatedClipRRectState createState() => _AnimatedClipRRectState(); 367 | } 368 | 369 | class _AnimatedClipRRectState 370 | extends AnimatedWidgetBaseState<_AnimatedClipRRect> { 371 | Tween? _topLeft; 372 | Tween? _topRight; 373 | Tween? _bottomLeft; 374 | Tween? _bottomRight; 375 | 376 | @override 377 | void forEachTween(TweenVisitor visitor) { 378 | _topLeft = visitor( 379 | _topLeft, 380 | widget.topLeft, 381 | (dynamic value) => Tween(begin: value as double), 382 | ) as Tween?; 383 | _topRight = visitor( 384 | _topRight, 385 | widget.topRight, 386 | (dynamic value) => Tween(begin: value as double), 387 | ) as Tween?; 388 | _bottomLeft = visitor( 389 | _bottomLeft, 390 | widget.bottomLeft, 391 | (dynamic value) => Tween(begin: value as double), 392 | ) as Tween?; 393 | _bottomRight = visitor( 394 | _bottomRight, 395 | widget.bottomRight, 396 | (dynamic value) => Tween(begin: value as double), 397 | ) as Tween?; 398 | } 399 | 400 | @override 401 | Widget build(BuildContext context) { 402 | return ClipRRect( 403 | clipper: widget.clipper, 404 | clipBehavior: widget.clipBehavior ?? Clip.antiAlias, 405 | borderRadius: BorderRadius.only( 406 | topLeft: _topLeft != null 407 | ? Radius.circular(_topLeft!.evaluate(animation)) 408 | : Radius.zero, 409 | topRight: _topRight != null 410 | ? Radius.circular(_topRight!.evaluate(animation)) 411 | : Radius.zero, 412 | bottomLeft: _bottomLeft != null 413 | ? Radius.circular(_bottomLeft!.evaluate(animation)) 414 | : Radius.zero, 415 | bottomRight: _bottomRight != null 416 | ? Radius.circular(_bottomRight!.evaluate(animation)) 417 | : Radius.zero, 418 | ), 419 | child: widget.child, 420 | ); 421 | } 422 | 423 | @override 424 | void debugFillProperties(DiagnosticPropertiesBuilder description) { 425 | super.debugFillProperties(description); 426 | //TODO: debug [_topLeft], [_topRight], [_bottomLeft], [_bottomRight] 427 | } 428 | } 429 | 430 | class _AnimatedBackgroundBlur extends ImplicitlyAnimatedWidget { 431 | /// Creates a widget that animates its opacity implicitly. 432 | /// 433 | /// The [opacity] argument must not be null and must be between 0.0 and 1.0, 434 | /// inclusive. The [curve] and [duration] arguments must not be null. 435 | const _AnimatedBackgroundBlur({ 436 | Key? key, 437 | this.child, 438 | required this.sigma, 439 | Curve curve = Curves.linear, 440 | required Duration duration, 441 | VoidCallback? onEnd, 442 | }) : assert(sigma >= 0.0), 443 | super( 444 | key: key, 445 | curve: curve, 446 | duration: duration, 447 | onEnd: onEnd, 448 | ); 449 | 450 | /// The widget below this widget in the tree. 451 | /// 452 | /// {@macro flutter.widgets.child} 453 | final Widget? child; 454 | 455 | final double sigma; 456 | 457 | @override 458 | _AnimatedBackgroundBlurState createState() => _AnimatedBackgroundBlurState(); 459 | 460 | @override 461 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 462 | super.debugFillProperties(properties); 463 | properties.add(DoubleProperty('background blur', sigma)); 464 | } 465 | } 466 | 467 | class _AnimatedBackgroundBlurState 468 | extends ImplicitlyAnimatedWidgetState<_AnimatedBackgroundBlur> { 469 | Tween? _sigma; 470 | 471 | @override 472 | void forEachTween(TweenVisitor visitor) { 473 | _sigma = visitor( 474 | _sigma, 475 | widget.opacity, 476 | (dynamic value) => Tween(begin: value as double), 477 | ) as Tween?; 478 | } 479 | 480 | @override 481 | Widget build(BuildContext context) { 482 | return BackdropFilter( 483 | filter: ImageFilter.blur( 484 | sigmaX: _sigma?.evaluate(animation) ?? 0, 485 | sigmaY: _sigma?.evaluate(animation) ?? 0, 486 | ), 487 | child: widget.child, 488 | ); 489 | } 490 | } 491 | 492 | class _AnimatedOverflowBox extends ImplicitlyAnimatedWidget { 493 | /// Creates a widget that animates its opacity implicitly. 494 | /// 495 | /// The [opacity] argument must not be null and must be between 0.0 and 1.0, 496 | /// inclusive. The [curve] and [duration] arguments must not be null. 497 | const _AnimatedOverflowBox({ 498 | Key? key, 499 | this.child, 500 | this.minWidth, 501 | this.maxWidth, 502 | this.minHeight, 503 | this.maxHeight, 504 | this.alignment, 505 | Curve curve = Curves.linear, 506 | required Duration duration, 507 | VoidCallback? onEnd, 508 | }) : super( 509 | key: key, 510 | curve: curve, 511 | duration: duration, 512 | onEnd: onEnd, 513 | ); 514 | 515 | /// The widget below this widget in the tree. 516 | /// 517 | /// {@macro flutter.widgets.child} 518 | final Widget? child; 519 | final AlignmentGeometry? alignment; 520 | final double? minWidth; 521 | final double? maxWidth; 522 | final double? minHeight; 523 | final double? maxHeight; 524 | 525 | @override 526 | _AnimatedOverflowBoxState createState() => _AnimatedOverflowBoxState(); 527 | } 528 | 529 | class _AnimatedOverflowBoxState 530 | extends ImplicitlyAnimatedWidgetState<_AnimatedOverflowBox> { 531 | Tween? _minWidth; 532 | Tween? _maxWidth; 533 | Tween? _minHeight; 534 | Tween? _maxHeight; 535 | AlignmentGeometryTween? _alignment; 536 | @override 537 | void forEachTween(TweenVisitor visitor) { 538 | _minWidth = visitor( 539 | _minWidth, 540 | widget.minWidth, 541 | (dynamic value) => Tween(begin: value as double), 542 | ) as Tween?; 543 | _maxWidth = visitor( 544 | _maxWidth, 545 | widget.maxWidth, 546 | (dynamic value) => Tween(begin: value as double), 547 | ) as Tween?; 548 | _minHeight = visitor( 549 | _minHeight, 550 | widget.minHeight, 551 | (dynamic value) => Tween(begin: value as double), 552 | ) as Tween?; 553 | _maxHeight = visitor( 554 | _maxHeight, 555 | widget.maxHeight, 556 | (dynamic value) => Tween(begin: value as double), 557 | ) as Tween?; 558 | _alignment = visitor( 559 | _alignment, 560 | widget.alignment, 561 | (dynamic value) => 562 | AlignmentGeometryTween(begin: value as AlignmentGeometry), 563 | ) as AlignmentGeometryTween?; 564 | } 565 | 566 | @override 567 | Widget build(BuildContext context) { 568 | return OverflowBox( 569 | minWidth: _minWidth?.evaluate(animation), 570 | maxWidth: _maxWidth?.evaluate(animation), 571 | minHeight: _minHeight?.evaluate(animation), 572 | maxHeight: _maxHeight?.evaluate(animation), 573 | alignment: _alignment?.evaluate(animation) ?? Alignment.center, 574 | child: widget.child, 575 | ); 576 | } 577 | } 578 | -------------------------------------------------------------------------------- /lib/src/extensions/widget_extension.dart: -------------------------------------------------------------------------------- 1 | part of '../../styled_widget.dart'; 2 | 3 | typedef GestureOnTapChangeCallback = void Function(bool tapState); 4 | 5 | extension StyledWidget on Widget { 6 | _StyledAnimatedModel _getAnimation(BuildContext context) { 7 | final _StyledAnimatedModel? animation = 8 | _StyledInheritedAnimation.of(context)?.animation; 9 | assert( 10 | animation != null, 11 | '[styled_widget]: You can`t animate without defining the animation. Call the method animate() higher in your widget hierarchy to define an animation', 12 | ); 13 | return animation!; 14 | } 15 | 16 | /// animated all properties before this method 17 | Widget animate( 18 | Duration duration, 19 | Curve curve, { 20 | Key? key, 21 | }) => 22 | _StyledInheritedAnimation( 23 | key: key, 24 | animation: _StyledAnimatedModel(duration: duration, curve: curve), 25 | child: this, 26 | ); 27 | 28 | /// Applies a parent to a child 29 | /// ```dart 30 | /// final parentWidget = ({required Widget child}) => Styled.widget(child: child) 31 | /// .alignment(Alignment.center) 32 | /// 33 | /// final childWidget = Text('some text') 34 | /// .padding(all: 10) 35 | /// 36 | /// Widget build(BuildContext) => childWidget 37 | /// .parent(parentWidget); 38 | /// ``` 39 | Widget parent(Widget Function({required Widget child}) parent) => 40 | parent(child: this); 41 | 42 | Widget padding({ 43 | Key? key, 44 | double? all, 45 | double? horizontal, 46 | double? vertical, 47 | double? top, 48 | double? bottom, 49 | double? left, 50 | double? right, 51 | bool animate = false, 52 | }) => 53 | animate 54 | ? Builder( 55 | key: key, 56 | builder: (BuildContext context) { 57 | final _StyledAnimatedModel animation = _getAnimation(context); 58 | return AnimatedPadding( 59 | padding: EdgeInsets.only( 60 | top: top ?? vertical ?? all ?? 0.0, 61 | bottom: bottom ?? vertical ?? all ?? 0.0, 62 | left: left ?? horizontal ?? all ?? 0.0, 63 | right: right ?? horizontal ?? all ?? 0.0, 64 | ), 65 | duration: animation.duration, 66 | curve: animation.curve, 67 | child: this, 68 | ); 69 | }, 70 | ) 71 | : Padding( 72 | key: key, 73 | padding: EdgeInsets.only( 74 | top: top ?? vertical ?? all ?? 0.0, 75 | bottom: bottom ?? vertical ?? all ?? 0.0, 76 | left: left ?? horizontal ?? all ?? 0.0, 77 | right: right ?? horizontal ?? all ?? 0.0, 78 | ), 79 | child: this, 80 | ); 81 | 82 | Widget paddingDirectional({ 83 | Key? key, 84 | double? all, 85 | double? horizontal, 86 | double? vertical, 87 | double? top, 88 | double? bottom, 89 | double? start, 90 | double? end, 91 | bool animate = false, 92 | }) => 93 | animate 94 | ? Builder( 95 | key: key, 96 | builder: (BuildContext context) { 97 | final _StyledAnimatedModel animation = _getAnimation(context); 98 | return AnimatedPadding( 99 | padding: EdgeInsetsDirectional.only( 100 | top: top ?? vertical ?? all ?? 0.0, 101 | bottom: bottom ?? vertical ?? all ?? 0.0, 102 | start: start ?? horizontal ?? all ?? 0.0, 103 | end: end ?? horizontal ?? all ?? 0.0, 104 | ), 105 | duration: animation.duration, 106 | curve: animation.curve, 107 | child: this, 108 | ); 109 | }, 110 | ) 111 | : Padding( 112 | key: key, 113 | padding: EdgeInsetsDirectional.only( 114 | top: top ?? vertical ?? all ?? 0.0, 115 | bottom: bottom ?? vertical ?? all ?? 0.0, 116 | start: start ?? horizontal ?? all ?? 0.0, 117 | end: end ?? horizontal ?? all ?? 0.0, 118 | ), 119 | child: this, 120 | ); 121 | 122 | Widget opacity( 123 | double opacity, { 124 | Key? key, 125 | bool animate = false, 126 | bool alwaysIncludeSemantics = false, 127 | }) => 128 | animate 129 | ? _StyledAnimatedBuilder( 130 | key: key, 131 | builder: (animation) { 132 | return AnimatedOpacity( 133 | opacity: opacity, 134 | alwaysIncludeSemantics: alwaysIncludeSemantics, 135 | duration: animation.duration, 136 | curve: animation.curve, 137 | child: this, 138 | ); 139 | }, 140 | ) 141 | : Opacity( 142 | key: key, 143 | opacity: opacity, 144 | alwaysIncludeSemantics: alwaysIncludeSemantics, 145 | child: this, 146 | ); 147 | 148 | Widget offstage({ 149 | Key? key, 150 | bool offstage = true, 151 | }) => 152 | Offstage( 153 | key: key, 154 | offstage: offstage, 155 | child: this, 156 | ); 157 | 158 | Widget alignment( 159 | AlignmentGeometry alignment, { 160 | Key? key, 161 | bool animate = false, 162 | }) => 163 | animate 164 | ? Builder( 165 | key: key, 166 | builder: (BuildContext context) { 167 | final _StyledAnimatedModel animation = _getAnimation(context); 168 | return AnimatedAlign( 169 | alignment: alignment, 170 | duration: animation.duration, 171 | curve: animation.curve, 172 | child: this, 173 | ); 174 | }, 175 | ) 176 | : Align( 177 | key: key, 178 | alignment: alignment, 179 | child: this, 180 | ); 181 | 182 | Widget backgroundColor( 183 | Color color, { 184 | Key? key, 185 | bool animate = false, 186 | }) => 187 | animate 188 | ? _StyledAnimatedBuilder( 189 | key: key, 190 | builder: (animation) { 191 | return _AnimatedDecorationBox( 192 | decoration: BoxDecoration(color: color), 193 | duration: animation.duration, 194 | curve: animation.curve, 195 | child: this, 196 | ); 197 | }, 198 | ) 199 | : DecoratedBox( 200 | key: key, 201 | decoration: BoxDecoration(color: color), 202 | child: this, 203 | ); 204 | 205 | Widget backgroundImage( 206 | DecorationImage image, { 207 | Key? key, 208 | bool animate = false, 209 | }) => 210 | animate 211 | ? _StyledAnimatedBuilder( 212 | key: key, 213 | builder: (animation) { 214 | return _AnimatedDecorationBox( 215 | decoration: BoxDecoration(image: image), 216 | duration: animation.duration, 217 | curve: animation.curve, 218 | child: this, 219 | ); 220 | }, 221 | ) 222 | : DecoratedBox( 223 | key: key, 224 | decoration: BoxDecoration(image: image), 225 | child: this, 226 | ); 227 | 228 | Widget backgroundGradient( 229 | Gradient gradient, { 230 | Key? key, 231 | bool animate = false, 232 | }) => 233 | animate 234 | ? _StyledAnimatedBuilder( 235 | key: key, 236 | builder: (animation) { 237 | return _AnimatedDecorationBox( 238 | decoration: BoxDecoration(gradient: gradient), 239 | duration: animation.duration, 240 | curve: animation.curve, 241 | child: this, 242 | ); 243 | }, 244 | ) 245 | : DecoratedBox( 246 | key: key, 247 | decoration: BoxDecoration(gradient: gradient), 248 | child: this, 249 | ); 250 | 251 | Widget backgroundLinearGradient({ 252 | Key? key, 253 | AlignmentGeometry begin = Alignment.centerLeft, 254 | AlignmentGeometry end = Alignment.centerRight, 255 | List? colors, 256 | List? stops, 257 | TileMode tileMode = TileMode.clamp, 258 | GradientTransform? transform, 259 | bool animate = false, 260 | }) { 261 | final BoxDecoration decoration = BoxDecoration( 262 | gradient: LinearGradient( 263 | begin: begin, 264 | end: end, 265 | colors: colors ?? [], 266 | stops: stops, 267 | tileMode: tileMode, 268 | transform: transform, 269 | ), 270 | ); 271 | return animate 272 | ? _StyledAnimatedBuilder( 273 | key: key, 274 | builder: (animation) { 275 | return _AnimatedDecorationBox( 276 | decoration: decoration, 277 | duration: animation.duration, 278 | curve: animation.curve, 279 | child: this, 280 | ); 281 | }, 282 | ) 283 | : DecoratedBox( 284 | key: key, 285 | decoration: decoration, 286 | child: this, 287 | ); 288 | } 289 | 290 | Widget backgroundRadialGradient({ 291 | Key? key, 292 | AlignmentGeometry center = Alignment.center, 293 | double radius = 0.5, 294 | List? colors, 295 | List? stops, 296 | TileMode tileMode = TileMode.clamp, 297 | AlignmentGeometry? focal, 298 | double focalRadius = 0.0, 299 | GradientTransform? transform, 300 | bool animate = false, 301 | }) { 302 | final BoxDecoration decoration = BoxDecoration( 303 | gradient: RadialGradient( 304 | center: center, 305 | radius: radius, 306 | colors: colors ?? [], 307 | stops: stops, 308 | tileMode: tileMode, 309 | focal: focal, 310 | focalRadius: focalRadius, 311 | transform: transform, 312 | ), 313 | ); 314 | return animate 315 | ? _StyledAnimatedBuilder( 316 | key: key, 317 | builder: (animation) { 318 | return _AnimatedDecorationBox( 319 | decoration: decoration, 320 | duration: animation.duration, 321 | curve: animation.curve, 322 | child: this, 323 | ); 324 | }, 325 | ) 326 | : DecoratedBox( 327 | key: key, 328 | decoration: decoration, 329 | child: this, 330 | ); 331 | } 332 | 333 | Widget backgroundSweepGradient({ 334 | Key? key, 335 | AlignmentGeometry center = Alignment.center, 336 | double startAngle = 0.0, 337 | double endAngle = pi * 2, 338 | List? colors, 339 | List? stops, 340 | TileMode tileMode = TileMode.clamp, 341 | GradientTransform? transform, 342 | bool animate = false, 343 | }) { 344 | final BoxDecoration decoration = BoxDecoration( 345 | gradient: SweepGradient( 346 | center: center, 347 | startAngle: startAngle, 348 | endAngle: endAngle, 349 | colors: colors ?? [], 350 | stops: stops, 351 | tileMode: tileMode, 352 | transform: transform, 353 | ), 354 | ); 355 | return animate 356 | ? _StyledAnimatedBuilder( 357 | key: key, 358 | builder: (animation) { 359 | return _AnimatedDecorationBox( 360 | decoration: decoration, 361 | duration: animation.duration, 362 | curve: animation.curve, 363 | child: this, 364 | ); 365 | }, 366 | ) 367 | : DecoratedBox( 368 | key: key, 369 | decoration: decoration, 370 | child: this, 371 | ); 372 | } 373 | 374 | Widget backgroundBlendMode( 375 | BlendMode blendMode, { 376 | Key? key, 377 | bool animate = false, 378 | }) => 379 | animate 380 | ? _StyledAnimatedBuilder( 381 | key: key, 382 | builder: (animation) { 383 | return _AnimatedDecorationBox( 384 | decoration: BoxDecoration(backgroundBlendMode: blendMode), 385 | duration: animation.duration, 386 | curve: animation.curve, 387 | child: this, 388 | ); 389 | }, 390 | ) 391 | : DecoratedBox( 392 | key: key, 393 | decoration: BoxDecoration(backgroundBlendMode: blendMode), 394 | child: this, 395 | ); 396 | 397 | Widget backgroundBlur( 398 | double sigma, { 399 | Key? key, 400 | bool animate = false, 401 | }) => 402 | animate 403 | ? _StyledAnimatedBuilder( 404 | key: key, 405 | builder: (animation) { 406 | return _AnimatedBackgroundBlur( 407 | sigma: sigma, 408 | duration: animation.duration, 409 | curve: animation.curve, 410 | child: this, 411 | ); 412 | }, 413 | ) 414 | : BackdropFilter( 415 | key: key, 416 | filter: ImageFilter.blur( 417 | sigmaX: sigma, 418 | sigmaY: sigma, 419 | ), 420 | child: this, 421 | ); 422 | 423 | Widget borderRadius({ 424 | Key? key, 425 | double? all, 426 | double? topLeft, 427 | double? topRight, 428 | double? bottomLeft, 429 | double? bottomRight, 430 | bool animate = false, 431 | }) { 432 | final BoxDecoration decoration = BoxDecoration( 433 | borderRadius: BorderRadius.only( 434 | topLeft: Radius.circular(topLeft ?? all ?? 0.0), 435 | topRight: Radius.circular(topRight ?? all ?? 0.0), 436 | bottomLeft: Radius.circular(bottomLeft ?? all ?? 0.0), 437 | bottomRight: Radius.circular(bottomRight ?? all ?? 0.0), 438 | ), 439 | ); 440 | return animate 441 | ? _StyledAnimatedBuilder( 442 | key: key, 443 | builder: (animation) { 444 | return _AnimatedDecorationBox( 445 | decoration: decoration, 446 | duration: animation.duration, 447 | curve: animation.curve, 448 | child: this, 449 | ); 450 | }, 451 | ) 452 | : DecoratedBox( 453 | key: key, 454 | decoration: decoration, 455 | child: this, 456 | ); 457 | } 458 | 459 | Widget borderRadiusDirectional({ 460 | Key? key, 461 | double? all, 462 | double? topStart, 463 | double? topEnd, 464 | double? bottomStart, 465 | double? bottomEnd, 466 | bool animate = false, 467 | }) { 468 | final BoxDecoration decoration = BoxDecoration( 469 | borderRadius: BorderRadiusDirectional.only( 470 | topStart: Radius.circular(topStart ?? all ?? 0.0), 471 | topEnd: Radius.circular(topEnd ?? all ?? 0.0), 472 | bottomStart: Radius.circular(bottomStart ?? all ?? 0.0), 473 | bottomEnd: Radius.circular(bottomEnd ?? all ?? 0.0), 474 | ), 475 | ); 476 | return animate 477 | ? _StyledAnimatedBuilder( 478 | key: key, 479 | builder: (animation) { 480 | return _AnimatedDecorationBox( 481 | decoration: decoration, 482 | duration: animation.duration, 483 | curve: animation.curve, 484 | child: this, 485 | ); 486 | }, 487 | ) 488 | : DecoratedBox( 489 | key: key, 490 | decoration: decoration, 491 | child: this, 492 | ); 493 | } 494 | 495 | Widget clipRRect({ 496 | Key? key, 497 | double? all, 498 | double? topLeft, 499 | double? topRight, 500 | double? bottomLeft, 501 | double? bottomRight, 502 | CustomClipper? clipper, 503 | Clip clipBehavior = Clip.antiAlias, 504 | bool animate = false, 505 | }) => 506 | animate 507 | ? _StyledAnimatedBuilder( 508 | key: key, 509 | builder: (animation) { 510 | return _AnimatedClipRRect( 511 | clipper: clipper, 512 | clipBehavior: clipBehavior, 513 | topLeft: topLeft ?? all ?? 0.0, 514 | topRight: topRight ?? all ?? 0.0, 515 | bottomLeft: bottomLeft ?? all ?? 0.0, 516 | bottomRight: bottomRight ?? all ?? 0.0, 517 | duration: animation.duration, 518 | curve: animation.curve, 519 | child: this, 520 | ); 521 | }, 522 | ) 523 | : ClipRRect( 524 | key: key, 525 | clipper: clipper, 526 | clipBehavior: clipBehavior, 527 | borderRadius: BorderRadius.only( 528 | topLeft: Radius.circular(topLeft ?? all ?? 0.0), 529 | topRight: Radius.circular(topRight ?? all ?? 0.0), 530 | bottomLeft: Radius.circular(bottomLeft ?? all ?? 0.0), 531 | bottomRight: Radius.circular(bottomRight ?? all ?? 0.0), 532 | ), 533 | child: this, 534 | ); 535 | 536 | Widget clipRect({ 537 | Key? key, 538 | CustomClipper? clipper, 539 | Clip clipBehavior = Clip.hardEdge, 540 | }) => 541 | ClipRect( 542 | key: key, 543 | clipper: clipper, 544 | clipBehavior: clipBehavior, 545 | child: this, 546 | ); 547 | 548 | Widget clipOval({Key? key}) => ClipOval( 549 | key: key, 550 | child: this, 551 | ); 552 | 553 | Widget border({ 554 | Key? key, 555 | double? all, 556 | double? left, 557 | double? right, 558 | double? top, 559 | double? bottom, 560 | Color color = const Color(0xFF000000), 561 | BorderStyle style = BorderStyle.solid, 562 | bool animate = false, 563 | }) { 564 | final BoxDecoration decoration = BoxDecoration( 565 | border: Border( 566 | left: (left ?? all) == null 567 | ? BorderSide.none 568 | : BorderSide(color: color, width: left ?? all ?? 0, style: style), 569 | right: (right ?? all) == null 570 | ? BorderSide.none 571 | : BorderSide(color: color, width: right ?? all ?? 0, style: style), 572 | top: (top ?? all) == null 573 | ? BorderSide.none 574 | : BorderSide(color: color, width: top ?? all ?? 0, style: style), 575 | bottom: (bottom ?? all) == null 576 | ? BorderSide.none 577 | : BorderSide(color: color, width: bottom ?? all ?? 0, style: style), 578 | ), 579 | ); 580 | return animate 581 | ? _StyledAnimatedBuilder( 582 | key: key, 583 | builder: (animation) { 584 | return _AnimatedDecorationBox( 585 | decoration: decoration, 586 | duration: animation.duration, 587 | curve: animation.curve, 588 | child: this, 589 | ); 590 | }, 591 | ) 592 | : DecoratedBox( 593 | key: key, 594 | decoration: decoration, 595 | child: this, 596 | ); 597 | } 598 | 599 | Widget decorated({ 600 | Key? key, 601 | Color? color, 602 | DecorationImage? image, 603 | BoxBorder? border, 604 | BorderRadius? borderRadius, 605 | List? boxShadow, 606 | Gradient? gradient, 607 | BlendMode? backgroundBlendMode, 608 | BoxShape shape = BoxShape.rectangle, 609 | DecorationPosition position = DecorationPosition.background, 610 | bool animate = false, 611 | }) { 612 | final BoxDecoration decoration = BoxDecoration( 613 | color: color, 614 | image: image, 615 | border: border, 616 | borderRadius: borderRadius, 617 | boxShadow: boxShadow, 618 | gradient: gradient, 619 | backgroundBlendMode: backgroundBlendMode, 620 | shape: shape, 621 | ); 622 | return animate 623 | ? _StyledAnimatedBuilder( 624 | key: key, 625 | builder: (animation) { 626 | return _AnimatedDecorationBox( 627 | decoration: decoration, 628 | position: position, 629 | duration: animation.duration, 630 | curve: animation.curve, 631 | child: this, 632 | ); 633 | }, 634 | ) 635 | : DecoratedBox( 636 | key: key, 637 | decoration: decoration, 638 | position: position, 639 | child: this, 640 | ); 641 | } 642 | 643 | double _elevationOpacityCurve(double x) => 644 | pow(x, 1 / 16) / sqrt(pow(x, 2) + 2) + 0.2; 645 | 646 | // TODO: Animate elevation 647 | Widget elevation( 648 | double elevation, { 649 | Key? key, 650 | BorderRadiusGeometry borderRadius = BorderRadius.zero, 651 | Color shadowColor = const Color(0xFF000000), 652 | }) => 653 | Material( 654 | key: key, 655 | color: Colors.transparent, 656 | elevation: elevation, 657 | borderRadius: borderRadius, 658 | shadowColor: shadowColor, 659 | child: this, 660 | ); 661 | 662 | Widget neumorphism({ 663 | Key? key, 664 | required double elevation, 665 | BorderRadius borderRadius = BorderRadius.zero, 666 | Color backgroundColor = const Color(0xffEDF1F5), 667 | double curve = 0.0, 668 | bool animate = false, 669 | }) { 670 | final double offset = elevation / 2; 671 | final int colorOffset = (40 * curve).toInt(); 672 | int adjustColor(int color, int colorOffset) { 673 | final int colorVal = color + colorOffset; 674 | if (colorVal > 255) { 675 | return 255; 676 | } else if (colorVal < 0) { 677 | return 0; 678 | } 679 | return colorVal; 680 | } 681 | 682 | final BoxDecoration decoration = BoxDecoration( 683 | gradient: LinearGradient( 684 | begin: Alignment.topLeft, 685 | end: Alignment.bottomRight, 686 | colors: [ 687 | Color.fromRGBO( 688 | adjustColor(backgroundColor.red, colorOffset), 689 | adjustColor(backgroundColor.green, colorOffset), 690 | adjustColor(backgroundColor.blue, colorOffset), 691 | 1.0, 692 | ), 693 | Color.fromRGBO( 694 | adjustColor(backgroundColor.red, -colorOffset), 695 | adjustColor(backgroundColor.green, -colorOffset), 696 | adjustColor(backgroundColor.blue, -colorOffset), 697 | 1.0, 698 | ), 699 | ], 700 | // stops: [0.90, 0.95], 701 | ), 702 | borderRadius: borderRadius, 703 | boxShadow: [ 704 | BoxShadow( 705 | color: Colors.white, 706 | blurRadius: elevation.abs(), 707 | offset: Offset(-offset, -offset), 708 | ), 709 | BoxShadow( 710 | color: const Color(0xAAA3B1C6), 711 | blurRadius: elevation.abs(), 712 | offset: Offset(offset, offset), 713 | ), 714 | ], 715 | ); 716 | 717 | return animate 718 | ? _StyledAnimatedBuilder( 719 | key: key, 720 | builder: (animation) { 721 | return _AnimatedDecorationBox( 722 | decoration: decoration, 723 | duration: animation.duration, 724 | curve: animation.curve, 725 | child: this, 726 | ); 727 | }, 728 | ) 729 | : DecoratedBox( 730 | key: key, 731 | decoration: decoration, 732 | child: this, 733 | ); 734 | } 735 | 736 | Widget boxShadow({ 737 | Key? key, 738 | Color color = const Color(0xFF000000), 739 | Offset offset = Offset.zero, 740 | double blurRadius = 0.0, 741 | double spreadRadius = 0.0, 742 | bool animate = false, 743 | }) { 744 | final BoxDecoration decoration = BoxDecoration( 745 | boxShadow: [ 746 | BoxShadow( 747 | color: color, 748 | blurRadius: blurRadius, 749 | spreadRadius: spreadRadius, 750 | offset: offset, 751 | ), 752 | ], 753 | ); 754 | return animate 755 | ? _StyledAnimatedBuilder( 756 | key: key, 757 | builder: (animation) { 758 | return _AnimatedDecorationBox( 759 | decoration: decoration, 760 | duration: animation.duration, 761 | curve: animation.curve, 762 | child: this, 763 | ); 764 | }, 765 | ) 766 | : DecoratedBox( 767 | key: key, 768 | decoration: decoration, 769 | child: this, 770 | ); 771 | } 772 | 773 | Widget constrained({ 774 | Key? key, 775 | double? width, 776 | double? height, 777 | double minWidth = 0.0, 778 | double maxWidth = double.infinity, 779 | double minHeight = 0.0, 780 | double maxHeight = double.infinity, 781 | bool animate = false, 782 | }) { 783 | BoxConstraints constraints = BoxConstraints( 784 | minWidth: minWidth, 785 | maxWidth: maxWidth, 786 | minHeight: minHeight, 787 | maxHeight: maxHeight, 788 | ); 789 | constraints = (width != null || height != null) 790 | ? constraints.tighten(width: width, height: height) 791 | : constraints; 792 | return animate 793 | ? _StyledAnimatedBuilder( 794 | key: key, 795 | builder: (animation) { 796 | return _AnimatedConstrainedBox( 797 | constraints: constraints, 798 | duration: animation.duration, 799 | curve: animation.curve, 800 | child: this, 801 | ); 802 | }, 803 | ) 804 | : ConstrainedBox( 805 | key: key, 806 | constraints: constraints, 807 | child: this, 808 | ); 809 | } 810 | 811 | Widget width( 812 | double width, { 813 | Key? key, 814 | bool animate = false, 815 | }) => 816 | animate 817 | ? _StyledAnimatedBuilder( 818 | key: key, 819 | builder: (animation) { 820 | return _AnimatedConstrainedBox( 821 | constraints: BoxConstraints.tightFor(width: width), 822 | duration: animation.duration, 823 | curve: animation.curve, 824 | child: this, 825 | ); 826 | }, 827 | ) 828 | : ConstrainedBox( 829 | key: key, 830 | constraints: BoxConstraints.tightFor(width: width), 831 | child: this, 832 | ); 833 | 834 | Widget height( 835 | double height, { 836 | Key? key, 837 | bool animate = false, 838 | }) => 839 | animate 840 | ? _StyledAnimatedBuilder( 841 | key: key, 842 | builder: (animation) { 843 | return _AnimatedConstrainedBox( 844 | constraints: BoxConstraints.tightFor(height: height), 845 | duration: animation.duration, 846 | curve: animation.curve, 847 | child: this, 848 | ); 849 | }, 850 | ) 851 | : ConstrainedBox( 852 | key: key, 853 | constraints: BoxConstraints.tightFor(height: height), 854 | child: this, 855 | ); 856 | 857 | // TODO: FEATURE: ripple animation 858 | Widget ripple({ 859 | Key? key, 860 | Color? focusColor, 861 | Color? hoverColor, 862 | Color? highlightColor, 863 | Color? splashColor, 864 | InteractiveInkFeatureFactory? splashFactory, 865 | double? radius, 866 | ShapeBorder? customBorder, 867 | bool enableFeedback = true, 868 | bool excludeFromSemantics = false, 869 | FocusNode? focusNode, 870 | bool canRequestFocus = true, 871 | bool autoFocus = false, 872 | bool enable = true, 873 | }) => 874 | enable 875 | ? Builder( 876 | key: key, 877 | builder: (BuildContext context) { 878 | // TODO: PERFORMANCE: findAncestorWidgetOfExactType vs InheritedWidget performance 879 | final GestureDetector? gestures = 880 | context.findAncestorWidgetOfExactType(); 881 | return Material( 882 | color: Colors.transparent, 883 | child: InkWell( 884 | focusColor: focusColor, 885 | hoverColor: hoverColor, 886 | highlightColor: highlightColor, 887 | splashColor: splashColor, 888 | splashFactory: splashFactory, 889 | radius: radius, 890 | customBorder: customBorder, 891 | enableFeedback: enableFeedback, 892 | excludeFromSemantics: excludeFromSemantics, 893 | focusNode: focusNode, 894 | canRequestFocus: canRequestFocus, 895 | autofocus: autoFocus, 896 | onTap: gestures?.onTap, 897 | child: this, 898 | ), 899 | ); 900 | }, 901 | ) 902 | : Builder( 903 | key: key, 904 | builder: (context) => this, 905 | ); 906 | 907 | // TODO: RotatedBox 908 | Widget rotate({ 909 | Key? key, 910 | required double angle, 911 | Offset? origin, 912 | AlignmentGeometry alignment = Alignment.center, 913 | bool transformHitTests = true, 914 | bool animate = false, 915 | }) => 916 | animate 917 | ? _StyledAnimatedBuilder( 918 | key: key, 919 | builder: (animation) { 920 | return _AnimatedTransform( 921 | transform: Matrix4.rotationZ(angle), 922 | alignment: alignment, 923 | origin: origin, 924 | transformHitTests: transformHitTests, 925 | duration: animation.duration, 926 | curve: animation.curve, 927 | child: this, 928 | ); 929 | }, 930 | ) 931 | : Transform.rotate( 932 | key: key, 933 | angle: angle, 934 | alignment: alignment, 935 | origin: origin, 936 | transformHitTests: transformHitTests, 937 | child: this, 938 | ); 939 | 940 | Widget scale({ 941 | Key? key, 942 | double? all, 943 | double? x, 944 | double? y, 945 | Offset? origin, 946 | AlignmentGeometry alignment = Alignment.center, 947 | bool transformHitTests = true, 948 | bool animate = false, 949 | }) => 950 | animate 951 | ? _StyledAnimatedBuilder( 952 | key: key, 953 | builder: (animation) { 954 | return _AnimatedTransform( 955 | transform: Matrix4.diagonal3Values( 956 | x ?? all ?? 0, 957 | y ?? all ?? 0, 958 | 1.0, 959 | ), 960 | alignment: alignment, 961 | transformHitTests: transformHitTests, 962 | duration: animation.duration, 963 | curve: animation.curve, 964 | child: this, 965 | ); 966 | }, 967 | ) 968 | : Transform( 969 | key: key, 970 | transform: 971 | Matrix4.diagonal3Values(x ?? all ?? 0, y ?? all ?? 0, 1.0), 972 | alignment: alignment, 973 | origin: origin, 974 | transformHitTests: transformHitTests, 975 | child: this, 976 | ); 977 | 978 | Widget translate({ 979 | Key? key, 980 | required Offset offset, 981 | bool transformHitTests = true, 982 | bool animate = false, 983 | }) => 984 | animate 985 | ? _StyledAnimatedBuilder( 986 | key: key, 987 | builder: (animation) { 988 | return _AnimatedTransform( 989 | transform: 990 | Matrix4.translationValues(offset.dx, offset.dy, 0.0), 991 | transformHitTests: transformHitTests, 992 | duration: animation.duration, 993 | curve: animation.curve, 994 | child: this, 995 | ); 996 | }, 997 | ) 998 | : Transform.translate( 999 | key: key, 1000 | offset: offset, 1001 | transformHitTests: transformHitTests, 1002 | child: this, 1003 | ); 1004 | 1005 | Widget transform({ 1006 | Key? key, 1007 | required Matrix4 transform, 1008 | Offset? origin, 1009 | AlignmentGeometry? alignment, 1010 | bool transformHitTests = true, 1011 | bool animate = false, 1012 | }) => 1013 | animate 1014 | ? _StyledAnimatedBuilder( 1015 | key: key, 1016 | builder: (animation) { 1017 | return _AnimatedTransform( 1018 | transform: transform, 1019 | origin: origin, 1020 | alignment: alignment, 1021 | transformHitTests: transformHitTests, 1022 | duration: animation.duration, 1023 | curve: animation.curve, 1024 | child: this, 1025 | ); 1026 | }, 1027 | ) 1028 | : Transform( 1029 | key: key, 1030 | transform: transform, 1031 | alignment: alignment, 1032 | origin: origin, 1033 | transformHitTests: transformHitTests, 1034 | child: this, 1035 | ); 1036 | 1037 | Widget overflow({ 1038 | Key? key, 1039 | AlignmentGeometry alignment = Alignment.center, 1040 | double? minWidth, 1041 | double? maxWidth, 1042 | double? minHeight, 1043 | double? maxHeight, 1044 | bool animate = false, 1045 | }) => 1046 | animate 1047 | ? _StyledAnimatedBuilder( 1048 | key: key, 1049 | builder: (animation) { 1050 | return _AnimatedOverflowBox( 1051 | alignment: alignment, 1052 | minWidth: minWidth, 1053 | maxWidth: minWidth, 1054 | minHeight: minHeight, 1055 | maxHeight: maxHeight, 1056 | duration: animation.duration, 1057 | curve: animation.curve, 1058 | child: this, 1059 | ); 1060 | }, 1061 | ) 1062 | : OverflowBox( 1063 | key: key, 1064 | alignment: alignment, 1065 | minWidth: minWidth, 1066 | maxWidth: minWidth, 1067 | minHeight: minHeight, 1068 | maxHeight: maxHeight, 1069 | child: this, 1070 | ); 1071 | 1072 | Widget scrollable({ 1073 | Key? key, 1074 | Axis scrollDirection = Axis.vertical, 1075 | bool reverse = false, 1076 | bool? primary, 1077 | ScrollPhysics? physics, 1078 | ScrollController? controller, 1079 | DragStartBehavior dragStartBehavior = DragStartBehavior.start, 1080 | EdgeInsetsGeometry? padding, 1081 | }) => 1082 | SingleChildScrollView( 1083 | key: key, 1084 | scrollDirection: scrollDirection, 1085 | reverse: reverse, 1086 | primary: primary, 1087 | physics: physics, 1088 | controller: controller, 1089 | dragStartBehavior: dragStartBehavior, 1090 | padding: padding, 1091 | child: this, 1092 | ); 1093 | 1094 | Widget expanded({ 1095 | Key? key, 1096 | int flex = 1, 1097 | }) => 1098 | Expanded( 1099 | key: key, 1100 | flex: flex, 1101 | child: this, 1102 | ); 1103 | 1104 | Widget flexible({ 1105 | Key? key, 1106 | int flex = 1, 1107 | FlexFit fit = FlexFit.loose, 1108 | }) => 1109 | Flexible( 1110 | key: key, 1111 | flex: flex, 1112 | fit: fit, 1113 | child: this, 1114 | ); 1115 | 1116 | Widget positioned({ 1117 | Key? key, 1118 | double? left, 1119 | double? top, 1120 | double? right, 1121 | double? bottom, 1122 | double? width, 1123 | double? height, 1124 | bool animate = false, 1125 | }) => 1126 | animate 1127 | ? _StyledAnimatedBuilder( 1128 | key: key, 1129 | builder: (animation) { 1130 | return AnimatedPositioned( 1131 | duration: animation.duration, 1132 | curve: animation.curve, 1133 | left: left, 1134 | top: top, 1135 | right: right, 1136 | bottom: bottom, 1137 | width: width, 1138 | height: height, 1139 | child: this, 1140 | ); 1141 | }, 1142 | ) 1143 | : Positioned( 1144 | key: key, 1145 | left: left, 1146 | top: top, 1147 | right: right, 1148 | bottom: bottom, 1149 | width: width, 1150 | height: height, 1151 | child: this, 1152 | ); 1153 | 1154 | Widget positionedDirectional({ 1155 | Key? key, 1156 | double? start, 1157 | double? end, 1158 | double? top, 1159 | double? bottom, 1160 | double? width, 1161 | double? height, 1162 | bool animate = false, 1163 | }) => 1164 | animate 1165 | ? _StyledAnimatedBuilder( 1166 | key: key, 1167 | builder: (animation) { 1168 | return AnimatedPositionedDirectional( 1169 | duration: animation.duration, 1170 | curve: animation.curve, 1171 | start: start, 1172 | end: end, 1173 | top: top, 1174 | bottom: bottom, 1175 | width: width, 1176 | height: height, 1177 | child: this, 1178 | ); 1179 | }, 1180 | ) 1181 | : PositionedDirectional( 1182 | key: key, 1183 | start: start, 1184 | end: end, 1185 | top: top, 1186 | bottom: bottom, 1187 | width: width, 1188 | height: height, 1189 | child: this, 1190 | ); 1191 | 1192 | Widget safeArea({ 1193 | Key? key, 1194 | bool top = true, 1195 | bool bottom = true, 1196 | bool left = true, 1197 | bool right = true, 1198 | }) => 1199 | SafeArea( 1200 | key: key, 1201 | top: top, 1202 | bottom: bottom, 1203 | left: left, 1204 | right: right, 1205 | child: this, 1206 | ); 1207 | 1208 | Widget semanticsLabel( 1209 | String label, { 1210 | Key? key, 1211 | }) => 1212 | Semantics.fromProperties( 1213 | key: key, 1214 | properties: SemanticsProperties(label: label), 1215 | child: this, 1216 | ); 1217 | 1218 | Widget gestures({ 1219 | Key? key, 1220 | GestureOnTapChangeCallback? onTapChange, 1221 | GestureTapDownCallback? onTapDown, 1222 | GestureTapUpCallback? onTapUp, 1223 | GestureTapCallback? onTap, 1224 | GestureTapCancelCallback? onTapCancel, 1225 | GestureTapDownCallback? onSecondaryTapDown, 1226 | GestureTapUpCallback? onSecondaryTapUp, 1227 | GestureTapCancelCallback? onSecondaryTapCancel, 1228 | GestureTapCallback? onDoubleTap, 1229 | GestureLongPressCallback? onLongPress, 1230 | GestureLongPressStartCallback? onLongPressStart, 1231 | GestureLongPressMoveUpdateCallback? onLongPressMoveUpdate, 1232 | GestureLongPressUpCallback? onLongPressUp, 1233 | GestureLongPressEndCallback? onLongPressEnd, 1234 | GestureDragDownCallback? onVerticalDragDown, 1235 | GestureDragStartCallback? onVerticalDragStart, 1236 | GestureDragUpdateCallback? onVerticalDragUpdate, 1237 | GestureDragEndCallback? onVerticalDragEnd, 1238 | GestureDragCancelCallback? onVerticalDragCancel, 1239 | GestureDragDownCallback? onHorizontalDragDown, 1240 | GestureDragStartCallback? onHorizontalDragStart, 1241 | GestureDragUpdateCallback? onHorizontalDragUpdate, 1242 | GestureDragEndCallback? onHorizontalDragEnd, 1243 | GestureDragCancelCallback? onHorizontalDragCancel, 1244 | GestureDragDownCallback? onPanDown, 1245 | GestureDragStartCallback? onPanStart, 1246 | GestureDragUpdateCallback? onPanUpdate, 1247 | GestureDragEndCallback? onPanEnd, 1248 | GestureDragCancelCallback? onPanCancel, 1249 | GestureScaleStartCallback? onScaleStart, 1250 | GestureScaleUpdateCallback? onScaleUpdate, 1251 | GestureScaleEndCallback? onScaleEnd, 1252 | GestureForcePressStartCallback? onForcePressStart, 1253 | GestureForcePressPeakCallback? onForcePressPeak, 1254 | GestureForcePressUpdateCallback? onForcePressUpdate, 1255 | GestureForcePressEndCallback? onForcePressEnd, 1256 | HitTestBehavior? behavior, 1257 | bool excludeFromSemantics = false, 1258 | DragStartBehavior dragStartBehavior = DragStartBehavior.start, 1259 | }) => 1260 | GestureDetector( 1261 | key: key, 1262 | onTapDown: (TapDownDetails tapDownDetails) { 1263 | if (onTapDown != null) onTapDown(tapDownDetails); 1264 | if (onTapChange != null) onTapChange(true); 1265 | }, 1266 | onTapCancel: () { 1267 | if (onTapCancel != null) onTapCancel(); 1268 | if (onTapChange != null) onTapChange(false); 1269 | }, 1270 | onTap: () { 1271 | if (onTap != null) onTap(); 1272 | if (onTapChange != null) onTapChange(false); 1273 | }, 1274 | onTapUp: onTapUp, 1275 | onDoubleTap: onDoubleTap, 1276 | onLongPress: onLongPress, 1277 | onLongPressStart: onLongPressStart, 1278 | onLongPressEnd: onLongPressEnd, 1279 | onLongPressMoveUpdate: onLongPressMoveUpdate, 1280 | onLongPressUp: onLongPressUp, 1281 | onVerticalDragStart: onVerticalDragStart, 1282 | onVerticalDragEnd: onVerticalDragEnd, 1283 | onVerticalDragDown: onVerticalDragDown, 1284 | onVerticalDragCancel: onVerticalDragCancel, 1285 | onVerticalDragUpdate: onVerticalDragUpdate, 1286 | onHorizontalDragStart: onHorizontalDragStart, 1287 | onHorizontalDragEnd: onHorizontalDragEnd, 1288 | onHorizontalDragCancel: onHorizontalDragCancel, 1289 | onHorizontalDragUpdate: onHorizontalDragUpdate, 1290 | onHorizontalDragDown: onHorizontalDragDown, 1291 | onForcePressStart: onForcePressStart, 1292 | onForcePressEnd: onForcePressEnd, 1293 | onForcePressPeak: onForcePressPeak, 1294 | onForcePressUpdate: onForcePressUpdate, 1295 | onPanStart: onPanStart, 1296 | onPanEnd: onPanEnd, 1297 | onPanCancel: onPanCancel, 1298 | onPanDown: onPanDown, 1299 | onPanUpdate: onPanUpdate, 1300 | onScaleStart: onScaleStart, 1301 | onScaleEnd: onScaleEnd, 1302 | onScaleUpdate: onScaleUpdate, 1303 | behavior: behavior, 1304 | excludeFromSemantics: excludeFromSemantics, 1305 | dragStartBehavior: dragStartBehavior, 1306 | child: this, 1307 | ); 1308 | 1309 | // TODO: FEATURE: animate aspectRatio widget 1310 | Widget aspectRatio({ 1311 | Key? key, 1312 | required double aspectRatio, 1313 | }) => 1314 | AspectRatio( 1315 | key: key, 1316 | aspectRatio: aspectRatio, 1317 | child: this, 1318 | ); 1319 | 1320 | // TODO: FEATURE: animate center widget 1321 | Widget center({ 1322 | Key? key, 1323 | double? widthFactor, 1324 | double? heightFactor, 1325 | }) => 1326 | Center( 1327 | key: key, 1328 | widthFactor: widthFactor, 1329 | heightFactor: heightFactor, 1330 | child: this, 1331 | ); 1332 | 1333 | // TODO: FEATURE: animate fittedBox 1334 | Widget fittedBox({ 1335 | Key? key, 1336 | BoxFit fit = BoxFit.contain, 1337 | AlignmentGeometry alignment = Alignment.center, 1338 | }) => 1339 | FittedBox( 1340 | key: key, 1341 | fit: fit, 1342 | alignment: alignment, 1343 | child: this, 1344 | ); 1345 | 1346 | // TODO: FEATURE: animate FractionallySizedBox 1347 | Widget fractionallySizedBox({ 1348 | Key? key, 1349 | AlignmentGeometry alignment = Alignment.center, 1350 | double? widthFactor, 1351 | double? heightFactor, 1352 | }) => 1353 | FractionallySizedBox( 1354 | key: key, 1355 | alignment: alignment, 1356 | widthFactor: widthFactor, 1357 | heightFactor: heightFactor, 1358 | child: this, 1359 | ); 1360 | 1361 | // TODO: FEATURE: animate card 1362 | Widget card({ 1363 | Key? key, 1364 | Color? color, 1365 | double? elevation, 1366 | ShapeBorder? shape, 1367 | bool borderOnForeground = true, 1368 | EdgeInsetsGeometry? margin, 1369 | Clip? clipBehavior, 1370 | bool semanticContainer = true, 1371 | }) => 1372 | Card( 1373 | key: key, 1374 | color: color, 1375 | elevation: elevation, 1376 | shape: shape, 1377 | borderOnForeground: borderOnForeground, 1378 | margin: margin, 1379 | clipBehavior: clipBehavior, 1380 | semanticContainer: semanticContainer, 1381 | child: this, 1382 | ); 1383 | 1384 | Widget limitedBox({ 1385 | Key? key, 1386 | double maxWidth = double.infinity, 1387 | double maxHeight = double.infinity, 1388 | }) => 1389 | LimitedBox( 1390 | key: key, 1391 | maxWidth: maxWidth, 1392 | maxHeight: maxHeight, 1393 | child: this, 1394 | ); 1395 | 1396 | Widget material({ 1397 | Key? key, 1398 | MaterialType type = MaterialType.canvas, 1399 | double elevation = 0.0, 1400 | Color? color, 1401 | Color? shadowColor, 1402 | TextStyle? textStyle, 1403 | BorderRadiusGeometry? borderRadius, 1404 | ShapeBorder? shape, 1405 | bool borderOnForeground = true, 1406 | Clip clipBehavior = Clip.none, 1407 | Duration animationDuration = kThemeChangeDuration, 1408 | }) => 1409 | Material( 1410 | key: key, 1411 | type: type, 1412 | elevation: elevation, 1413 | color: color, 1414 | shadowColor: shadowColor, 1415 | textStyle: textStyle, 1416 | borderRadius: borderRadius, 1417 | shape: shape, 1418 | borderOnForeground: borderOnForeground, 1419 | clipBehavior: clipBehavior, 1420 | animationDuration: animationDuration, 1421 | child: this, 1422 | ); 1423 | 1424 | Widget mouseRegion({ 1425 | Key? key, 1426 | void Function(PointerEnterEvent)? onEnter, 1427 | void Function(PointerExitEvent)? onExit, 1428 | void Function(PointerHoverEvent)? onHover, 1429 | MouseCursor cursor = MouseCursor.defer, 1430 | bool opaque = true, 1431 | }) => 1432 | MouseRegion( 1433 | key: key, 1434 | onEnter: onEnter, 1435 | onExit: onExit, 1436 | onHover: onHover, 1437 | cursor: cursor, 1438 | opaque: opaque, 1439 | child: this, 1440 | ); 1441 | } 1442 | --------------------------------------------------------------------------------