From ec369f02d48ecdc936c89bdf02ede0622c49452b Mon Sep 17 00:00:00 2001 From: Bohdan Date: Sun, 29 Sep 2019 16:09:57 +0300 Subject: [PATCH 1/5] state of annimations --- example/lib/main.dart | 129 +++++++++++++++++++++++------------------ lib/shimmer.dart | 79 +++++++++++++++++-------- test/shimmer_test.dart | 17 +++--- 3 files changed, 137 insertions(+), 88 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 8b4e538..0cf1e36 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,28 +1,28 @@ import 'package:flutter/material.dart'; import 'package:shimmer/shimmer.dart'; -void main() => runApp(new MyApp()); +void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return new MaterialApp( + return MaterialApp( title: 'Shimmer', - routes: { + routes: { 'loading': (_) => LoadingListPage(), 'slide': (_) => SlideToUnlockPage(), }, - theme: new ThemeData( + theme: ThemeData( primarySwatch: Colors.blue, ), - home: new MyHomePage(), + home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override - _MyHomePageState createState() => new _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -30,16 +30,16 @@ class _MyHomePageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('Shimmer'), + title: const Text('Shimmer'), ), body: Column( - children: [ + children: [ ListTile( - title: Text('Loading List'), + title: const Text('Loading List'), onTap: () => Navigator.of(context).pushNamed('loading'), ), ListTile( - title: Text('Slide To Unlock'), + title: const Text('Slide To Unlock'), onTap: () => Navigator.of(context).pushNamed('slide'), ), ], @@ -54,13 +54,13 @@ class LoadingListPage extends StatefulWidget { } class _LoadingListPageState extends State { - bool _enabled = true; + ShimmerState _currentShimmerState = ShimmerState.running; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('Loading List'), + title: const Text('Loading List'), ), body: Container( width: double.infinity, @@ -71,44 +71,43 @@ class _LoadingListPageState extends State { Shimmer.fromColors( baseColor: Colors.grey[300], highlightColor: Colors.grey[100], - enabled: _enabled, + shimmerState: _currentShimmerState, child: Column( - children: [0, 1, 2, 3, 4, 5, 6] + children: [0, 1, 2, 3, 4, 5, 6] .map((_) => Padding( padding: const EdgeInsets.only(bottom: 8.0), child: Row( crossAxisAlignment: CrossAxisAlignment.start, - children: [ + children: [ Container( width: 48.0, height: 48.0, color: Colors.white, ), - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8.0), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 8.0), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [ + children: [ Container( width: double.infinity, height: 8.0, color: Colors.white, ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 2.0), + const Padding( + padding: + EdgeInsets.symmetric(vertical: 2.0), ), Container( width: double.infinity, height: 8.0, color: Colors.white, ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 2.0), + const Padding( + padding: + EdgeInsets.symmetric(vertical: 2.0), ), Container( width: 40.0, @@ -127,30 +126,48 @@ class _LoadingListPageState extends State { Expanded( child: Container(), ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: FlatButton( - onPressed: () { - setState(() { - _enabled = !_enabled; - }); - }, - child: Text( - _enabled ? 'Stop' : 'Play', - style: Theme.of(context).textTheme.button.copyWith( - fontSize: 18.0, - color: _enabled ? Colors.redAccent : Colors.green), - )), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildButtonToggle(() { + setState(() { + _currentShimmerState = ShimmerState.running; + }); + }, 'Play', Colors.green), + _buildButtonToggle(() { + setState(() { + _currentShimmerState = ShimmerState.paused; + }); + }, 'Pause', Colors.deepOrange), + _buildButtonToggle(() { + setState(() { + _currentShimmerState = ShimmerState.stopped; + }); + }, 'Stop', Colors.red), + ], ) ], ), ), ); } + + Widget _buildButtonToggle(Function action, String text, Color color) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: FlatButton( + onPressed: action, + child: Text(text, + style: Theme.of(context).textTheme.button.copyWith( + fontSize: 18.0, + color: color, + )), + )); + } } class SlideToUnlockPage extends StatelessWidget { - final days = [ + final List days = [ 'Monday', 'Tuesday', 'Wednesday', @@ -159,7 +176,7 @@ class SlideToUnlockPage extends StatelessWidget { 'Saturday', 'Sunday' ]; - final months = [ + final List months = [ 'January', 'February', 'March', @@ -176,19 +193,19 @@ class SlideToUnlockPage extends StatelessWidget { @override Widget build(BuildContext context) { - final time = DateTime.now(); - final hour = time.hour; - final minute = time.minute; - final day = time.weekday; - final month = time.month; - final dayInMonth = time.day; + final DateTime time = DateTime.now(); + final int hour = time.hour; + final int minute = time.minute; + final int day = time.weekday; + final int month = time.month; + final int dayInMonth = time.day; return Scaffold( appBar: AppBar( - title: Text('Slide To Unlock'), + title: const Text('Slide To Unlock'), ), body: Stack( fit: StackFit.expand, - children: [ + children: [ Image.asset( 'assets/images/background.jpg', fit: BoxFit.cover, @@ -199,7 +216,7 @@ class SlideToUnlockPage extends StatelessWidget { left: 0.0, child: Center( child: Column( - children: [ + children: [ Text( '${hour < 10 ? '0$hour' : '$hour'}:${minute < 10 ? '0$minute' : '$minute'}', style: TextStyle( @@ -207,8 +224,8 @@ class SlideToUnlockPage extends StatelessWidget { color: Colors.white, ), ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 4.0), + const Padding( + padding: EdgeInsets.symmetric(vertical: 4.0), ), Text( '${days[day - 1]}, ${months[month - 1]} $dayInMonth', @@ -228,15 +245,15 @@ class SlideToUnlockPage extends StatelessWidget { child: Shimmer.fromColors( child: Row( mainAxisSize: MainAxisSize.min, - children: [ + children: [ Image.asset( 'assets/images/chevron_right.png', height: 20.0, ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 4.0), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 4.0), ), - Text( + const Text( 'Slide to unlock', style: TextStyle( fontSize: 28.0, diff --git a/lib/shimmer.dart b/lib/shimmer.dart index fcd4889..5f25731 100644 --- a/lib/shimmer.dart +++ b/lib/shimmer.dart @@ -20,6 +20,19 @@ import 'package:flutter/rendering.dart'; /// enum ShimmerDirection { ltr, rtl, ttb, btt } +/// +/// An enum defines all supported states of shimmer effect +/// +/// * [ShimmerDirection.running] shimmer effect is presenting and animation is running +/// * [ShimmerDirection.paused] shimmer effect is presenting and animation is paused +/// * [ShimmerDirection.stopped] shimmer effect is presenting and animation is paused +/// +enum ShimmerState { + running, + paused, + stopped, +} + /// /// A widget renders shimmer effect over [child] widget tree. /// @@ -44,9 +57,8 @@ enum ShimmerDirection { ltr, rtl, ttb, btt } /// [loop] the number of animation loop, set value of `0` to make animation run /// forever. /// -/// [enabled] controls if shimmer effect is active. When set to false the animation -/// is paused -/// +/// [shimmerState] controls if shimmer effect is active. +/// The default value is [ShimmerState.running]. /// /// ## Pro tips: /// @@ -60,18 +72,18 @@ class Shimmer extends StatefulWidget { final Widget child; final Duration period; final ShimmerDirection direction; + final ShimmerState shimmerState; final Gradient gradient; final int loop; - final bool enabled; const Shimmer({ Key key, @required this.child, @required this.gradient, this.direction = ShimmerDirection.ltr, + this.shimmerState = ShimmerState.running, this.period = const Duration(milliseconds: 1500), this.loop = 0, - this.enabled = true, }) : super(key: key); /// @@ -87,7 +99,7 @@ class Shimmer extends StatefulWidget { this.period = const Duration(milliseconds: 1500), this.direction = ShimmerDirection.ltr, this.loop = 0, - this.enabled = true, + this.shimmerState = ShimmerState.running, }) : gradient = LinearGradient( begin: Alignment.topLeft, end: Alignment.centerRight, @@ -118,8 +130,9 @@ class Shimmer extends StatefulWidget { properties.add(EnumProperty('direction', direction)); properties.add( DiagnosticsProperty('period', period, defaultValue: null)); - properties - .add(DiagnosticsProperty('enabled', enabled, defaultValue: null)); + properties.add(DiagnosticsProperty( + 'shimmerState', shimmerState, + defaultValue: null)); } } @@ -143,33 +156,51 @@ class _ShimmerState extends State with SingleTickerProviderStateMixin { _controller.forward(from: 0.0); } }); - if (widget.enabled) { - _controller.forward(); + switch (widget.shimmerState) { + case ShimmerState.running: + _controller.forward(); + break; + default: + break; } } @override void didUpdateWidget(Shimmer oldWidget) { - if (widget.enabled) { - _controller.forward(); - } else { - _controller.stop(); + switch (widget.shimmerState) { + case ShimmerState.running: + _controller.forward(); + break; + default: + _controller.stop(); } super.didUpdateWidget(oldWidget); } @override Widget build(BuildContext context) { - return AnimatedBuilder( - animation: _controller, - child: widget.child, - builder: (BuildContext context, Widget child) => _Shimmer( - child: child, - direction: widget.direction, - gradient: widget.gradient, - percent: _controller.value, - enabled: widget.enabled, - ), + Widget widgetToDisplay = widget.child; + switch (widget.shimmerState) { + case ShimmerState.running: + case ShimmerState.paused: + widgetToDisplay = AnimatedBuilder( + animation: _controller, + child: widget.child, + builder: (BuildContext context, Widget child) => _Shimmer( + child: child, + direction: widget.direction, + gradient: widget.gradient, + percent: _controller.value, + enabled: widget.shimmerState == ShimmerState.running, + ), + ); + break; + default: + break; + } + return AnimatedSwitcher( + child: widgetToDisplay, + duration: const Duration(microseconds: 350), ); } diff --git a/test/shimmer_test.dart b/test/shimmer_test.dart index 3a22c99..3114794 100644 --- a/test/shimmer_test.dart +++ b/test/shimmer_test.dart @@ -1,15 +1,16 @@ -import 'package:shimmer/shimmer.dart'; -import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:shimmer/shimmer.dart'; void main() { - - testWidgets('Shimmer.fromColors() can be constructed', (tester) async { + testWidgets('Shimmer.fromColors() can be constructed', + (WidgetTester tester) async { await tester.pumpWidget(Shimmer.fromColors( - child: Container(width: 100.0, height: 100.0,), + child: Container( + width: 100.0, + height: 100.0, + ), baseColor: Colors.red, - highlightColor: Colors.yellow) - ); + highlightColor: Colors.yellow)); }); - } From c07f2b381189131e834777f6f2af8a07978624b9 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Thu, 3 Oct 2019 16:20:01 +0300 Subject: [PATCH 2/5] Fix docs --- lib/shimmer.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/shimmer.dart b/lib/shimmer.dart index 5f25731..d4e293e 100644 --- a/lib/shimmer.dart +++ b/lib/shimmer.dart @@ -23,9 +23,9 @@ enum ShimmerDirection { ltr, rtl, ttb, btt } /// /// An enum defines all supported states of shimmer effect /// -/// * [ShimmerDirection.running] shimmer effect is presenting and animation is running -/// * [ShimmerDirection.paused] shimmer effect is presenting and animation is paused -/// * [ShimmerDirection.stopped] shimmer effect is presenting and animation is paused +/// * [ShimmerState.running] shimmer effect is presenting and animation is running +/// * [ShimmerState.paused] shimmer effect is presenting and animation is paused +/// * [ShimmerState.stopped] shimmer effect isn't presenting and animation is paused /// enum ShimmerState { running, @@ -57,7 +57,7 @@ enum ShimmerState { /// [loop] the number of animation loop, set value of `0` to make animation run /// forever. /// -/// [shimmerState] controls if shimmer effect is active. +/// [shimmerState] controls if shimmer effect is active/paused/removed. /// The default value is [ShimmerState.running]. /// /// ## Pro tips: From df7eae491cdb5ce72d5bcdd4cbf894e1b2ac93c7 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Sun, 30 Aug 2020 02:12:28 +0300 Subject: [PATCH 3/5] fixed build --- example/ios/Flutter/.last_build_id | 1 + example/ios/Runner.xcodeproj/project.pbxproj | 83 +++++++++++--- .../contents.xcworkspacedata | 3 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 -- example/pubspec.lock | 103 ++++++------------ 5 files changed, 103 insertions(+), 95 deletions(-) create mode 100644 example/ios/Flutter/.last_build_id delete mode 100644 example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example/ios/Flutter/.last_build_id b/example/ios/Flutter/.last_build_id new file mode 100644 index 0000000..5b3c71d --- /dev/null +++ b/example/ios/Flutter/.last_build_id @@ -0,0 +1 @@ +e5ece1776623d145d7a0343acbdc7b98 \ No newline at end of file diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 377ce4c..0f02f15 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,10 +9,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7FCB8418CA8E94A5E113B4DA /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F06D4E789402B942C42CBFA /* libPods-Runner.a */; }; 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; @@ -29,8 +26,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -41,13 +36,14 @@ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; + 3F06D4E789402B942C42CBFA /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4123B36C1F1A65BC0864C54A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 425356BED7DD0567EFE68F79 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -61,8 +57,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, + 7FCB8418CA8E94A5E113B4DA /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -72,9 +67,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -88,6 +81,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + DB4BABD379A440CB0FDC500B /* Pods */, + F05DB92D2D8761190FE354C3 /* Frameworks */, ); sourceTree = ""; }; @@ -123,6 +118,24 @@ name = "Supporting Files"; sourceTree = ""; }; + DB4BABD379A440CB0FDC500B /* Pods */ = { + isa = PBXGroup; + children = ( + 4123B36C1F1A65BC0864C54A /* Pods-Runner.debug.xcconfig */, + 425356BED7DD0567EFE68F79 /* Pods-Runner.release.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + F05DB92D2D8761190FE354C3 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 3F06D4E789402B942C42CBFA /* libPods-Runner.a */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -130,12 +143,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 220F837A394F909E886E7DFB /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 6176D6CFA1E63E46AE91B67A /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -196,6 +211,28 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 220F837A394F909E886E7DFB /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -208,7 +245,25 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 6176D6CFA1E63E46AE91B67A /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; @@ -261,7 +316,6 @@ /* Begin XCBuildConfiguration section */ 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -315,7 +369,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index 949b678..0000000 --- a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - BuildSystemType - Original - - diff --git a/example/pubspec.lock b/example/pubspec.lock index 3236025..7dd080c 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -1,69 +1,62 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - archive: - dependency: transitive - description: - name: archive - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.11" - args: - dependency: transitive - description: - name: args - url: "https://pub.dartlang.org" - source: hosted - version: "1.5.2" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "2.4.2" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" - charcode: + version: "2.0.0" + characters: dependency: transitive description: - name: charcode + name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.2" - collection: + version: "1.0.0" + charcode: dependency: transitive description: - name: collection + name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.14.11" - convert: + version: "1.1.3" + clock: dependency: transitive description: - name: convert + name: clock url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" - crypto: + version: "1.0.1" + collection: dependency: transitive description: - name: crypto + name: collection url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "1.14.13" cupertino_icons: dependency: "direct main" description: name: cupertino_icons url: "https://pub.dartlang.org" source: hosted - version: "0.1.2" + version: "0.1.3" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -74,20 +67,13 @@ packages: description: flutter source: sdk version: "0.0.0" - image: - dependency: transitive - description: - name: image - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.4" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.6" + version: "0.12.8" meta: dependency: transitive description: @@ -101,35 +87,14 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.6.4" - pedantic: - dependency: transitive - description: - name: pedantic - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0+1" - petitparser: - dependency: transitive - description: - name: petitparser - url: "https://pub.dartlang.org" - source: hosted - version: "2.4.0" - quiver: - dependency: transitive - description: - name: quiver - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.5" + version: "1.7.0" shimmer: dependency: "direct main" description: path: ".." relative: true source: path - version: "1.1.0" + version: "1.1.1" sky_engine: dependency: transitive description: flutter @@ -141,14 +106,14 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.5.5" + version: "1.7.0" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.9.3" + version: "1.9.5" stream_channel: dependency: transitive description: @@ -176,14 +141,14 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.11" + version: "0.2.17" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.1.6" + version: "1.2.0" vector_math: dependency: transitive description: @@ -191,12 +156,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.8" - xml: - dependency: transitive - description: - name: xml - url: "https://pub.dartlang.org" - source: hosted - version: "3.5.0" sdks: - dart: ">=2.4.0 <3.0.0" + dart: ">=2.9.0-14.0.dev <3.0.0" + flutter: ">=1.9.1 <2.0.0" From ed4eb62cc237f389bacac0ed1f4d531ae42588d9 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Sun, 30 Aug 2020 14:07:28 +0300 Subject: [PATCH 4/5] shorter form --- lib/shimmer.dart | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/shimmer.dart b/lib/shimmer.dart index aafa476..3cb3b65 100644 --- a/lib/shimmer.dart +++ b/lib/shimmer.dart @@ -224,9 +224,8 @@ class _Shimmer extends SingleChildRenderObjectWidget { }) : super(child: child); @override - _ShimmerFilter createRenderObject(BuildContext context) { - return _ShimmerFilter(percent, direction, gradient); - } + _ShimmerFilter createRenderObject(BuildContext context) => + _ShimmerFilter(percent, direction, gradient); @override void updateRenderObject(BuildContext context, _ShimmerFilter shimmer) { @@ -304,7 +303,6 @@ class _ShimmerFilter extends RenderProxyBox { } } - double _offset(double start, double end, double percent) { - return start + (end - start) * percent; - } + double _offset(double start, double end, double percent) => + start + (end - start) * percent; } From 22ec63c0b56d7853a2f89b2aa04b02f38f6fe7ef Mon Sep 17 00:00:00 2001 From: Bohdan Date: Mon, 31 Aug 2020 17:21:39 +0300 Subject: [PATCH 5/5] Make it testable + tests --- lib/animation_controller_factory.dart | 18 +++++++ lib/shimmer.dart | 77 ++++++++++++++++----------- test/moc.dart | 12 +++++ test/shimmer_test.dart | 49 ++++++++++++++++- 4 files changed, 124 insertions(+), 32 deletions(-) create mode 100644 lib/animation_controller_factory.dart create mode 100644 test/moc.dart diff --git a/lib/animation_controller_factory.dart b/lib/animation_controller_factory.dart new file mode 100644 index 0000000..be677b2 --- /dev/null +++ b/lib/animation_controller_factory.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +abstract class AnimationControllerFactory { + AnimationController controller(TickerProvider provider); + const AnimationControllerFactory(); +} + +class PackageAnimationControllerFactory extends AnimationControllerFactory { + final Duration period; + const PackageAnimationControllerFactory( + {this.period = const Duration(milliseconds: 1500)}) + : super(); + + @override + AnimationController controller(TickerProvider provider) { + return AnimationController(vsync: provider, duration: period); + } +} diff --git a/lib/shimmer.dart b/lib/shimmer.dart index 3cb3b65..cca5097 100644 --- a/lib/shimmer.dart +++ b/lib/shimmer.dart @@ -9,6 +9,7 @@ library shimmer; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:shimmer/animation_controller_factory.dart'; /// /// An enum defines all supported directions of shimmer effect @@ -70,21 +71,23 @@ enum ShimmerState { @immutable class Shimmer extends StatefulWidget { final Widget child; - final Duration period; final ShimmerDirection direction; final ShimmerState shimmerState; final Gradient gradient; final int loop; - + final AnimationControllerFactory animationControllerFactory; const Shimmer({ Key key, + this.animationControllerFactory = const PackageAnimationControllerFactory(), @required this.child, @required this.gradient, this.direction = ShimmerDirection.ltr, this.shimmerState = ShimmerState.running, - this.period = const Duration(milliseconds: 1500), this.loop = 0, - }) : super(key: key); + }) : assert(animationControllerFactory != null), + assert(child != null), + assert(gradient != null), + super(key: key); /// /// A convenient constructor provides an easy and convenient way to create a @@ -93,14 +96,18 @@ class Shimmer extends StatefulWidget { /// Shimmer.fromColors({ Key key, + this.animationControllerFactory = const PackageAnimationControllerFactory(), @required this.child, @required Color baseColor, @required Color highlightColor, - this.period = const Duration(milliseconds: 1500), this.direction = ShimmerDirection.ltr, this.loop = 0, this.shimmerState = ShimmerState.running, - }) : gradient = LinearGradient( + }) : assert(animationControllerFactory != null), + assert(child != null), + assert(baseColor != null), + assert(highlightColor != null), + gradient = LinearGradient( begin: Alignment.topLeft, end: Alignment.centerRight, colors: [ @@ -120,7 +127,7 @@ class Shimmer extends StatefulWidget { super(key: key); @override - _ShimmerState createState() => _ShimmerState(); + _ShimmerState createState() => _ShimmerState(animationControllerFactory); @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { @@ -128,8 +135,9 @@ class Shimmer extends StatefulWidget { properties.add(DiagnosticsProperty('gradient', gradient, defaultValue: null)); properties.add(EnumProperty('direction', direction)); - properties.add( - DiagnosticsProperty('period', period, defaultValue: null)); + properties.add(DiagnosticsProperty( + 'animationControllerFactory', animationControllerFactory, + defaultValue: PackageAnimationControllerFactory)); properties.add(DiagnosticsProperty( 'shimmerState', shimmerState, defaultValue: null)); @@ -140,41 +148,48 @@ class _ShimmerState extends State with SingleTickerProviderStateMixin { AnimationController _controller; int _count; + _ShimmerState(AnimationControllerFactory factory) { + _controller = factory.controller(this); + } + @override void initState() { super.initState(); _count = 0; - _controller = AnimationController(vsync: this, duration: widget.period) - ..addStatusListener((AnimationStatus status) { - if (status != AnimationStatus.completed) { - return; - } - _count++; - if (widget.loop <= 0) { - _controller.repeat(); - } else if (_count < widget.loop) { - _controller.forward(from: 0.0); - } - }); - switch (widget.shimmerState) { - case ShimmerState.running: - _controller.forward(); - break; - default: - break; - } + _controller.addStatusListener((AnimationStatus status) { + if (status != AnimationStatus.completed) { + return; + } + _count++; + if (widget.loop <= 0) { + _controller.repeat(); + } else if (_count < widget.loop) { + _controller.forward(from: 0.0); + } + }); + _handleChange(widget.shimmerState); } @override void didUpdateWidget(Shimmer oldWidget) { - switch (widget.shimmerState) { + if (oldWidget.shimmerState != widget.shimmerState) { + _handleChange(widget.shimmerState); + } + super.didUpdateWidget(oldWidget); + } + + void _handleChange(ShimmerState shimmerState) { + switch (shimmerState) { case ShimmerState.running: _controller.forward(); break; - default: + case ShimmerState.stopped: + _controller.reset(); + break; + case ShimmerState.paused: _controller.stop(); + break; } - super.didUpdateWidget(oldWidget); } @override diff --git a/test/moc.dart b/test/moc.dart new file mode 100644 index 0000000..ffd4c51 --- /dev/null +++ b/test/moc.dart @@ -0,0 +1,12 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:shimmer/animation_controller_factory.dart'; + +class TestPackageAnimationController extends AnimationControllerFactory { + final AnimationController moc = AnimationController( + vsync: const TestVSync(), duration: const Duration(seconds: 1)); + @override + AnimationController controller(TickerProvider provider) { + return moc; + } +} diff --git a/test/shimmer_test.dart b/test/shimmer_test.dart index 7e7aa70..6032a82 100644 --- a/test/shimmer_test.dart +++ b/test/shimmer_test.dart @@ -1,15 +1,62 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:shimmer/animation_controller_factory.dart'; import 'package:shimmer/shimmer.dart'; +import 'moc.dart'; + void main() { - testWidgets('Shimmer.fromColors() can be constructed', (tester) async { + Future _prepareWidgetFor( + WidgetTester tester, + AnimationControllerFactory animationControllerFactory, + ShimmerState state) async { + await tester.pumpWidget( + Shimmer.fromColors( + child: Container( + width: 100.0, + height: 100.0, + ), + animationControllerFactory: animationControllerFactory, + shimmerState: state, + baseColor: Colors.red, + highlightColor: Colors.yellow), + ); + } + + testWidgets('Shimmer.fromColors() can be constructed', + (WidgetTester tester) async { await tester.pumpWidget(Shimmer.fromColors( child: Container( width: 100.0, height: 100.0, ), + animationControllerFactory: TestPackageAnimationController(), baseColor: Colors.red, highlightColor: Colors.yellow)); }); + + testWidgets('Shimmer stopped state', (WidgetTester tester) async { + final TestPackageAnimationController animationControllerFactory = + TestPackageAnimationController(); + final AnimationController animationController = + animationControllerFactory.moc; + + await _prepareWidgetFor( + tester, animationControllerFactory, ShimmerState.stopped); + + expect(find.byType(AnimatedBuilder), findsNothing); + expect(animationController.isAnimating, false); + }); + + testWidgets('Shimmer running state', (WidgetTester tester) async { + final TestPackageAnimationController animationControllerFactory = + TestPackageAnimationController(); + final AnimationController animationController = + animationControllerFactory.moc; + await _prepareWidgetFor( + tester, animationControllerFactory, ShimmerState.running); + + expect(find.byType(AnimatedBuilder), findsOneWidget); + expect(animationController.isAnimating, true); + }); }