diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b4f215..bdc718e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,26 +1,33 @@ -## [0.2.1] - (2021-04-02) +## 0.3.0 (2022-03-18) + +- Several minor Fixes +- Performance improvements +- BREAKING: Changes default value of `padding` to `EdgeInsets.zero` +- BREAKING: Changes default color to `Theme.of(context).colorScheme.secondary` + +## 0.2.1 (2021-04-02) - Minor Changes/Fixes -- Changes default values of CircularWidgetLoading +- Changes default values of `CircularWidgetLoading` -## [0.2.0] - (2021-03-04) +## 0.2.0 (2021-03-04) - Migrates to null safety -## [0.1.2] - (2021-03-02) +## 0.1.2 (2021-03-02) -- Fixes key handling if animatedSize is false +- Fixes key handling if `animatedSize` is false - Removes warnings in code - Improves performance -## [0.1.1] - (2021-03-02) +## 0.1.1 (2021-03-02) -- Fixes key handling in CircularWidgetLoading +- Fixes key handling in `CircularWidgetLoading` - Adds some comments in the code - Adds GIF in README.md -## [0.1.0] - (2021-03-02) +## 0.1.0 (2021-03-02) - Initial release -- WiperLoading -- CircularWidgetLoading +- `WiperLoading` +- `CircularWidgetLoading` diff --git a/README.md b/README.md index 13da3ed..6e3544d 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ pub.dev github [![likes](https://badges.bar/widget_loading/likes)](https://pub.dev/packages/widget_loading/score) +[![popularity](https://badges.bar/widget_loading/popularity)](https://pub.dev/packages/widget_loading/score) [![pub points](https://badges.bar/widget_loading/pub%20points)](https://pub.dev/packages/widget_loading/score) license diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 6b4c0f7..f2872cf 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 9.0 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 1aec2aa..9690c8f 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -127,7 +127,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1d526a1..919434a 100644 --- a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140c..3db53b6 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ { ), counterCard(Curves.easeInOutCirc), counterCard(Curves.easeInOutCirc, - builder: (width, height) => - Container(width: width, height: height, decoration: BoxDecoration(color: Colors.purple, borderRadius: BorderRadius.circular(5.0))), + builder: (width, height) => Container( + width: width, + height: height, + decoration: BoxDecoration( + color: Colors.purple, + borderRadius: BorderRadius.circular(5.0))), wiperWidth: 50), counterCard( Curves.linear, - builder: (width, height) => - Container(width: width, height: height, decoration: BoxDecoration(color: Colors.purple, borderRadius: BorderRadius.circular(5.0))), + builder: (width, height) => Container( + width: width, + height: height, + decoration: BoxDecoration( + color: Colors.purple, + borderRadius: BorderRadius.circular(5.0))), wiperWidth: 10, deformingFactor: 0.2, direction: WiperDirection.up, ), - counterCard(Curves.easeInOutCirc, padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 30.0)), + counterCard(Curves.easeInOutCirc, + padding: EdgeInsets.symmetric( + horizontal: 15.0, vertical: 30.0)), counterCard(Curves.easeOutSine, builder: (width, height) => Container( width: width, height: height, - decoration: BoxDecoration(color: Colors.red, shape: BoxShape.circle), + decoration: BoxDecoration( + color: Colors.red, shape: BoxShape.circle), ), wiperWidth: 20), counterCard(Curves.easeOutSine, - builder: (width, height) => - Container(width: width, height: height, decoration: BoxDecoration(color: Colors.purple, borderRadius: BorderRadius.circular(5.0))), + builder: (width, height) => Container( + width: width, + height: height, + decoration: BoxDecoration( + color: Colors.purple, + borderRadius: BorderRadius.circular(5.0))), wiperWidth: 20, direction: WiperDirection.left), counterCardCircle(Curves.linear), @@ -171,7 +186,8 @@ class _ExampleState extends State { ); Widget counterCardCircle(Curve curve, {WiperBuilder? builder}) => InkWell( - onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (c) => LoadingScaffold())), + onTap: () => Navigator.of(context) + .push(MaterialPageRoute(builder: (c) => LoadingScaffold())), child: Card( elevation: 5.0, child: CircularWidgetLoading( @@ -180,7 +196,8 @@ class _ExampleState extends State { rollingFactor: 0.8, loading: loading, child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 50.0), + padding: const EdgeInsets.symmetric( + horizontal: 15.0, vertical: 50.0), child: ListTile( leading: Text( 'Counter', diff --git a/example/pubspec.lock b/example/pubspec.lock index e5dd076..bc87a11 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.8.2" boolean_selector: dependency: transitive description: @@ -21,14 +21,14 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" clock: dependency: transitive description: @@ -73,14 +73,21 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.0" path: dependency: transitive description: @@ -99,7 +106,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" stack_trace: dependency: transitive description: @@ -134,7 +141,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19" + version: "0.4.8" typed_data: dependency: transitive description: @@ -148,14 +155,14 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" widget_loading: dependency: "direct main" description: path: ".." relative: true source: path - version: "0.2.1" + version: "0.3.0" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.14.0 <3.0.0" flutter: ">=1.17.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 8b8bc1a..579e15e 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,19 +1,19 @@ name: example description: Example Project for the package widget_loading. +publish_to: 'none' + version: 1.0.0+1 environment: - sdk: ">=2.12.0-259.16.beta <3.0.0" + sdk: ">=2.15.0 <3.0.0" dependencies: flutter: sdk: flutter widget_loading: - path: ../ - - cupertino_icons: ^1.0.0 + path: '..' dev_dependencies: flutter_test: diff --git a/lib/src/utils/extensions.dart b/lib/src/utils/extensions.dart index 7e0fc3f..ae30a7f 100644 --- a/lib/src/utils/extensions.dart +++ b/lib/src/utils/extensions.dart @@ -11,11 +11,13 @@ extension Derivation on CurvedAnimation { case AnimationStatus.forward: double value = max(this.parent.value - dif, 0.0); if (value == this.parent.value) return 0.0; - return (this.value - this.curve.transform(value)) / (this.parent.value - value); + return (this.value - this.curve.transform(value)) / + (this.parent.value - value); case AnimationStatus.reverse: double value = min(this.parent.value + dif, 1.0); if (value == this.parent.value) return 0.0; - return (this.value - this.curve.transform(value)) / (value - this.parent.value); + return (this.value - this.curve.transform(value)) / + (value - this.parent.value); case AnimationStatus.dismissed: case AnimationStatus.completed: break; diff --git a/lib/src/widgets/circular_widget_loading.dart b/lib/src/widgets/circular_widget_loading.dart index a010f98..2d95213 100644 --- a/lib/src/widgets/circular_widget_loading.dart +++ b/lib/src/widgets/circular_widget_loading.dart @@ -3,7 +3,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:widget_loading/src/utils/loading_state.dart'; import 'package:widget_loading/src/widgets/loading_widget.dart'; -import 'package:widget_loading/src/widgets/widget_sized_box.dart'; +import 'package:widget_loading/src/widgets/widget_wrapper.dart'; typedef DotBuilder = Widget Function(double radius); @@ -77,7 +77,7 @@ class CircularWidgetLoading extends StatefulWidget { this.loadingDuration = const Duration(milliseconds: 2000), this.appearingCurve = Curves.fastOutSlowIn, this.loadingCurve = Curves.easeInOutCirc, - this.padding = const EdgeInsets.all(10.0), + this.padding = EdgeInsets.zero, this.dotBuilder, this.rollingDuration = 1.0, this.dotCount = 5, @@ -91,15 +91,18 @@ class CircularWidgetLoading extends StatefulWidget { _CircularWidgetLoadingState createState() => _CircularWidgetLoadingState(); } -class _CircularWidgetLoadingState extends State with TickerProviderStateMixin, LoadingWidgetState { +class _CircularWidgetLoadingState + extends LoadingWidgetState + with TickerProviderStateMixin { late AnimationController _controller; late AnimationController _appearingController; - late Animation _appearingAnimation; - List> _animations = []; + late CurvedAnimation _appearingAnimation; + List _animations = []; final _childKey = GlobalKey(); + final _animatedSizeKey = GlobalKey(); - Widget _child = Container(); + Widget _child = SizedBox(); @override void initState() { @@ -109,19 +112,12 @@ class _CircularWidgetLoadingState extends State with Tick assert(widget.rollingFactor >= 0 && widget.rollingFactor <= 1); assert(widget.minDotRadiusFactor >= 0 && widget.minDotRadiusFactor <= 1); - loadingState = widget.loading ? LoadingState.LOADING : LoadingState.LOADED; - _child = widget.child; _appearingController = AnimationController( duration: widget.appearingDuration, vsync: this, - ) - ..addListener(() { - if (!appearing && !disappearing) return; - setState(() {}); - }) - ..addStatusListener((status) { + )..addStatusListener((status) { switch (status) { case AnimationStatus.dismissed: if (disappearing) { @@ -139,17 +135,13 @@ class _CircularWidgetLoadingState extends State with Tick } }); - _appearingAnimation = CurvedAnimation(parent: _appearingController, curve: widget.appearingCurve); + _appearingAnimation = CurvedAnimation( + parent: _appearingController, curve: widget.appearingCurve); _controller = AnimationController( duration: widget.loadingDuration, vsync: this, - ) - ..addListener(() { - if (!loading) return; - setState(() {}); - }) - ..addStatusListener((status) { + )..addStatusListener((status) { switch (status) { case AnimationStatus.forward: break; @@ -161,19 +153,42 @@ class _CircularWidgetLoadingState extends State with Tick if (!widget.loading && loading) { loadingState = LoadingState.APPEARING; _appearingController.forward(from: 0.0); - } else - WidgetsBinding.instance?.addPostFrameCallback((_) => _controller.forward(from: 0.0)); + } else { + _controller.forward(from: 0.0); + } break; } }); - double dif = widget.dotCount <= 1 ? 0 : widget.rollingDuration * (1 - widget.rollingFactor) / (widget.dotCount - 1); - double singleRollingDuration = widget.rollingDuration * widget.rollingFactor; - for (int i = 0; i < widget.dotCount; i++) { - _animations.add(CurvedAnimation(parent: _controller, curve: Interval(i * dif, singleRollingDuration + i * dif, curve: widget.loadingCurve))); - } + _generateDotAnimations(); + _setDotCurves(); + + setLoadingState(widget.loading ? LoadingState.LOADING : LoadingState.LOADED, + rebuild: false); + if (loading) + _controller.forward(); + else + _appearingController.value = 1.0; + } + + void _generateDotAnimations() { + _animations.forEach((a) => a.dispose()); + _animations = List.generate(widget.dotCount, + (index) => CurvedAnimation(parent: _controller, curve: Curves.linear)); + } - _controller.forward(); + void _setDotCurves() { + double dif = _animations.length <= 1 + ? 0 + : widget.rollingDuration * + (1 - widget.rollingFactor) / + (_animations.length - 1); + double singleRollingDuration = + widget.rollingDuration * widget.rollingFactor; + for (int i = 0; i < _animations.length; i++) { + _animations[i].curve = Interval(i * dif, singleRollingDuration + i * dif, + curve: widget.loadingCurve); + } } @override @@ -183,86 +198,129 @@ class _CircularWidgetLoadingState extends State with Tick super.dispose(); } - Widget animatedSizeWidget(Key key) => Stack( - children: [ - //Container(width: widget.minWidth, height: widget.minHeight,), - Padding( - padding: widget.padding, - child: IgnorePointer( - ignoring: !loaded, - child: widget.animatedSize - ? AnimatedSize(key: key, duration: widget.sizeDuration, vsync: this, curve: widget.sizeCurve, child: _child) - : Container(key: key, child: _child)), - ), - ], - ); + Widget get _animatedSizeWidget { + final wrappedChild = WidgetWrapper(key: _childKey, child: _child); + + return Padding( + padding: widget.padding, + child: IgnorePointer( + ignoring: !loaded, + child: widget.animatedSize + ? AnimatedSize( + key: _animatedSizeKey, + duration: widget.sizeDuration, + curve: widget.sizeCurve, + child: wrappedChild) + : wrappedChild), + ); + } @override - Widget build(BuildContext context) { + void didUpdateWidget(covariant CircularWidgetLoading oldWidget) { + super.didUpdateWidget(oldWidget); + + if (_animations.length != widget.dotCount) _generateDotAnimations(); + if (_animations.length != widget.dotCount || + oldWidget.rollingFactor != widget.rollingFactor || + oldWidget.rollingDuration != widget.rollingFactor || + oldWidget.loadingCurve != widget.loadingCurve) _setDotCurves(); + + _appearingController.duration = widget.appearingDuration; + _appearingAnimation.curve = widget.appearingCurve; + _controller.duration = widget.loadingDuration; + if ((loaded || appearing) && widget.loading) { - loadingState = LoadingState.DISAPPEARING; + setLoadingState(LoadingState.DISAPPEARING, rebuild: false); _appearingController.reverse(); } else if (disappearing && !widget.loading) { - loadingState = LoadingState.APPEARING; + setLoadingState(LoadingState.APPEARING, rebuild: false); _appearingController.forward(); } + } + @override + Widget build(BuildContext context) { if (!disappearing) _child = widget.child; - - Widget loadedChild = animatedSizeWidget(_childKey); ThemeData theme = Theme.of(context); - Color dotColor = widget.dotColor ?? theme.accentColor; - TextDirection textDirection = Directionality.maybeOf(context) ?? TextDirection.ltr; + Widget loadedChild = _animatedSizeWidget; + Color dotColor = widget.dotColor ?? theme.colorScheme.secondary; + TextDirection textDirection = + Directionality.maybeOf(context) ?? TextDirection.ltr; Widget stack = Stack( children: [ - if (loading) + if (loading) ...[ WidgetSizedBox( child: loadedChild, ), - if (loaded) - loadedChild - else if (appearing || disappearing) - ClipOval( - clipper: _DotClipper(_appearingAnimation.value, widget.dotRadius, widget.maxLoadingCircleSize, widget.loadingCirclePadding), - child: Stack(children: [ - Container( - foregroundDecoration: BoxDecoration(color: dotColor.withOpacity(dotColor.opacity * (1 - _appearingAnimation.value))), child: loadedChild) - ]), - ) - else Positioned.fill( child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { - double radius = - min(widget.maxLoadingCircleSize, min(constraints.maxWidth, constraints.maxHeight) - 2 * widget.loadingCirclePadding) / 2 - widget.dotRadius; + double radius = min( + widget.maxLoadingCircleSize, + min(constraints.maxWidth, constraints.maxHeight) - + 2 * widget.loadingCirclePadding) / + 2 - + widget.dotRadius; double x = constraints.maxWidth / 2; double y = constraints.maxHeight / 2; return Stack( - children: List.generate(_animations.length, (index) => index).map((i) { + children: + List.generate(_animations.length, (index) => index) + .map((i) { Animation animation = _animations[i]; - double radian = 0.5 * pi - 2 * pi * animation.value; - double dotRadius = widget.dotRadius * (widget.minDotRadiusFactor + (1 - widget.minDotRadiusFactor) * (1 - i / _animations.length)); - return Positioned( - child: widget.dotBuilder?.call(widget.dotRadius) ?? loadingPoint(dotRadius), - top: y - radius * sin(radian) - dotRadius, - left: x - radius * cos(radian) - dotRadius, - ); + double dotRadius = widget.dotRadius * + (widget.minDotRadiusFactor + + (1 - widget.minDotRadiusFactor) * + (1 - i / _animations.length)); + return AnimatedBuilder( + animation: animation, + child: widget.dotBuilder?.call(widget.dotRadius) ?? + loadingPoint(dotRadius, dotColor), + builder: (context, child) { + double radian = 0.5 * pi - 2 * pi * animation.value; + return Positioned( + child: child!, + top: y - radius * sin(radian) - dotRadius, + left: x - radius * cos(radian) - dotRadius, + ); + }); }).toList()); }, ), ), + ] else if (loaded) + loadedChild + else + AnimatedBuilder( + animation: _appearingAnimation, + builder: (context, child) => ClipOval( + clipper: _DotClipper(_appearingAnimation.value, widget.dotRadius, + widget.maxLoadingCircleSize, widget.loadingCirclePadding), + child: Stack(children: [ + DecoratedBox( + position: DecorationPosition.foreground, + decoration: BoxDecoration( + color: dotColor.withOpacity(dotColor.opacity * + (1 - _appearingAnimation.value))), + child: loadedChild) + ]), + ), + ) ], ); return Directionality(textDirection: textDirection, child: stack); } - Widget loadingPoint(double radius) { + Widget loadingPoint(double radius, Color color) { return Container( width: radius * 2, height: radius * 2, - decoration: BoxDecoration(color: widget.dotColor ?? Theme.of(context).accentColor, borderRadius: BorderRadius.all(Radius.circular(radius))), + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.all(Radius.circular(radius)), + ), ); } } @@ -273,17 +331,23 @@ class _DotClipper extends CustomClipper { final double maxLoadingCircleSize; final double loadingCirclePadding; - _DotClipper(this.factor, this.dotRadius, this.maxLoadingCircleSize, this.loadingCirclePadding); + _DotClipper(this.factor, this.dotRadius, this.maxLoadingCircleSize, + this.loadingCirclePadding); @override Rect getClip(Size size) { - double radius = min(maxLoadingCircleSize, min(size.width, size.height) - 2 * loadingCirclePadding) / 2 - dotRadius; + double radius = min(maxLoadingCircleSize, + min(size.width, size.height) - 2 * loadingCirclePadding) / + 2 - + dotRadius; double x = size.width / 2; double y = size.height / 2; double maxAppearingRadius = sqrt(x * x + y * y); - double appearingRadius = dotRadius + factor * (maxAppearingRadius - dotRadius); - return Rect.fromCircle(center: Offset(x, y - radius), radius: appearingRadius); + double appearingRadius = + dotRadius + factor * (maxAppearingRadius - dotRadius); + return Rect.fromCircle( + center: Offset(x, y - radius), radius: appearingRadius); } @override diff --git a/lib/src/widgets/loading_widget.dart b/lib/src/widgets/loading_widget.dart index 70f4060..1186c69 100644 --- a/lib/src/widgets/loading_widget.dart +++ b/lib/src/widgets/loading_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:widget_loading/src/utils/loading_state.dart'; +// unused because of problems with documentation of constructor parameters abstract class LoadingWidget extends StatefulWidget { final Widget child; @@ -46,13 +47,20 @@ abstract class LoadingWidget extends StatefulWidget { : super(key: key); } -abstract class LoadingWidgetState { +abstract class LoadingWidgetState extends State { LoadingState _loadingState = LoadingState.LOADED; set loadingState(LoadingState value) { + setLoadingState(value); + } + + void setLoadingState(LoadingState value, {bool rebuild = true}) { _loadingState = value; + if (rebuild && mounted) setState(() {}); } + LoadingState get loadingState => _loadingState; + bool get disappearing => _loadingState == LoadingState.DISAPPEARING; bool get appearing => _loadingState == LoadingState.APPEARING; diff --git a/lib/src/widgets/widget_sized_box.dart b/lib/src/widgets/widget_wrapper.dart similarity index 58% rename from lib/src/widgets/widget_sized_box.dart rename to lib/src/widgets/widget_wrapper.dart index dea9465..e451e24 100644 --- a/lib/src/widgets/widget_sized_box.dart +++ b/lib/src/widgets/widget_wrapper.dart @@ -13,3 +13,13 @@ class WidgetSizedBox extends StatelessWidget { ); } } + +class WidgetWrapper extends StatelessWidget { + final Widget child; + const WidgetWrapper({Key? key, required this.child}) : super(key: key); + + @override + Widget build(BuildContext context) { + return child; + } +} diff --git a/lib/src/widgets/wiper_loading.dart b/lib/src/widgets/wiper_loading.dart index 3067eea..0f582c5 100644 --- a/lib/src/widgets/wiper_loading.dart +++ b/lib/src/widgets/wiper_loading.dart @@ -1,11 +1,10 @@ import 'dart:async'; import 'dart:math'; -import 'package:flutter/animation.dart'; import 'package:flutter/material.dart'; import 'package:widget_loading/src/utils/extensions.dart'; import 'package:widget_loading/src/utils/loading_state.dart'; -import 'package:widget_loading/src/widgets/widget_sized_box.dart'; +import 'package:widget_loading/src/widgets/widget_wrapper.dart'; import 'loading_widget.dart'; @@ -121,9 +120,11 @@ class WiperLoading extends StatefulWidget { _WiperLoadingState createState() => _WiperLoadingState(); } -class _WiperLoadingState extends State with TickerProviderStateMixin, LoadingWidgetState { +class _WiperLoadingState extends LoadingWidgetState + with SingleTickerProviderStateMixin { late final AnimationController _controller; late final CurvedAnimation _animation; + final _animatedSizeKey = GlobalKey(); double _pointPosition = 0; late Widget _child; @@ -135,7 +136,8 @@ class _WiperLoadingState extends State with TickerProviderStateMix _child = widget.child; - loadingState = widget.loading ? LoadingState.LOADING : LoadingState.LOADED; + setLoadingState(widget.loading ? LoadingState.LOADING : LoadingState.LOADED, + rebuild: false); _controller = AnimationController( duration: widget.interval, @@ -176,6 +178,8 @@ class _WiperLoadingState extends State with TickerProviderStateMix break; } }); + + if (loaded) _controller.value = 1.0; } @override @@ -192,7 +196,8 @@ class _WiperLoadingState extends State with TickerProviderStateMix margin: EdgeInsets.zero, color: color, elevation: 5.0, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(min(width, height))), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(min(width, height))), child: Container( width: width, height: height, @@ -201,20 +206,27 @@ class _WiperLoadingState extends State with TickerProviderStateMix ); } - Widget animatedSizeWidget(Key key) => Stack( - children: [ - Container( - width: widget.minWidth, - height: widget.minHeight, - ), - IgnorePointer( - ignoring: !loaded, - child: widget.animatedSize - ? AnimatedSize(key: key, duration: widget.sizeDuration, vsync: this, curve: widget.sizeCurve, child: _child) - : Container(key: key, child: _child), - ), - ], - ); + Widget animatedSizeWidget(Key key) { + final wrappedChild = WidgetWrapper(key: _childKey, child: _child); + return Stack( + children: [ + SizedBox( + width: widget.minWidth, + height: widget.minHeight, + ), + IgnorePointer( + ignoring: !loaded, + child: widget.animatedSize + ? AnimatedSize( + key: _animatedSizeKey, + duration: widget.sizeDuration, + curve: widget.sizeCurve, + child: wrappedChild) + : wrappedChild, + ), + ], + ); + } bool get up => widget.direction == WiperDirection.up; @@ -225,55 +237,65 @@ class _WiperLoadingState extends State with TickerProviderStateMix bool get left => widget.direction == WiperDirection.left; @override - Widget build(BuildContext context) { + void didUpdateWidget(covariant WiperLoading oldWidget) { + super.didUpdateWidget(oldWidget); if (loaded && widget.loading) { - loadingState = LoadingState.DISAPPEARING; + setLoadingState(LoadingState.DISAPPEARING, rebuild: false); _controller.animateBack(0.0); } + } + @override + Widget build(BuildContext context) { if (!disappearing) _child = widget.child; Widget _loadedChild = animatedSizeWidget(_childKey); - Color color = widget.wiperColor ?? Theme.of(context).accentColor; + Color color = widget.wiperColor ?? Theme.of(context).colorScheme.secondary; bool vertical = up || down; - TextDirection textDirection = Directionality.maybeOf(context) ?? TextDirection.ltr; + TextDirection textDirection = + Directionality.maybeOf(context) ?? TextDirection.ltr; Widget stack = Stack( children: [ - //WidgetSizedBox(child: animatedSizeWidget(pseudoChildKey),), - Container( - width: widget.minWidth, - height: widget.minHeight, - ), - if (!loaded) + if (loaded) + _loadedChild + else ...[ appearing || disappearing - ? ClipRect( - clipper: _WiperRectClipper(widget.direction, _animation.value), - child: _loadedChild, + ? AnimatedBuilder( + animation: _animation, + builder: (context, child) => ClipRect( + clipper: + _WiperRectClipper(widget.direction, _animation.value), + child: _loadedChild, + ), ) : WidgetSizedBox( child: _loadedChild, ), - loaded - ? _loadedChild - : Positioned.fill( - child: LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - Size biggest = constraints.biggest; - double height = constraints.hasBoundedHeight ? biggest.height : 50; - double width = biggest.width; - double _circleWidth = - widget.wiperWidth * (1 + _animation.speed.abs() * widget.wiperDeformingFactor) * (appearing || disappearing ? 1 - _animation.value : 1); - /*((appearing || disappearing) - ? (_animation.value > 0.5 - ? (2 * (1 - _animation.value) + 9 * pow(1 - (_animation.value), 2)) - : (1.0 + 9 * pow(0.5 - (_animation.value - 0.5).abs(), 2))) - : (loaded ? 0.0 : (1.0 + 9 * pow(0.5 - (_animation.value - 0.5).abs(), 2))));*/ - Widget wiper = widget.wiperBuilder?.call(vertical ? width : _circleWidth, vertical ? _circleWidth : height) ?? - _loadingWiper(vertical ? width : _circleWidth, vertical ? _circleWidth : height, color); - _pointPosition = (_animation.value * ((vertical ? height : width) - _circleWidth)); - return Container( + Positioned.fill( + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + Size biggest = constraints.biggest; + double height = + constraints.hasBoundedHeight ? biggest.height : 50; + double width = biggest.width; + return AnimatedBuilder( + animation: _animation, + builder: (context, child) { + double _circleWidth = widget.wiperWidth * + (1 + + _animation.speed.abs() * + widget.wiperDeformingFactor) * + (appearing || disappearing ? 1 - _animation.value : 1); + Widget wiper = widget.wiperBuilder?.call( + vertical ? width : _circleWidth, + vertical ? _circleWidth : height) ?? + _loadingWiper(vertical ? width : _circleWidth, + vertical ? _circleWidth : height, color); + _pointPosition = (_animation.value * + ((vertical ? height : width) - _circleWidth)); + return SizedBox( width: width, height: height, child: Stack( @@ -289,8 +311,11 @@ class _WiperLoadingState extends State with TickerProviderStateMix ), ); }, - ), - ), + ); + }, + ), + ), + ], ], ); return Directionality(textDirection: textDirection, child: stack); @@ -306,11 +331,13 @@ class _WiperRectClipper extends CustomClipper { @override Rect getClip(Size size) { return direction == WiperDirection.left - ? Rect.fromLTWH(size.width * (1 - factor), 0, size.width * factor, size.height) + ? Rect.fromLTWH( + size.width * (1 - factor), 0, size.width * factor, size.height) : direction == WiperDirection.right ? Rect.fromLTWH(0, 0, size.width * factor, size.height) : direction == WiperDirection.up - ? Rect.fromLTWH(0, size.height * (1 - factor), size.width, size.height * factor) + ? Rect.fromLTWH(0, size.height * (1 - factor), size.width, + size.height * factor) : Rect.fromLTWH(0, 0, size.width, size.height * factor); } @@ -321,7 +348,11 @@ class _WiperRectClipper extends CustomClipper { @override bool operator ==(Object other) => - identical(this, other) || other is _WiperRectClipper && runtimeType == other.runtimeType && direction == other.direction && factor == other.factor; + identical(this, other) || + other is _WiperRectClipper && + runtimeType == other.runtimeType && + direction == other.direction && + factor == other.factor; @override int get hashCode => direction.hashCode ^ factor.hashCode; diff --git a/pubspec.lock b/pubspec.lock index b55f540..1df6b69 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.8.2" boolean_selector: dependency: transitive description: @@ -21,14 +21,14 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" clock: dependency: transitive description: @@ -66,14 +66,21 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.0" path: dependency: transitive description: @@ -92,7 +99,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" stack_trace: dependency: transitive description: @@ -127,7 +134,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19" + version: "0.4.8" typed_data: dependency: transitive description: @@ -141,7 +148,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.14.0 <3.0.0" flutter: ">=1.17.0" diff --git a/pubspec.yaml b/pubspec.yaml index 50590fa..c9b70ab 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: widget_loading description: An easy way to hide a widget when you have nothing to show yet and need a loading animation at the same time. -version: 0.2.1 +version: 0.3.0 repository: https://github.com/SplashByte/widget_loading environment: diff --git a/test/widget_loading_test.dart b/test/widget_loading_test.dart index 1a13b85..c1574ad 100644 --- a/test/widget_loading_test.dart +++ b/test/widget_loading_test.dart @@ -4,4 +4,4 @@ void main() { //test('', () {}); -} \ No newline at end of file +}