From 3dc744e5df4eb6679f76487126bb9b26090b362a Mon Sep 17 00:00:00 2001 From: Ilia Sixtel Date: Sat, 8 Oct 2022 15:11:22 +0300 Subject: [PATCH 1/2] Update package and analysis versions --- analysis_options.yaml | 2 +- pubspec.yaml | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 8212f8d..03c3614 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:pedantic/analysis_options.yaml +include: package:flutter_lints/flutter.yaml analyzer: exclude: diff --git a/pubspec.yaml b/pubspec.yaml index 514bff5..84555c2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,15 +5,18 @@ repository: https://github.com/biocarl/drawing_animation homepage: https://twitter.com/cahaucks environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.17.0 <3.0.0" dependencies: flutter: sdk: flutter - xml: ^5.1.2 - path_parsing: ^1.0.0 + xml: ^6.1.0 + path_parsing: ^1.0.1 + collection: ^1.16.0 + equatable: ^2.0.5 dev_dependencies: + flutter_lints: ^2.0.1 flutter_test: sdk: flutter - mockito: ^5.0.10 + mockito: ^5.3.2 From 331a72dbfb919adb8c3923b8243767b11bad17f0 Mon Sep 17 00:00:00 2001 From: Ilia Sixtel Date: Sat, 8 Oct 2022 15:11:36 +0300 Subject: [PATCH 2/2] Fixing linter rules and deprecated API calls --- example/example_03/lib/main.dart | 284 ++++++++++++++-------------- lib/src/abstract_drawing_state.dart | 1 - lib/src/drawing_widget.dart | 7 +- lib/src/painter.dart | 74 ++++---- lib/src/parser.dart | 19 +- lib/src/range.dart | 11 +- test/unit_widget_arg.dart | 4 +- 7 files changed, 196 insertions(+), 204 deletions(-) diff --git a/example/example_03/lib/main.dart b/example/example_03/lib/main.dart index 284083a..547ea02 100644 --- a/example/example_03/lib/main.dart +++ b/example/example_03/lib/main.dart @@ -1,18 +1,17 @@ import 'dart:async'; import 'dart:core'; -import 'dart:ui'; import 'package:drawing_animation/drawing_animation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:url_launcher/url_launcher_string.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom]); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, + overlays: [SystemUiOverlay.bottom]); return MaterialApp( theme: ThemeData( primarySwatch: Colors.blue, @@ -157,7 +156,7 @@ class HomePageState extends State with TickerProviderStateMixin { Widget createPage(int i, BuildContext context) { var isLandscape = - (MediaQuery.of(context).orientation == Orientation.portrait); + (MediaQuery.of(context).orientation == Orientation.portrait); if (previousScreen != i) { isRunning = false; showStartButton = true; @@ -172,11 +171,11 @@ class HomePageState extends State with TickerProviderStateMixin { (isLandscape) ? Expanded(flex: 3, child: Container()) : Container(), (isLandscape) ? Expanded( - flex: 6, - child: Center( - child: Padding( - padding: EdgeInsets.all(24.0), - child: createInstructions(i)))) + flex: 6, + child: Center( + child: Padding( + padding: EdgeInsets.all(24.0), + child: createInstructions(i)))) : Container(), Flexible( flex: 12, @@ -186,32 +185,32 @@ class HomePageState extends State with TickerProviderStateMixin { child: Stack(children: [ (!isRunning && showStartButton) ? Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'Start animation', - style: TextStyle( - color: (assets[i][4] == Colors.black) - ? Colors.white - : Colors.black, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Start animation', + style: TextStyle( + color: (assets[i][4] == Colors.black) + ? Colors.white + : Colors.black, + ), ), - ), - IconButton( - icon: Icon( - Icons.touch_app, - color: (assets[i][4] == Colors.black) - ? Colors.white - : Colors.black, - ), - onPressed: () { - setState(() { - startAnimation(i); - }); - }, - ) - ])) + IconButton( + icon: Icon( + Icons.touch_app, + color: (assets[i][4] == Colors.black) + ? Colors.white + : Colors.black, + ), + onPressed: () { + setState(() { + startAnimation(i); + }); + }, + ) + ])) : Container(), GestureDetector( onTap: () => startAnimation(i), @@ -260,29 +259,28 @@ class HomePageState extends State with TickerProviderStateMixin { ]), (isLandscape) ? Column(children: [ - AnimatedSize( - curve: Curves.bounceInOut, - vsync: this, - duration: Duration(milliseconds: 800), - child: Card( - margin: EdgeInsets.all(20.0), - color: Colors.grey[250], - child: Container( - padding: EdgeInsets.all(10.0), - child: Column(children: [ - Row(children: [ - Flexible( - flex: 2, - child: Column( - children: createCardOptions(i), - )) - ]), - ])))), - Expanded( - flex: 4, - child: Container(), - ) //TODO Fix, Find Expanded in the Card Widget tree - ]) + AnimatedSize( + curve: Curves.bounceInOut, + duration: Duration(milliseconds: 800), + child: Card( + margin: EdgeInsets.all(20.0), + color: Colors.grey[250], + child: Container( + padding: EdgeInsets.all(10.0), + child: Column(children: [ + Row(children: [ + Flexible( + flex: 2, + child: Column( + children: createCardOptions(i), + )) + ]), + ])))), + Expanded( + flex: 4, + child: Container(), + ) //TODO Fix, Find Expanded in the Card Widget tree + ]) : Container(), ]); } @@ -292,17 +290,17 @@ class HomePageState extends State with TickerProviderStateMixin { Row(children: [ (cardExpanded) ? Expanded( - flex: 1, - child: Text('Asset: ', - style: TextStyle(fontWeight: FontWeight.bold))) + flex: 1, + child: Text('Asset: ', + style: TextStyle(fontWeight: FontWeight.bold))) : Text('${assets[i][8]}', - style: TextStyle(fontWeight: FontWeight.bold)), + style: TextStyle(fontWeight: FontWeight.bold)), (cardExpanded) ? Expanded( - flex: 1, - child: Align( - alignment: Alignment.centerLeft, - child: Text('${assets[i][0]}'))) + flex: 1, + child: Align( + alignment: Alignment.centerLeft, + child: Text('${assets[i][0]}'))) : Container(), Expanded( flex: 1, @@ -332,28 +330,28 @@ class HomePageState extends State with TickerProviderStateMixin { style: TextStyle(fontWeight: FontWeight.bold))), Expanded( child: ChoiceChip( - label: Text('allAtOnce'), - selected: assets[i][1] == LineAnimation.allAtOnce, - onSelected: (bool selected) { - setState(() { - assets[i][1] = selected - ? LineAnimation.allAtOnce - : LineAnimation.oneByOne; - }); - }, - )), + label: Text('allAtOnce'), + selected: assets[i][1] == LineAnimation.allAtOnce, + onSelected: (bool selected) { + setState(() { + assets[i][1] = selected + ? LineAnimation.allAtOnce + : LineAnimation.oneByOne; + }); + }, + )), Expanded( child: ChoiceChip( - label: Text('oneByOne'), - selected: assets[i][1] == LineAnimation.oneByOne, - onSelected: (bool selected) { - setState(() { - assets[i][1] = selected - ? LineAnimation.oneByOne - : LineAnimation.allAtOnce; - }); - }, - )), + label: Text('oneByOne'), + selected: assets[i][1] == LineAnimation.oneByOne, + onSelected: (bool selected) { + setState(() { + assets[i][1] = selected + ? LineAnimation.oneByOne + : LineAnimation.allAtOnce; + }); + }, + )), ])), ]), Row(children: [ @@ -366,24 +364,24 @@ class HomePageState extends State with TickerProviderStateMixin { style: TextStyle(fontWeight: FontWeight.bold))), Expanded( child: ChoiceChip( - label: Text('Once'), - selected: assets[i][5] == false, - onSelected: (bool selected) { - setState(() { - assets[i][5] = !selected; - }); - }, - )), + label: Text('Once'), + selected: assets[i][5] == false, + onSelected: (bool selected) { + setState(() { + assets[i][5] = !selected; + }); + }, + )), Expanded( child: ChoiceChip( - label: Text('Infinite'), - selected: assets[i][5] == true, - onSelected: (bool selected) { - setState(() { - assets[i][5] = selected; - }); - }, - )), + label: Text('Infinite'), + selected: assets[i][5] == true, + onSelected: (bool selected) { + setState(() { + assets[i][5] = selected; + }); + }, + )), ])), ]), Row(children: [ @@ -535,53 +533,53 @@ class HomePageState extends State with TickerProviderStateMixin { Widget createChoiceChip(int i, int j, String text, Object object) { return Expanded( child: ChoiceChip( - label: Text(text), - selected: assets[i][j] == object, - onSelected: (bool selected) { - if (selected) { - setState(() { - //Restart animation - Pause - isRunning = false; - showStartButton = false; - }); + label: Text(text), + selected: assets[i][j] == object, + onSelected: (bool selected) { + if (selected) { + setState(() { + //Restart animation - Pause + isRunning = false; + showStartButton = false; + }); - Timer(Duration(milliseconds: 10), () { - setState(() { - assets[i][j] = object; - isRunning = true; - }); - }); - } - }, - )); + Timer(Duration(milliseconds: 10), () { + setState(() { + assets[i][j] = object; + isRunning = true; + }); + }); + } + }, + )); } Widget createChoiceChipMulti( int i, List jj, String text, List objects) { return Expanded( child: ChoiceChip( - label: Text(text), - selected: assets[i][jj.first] == - objects.first, //boolean depends on first object - onSelected: (bool selected) { - if (selected) { - setState(() { - //Restart animation - Pause - isRunning = false; - showStartButton = false; - }); + label: Text(text), + selected: assets[i][jj.first] == + objects.first, //boolean depends on first object + onSelected: (bool selected) { + if (selected) { + setState(() { + //Restart animation - Pause + isRunning = false; + showStartButton = false; + }); - Timer(Duration(milliseconds: 10), () { - setState(() { - for (var m = 0; m < objects.length; m++) { - assets[i][jj[m]] = objects[m]; - } - isRunning = true; - }); - }); - } - }, - )); + Timer(Duration(milliseconds: 10), () { + setState(() { + for (var m = 0; m < objects.length; m++) { + assets[i][jj[m]] = objects[m]; + } + isRunning = true; + }); + }); + } + }, + )); } Widget wrap(List widgets) { @@ -598,8 +596,8 @@ class HomePageState extends State with TickerProviderStateMixin { void launchURL(int i) async { var url = assets[i][7] as String; - if (await canLaunch(url)) { - await launch(url); + if (await canLaunchUrlString(url)) { + await launchUrlString(url); } else { throw 'Could not launch $url'; } diff --git a/lib/src/abstract_drawing_state.dart b/lib/src/abstract_drawing_state.dart index 6bf6ee3..c130827 100644 --- a/lib/src/abstract_drawing_state.dart +++ b/lib/src/abstract_drawing_state.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'dart:ui'; import 'drawing_widget.dart'; import 'debug.dart'; import 'line_animation.dart'; diff --git a/lib/src/drawing_widget.dart b/lib/src/drawing_widget.dart index f30b7f3..b711abf 100644 --- a/lib/src/drawing_widget.dart +++ b/lib/src/drawing_widget.dart @@ -1,6 +1,4 @@ -import 'dart:ui'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'abstract_drawing_state.dart'; import 'debug.dart'; import 'drawing_state.dart'; @@ -34,6 +32,7 @@ class AnimatedDrawing extends StatefulWidget { /// ``` AnimatedDrawing.svg( this.assetPath, { + super.key, //Standard this.controller, //Simplified version @@ -79,6 +78,7 @@ class AnimatedDrawing extends StatefulWidget { /// Optionally, [paints] can be provided which specifies a [Paint] object for each [Path] element in [paths]. AnimatedDrawing.paths( this.paths, { + super.key, //AnimatedDrawing.paths this.paints = const [], //Standard @@ -190,7 +190,6 @@ class AnimatedDrawing extends StatefulWidget { // TODO Refactor SRP void assertAnimationParameters() { - assert(!(controller == null && - (run == null || duration == null))); + assert(!(controller == null && (run == null || duration == null))); } } diff --git a/lib/src/painter.dart b/lib/src/painter.dart index c294d90..d170133 100644 --- a/lib/src/painter.dart +++ b/lib/src/painter.dart @@ -4,7 +4,6 @@ import 'dart:math'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'debug.dart'; import 'parser.dart'; @@ -27,9 +26,9 @@ class PaintedPainter extends PathPainter { @override void paint(Canvas canvas, Size size) { canvas = super.paintOrDebug(canvas, size); - if (canPaint) { + if (canPaint && pathSegments != null) { //pathSegments for AllAtOncePainter are always in the order of PathOrders.original - pathSegments!.forEach((segment) { + for (var segment in pathSegments!) { var paint = (paints.isNotEmpty) ? paints[segment.pathIndex] : (Paint() @@ -38,7 +37,7 @@ class PaintedPainter extends PathPainter { ..strokeCap = StrokeCap.square ..strokeWidth = segment.strokeWidth); canvas.drawPath(segment.path, paint); - }); + } //No callback etc. needed // super.onFinish(canvas, size); @@ -64,7 +63,7 @@ class AllAtOncePainter extends PathPainter { canvas = super.paintOrDebug(canvas, size); if (canPaint) { //pathSegments for AllAtOncePainter are always in the order of PathOrders.original - pathSegments!.forEach((segment) { + for (var segment in pathSegments!) { var subPath = segment.path .computeMetrics() .first @@ -78,7 +77,7 @@ class AllAtOncePainter extends PathPainter { ..strokeCap = StrokeCap.square ..strokeWidth = segment.strokeWidth); canvas.drawPath(subPath, paint); - }); + } super.onFinish(canvas, size); } @@ -99,7 +98,9 @@ class OneByOnePainter extends PathPainter { super(animation, pathSegments, customDimensions, paints, onFinishCallback, scaleToViewport, debugOptions) { if (this.pathSegments != null) { - this.pathSegments!.forEach((e) => totalPathSum += e.length); + for (var segment in this.pathSegments!) { + totalPathSum += segment.length; + } } } @@ -158,8 +159,9 @@ class OneByOnePainter extends PathPainter { } //[3.2] Restore rendering order - last path element in original PathOrder should be last painted -> most visible //[3.3] Paint elements - (toPaint..sort(Extractor.getComparator(PathOrders.original))) - .forEach((segment) { + var sorted = + (toPaint..sort(Extractor.getComparator(PathOrders.original))); + for (var segment in sorted) { paint = (paints.isNotEmpty) ? paints[segment.pathIndex] : (Paint() //Paint per path TODO implement Paint per PathSegment? @@ -170,7 +172,7 @@ class OneByOnePainter extends PathPainter { ..strokeCap = StrokeCap.square ..strokeWidth = segment.strokeWidth); canvas.drawPath(segment.path, paint); - }); + } if (animation.value != 1.0) { //[3.4] Remove last subPath @@ -238,19 +240,19 @@ abstract class PathPainter extends CustomPainter { var bb = pathSegments!.first.path.getBounds(); var strokeWidth = 0; - pathSegments!.forEach((e) { - bb = bb.expandToInclude(e.path.getBounds()); - if (strokeWidth < e.strokeWidth) { - strokeWidth = e.strokeWidth.toInt(); + for (var seg in pathSegments!) { + bb = bb.expandToInclude(seg.path.getBounds()); + if (strokeWidth < seg.strokeWidth) { + strokeWidth = seg.strokeWidth.toInt(); } - }); + } if (paints.isNotEmpty) { - paints.forEach((e) { - if (strokeWidth < e.strokeWidth) { - strokeWidth = e.strokeWidth.toInt(); + for (var p in paints) { + if (strokeWidth < p.strokeWidth) { + strokeWidth = p.strokeWidth.toInt(); } - }); + } } pathBoundingBox = bb.inflate(strokeWidth / 2); this.strokeWidth = strokeWidth.toDouble(); @@ -259,10 +261,10 @@ abstract class PathPainter extends CustomPainter { void onFinish(Canvas canvas, Size size, {int lastPainted = -1}) { //-1: no segment was painted yet, 0 first segment if (debugOptions.recordFrames) { - final picture = recorder.endRecording(); + final picture = recorder.endRecording(); var frame = getFrameCount(debugOptions); if (frame >= 0) { - print('Write frame $frame'); + debugPrint('Write frame $frame'); //pass size when you want the whole viewport of the widget writeToFile( picture, @@ -280,8 +282,8 @@ abstract class PathPainter extends CustomPainter { //Color background // canvas.drawColor(Color.fromRGBO(224, 121, 42, 1.0),BlendMode.srcOver); //factor for higher resolution - canvas.scale(debugOptions.resolutionFactor, - debugOptions.resolutionFactor); + canvas.scale( + debugOptions.resolutionFactor, debugOptions.resolutionFactor); } paintPrepare(canvas, size); return canvas; @@ -296,24 +298,20 @@ abstract class PathPainter extends CustomPainter { Future writeToFile( ui.Picture picture, String fileName, Size size) async { - var scale = calculateScaleFactor(size); + var scale = _calculateScaleFactor(size); var byteData = await ((await picture.toImage( - (scale.x * - debugOptions.resolutionFactor * - pathBoundingBox!.width) + (scale.x * debugOptions.resolutionFactor * pathBoundingBox!.width) .round(), - (scale.y * - debugOptions.resolutionFactor * - pathBoundingBox!.height) + (scale.y * debugOptions.resolutionFactor * pathBoundingBox!.height) .round())) .toByteData(format: ui.ImageByteFormat.png)); final buffer = byteData!.buffer; await File(fileName).writeAsBytes( buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes)); - print('File: $fileName written.'); + debugPrint('File: $fileName written.'); } - _ScaleFactor calculateScaleFactor(Size viewBox) { + _ScaleFactor _calculateScaleFactor(Size viewBox) { //Scale factors var dx = (viewBox.width) / pathBoundingBox!.width; var dy = (viewBox.height) / pathBoundingBox!.height; @@ -354,10 +352,9 @@ abstract class PathPainter extends CustomPainter { if (scaleToViewport) { //Viewbox with Offset.zero - var viewBox = (customDimensions != null) - ? customDimensions - : Size.copy(size); - var scale = calculateScaleFactor(viewBox!); + var viewBox = + (customDimensions != null) ? customDimensions : Size.copy(size); + var scale = _calculateScaleFactor(viewBox!); canvas.scale(scale.x, scale.y); //If offset @@ -366,8 +363,7 @@ abstract class PathPainter extends CustomPainter { //Center offset - TODO should this be a option flag? if (debugOptions.recordFrames != true) { - var center = Offset( - (size.width / scale.x - pathBoundingBox!.width) / 2, + var center = Offset((size.width / scale.x - pathBoundingBox!.width) / 2, (size.height / scale.y - pathBoundingBox!.height) / 2); canvas.translate(center.dx, center.dy); } @@ -389,7 +385,7 @@ abstract class PathPainter extends CustomPainter { } @override - bool shouldRepaint(PathPainter old) => true; + bool shouldRepaint(PathPainter oldDelegate) => true; } class _ScaleFactor { diff --git a/lib/src/parser.dart b/lib/src/parser.dart index 1098c95..9ae7927 100644 --- a/lib/src/parser.dart +++ b/lib/src/parser.dart @@ -1,7 +1,5 @@ import 'dart:async'; -import 'dart:ui'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:path_parsing/path_parsing.dart'; import 'package:xml/xml.dart' as xml; @@ -70,7 +68,8 @@ class SvgParser { double? strokeWidth; //Attributes - [1] css-styling - var style = attributes.firstWhereOrNull((attr) => attr.name.local == 'style'); + var style = + attributes.firstWhereOrNull((attr) => attr.name.local == 'style'); if (style != null) { //Parse color of stroke var exp = RegExp(r'stroke:([^;]+);'); @@ -85,14 +84,14 @@ class SvgParser { } //Attributes - [2] svg-attributes - var strokeElement = attributes.firstWhereOrNull( - (attr) => attr.name.local == 'stroke'); + var strokeElement = + attributes.firstWhereOrNull((attr) => attr.name.local == 'stroke'); if (strokeElement != null) { color = parseColor(strokeElement.value); } - var strokeWidthElement = attributes.firstWhereOrNull( - (attr) => attr.name.local == 'stroke-width'); + var strokeWidthElement = attributes + .firstWhereOrNull((attr) => attr.name.local == 'stroke-width'); if (strokeWidthElement != null) { strokeWidth = double.tryParse(strokeWidthElement.value); } @@ -109,12 +108,12 @@ class SvgParser { _paths = paths; var index = 0; - paths.forEach((p) { + for (var path in paths) { //TODO consider allowing this and just continue if the case - addPathSegments(p, index, null, + addPathSegments(path, index, null, null); //TODO Apply `paints` already here? not so SOLID[0] index++; - }); + } } /// Parses Svg from provided asset path diff --git a/lib/src/range.dart b/lib/src/range.dart index c85c5ca..e2d3967 100644 --- a/lib/src/range.dart +++ b/lib/src/range.dart @@ -1,19 +1,20 @@ +import 'package:equatable/equatable.dart'; + /// Denotes a range of path segments over which a animation is built. /// /// Segments below that range will be painted with the first frame of the animation and therefore not iteratively. Segments above that range will be excluded from the animation. This class must not be inherited. -abstract class AnimationRange { +abstract class AnimationRange extends Equatable { AnimationRange(this.start, this.end) { assert(start! <= end! && start! >= 0 && end! >= 0); } final int? start; final int? end; + @override + List get props => [start, end]; + bool get isLower => start != null; bool get isUpper => end != null; - - @override - bool operator ==(Object o) => - o is AnimationRange && start == o.start && end == o.end; } /// Denotes a range by its relative position in the Path array provided. diff --git a/test/unit_widget_arg.dart b/test/unit_widget_arg.dart index ab4d080..7cd9468 100644 --- a/test/unit_widget_arg.dart +++ b/test/unit_widget_arg.dart @@ -18,7 +18,7 @@ void main() { 'test.svg', controller: null, run: true, - duration: Duration(seconds: 5), + duration: const Duration(seconds: 5), ), const TypeMatcher()); @@ -34,7 +34,7 @@ void main() { //[C] AnimatedDrawing.paths //Not working - missing arguments: empty list of paths expect( - () => AnimatedDrawing.paths([], + () => AnimatedDrawing.paths(const [], controller: MockAnimationController()), throwsA(const TypeMatcher())); });