diff --git a/.ci/Dockerfile b/.ci/Dockerfile index 2ffdf124..39f8d3f9 100644 --- a/.ci/Dockerfile +++ b/.ci/Dockerfile @@ -1,4 +1,4 @@ -# Last updated 06/07/2023 (to rebuild the docker image, update this timestamp) +# Last updated 12/06/2024 (to rebuild the docker image, update this timestamp) FROM cirrusci/flutter:beta RUN sudo apt-get update && \ diff --git a/bin/generate.dart b/bin/generate.dart index a6edd71a..ed0942fc 100644 --- a/bin/generate.dart +++ b/bin/generate.dart @@ -32,7 +32,7 @@ class DiagramGenerator { required this.temporaryDirectory, this.cleanup = true, }) : processRunner = - processRunner ?? ProcessRunner(printOutputDefault: true) { + processRunner ?? ProcessRunner(printOutputDefault: true) { print('Dart path: $generatorMain'); print('Temp directory: ${temporaryDirectory.path}'); } @@ -50,10 +50,7 @@ class DiagramGenerator { ); /// The path to the dart program to be run for generating the diagram. - static final String generatorMain = path.join( - 'lib', - 'main.dart', - ); + static final String generatorMain = path.join('lib', 'main.dart'); /// The class that the app runs as. static const String appClass = 'dev.flutter.diagram_generator'; @@ -61,8 +58,9 @@ class DiagramGenerator { /// The path to the top of the repo. static String get projectDir { if (Platform.script.isScheme('file')) { - return path - .dirname(path.dirname(path.absolute(path.fromUri(Platform.script)))); + return path.dirname( + path.dirname(path.absolute(path.fromUri(Platform.script))), + ); } else { // Tests can sometimes have data URIs, so we just return the current // directory for those. @@ -103,13 +101,15 @@ class DiagramGenerator { final DateTime startTime = DateTime.now(); if (!await _findIdForDeviceName()) { throw GeneratorException( - 'Unable to find device ID for device $device. Are you sure it is attached?'); + 'Unable to find device ID for device $device. Are you sure it is attached?', + ); } try { await _createScreenshots(categories, names, steps); - final List outputFiles = - await _combineAnimations(await _transferImages()); + final List outputFiles = await _combineAnimations( + await _transferImages(), + ); await _optimizeImages(outputFiles); } finally { if (cleanup) { @@ -117,7 +117,8 @@ class DiagramGenerator { } } print( - 'Elapsed time for diagram generation: ${DateTime.now().difference(startTime)}'); + 'Elapsed time for diagram generation: ${DateTime.now().difference(startTime)}', + ); } Future _createScreenshots( @@ -159,18 +160,20 @@ class DiagramGenerator { filters.add('fuchsia'); } else { throw GeneratorException( - 'Unsupported target platform $deviceTargetPlatform for device $deviceId'); + 'Unsupported target platform $deviceTargetPlatform for device $deviceId', + ); } filters.add('--output-dir'); filters.add(temporaryDirectory.absolute.path); late final List filterArgs; if (deviceTargetPlatform.startsWith('android')) { - filterArgs = filters.isNotEmpty - ? [ - '--route', - 'args:${Uri.encodeComponent(filters.join(' '))}' - ] - : []; + filterArgs = + filters.isNotEmpty + ? [ + '--route', + 'args:${Uri.encodeComponent(filters.join(' '))}', + ] + : []; } else { filterArgs = []; for (final String arg in filters) { @@ -179,12 +182,8 @@ class DiagramGenerator { } } final List deviceArgs = ['-d', deviceId]; - final List args = [ - flutterCommand, - 'run', - ] + - deviceArgs + - filterArgs; + final List args = + [flutterCommand, 'run'] + deviceArgs + filterArgs; await processRunner.runProcess( args, workingDirectory: Directory(generatorDir), @@ -193,11 +192,7 @@ class DiagramGenerator { Future _findIdForDeviceName() async { final ProcessRunnerResult result = await processRunner.runProcess( - [ - flutterCommand, - 'devices', - '--machine', - ], + [flutterCommand, 'devices', '--machine'], workingDirectory: temporaryDirectory, printOutput: false, ); @@ -205,9 +200,9 @@ class DiagramGenerator { final List devices = jsonDecode(result.stdout) as List; for (final Map entry in devices.cast>()) { - if ((entry['name'] as String) - .toLowerCase() - .startsWith(device.toLowerCase()) || + if ((entry['name'] as String).toLowerCase().startsWith( + device.toLowerCase(), + ) || (entry['id'] as String) == device) { deviceId = entry['id'] as String; deviceTargetPlatform = @@ -240,8 +235,9 @@ class DiagramGenerator { workingDirectory: temporaryDirectory, printOutput: false, ); - for (final ArchiveFile file - in TarDecoder().decodeBytes(tarData.stdoutRaw)) { + for (final ArchiveFile file in TarDecoder().decodeBytes( + tarData.stdoutRaw, + )) { if (file.isFile) { files.add(File(file.name)); File(path.join(temporaryDirectory.absolute.path, file.name)) @@ -250,11 +246,15 @@ class DiagramGenerator { } } } else { - await for (final FileSystemEntity entity - in temporaryDirectory.list(recursive: true, followLinks: false)) { + await for (final FileSystemEntity entity in temporaryDirectory.list( + recursive: true, + followLinks: false, + )) { if (entity is File) { - final String relativePath = - path.relative(entity.path, from: temporaryDirectory.path); + final String relativePath = path.relative( + entity.path, + from: temporaryDirectory.path, + ); files.add(File(relativePath)); } } @@ -272,7 +272,8 @@ class DiagramGenerator { } Future> _buildMoviesFromMetadata( - List metadataList) async { + List metadataList, + ) async { final Directory destDir = Directory(assetDir); final List outputs = []; // The key represent the iterations and starts with 0. Iterations are @@ -285,15 +286,23 @@ class DiagramGenerator { for (final AnimationMetadata metadata in metadataList) { final String prefix = '${metadata.category}/${metadata.name}'; - final File destination = File(path.join(destDir.path, '$prefix.${metadata.videoFormat.name}')); + final File destination = File( + path.join(destDir.path, '$prefix.${metadata.videoFormat.name}'), + ); if (destination.existsSync()) { destination.deleteSync(); } if (!destination.parent.existsSync()) { destination.parent.createSync(recursive: true); } - print('Converting ${metadata.name} animation to ${metadata.videoFormat.name}.'); - _generateCommands(metadata: metadata, destination: destination.path, jobs: jobs); + print( + 'Converting ${metadata.name} animation to ${metadata.videoFormat.name}.', + ); + _generateCommands( + metadata: metadata, + destination: destination.path, + jobs: jobs, + ); outputs.add(destination); } final ProcessPool pool = ProcessPool(processRunner: processRunner); @@ -310,11 +319,19 @@ class DiagramGenerator { required String destination, required Map> jobs, }) { - switch(metadata.videoFormat) { + switch (metadata.videoFormat) { case VideoFormat.mp4: - _generateMp4Commands(metadata: metadata, destination: destination, jobs: jobs); + _generateMp4Commands( + metadata: metadata, + destination: destination, + jobs: jobs, + ); case VideoFormat.gif: - _generateGifCommands(metadata: metadata, destination: destination, jobs: jobs); + _generateGifCommands( + metadata: metadata, + destination: destination, + jobs: jobs, + ); } } @@ -323,33 +340,35 @@ class DiagramGenerator { required String destination, required Map> jobs, }) { - jobs.putIfAbsent(0, () => []).add( - WorkerJob( - [ - ffmpegCommand, - '-loglevel', 'fatal', // Only print fatal errors. - '-framerate', metadata.frameRate.toStringAsFixed(2), - '-i', '-', // read in the concatenated frame files from stdin. - // Yes, specify the -framerate flag twice: once for input, once for - // output. - '-framerate', metadata.frameRate.toStringAsFixed(2), - '-tune', 'animation', // Optimize the encoder for cell animation. - '-preset', - 'veryslow', // Use the slowest (best quality) compression preset. - // Almost lossless quality (can't use lossless '0' because Safari - // doesn't support it). - '-crf', '1', - '-c:v', 'libx264', // encode to mp4 H.264 - '-y', // overwrite output - // Video format set to YUV420 color space for compatibility. - '-vf', 'format=yuv420p', - destination, // output movie. - ], - workingDirectory: temporaryDirectory, - stdinRaw: _concatInputs(metadata.frameFiles), - printOutput: true, - ), - ); + jobs + .putIfAbsent(0, () => []) + .add( + WorkerJob( + [ + ffmpegCommand, + '-loglevel', 'fatal', // Only print fatal errors. + '-framerate', metadata.frameRate.toStringAsFixed(2), + '-i', '-', // read in the concatenated frame files from stdin. + // Yes, specify the -framerate flag twice: once for input, once for + // output. + '-framerate', metadata.frameRate.toStringAsFixed(2), + '-tune', 'animation', // Optimize the encoder for cell animation. + '-preset', + 'veryslow', // Use the slowest (best quality) compression preset. + // Almost lossless quality (can't use lossless '0' because Safari + // doesn't support it). + '-crf', '1', + '-c:v', 'libx264', // encode to mp4 H.264 + '-y', // overwrite output + // Video format set to YUV420 color space for compatibility. + '-vf', 'format=yuv420p', + destination, // output movie. + ], + workingDirectory: temporaryDirectory, + stdinRaw: _concatInputs(metadata.frameFiles), + printOutput: true, + ), + ); } void _generateGifCommands({ @@ -357,44 +376,54 @@ class DiagramGenerator { required String destination, required Map> jobs, }) { - final String palette = path.join(temporaryDirectory.path, '${metadata.category}_${metadata.name}.png'); - // Generate palette. - jobs.putIfAbsent(0, () => []).add( - WorkerJob( - [ - ffmpegCommand, - '-loglevel', 'fatal', // Only print fatal errors. - '-i', '-', // read in the concatenated frame files from stdin. - '-vf', 'fps=${metadata.frameRate.toStringAsFixed(0)},scale=${metadata.width}:-1:flags=lanczos,palettegen', - palette, - ], - workingDirectory: temporaryDirectory, - stdinRaw: _concatInputs(metadata.frameFiles), - printOutput: true, - ), + final String palette = path.join( + temporaryDirectory.path, + '${metadata.category}_${metadata.name}.png', ); + // Generate palette. + jobs + .putIfAbsent(0, () => []) + .add( + WorkerJob( + [ + ffmpegCommand, + '-loglevel', 'fatal', // Only print fatal errors. + '-i', '-', // read in the concatenated frame files from stdin. + '-vf', + 'fps=${metadata.frameRate.toStringAsFixed(0)},scale=${metadata.width}:-1:flags=lanczos,palettegen', + palette, + ], + workingDirectory: temporaryDirectory, + stdinRaw: _concatInputs(metadata.frameFiles), + printOutput: true, + ), + ); // Create the final gif with the palette. - jobs.putIfAbsent(1, () => []).add( - WorkerJob( - [ - ffmpegCommand, - '-loglevel', 'fatal', // Only print fatal errors. - '-i', '-', - '-i', palette, - '-filter_complex', 'fps=${metadata.frameRate.toStringAsFixed(0)},scale=${metadata.width}:-1:flags=lanczos[x];[x][1:v]paletteuse', - destination, - ], - workingDirectory: temporaryDirectory, - stdinRaw: _concatInputs(metadata.frameFiles), - printOutput: true, - ), - ); + jobs + .putIfAbsent(1, () => []) + .add( + WorkerJob( + [ + ffmpegCommand, + '-loglevel', 'fatal', // Only print fatal errors. + '-i', '-', + '-i', palette, + '-filter_complex', + 'fps=${metadata.frameRate.toStringAsFixed(0)},scale=${metadata.width}:-1:flags=lanczos[x];[x][1:v]paletteuse', + destination, + ], + workingDirectory: temporaryDirectory, + stdinRaw: _concatInputs(metadata.frameFiles), + printOutput: true, + ), + ); } Future> _combineAnimations(List inputFiles) async { - final List errorFiles = inputFiles - .where((File input) => path.basename(input.path) == 'error.log') - .toList(); + final List errorFiles = + inputFiles + .where((File input) => path.basename(input.path) == 'error.log') + .toList(); if (errorFiles.length != 1) { throw GeneratorException('Subprocess did not complete cleanly!'); @@ -402,8 +431,10 @@ class DiagramGenerator { print('Processing ${inputFiles.length - 1} files...'); - final String errorsFileName = - path.join(temporaryDirectory.absolute.path, errorFiles.single.path); + final String errorsFileName = path.join( + temporaryDirectory.absolute.path, + errorFiles.single.path, + ); final String errors = await File(errorsFileName).readAsString(); if (errors.isNotEmpty) { print('Failed. Errors:'); @@ -411,9 +442,10 @@ class DiagramGenerator { throw GeneratorException('Failed with errors (see $errorsFileName).'); } - final List metadataFiles = inputFiles - .where((File input) => path.extension(input.path) == '.json') - .toList(); + final List metadataFiles = + inputFiles + .where((File input) => path.extension(input.path) == '.json') + .toList(); // Collect all the animation frames that are in the metadata files so that // we can eliminate them from the other files that were transferred. @@ -427,27 +459,31 @@ class DiagramGenerator { ), ); } - final AnimationMetadata metadata = - AnimationMetadata.fromFile(metadataFile); + final AnimationMetadata metadata = AnimationMetadata.fromFile( + metadataFile, + ); metadataList.add(metadata); animationFiles.add(metadata.metadataFile.absolute.path); - animationFiles - .addAll(metadata.frameFiles.map((File file) => file.absolute.path)); + animationFiles.addAll( + metadata.frameFiles.map((File file) => file.absolute.path), + ); } - final List staticFiles = inputFiles.where((File input) { - if (!input.isAbsolute) { - input = File( - path.normalize( - path.join(temporaryDirectory.absolute.path, input.path), - ), - ); - } else { - input = File(path.normalize(input.path)); - } - return !animationFiles.contains(input.absolute.path); - }).toList(); - final List convertedFiles = - await _buildMoviesFromMetadata(metadataList); + final List staticFiles = + inputFiles.where((File input) { + if (!input.isAbsolute) { + input = File( + path.normalize( + path.join(temporaryDirectory.absolute.path, input.path), + ), + ); + } else { + input = File(path.normalize(input.path)); + } + return !animationFiles.contains(input.absolute.path); + }).toList(); + final List convertedFiles = await _buildMoviesFromMetadata( + metadataList, + ); return staticFiles..addAll(convertedFiles); } @@ -457,8 +493,9 @@ class DiagramGenerator { if (!imagePath.path.endsWith('.png')) { continue; } - final File destination = - File(path.join(Directory(assetDir).path, imagePath.path)); + final File destination = File( + path.join(Directory(assetDir).path, imagePath.path), + ); final Directory destDir = destination.parent; if (!destDir.existsSync()) { destDir.createSync(recursive: true); @@ -466,20 +503,22 @@ class DiagramGenerator { if (destination.existsSync()) { destination.deleteSync(); } - jobs.add(WorkerJob( - [ - optiPngCommand, - '-zc1-9', - '-zm1-9', - '-zs0-3', - '-f0-5', - imagePath.path, - '-out', - destination.path, - ], - workingDirectory: temporaryDirectory, - name: 'optipng ${destination.path}', - )); + jobs.add( + WorkerJob( + [ + optiPngCommand, + '-zc1-9', + '-zm1-9', + '-zs0-3', + '-f0-5', + imagePath.path, + '-out', + destination.path, + ], + workingDirectory: temporaryDirectory, + name: 'optipng ${destination.path}', + ), + ); } if (jobs.isNotEmpty) { final ProcessPool pool = ProcessPool(processRunner: processRunner); @@ -509,10 +548,11 @@ bool _hasJobFailed(WorkerJob job) { } Future>> listAvailableDevices() async { - final ProcessRunnerResult result = await ProcessRunner().runProcess( - ['flutter', 'devices', '--machine'], - printOutput: false, - ); + final ProcessRunnerResult result = await ProcessRunner().runProcess([ + 'flutter', + 'devices', + '--machine', + ], printOutput: false); final Map> devices = >{}; @@ -521,7 +561,7 @@ Future>> listAvailableDevices() async { in devicesJson.cast>()) { devices[(entry['name'] as String).toLowerCase()] = { 'id': entry['id'] as String, - 'targetPlatform': entry['targetPlatform'] as String + 'targetPlatform': entry['targetPlatform'] as String, }; } return devices; @@ -554,33 +594,48 @@ Future main(List arguments) async { final ArgParser parser = ArgParser(); parser.addFlag('help', help: 'Print help.'); - parser.addFlag('keep-tmp', - help: "Don't cleanup after a run (don't remove temporary directory)."); - parser.addOption('tmpdir', - abbr: 't', - help: 'Specify a temporary directory to use (implies --keep-tmp)'); - parser.addOption('device-id', - abbr: 'd', - help: 'Specify a device ID to use for generating the diagrams. Defaults ' - 'to the host platform that the script is run on, if that platform is ' - 'supported, or an attached device if not. Available devices ' - 'are:\n${getDeviceList(devices)}\n', - defaultsTo: getDefaultDevice(devices)); - parser.addMultiOption('category', - abbr: 'c', - help: 'Specify the categories of diagrams that should be generated. The ' - 'category is the name of the subdirectory of the assets/ directory ' - 'into which the images will be placed, as determined by the ' - 'DiagramStep.category property.'); - parser.addMultiOption('name', - abbr: 'n', - help: 'Specify the names of diagrams that should be generated. The ' - 'name is the basename of the output file and may be specified with ' - 'or without the suffix.'); - parser.addMultiOption('step', - abbr: 's', - help: - 'Specify the class names of the DiagramSteps that should be generated.'); + parser.addFlag( + 'keep-tmp', + help: "Don't cleanup after a run (don't remove temporary directory).", + ); + parser.addOption( + 'tmpdir', + abbr: 't', + help: 'Specify a temporary directory to use (implies --keep-tmp)', + ); + parser.addOption( + 'device-id', + abbr: 'd', + help: + 'Specify a device ID to use for generating the diagrams. Defaults ' + 'to the host platform that the script is run on, if that platform is ' + 'supported, or an attached device if not. Available devices ' + 'are:\n${getDeviceList(devices)}\n', + defaultsTo: getDefaultDevice(devices), + ); + parser.addMultiOption( + 'category', + abbr: 'c', + help: + 'Specify the categories of diagrams that should be generated. The ' + 'category is the name of the subdirectory of the assets/ directory ' + 'into which the images will be placed, as determined by the ' + 'DiagramStep.category property.', + ); + parser.addMultiOption( + 'name', + abbr: 'n', + help: + 'Specify the names of diagrams that should be generated. The ' + 'name is the basename of the output file and may be specified with ' + 'or without the suffix.', + ); + parser.addMultiOption( + 'step', + abbr: 's', + help: + 'Specify the class names of the DiagramSteps that should be generated.', + ); final ArgResults flags = parser.parse(arguments); if (flags['help'] as bool) { diff --git a/bin/pubspec.yaml b/bin/pubspec.yaml index 480dadee..7ba350e2 100644 --- a/bin/pubspec.yaml +++ b/bin/pubspec.yaml @@ -15,4 +15,4 @@ dev_dependencies: process: ^5.0.1 environment: - sdk: ">=3.0.0 <4.0.0" + sdk: "^3.7.0-0" diff --git a/bin/test/fake_process_manager.dart b/bin/test/fake_process_manager.dart index e42a3c17..9e2e36c5 100644 --- a/bin/test/fake_process_manager.dart +++ b/bin/test/fake_process_manager.dart @@ -61,8 +61,11 @@ class FakeProcessManager implements ProcessManager { expect(invocations.length, equals(calls.length)); for (final FakeInvocationRecord call in calls) { expect(invocations[index].invocation, orderedEquals(call.invocation)); - expect(invocations[index].workingDirectory, equals(call.workingDirectory), - reason: 'Wrong working directory for ${call.invocation}'); + expect( + invocations[index].workingDirectory, + equals(call.workingDirectory), + reason: 'Wrong working directory for ${call.invocation}', + ); index++; } } @@ -88,8 +91,11 @@ class FakeProcessManager implements ProcessManager { break; } } - expect(foundResult, isNotNull, - reason: '$command not found in expected results: ${fakeResults.keys}'); + expect( + foundResult, + isNotNull, + reason: '$command not found in expected results: ${fakeResults.keys}', + ); return fakeResults[foundCommand]?.removeAt(0) ?? ProcessResult(0, 0, '', ''); } @@ -98,25 +104,37 @@ class FakeProcessManager implements ProcessManager { FakeProcess(_popResult(command), stdinResults); Future _nextProcess( - List invocation, String? workingDirectory) async { - final FakeInvocationRecord record = - FakeInvocationRecord(invocation, workingDirectory: workingDirectory); + List invocation, + String? workingDirectory, + ) async { + final FakeInvocationRecord record = FakeInvocationRecord( + invocation, + workingDirectory: workingDirectory, + ); invocations.add(record); return Future.value(_popProcess(record)); } ProcessResult _nextResultSync( - List invocation, String? workingDirectory) { - final FakeInvocationRecord record = - FakeInvocationRecord(invocation, workingDirectory: workingDirectory); + List invocation, + String? workingDirectory, + ) { + final FakeInvocationRecord record = FakeInvocationRecord( + invocation, + workingDirectory: workingDirectory, + ); invocations.add(record); return _popResult(record); } Future _nextResult( - List invocation, String? workingDirectory) async { - final FakeInvocationRecord record = - FakeInvocationRecord(invocation, workingDirectory: workingDirectory); + List invocation, + String? workingDirectory, + ) async { + final FakeInvocationRecord record = FakeInvocationRecord( + invocation, + workingDirectory: workingDirectory, + ); invocations.add(record); return Future.value(_popResult(record)); } @@ -185,12 +203,14 @@ typedef StdinResults = void Function(String input); /// FakeProcessManager. class FakeProcess implements Process { FakeProcess(ProcessResult result, StdinResults stdinResults) - : stdoutStream = - Stream>.value((result.stdout as String).codeUnits), - stderrStream = - Stream>.value((result.stderr as String).codeUnits), - desiredExitCode = result.exitCode, - stdinSink = IOSink(StringStreamConsumer(stdinResults)); + : stdoutStream = Stream>.value( + (result.stdout as String).codeUnits, + ), + stderrStream = Stream>.value( + (result.stderr as String).codeUnits, + ), + desiredExitCode = result.exitCode, + stdinSink = IOSink(StringStreamConsumer(stdinResults)); final IOSink stdinSink; final Stream> stdoutStream; diff --git a/bin/test/generate_test.dart b/bin/test/generate_test.dart index d7c38c56..42212b36 100644 --- a/bin/test/generate_test.dart +++ b/bin/test/generate_test.dart @@ -11,8 +11,9 @@ import 'package:test/test.dart'; import '../generate.dart'; import 'fake_process_manager.dart'; -final String repoRoot = - path.dirname(path.dirname(path.dirname(path.fromUri(Platform.script)))); +final String repoRoot = path.dirname( + path.dirname(path.dirname(path.fromUri(Platform.script))), +); void main() { group('DiagramGenerator', () { @@ -22,8 +23,9 @@ void main() { setUp(() { processManager = FakeProcessManager((String input) {}); - temporaryDirectory = - Directory.systemTemp.createTempSync('flutter_generate_test.'); + temporaryDirectory = Directory.systemTemp.createTempSync( + 'flutter_generate_test.', + ); generator = DiagramGenerator( processRunner: ProcessRunner(processManager: processManager), temporaryDirectory: temporaryDirectory, @@ -37,17 +39,19 @@ void main() { try { test('make sure generate generates', () async { - final Map> calls = - >{ - FakeInvocationRecord( - ['flutter', 'devices', '--machine'], - workingDirectory: temporaryDirectory.path, - ): [ + final Map> + calls = >{ + FakeInvocationRecord([ + 'flutter', + 'devices', + '--machine', + ], workingDirectory: temporaryDirectory.path): [ ProcessResult( - 0, - 0, - '[{"name": "linux", "id": "linux", "targetPlatform": "linux"}]', - ''), + 0, + 0, + '[{"name": "linux", "id": "linux", "targetPlatform": "linux"}]', + '', + ), ], FakeInvocationRecord( [ @@ -65,33 +69,33 @@ void main() { temporaryDirectory.path, ], workingDirectory: path.join( - DiagramGenerator.projectDir, 'packages', 'diagram_generator'), - ): [ - ProcessResult(0, 0, '', ''), - ], - FakeInvocationRecord( - [ - 'optipng', - '-zc1-9', - '-zm1-9', - '-zs0-3', - '-f0-5', - 'output.png', - '-out', - path.join(DiagramGenerator.projectDir, 'assets', 'output.png'), - ], - workingDirectory: temporaryDirectory.path, - ): [ + DiagramGenerator.projectDir, + 'packages', + 'diagram_generator', + ), + ): [ProcessResult(0, 0, '', '')], + FakeInvocationRecord([ + 'optipng', + '-zc1-9', + '-zm1-9', + '-zs0-3', + '-f0-5', + 'output.png', + '-out', + path.join(DiagramGenerator.projectDir, 'assets', 'output.png'), + ], workingDirectory: temporaryDirectory.path): [ ProcessResult(0, 0, '', ''), ], }; processManager.fakeResults = calls; // Fake an output file - final File errorLog = - File(path.join(temporaryDirectory.path, 'error.log')); + final File errorLog = File( + path.join(temporaryDirectory.path, 'error.log'), + ); errorLog.writeAsString(''); - final File output = - File(path.join(temporaryDirectory.path, 'output.png')); + final File output = File( + path.join(temporaryDirectory.path, 'output.png'), + ); output.writeAsString(''); await generator.generateDiagrams(); processManager.verifyCalls(calls.keys.toList()); diff --git a/packages/animation_metadata/lib/animation_metadata.dart b/packages/animation_metadata/lib/animation_metadata.dart index f31e9463..28f64a51 100644 --- a/packages/animation_metadata/lib/animation_metadata.dart +++ b/packages/animation_metadata/lib/animation_metadata.dart @@ -7,10 +7,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:path/path.dart' as path; -enum VideoFormat { - mp4, - gif, -} +enum VideoFormat { mp4, gif } /// Container class to read and write a JSON file containing metadata generated /// by the [DiagramController.drawAnimatedDiagramToFiles] method. @@ -33,13 +30,12 @@ class AnimationMetadata { json.decode(metadataFile.readAsStringSync()) as Map; final String baseDir = path.dirname(metadataFile.absolute.path); final List frameFiles = - (metadata[_frameFilesKey]! as List).map( - (dynamic name) { - return File(path.normalize(path.join(baseDir, name.toString()))); - }, - ).toList(); - final Duration duration = - Duration(milliseconds: metadata[_durationMsKey]! as int); + (metadata[_frameFilesKey]! as List).map((dynamic name) { + return File(path.normalize(path.join(baseDir, name.toString()))); + }).toList(); + final Duration duration = Duration( + milliseconds: metadata[_durationMsKey]! as int, + ); return AnimationMetadata.fromData( metadataFile: metadataFile, name: metadata[_nameKey]! as String, @@ -48,8 +44,9 @@ class AnimationMetadata { frameRate: metadata[_frameRateKey]! as double, frameFiles: frameFiles, width: metadata[_widthKey]! as int, - videoFormat: - VideoFormat.values.byName(metadata[_videoFormatKey]! as String), + videoFormat: VideoFormat.values.byName( + metadata[_videoFormatKey]! as String, + ), ); } @@ -69,15 +66,19 @@ class AnimationMetadata { _videoFormatKey: videoFormat.name, _frameRateKey: frameRate, _widthKey: width, - _frameFilesKey: frameFiles.map((File file) { - return path.relative(file.path, - from: path.dirname(metadataFile.absolute.path)); - }).toList(), + _frameFilesKey: + frameFiles.map((File file) { + return path.relative( + file.path, + from: path.dirname(metadataFile.absolute.path), + ); + }).toList(), }; const JsonEncoder encoder = JsonEncoder.withIndent(' '); final String jsonMetadata = encoder.convert(metadata); print( - 'Writing metadata for ${duration.inMilliseconds}ms animation (${frameFiles.length} frames) to: ${metadataFile.path}'); + 'Writing metadata for ${duration.inMilliseconds}ms animation (${frameFiles.length} frames) to: ${metadataFile.path}', + ); return metadataFile.writeAsString(jsonMetadata); } diff --git a/packages/animation_metadata/pubspec.yaml b/packages/animation_metadata/pubspec.yaml index 44f8d892..761257c8 100644 --- a/packages/animation_metadata/pubspec.yaml +++ b/packages/animation_metadata/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.1.0 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: "^3.7.0-0" dependencies: path: ^1.8.0 diff --git a/packages/diagram_capture/example/lib/main.dart b/packages/diagram_capture/example/lib/main.dart index f75ac85e..190538ed 100644 --- a/packages/diagram_capture/example/lib/main.dart +++ b/packages/diagram_capture/example/lib/main.dart @@ -43,7 +43,9 @@ Future main() async { DiagramFlutterBinding.ensureInitialized(); final Directory directory = Directory( path.join( - (await getApplicationDocumentsDirectory()).absolute.path, 'output'), + (await getApplicationDocumentsDirectory()).absolute.path, + 'output', + ), ); if (directory.existsSync()) { directory.deleteSync(recursive: true); diff --git a/packages/diagram_capture/example/pubspec.yaml b/packages/diagram_capture/example/pubspec.yaml index 5f44df37..0c13088c 100644 --- a/packages/diagram_capture/example/pubspec.yaml +++ b/packages/diagram_capture/example/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.1.0 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: "^3.7.0-0" flutter: ">=1.0.0 <4.0.0" dependencies: diff --git a/packages/diagram_capture/lib/diagram_capture.dart b/packages/diagram_capture/lib/diagram_capture.dart index 55f60a8d..e6ed3f5b 100644 --- a/packages/diagram_capture/lib/diagram_capture.dart +++ b/packages/diagram_capture/lib/diagram_capture.dart @@ -63,10 +63,7 @@ class _Diagram extends StatelessWidget { child: Builder( builder: (BuildContext context) { return Center( - child: RepaintBoundary( - key: boundaryKey, - child: child, - ), + child: RepaintBoundary(key: boundaryKey, child: child), ); }, ), @@ -83,27 +80,24 @@ const Size _kDefaultDiagramViewportSize = Size(1280.0, 1024.0); // so that the diagram surface fits on the device, but it doesn't affect the // captured image pixels. class _DiagramViewConfiguration extends ViewConfiguration { - _DiagramViewConfiguration({ - Size size = _kDefaultDiagramViewportSize, - }) : _paintMatrix = _getMatrix( - size, - ui.PlatformDispatcher.instance.implicitView?.devicePixelRatio ?? + _DiagramViewConfiguration({Size size = _kDefaultDiagramViewportSize}) + : _paintMatrix = _getMatrix( + size, + ui.PlatformDispatcher.instance.implicitView?.devicePixelRatio ?? 1.0, + ), + super( + physicalConstraints: + BoxConstraints.tightFor(width: size.width, height: size.height) * + (ui.PlatformDispatcher.instance.implicitView?.devicePixelRatio ?? 1.0), - super( - physicalConstraints: BoxConstraints.tightFor( - width: size.width, - height: size.height, - ) * - (ui.PlatformDispatcher.instance.implicitView?.devicePixelRatio ?? - 1.0), - logicalConstraints: BoxConstraints.tightFor( - width: size.width, - height: size.height, - ), - devicePixelRatio: - ui.PlatformDispatcher.instance.implicitView?.devicePixelRatio ?? - 1.0, - ); + logicalConstraints: BoxConstraints.tightFor( + width: size.width, + height: size.height, + ), + devicePixelRatio: + ui.PlatformDispatcher.instance.implicitView?.devicePixelRatio ?? + 1.0, + ); static Matrix4 _getMatrix(Size size, double devicePixelRatio) { final double baseRatio = @@ -126,10 +120,10 @@ class _DiagramViewConfiguration extends ViewConfiguration { shiftY = (actualHeight - desiredHeight * scale) / 2.0; } final Matrix4 matrix = Matrix4.compose( - Vector3(shiftX, shiftY, 0.0), // translation - Quaternion.identity(), // rotation - Vector3(scale, scale, 1.0) // scale - ); + Vector3(shiftX, shiftY, 0.0), // translation + Quaternion.identity(), // rotation + Vector3(scale, scale, 1.0), // scale + ); return matrix; } @@ -151,9 +145,7 @@ class _DiagramWidgetController extends WidgetController DiagramFlutterBinding get binding => super.binding as DiagramFlutterBinding; @override - Future pump([ - Duration duration = Duration.zero, - ]) { + Future pump([Duration duration = Duration.zero]) { return TestAsyncUtils.guard(() => binding.pump(duration: duration)); } @@ -173,7 +165,8 @@ class _DiagramWidgetController extends WidgetController @override Future> handlePointerEventRecord( - List records) async { + List records, + ) async { return const []; } @@ -290,15 +283,14 @@ class DiagramFlutterBinding extends BindingBase @override ViewConfiguration createViewConfigurationFor(RenderView renderView) { - return _DiagramViewConfiguration( - size: screenDimensions, - ); + return _DiagramViewConfiguration(size: screenDimensions); } /// Captures an image of the [RepaintBoundary] with the given key. Future takeSnapshot() { - final RenderRepaintBoundary object = _boundaryKey.currentContext! - .findRenderObject()! as RenderRepaintBoundary; + final RenderRepaintBoundary object = + _boundaryKey.currentContext!.findRenderObject()! + as RenderRepaintBoundary; return object.toImage(pixelRatio: pixelRatio); } @@ -308,11 +300,13 @@ class DiagramFlutterBinding extends BindingBase WidgetBuilder builder, { Duration duration = Duration.zero, }) { - final Widget rootWidget = wrapWithDefaultView(_Diagram( - boundaryKey: _boundaryKey, - widgetController: _controller, - child: Builder(builder: builder), - )); + final Widget rootWidget = wrapWithDefaultView( + _Diagram( + boundaryKey: _boundaryKey, + widgetController: _controller, + child: Builder(builder: builder), + ), + ); attachRootWidget(rootWidget); pump(); } @@ -347,8 +341,8 @@ typedef DiagramKeyframe = void Function(Duration duration); /// A callback given to drawAnimatedDiagramToFiles that is called for each /// frame. -typedef DiagramGestureCallback = void Function( - DiagramController diagram, Duration now); +typedef DiagramGestureCallback = + void Function(DiagramController diagram, Duration now); /// A controller for creating diagrams generated by using Flutter widgets. /// @@ -361,9 +355,10 @@ class DiagramController { Directory? outputDirectory, double? pixelRatio, Size screenDimensions = _kDefaultDiagramViewportSize, - }) : outputDirectory = outputDirectory ?? Directory.current, - _builder = builder { - _binding.pixelRatio = pixelRatio ?? + }) : outputDirectory = outputDirectory ?? Directory.current, + _builder = builder { + _binding.pixelRatio = + pixelRatio ?? ui.PlatformDispatcher.instance.implicitView?.devicePixelRatio ?? 1.0; _binding.screenDimensions = screenDimensions; @@ -415,8 +410,10 @@ class DiagramController { /// gesture ([startGesture] automatically does this for you for the initial /// tap down event). Future startGesture(Offset location, {int? pointer}) async { - final TestGesture gesture = - await _binding.startGesture(location, pointer: pointer); + final TestGesture gesture = await _binding.startGesture( + location, + pointer: pointer, + ); advanceTime(); // Schedule a frame. return gesture; } @@ -443,8 +440,9 @@ class DiagramController { }) async { // Even though we are only capturing a single frame, advance time at minimum // increments to let tickers update and async tasks run. - final Duration framerateDuration = - Duration(milliseconds: 1000 ~/ framerate); + final Duration framerateDuration = Duration( + milliseconds: 1000 ~/ framerate, + ); while (timestamp != Duration.zero) { final Duration advanceBy = timestamp > framerateDuration ? framerateDuration : timestamp; @@ -469,8 +467,9 @@ class DiagramController { }) async { if (!outputFile.isAbsolute) { // If output path is relative, make it relative to the output directory. - outputFile = - File(path.join(outputDirectory.absolute.path, outputFile.path)); + outputFile = File( + path.join(outputDirectory.absolute.path, outputFile.path), + ); } assert(outputFile.path.endsWith('.png')); await advanceTime(); @@ -480,8 +479,10 @@ class DiagramController { ); final ByteData? encoded = await captured.toByteData(format: format); final List bytes = encoded!.buffer.asUint8List().toList(); - print('Writing ${bytes.length} bytes, ${captured.width}x${captured.height} ' - '${_byteFormatToString(format)}, to: ${outputFile.absolute.path}'); + print( + 'Writing ${bytes.length} bytes, ${captured.width}x${captured.height} ' + '${_byteFormatToString(format)}, to: ${outputFile.absolute.path}', + ); await outputFile.writeAsBytes(bytes); return outputFile; } @@ -565,8 +566,9 @@ class DiagramController { assert(start >= Duration.zero); Duration now = start; - final Duration frameDuration = - Duration(microseconds: (1e6 / frameRate).round()); + final Duration frameDuration = Duration( + microseconds: (1e6 / frameRate).round(), + ); int index = 0; final List outputFiles = []; List keys = []; @@ -602,8 +604,9 @@ class DiagramController { ++index; } print('Wrote $index frames of $name to ${outputDirectory.path}'); - final File metadataFile = - File(path.join(outputDirectory.absolute.path, '$name.json')); + final File metadataFile = File( + path.join(outputDirectory.absolute.path, '$name.json'), + ); final AnimationMetadata metadata = AnimationMetadata.fromData( name: name, category: category, diff --git a/packages/diagram_capture/pubspec.yaml b/packages/diagram_capture/pubspec.yaml index 63a4407a..dfabb51f 100644 --- a/packages/diagram_capture/pubspec.yaml +++ b/packages/diagram_capture/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.1.0 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: "^3.7.0-0" flutter: ">=1.0.0 <4.0.0" dependencies: diff --git a/packages/diagram_capture/test/diagram_capture_test.dart b/packages/diagram_capture/test/diagram_capture_test.dart index 352edbf7..7988a7f3 100644 --- a/packages/diagram_capture/test/diagram_capture_test.dart +++ b/packages/diagram_capture/test/diagram_capture_test.dart @@ -18,8 +18,9 @@ void main() { late Directory outputDir; setUp(() async { - outputDir = - Directory.systemTemp.createTempSync('flutter_diagram_capture_test.'); + outputDir = Directory.systemTemp.createTempSync( + 'flutter_diagram_capture_test.', + ); }); tearDown(() { @@ -64,8 +65,9 @@ void main() { ); final File outputFile = File('test1.png'); - final File actualOutputFile = - await controller.drawDiagramToFile(outputFile); + final File actualOutputFile = await controller.drawDiagramToFile( + outputFile, + ); expect(actualOutputFile.existsSync(), isTrue); final Uint8List imageContents = actualOutputFile.readAsBytesSync(); final image.Image decodedImage = image.decodePng(imageContents)!; @@ -90,11 +92,11 @@ void main() { controller.builder = (BuildContext context) => TestAnimatedDiagram(key: key, size: 50.0); - final List outputImages = - await controller.drawAnimatedDiagramToImages( - end: const Duration(milliseconds: 1200), - frameDuration: const Duration(milliseconds: 200), - ); + final List outputImages = await controller + .drawAnimatedDiagramToImages( + end: const Duration(milliseconds: 1200), + frameDuration: const Duration(milliseconds: 200), + ); expect(outputImages.length, equals(7)); final List expectedSizes = [1, 11, 21, 31, 41, 50, 50]; for (int i = 0; i < outputImages.length; i++) { @@ -126,13 +128,16 @@ void main() { expect(outputFile.lengthSync(), greaterThan(0)); Map loadMetadata(File metadataFile) { - final Map metadata = json - .decode(metadataFile.readAsStringSync()) as Map; + final Map metadata = + json.decode(metadataFile.readAsStringSync()) + as Map; final String baseDir = path.dirname(metadataFile.absolute.path); final List frameFiles = (metadata['frame_files']! as List) - .map((dynamic name) => - File(path.normalize(path.join(baseDir, name as String)))) + .map( + (dynamic name) => + File(path.normalize(path.join(baseDir, name as String))), + ) .toList(); metadata['frame_files'] = frameFiles; return metadata; @@ -163,8 +168,9 @@ void main() { ); final File outputFile = File('test2.png'); - final File actualOutputFile = - await controller.drawDiagramToFile(outputFile); + final File actualOutputFile = await controller.drawDiagramToFile( + outputFile, + ); expect(actualOutputFile.existsSync(), isTrue); final Uint8List imageContents = actualOutputFile.readAsBytesSync(); final image.Image decodedImage = image.decodePng(imageContents)!; @@ -199,8 +205,9 @@ void main() { expect(testPixel.g, equals(0x96)); expect(testPixel.b, equals(0xf3)); - final TestGesture gesture = - await controller.startGesture(const Offset(50.0, 50.0)); + final TestGesture gesture = await controller.startGesture( + const Offset(50.0, 50.0), + ); await gesture.up(); controller.advanceTime(const Duration(seconds: 1)); @@ -258,8 +265,9 @@ class _TestTappableDiagramState extends State { Widget build(BuildContext context) { return TextButton( style: ButtonStyle( - backgroundColor: - WidgetStateProperty.all(on ? Colors.red : Colors.blue), + backgroundColor: WidgetStateProperty.all( + on ? Colors.red : Colors.blue, + ), ), onPressed: () { setState(() { diff --git a/packages/diagram_generator/lib/main.dart b/packages/diagram_generator/lib/main.dart index 814d1afb..2f8be25d 100644 --- a/packages/diagram_generator/lib/main.dart +++ b/packages/diagram_generator/lib/main.dart @@ -41,11 +41,12 @@ Future main(List args) async { DiagramFlutterBinding.ensureInitialized(); late final List arguments; if (platform.isAndroid) { - arguments = PlatformDispatcher.instance.defaultRouteName.length > 5 - ? Uri.decodeComponent( - PlatformDispatcher.instance.defaultRouteName.substring(5)) - .split(' ') - : []; + arguments = + PlatformDispatcher.instance.defaultRouteName.length > 5 + ? Uri.decodeComponent( + PlatformDispatcher.instance.defaultRouteName.substring(5), + ).split(' ') + : []; } else { arguments = args; } @@ -71,12 +72,16 @@ Future main(List args) async { final List categories = flags['category'] as List; final List names = flags['name'] as List; final List steps = flags['step'] as List; - final Set platforms = (flags['platform'] as List) - .map((String platformStr) { - assert(diagramStepPlatformNames.containsKey(platformStr), - 'Invalid platform $platformStr'); - return diagramStepPlatformNames[platformStr]!; - }).toSet(); + final Set platforms = + (flags['platform'] as List).map(( + String platformStr, + ) { + assert( + diagramStepPlatformNames.containsKey(platformStr), + 'Invalid platform $platformStr', + ); + return diagramStepPlatformNames[platformStr]!; + }).toSet(); print( 'Filters:\n categories: $categories\n names: $names\n steps: $steps', @@ -84,7 +89,8 @@ Future main(List args) async { final DateTime start = DateTime.now(); final Directory outputDirectory = await prepareOutputDirectory( - platform.isAndroid ? null : flags['output-dir'] as String?); + platform.isAndroid ? null : flags['output-dir'] as String?, + ); final DiagramController controller = DiagramController( outputDirectory: outputDirectory, @@ -93,94 +99,99 @@ Future main(List args) async { ); final Completer done = Completer(); - Zone.current.fork(specification: ZoneSpecification( - handleUncaughtError: ( - Zone self, - ZoneDelegate parent, - Zone zone, - Object error, - StackTrace stackTrace, - ) { - print('Exception! $error\n$stackTrace'); - errorLog.writeln(error); - errorLog.writeln(stackTrace); - }, - )).runGuarded(() async { - for (final DiagramStep step in allDiagramSteps) { - if ((categories.isNotEmpty && !categories.contains(step.category)) || - (platforms.isNotEmpty && - platforms.intersection(step.platforms).isEmpty) || - (steps.isNotEmpty && - !steps.any((String name) => - step.runtimeType.toString().toLowerCase() == - name.toLowerCase()))) { - continue; - } - final Directory stepOutputDirectory = - Directory(path.join(outputDirectory.absolute.path, step.category)); - stepOutputDirectory.createSync(recursive: true); - controller.outputDirectory = stepOutputDirectory; - controller.pixelRatio = 1.0; - print('Working on step ${step.runtimeType}'); - - for (final DiagramMetadata diagram in await step.diagrams) { - if (names.isNotEmpty && !names.contains(diagram.name)) { - continue; - } + Zone.current + .fork( + specification: ZoneSpecification( + handleUncaughtError: ( + Zone self, + ZoneDelegate parent, + Zone zone, + Object error, + StackTrace stackTrace, + ) { + print('Exception! $error\n$stackTrace'); + errorLog.writeln(error); + errorLog.writeln(stackTrace); + }, + ), + ) + .runGuarded(() async { + for (final DiagramStep step in allDiagramSteps) { + if ((categories.isNotEmpty && !categories.contains(step.category)) || + (platforms.isNotEmpty && + platforms.intersection(step.platforms).isEmpty) || + (steps.isNotEmpty && + !steps.any( + (String name) => + step.runtimeType.toString().toLowerCase() == + name.toLowerCase(), + ))) { + continue; + } + final Directory stepOutputDirectory = Directory( + path.join(outputDirectory.absolute.path, step.category), + ); + stepOutputDirectory.createSync(recursive: true); + controller.outputDirectory = stepOutputDirectory; + controller.pixelRatio = 1.0; + print('Working on step ${step.runtimeType}'); + + for (final DiagramMetadata diagram in await step.diagrams) { + if (names.isNotEmpty && !names.contains(diagram.name)) { + continue; + } - // Set up a custom onError to hide errors that the diagram expects, like - // RenderFlex overflows. - final FlutterExceptionHandler? oldOnError = FlutterError.onError; - FlutterError.onError = (FlutterErrorDetails details) { - final String exception = details.exception.toString(); - for (final Pattern pattern in diagram.expectedErrors) { - if (pattern.allMatches(exception).isNotEmpty) { - return; + // Set up a custom onError to hide errors that the diagram expects, like + // RenderFlex overflows. + final FlutterExceptionHandler? oldOnError = FlutterError.onError; + FlutterError.onError = (FlutterErrorDetails details) { + final String exception = details.exception.toString(); + for (final Pattern pattern in diagram.expectedErrors) { + if (pattern.allMatches(exception).isNotEmpty) { + return; + } + } + if (oldOnError != null) { + oldOnError(details); + } + }; + + try { + final GlobalKey key = GlobalKey(); + controller.builder = (BuildContext context) { + return KeyedSubtree(key: key, child: diagram); + }; + await diagram.setUp(key); + if (diagram.duration != null) { + await controller.drawAnimatedDiagramToFiles( + end: diagram.duration!, + frameRate: diagram.frameRate, + category: step.category, + name: diagram.name, + start: diagram.startAt, + videoFormat: diagram.videoFormat, + ); + } else { + await controller.drawDiagramToFile( + File('${diagram.name}.png'), + timestamp: diagram.startAt, + framerate: diagram.frameRate, + ); + } + } finally { + FlutterError.onError = oldOnError; } } - if (oldOnError != null) { - oldOnError(details); - } - }; - - try { - final GlobalKey key = GlobalKey(); - controller.builder = (BuildContext context) { - return KeyedSubtree( - key: key, - child: diagram, - ); - }; - await diagram.setUp(key); - if (diagram.duration != null) { - await controller.drawAnimatedDiagramToFiles( - end: diagram.duration!, - frameRate: diagram.frameRate, - category: step.category, - name: diagram.name, - start: diagram.startAt, - videoFormat: diagram.videoFormat, - ); - } else { - await controller.drawDiagramToFile( - File('${diagram.name}.png'), - timestamp: diagram.startAt, - framerate: diagram.frameRate, - ); - } - } finally { - FlutterError.onError = oldOnError; } - } - } - done.complete(); - }); + done.complete(); + }); await done.future; // Save errors, if any. (We always create the file, even if empty, to signal we got to the end.) final String errors = errorLog.toString(); - final File errorsFile = - File(path.join(outputDirectory.absolute.path, 'error.log')); + final File errorsFile = File( + path.join(outputDirectory.absolute.path, 'error.log'), + ); errorsFile.writeAsStringSync(errors); if (errors.isNotEmpty) { print('Wrote errors to: ${errorsFile.path}'); @@ -207,9 +218,6 @@ class SmokeTestApp extends StatelessWidget { @override Widget build(BuildContext context) { - return const MaterialApp( - title: 'Smoke Test', - home: Placeholder(), - ); + return const MaterialApp(title: 'Smoke Test', home: Placeholder()); } } diff --git a/packages/diagram_generator/pubspec.yaml b/packages/diagram_generator/pubspec.yaml index fad5e3f7..2b7df362 100644 --- a/packages/diagram_generator/pubspec.yaml +++ b/packages/diagram_generator/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.1.0 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: "^3.7.0-0" flutter: ">=1.0.0 <4.0.0" dependencies: diff --git a/packages/diagram_viewer/lib/components/brightness_toggle.dart b/packages/diagram_viewer/lib/components/brightness_toggle.dart index be4a2098..8c530757 100644 --- a/packages/diagram_viewer/lib/components/brightness_toggle.dart +++ b/packages/diagram_viewer/lib/components/brightness_toggle.dart @@ -7,10 +7,7 @@ import 'package:flutter/material.dart'; import '../main.dart'; class BrightnessToggleButton extends StatelessWidget { - const BrightnessToggleButton({ - super.key, - this.color, - }); + const BrightnessToggleButton({super.key, this.color}); final Color? color; diff --git a/packages/diagram_viewer/lib/components/staggered_list.dart b/packages/diagram_viewer/lib/components/staggered_list.dart index 60b12e8b..37cf3beb 100644 --- a/packages/diagram_viewer/lib/components/staggered_list.dart +++ b/packages/diagram_viewer/lib/components/staggered_list.dart @@ -38,9 +38,8 @@ class RenderStaggeredList extends RenderBox with ContainerRenderObjectMixin, RenderBoxContainerDefaultsMixin { - RenderStaggeredList({ - required double minColumnWidth, - }) : _minColumnWidth = minColumnWidth; + RenderStaggeredList({required double minColumnWidth}) + : _minColumnWidth = minColumnWidth; double _minColumnWidth; @@ -104,10 +103,7 @@ class RenderStaggeredList extends RenderBox child = parentData.nextSibling; } - size = Size( - constraints.maxWidth, - columnHeights.reduce(max), - ); + size = Size(constraints.maxWidth, columnHeights.reduce(max)); } @override diff --git a/packages/diagram_viewer/lib/logic/diagram_ticker_controller.dart b/packages/diagram_viewer/lib/logic/diagram_ticker_controller.dart index 28a4c2ba..faeef4d5 100644 --- a/packages/diagram_viewer/lib/logic/diagram_ticker_controller.dart +++ b/packages/diagram_viewer/lib/logic/diagram_ticker_controller.dart @@ -11,17 +11,16 @@ import 'package:flutter/widgets.dart'; /// A controller that manages the tick state, global key, and progress of a /// diagram. class DiagramTickerController extends ChangeNotifier with Diagnosticable { - DiagramTickerController({ - required this.diagram, - }) { + DiagramTickerController({required this.diagram}) { elapsed.addListener(_onElapsed); } final DiagramMetadata diagram; /// Notifier for how much time has elapsed from the diagram's perspective. - final ValueNotifier elapsed = - ValueNotifier(Duration.zero); + final ValueNotifier elapsed = ValueNotifier( + Duration.zero, + ); /// Whether or not the TickerMode should be enabled. bool ticking = false; @@ -177,9 +176,7 @@ class DiagramTickerController extends ChangeNotifier with Diagnosticable { @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties.add( - DiagnosticsProperty('elapsed', elapsed.value), - ); + properties.add(DiagnosticsProperty('elapsed', elapsed.value)); properties.add(DiagnosticsProperty('ticking', ticking)); properties.add(DiagnosticsProperty('settingUp', settingUp)); properties.add(DiagnosticsProperty('ready', ready)); diff --git a/packages/diagram_viewer/lib/logic/subtree_widget_controller.dart b/packages/diagram_viewer/lib/logic/subtree_widget_controller.dart index 0b67ed92..114e21e4 100644 --- a/packages/diagram_viewer/lib/logic/subtree_widget_controller.dart +++ b/packages/diagram_viewer/lib/logic/subtree_widget_controller.dart @@ -21,8 +21,9 @@ class SubtreeWidgetController extends LiveWidgetController { // Send the pointer event only if it is inside the target area. final RenderBox renderObject = context.findRenderObject()! as RenderBox; final Offset topLeft = renderObject.localToGlobal(Offset.zero); - final Offset bottomRight = - renderObject.localToGlobal(renderObject.size.bottomRight(Offset.zero)); + final Offset bottomRight = renderObject.localToGlobal( + renderObject.size.bottomRight(Offset.zero), + ); if (event.position >= topLeft && event.position <= bottomRight) { binding.handlePointerEvent(event); } @@ -30,7 +31,8 @@ class SubtreeWidgetController extends LiveWidgetController { @override Future> handlePointerEventRecord( - List records) { + List records, + ) { assert(records != null); assert(records.isNotEmpty); return TestAsyncUtils.guard>(() async { diff --git a/packages/diagram_viewer/lib/main.dart b/packages/diagram_viewer/lib/main.dart index b6c9e420..5e91f878 100644 --- a/packages/diagram_viewer/lib/main.dart +++ b/packages/diagram_viewer/lib/main.dart @@ -11,10 +11,7 @@ void main() { } class DiagramViewerApp extends StatefulWidget { - const DiagramViewerApp({ - super.key, - this.home, - }); + const DiagramViewerApp({super.key, this.home}); final Widget? home; diff --git a/packages/diagram_viewer/lib/pages/diagram_catalog.dart b/packages/diagram_viewer/lib/pages/diagram_catalog.dart index 557a74ec..ce940d5c 100644 --- a/packages/diagram_viewer/lib/pages/diagram_catalog.dart +++ b/packages/diagram_viewer/lib/pages/diagram_catalog.dart @@ -36,60 +36,53 @@ class _DiagramCatalogPageState extends State { @override Widget build(BuildContext context) { - return LayoutBuilder(builder: ( - BuildContext context, - BoxConstraints constraints, - ) { - const double maxWidth = 1200.0; - final num extraWidth = max(0, constraints.maxWidth - maxWidth); - final double appBarPadding = extraWidth / 2; - return Scaffold( - appBar: AppBar( - title: Padding( - padding: EdgeInsets.only(left: appBarPadding), - child: const Text('Catalog'), - ), - centerTitle: false, - actions: [ - Padding( - padding: EdgeInsets.only(right: appBarPadding), - child: const BrightnessToggleButton(), + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + const double maxWidth = 1200.0; + final num extraWidth = max(0, constraints.maxWidth - maxWidth); + final double appBarPadding = extraWidth / 2; + return Scaffold( + appBar: AppBar( + title: Padding( + padding: EdgeInsets.only(left: appBarPadding), + child: const Text('Catalog'), ), - ], - ), - body: SingleChildScrollView( - child: Padding( - padding: EdgeInsets.only( - top: max(8, min(75, extraWidth / 2)), - ), - child: Center( - child: SizedBox( - width: 1200, - child: StaggeredList( - minColumnWidth: 350.0, - children: [ - for (int index = 0; index < categories.length; index++) - CatalogTile( - name: categories[index], - steps: steps[categories[index]]!, - ) - ], + centerTitle: false, + actions: [ + Padding( + padding: EdgeInsets.only(right: appBarPadding), + child: const BrightnessToggleButton(), + ), + ], + ), + body: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.only(top: max(8, min(75, extraWidth / 2))), + child: Center( + child: SizedBox( + width: 1200, + child: StaggeredList( + minColumnWidth: 350.0, + children: [ + for (int index = 0; index < categories.length; index++) + CatalogTile( + name: categories[index], + steps: steps[categories[index]]!, + ), + ], + ), ), ), ), ), - ), - ); - }); + ); + }, + ); } } class CatalogTile extends StatelessWidget { - const CatalogTile({ - super.key, - required this.name, - required this.steps, - }); + const CatalogTile({super.key, required this.name, required this.steps}); final String name; final List steps; @@ -111,9 +104,10 @@ class CatalogTile extends StatelessWidget { child: Text( name, style: TextStyle( - color: Theme.of(context).brightness == Brightness.light - ? Colors.white - : Colors.black, + color: + Theme.of(context).brightness == Brightness.light + ? Colors.white + : Colors.black, fontWeight: FontWeight.bold, fontSize: 16.0, ), @@ -131,10 +125,7 @@ class CatalogTile extends StatelessWidget { } class StepTile extends StatelessWidget { - const StepTile({ - super.key, - required this.step, - }); + const StepTile({super.key, required this.step}); final DiagramStep step; @@ -145,10 +136,9 @@ class StepTile extends StatelessWidget { final List diagrams = await step.diagrams; Navigator.of(context).push( MaterialPageRoute( - builder: (BuildContext context) => DiagramViewerPage( - step: step, - diagrams: diagrams, - ), + builder: + (BuildContext context) => + DiagramViewerPage(step: step, diagrams: diagrams), ), ); } @@ -158,15 +148,16 @@ class StepTile extends StatelessWidget { return ListTile( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)), title: Text(step.runtimeType.toString()), - trailing: step.platforms.containsAll(DiagramPlatform.values) - ? null - : Row( - mainAxisSize: MainAxisSize.min, - children: [ - for (final DiagramPlatform platform in step.platforms) - Chip(label: Text(platform.name)), - ], - ), + trailing: + step.platforms.containsAll(DiagramPlatform.values) + ? null + : Row( + mainAxisSize: MainAxisSize.min, + children: [ + for (final DiagramPlatform platform in step.platforms) + Chip(label: Text(platform.name)), + ], + ), visualDensity: const VisualDensity(vertical: -4), onTap: () => openDiagramStepViewer(context, step), ); diff --git a/packages/diagram_viewer/lib/pages/diagram_viewer.dart b/packages/diagram_viewer/lib/pages/diagram_viewer.dart index e155d5ce..bb0adcc0 100644 --- a/packages/diagram_viewer/lib/pages/diagram_viewer.dart +++ b/packages/diagram_viewer/lib/pages/diagram_viewer.dart @@ -36,25 +36,22 @@ class _DiagramViewerPageState extends State final GlobalKey tabBarViewKey = GlobalKey(); // Only allow the body of the viewer to receive pointer events from diagrams. - late final SubtreeWidgetController widgetController = - SubtreeWidgetController(WidgetsBinding.instance, tabBarViewKey); + late final SubtreeWidgetController widgetController = SubtreeWidgetController( + WidgetsBinding.instance, + tabBarViewKey, + ); @override void initState() { super.initState(); - tabController = TabController( - length: diagrams.length, - vsync: this, - ); + tabController = TabController(length: diagrams.length, vsync: this); tabController.addListener(onTabChange); controllers = [ for (final DiagramMetadata diagram in diagrams) - DiagramTickerController( - diagram: diagram, - ), + DiagramTickerController(diagram: diagram), ]; ticker = createTicker((Duration elapsed) { @@ -115,9 +112,10 @@ class _DiagramViewerPageState extends State padding: const EdgeInsets.all(8.0), child: Material( shape: const StadiumBorder(), - color: theme.brightness == Brightness.light - ? theme.primaryColor - : theme.colorScheme.surface, + color: + theme.brightness == Brightness.light + ? theme.primaryColor + : theme.colorScheme.surface, elevation: 4, ), ), @@ -178,31 +176,21 @@ class _DiagramViewerPageState extends State builder: (BuildContext context, double value, Widget? child) { return Opacity( opacity: value, - child: SizedBox( - width: 300 * value, - child: child, - ), + child: SizedBox(width: 300 * value, child: child), ); }, child: SliderTheme( - data: SliderThemeData( - disabledThumbColor: Colors.grey.shade200, - ), + data: SliderThemeData(disabledThumbColor: Colors.grey.shade200), child: AnimatedBuilder( animation: controller.elapsed, builder: (BuildContext context, Widget? child) { - return Slider( - value: controller.progress, - onChanged: null, - ); + return Slider(value: controller.progress, onChanged: null); }, ), ), ), ), - const BrightnessToggleButton( - color: Colors.white, - ), + const BrightnessToggleButton(color: Colors.white), ], ); } @@ -234,19 +222,14 @@ class _DiagramViewerPageState extends State child: widget.diagrams[index], ), ), - ) + ), ], ); final Widget body = Stack( children: [ Padding( - padding: const EdgeInsets.only( - left: 8, - top: 8, - right: 8, - bottom: 48, - ), + padding: const EdgeInsets.only(left: 8, top: 8, right: 8, bottom: 48), child: DiagramWidgetController( controller: widgetController, child: tabBarView, @@ -277,12 +260,7 @@ class _DiagramViewerPageState extends State }, ), Expanded( - child: ClipRect( - child: SafeArea( - left: false, - child: body, - ), - ), + child: ClipRect(child: SafeArea(left: false, child: body)), ), ], ), @@ -297,12 +275,7 @@ class _DiagramViewerPageState extends State ), body: Column( children: [ - Expanded( - child: SafeArea( - left: false, - child: body, - ), - ), + Expanded(child: SafeArea(left: false, child: body)), ], ), ); @@ -373,10 +346,7 @@ class DiagramSwitchDrawer extends StatelessWidget { Widget buildChild(BuildContext context, int index) { final DiagramMetadata diagram = diagrams[index]; - return ListTile( - title: Text(diagram.name), - onTap: () => onChanged(index), - ); + return ListTile(title: Text(diagram.name), onTap: () => onChanged(index)); } @override @@ -390,10 +360,7 @@ class DiagramSwitchDrawer extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - AppBar( - elevation: 0, - title: Text('${step.runtimeType}'), - ), + AppBar(elevation: 0, title: Text('${step.runtimeType}')), Expanded( child: ListView.builder( itemBuilder: buildChild, diff --git a/packages/diagram_viewer/pubspec.yaml b/packages/diagram_viewer/pubspec.yaml index 3dba79af..aa36afdd 100644 --- a/packages/diagram_viewer/pubspec.yaml +++ b/packages/diagram_viewer/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.1.0 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: "^3.7.0-0" flutter: ">=1.0.0 <4.0.0" dependencies: diff --git a/packages/diagram_viewer/test/diagram_viewer_test.dart b/packages/diagram_viewer/test/diagram_viewer_test.dart index f417dbcf..a23c413b 100644 --- a/packages/diagram_viewer/test/diagram_viewer_test.dart +++ b/packages/diagram_viewer/test/diagram_viewer_test.dart @@ -65,8 +65,9 @@ class TestDiagram extends StatefulWidget with DiagramMetadata { } class _TestDiagramState extends State { - final ValueNotifier durationNotifier = - ValueNotifier(Duration.zero); + final ValueNotifier durationNotifier = ValueNotifier( + Duration.zero, + ); @override Widget build(BuildContext context) { @@ -82,9 +83,7 @@ class _TestDiagramState extends State { void main() { testWidgets('Catalog shows all steps', (WidgetTester tester) async { - await tester.pumpWidget( - const DiagramViewerApp(home: DiagramCatalogPage()), - ); + await tester.pumpWidget(const DiagramViewerApp(home: DiagramCatalogPage())); await tester.pumpAndSettle(); for (final DiagramStep step in allDiagramSteps) { expect( @@ -99,12 +98,7 @@ void main() { final DiagramStep step = TestDiagramStep(); final List diagrams = await step.diagrams; await tester.pumpWidget( - DiagramViewerApp( - home: DiagramViewerPage( - step: step, - diagrams: diagrams, - ), - ), + DiagramViewerApp(home: DiagramViewerPage(step: step, diagrams: diagrams)), ); await tester.pump(const Duration(minutes: 1)); for (final DiagramMetadata diagram in diagrams) { @@ -116,8 +110,9 @@ void main() { } }); - testWidgets('DiagramTickerController with still diagram', - (WidgetTester tester) async { + testWidgets('DiagramTickerController with still diagram', ( + WidgetTester tester, + ) async { final DiagramTickerController controller = DiagramTickerController( diagram: TestDiagramStep.stillDiagram, ); @@ -171,8 +166,9 @@ void main() { expect(controller.ready, true); }); - testWidgets('DiagramTickerController with still delayed diagram', - (WidgetTester tester) async { + testWidgets('DiagramTickerController with still delayed diagram', ( + WidgetTester tester, + ) async { final DiagramTickerController controller = DiagramTickerController( diagram: TestDiagramStep.stillDelayedDiagram, ); @@ -224,8 +220,9 @@ void main() { expect(controller.ready, true); }); - testWidgets('DiagramTickerController with animated diagram', - (WidgetTester tester) async { + testWidgets('DiagramTickerController with animated diagram', ( + WidgetTester tester, + ) async { final DiagramTickerController controller = DiagramTickerController( diagram: TestDiagramStep.animatedDiagram, ); diff --git a/packages/diagrams/lib/src/adjust_drag_offset.dart b/packages/diagrams/lib/src/adjust_drag_offset.dart index 22e8ff00..090a784d 100644 --- a/packages/diagrams/lib/src/adjust_drag_offset.dart +++ b/packages/diagrams/lib/src/adjust_drag_offset.dart @@ -30,23 +30,24 @@ class AdjustDragOffsetDiagram extends StatelessWidget with DiagramMetadata { width: 300, height: 150, child: const Center( - child: Text( - 'Target Rectangle', - style: TextStyle(fontSize: 30), - ), + child: Text('Target Rectangle', style: TextStyle(fontSize: 30)), ), ), const Positioned( top: 206.5, left: 546, - child: - Text('- - - - - - - - - - - -', style: TextStyle(fontSize: 30)), + child: Text( + '- - - - - - - - - - - -', + style: TextStyle(fontSize: 30), + ), ), const Positioned( top: 353, right: 545, - child: - Text('- - - - - - - - - - - -', style: TextStyle(fontSize: 30)), + child: Text( + '- - - - - - - - - - - -', + style: TextStyle(fontSize: 30), + ), ), const Positioned( top: 100, diff --git a/packages/diagrams/lib/src/alert_dialog.dart b/packages/diagrams/lib/src/alert_dialog.dart index 192637f0..44eeff29 100644 --- a/packages/diagrams/lib/src/alert_dialog.dart +++ b/packages/diagrams/lib/src/alert_dialog.dart @@ -35,8 +35,9 @@ class _AlertDialogDiagramState extends State final RenderBox target = _openDialogKey.currentContext!.findRenderObject()! as RenderBox; - final Offset targetOffset = - target.localToGlobal(target.size.center(Offset.zero)); + final Offset targetOffset = target.localToGlobal( + target.size.center(Offset.zero), + ); final WidgetController controller = DiagramWidgetController.of(context); final TestGesture gesture = await controller.startGesture(targetOffset); @@ -66,9 +67,7 @@ class _AlertDialogDiagramState extends State Animation secondaryAnimation, ) { return Scaffold( - appBar: AppBar( - title: const Text('AlertDialog Demo'), - ), + appBar: AppBar(title: const Text('AlertDialog Demo')), body: Center( child: Builder( builder: (BuildContext context) { @@ -122,6 +121,6 @@ class AlertDialogDiagramStep extends DiagramStep { @override Future> get diagrams async => [ - const AlertDialogDiagram('alert_dialog'), - ]; + const AlertDialogDiagram('alert_dialog'), + ]; } diff --git a/packages/diagrams/lib/src/align.dart b/packages/diagrams/lib/src/align.dart index f6397de5..eba6a7d9 100644 --- a/packages/diagrams/lib/src/align.dart +++ b/packages/diagrams/lib/src/align.dart @@ -17,10 +17,7 @@ class AlignDiagram extends StatelessWidget with DiagramMetadata { @override Widget build(BuildContext context) { const Widget logo = FlutterLogo(size: 60); - const Icon origin = Icon( - Icons.gps_fixed, - size: 20, - ); + const Icon origin = Icon(Icons.gps_fixed, size: 20); Widget heading; Widget containerChild; switch (name) { @@ -35,13 +32,8 @@ class AlignDiagram extends StatelessWidget with DiagramMetadata { heading = const Text('Alignment Origin'); containerChild = const Stack( children: [ - Align( - alignment: Alignment(0.2, 0.6), - child: logo, - ), - Align( - child: origin, - ), + Align(alignment: Alignment(0.2, 0.6), child: logo), + Align(child: origin), ], ); @@ -50,14 +42,8 @@ class AlignDiagram extends StatelessWidget with DiagramMetadata { heading = const Text('Fractional Offset Origin'); containerChild = const Stack( children: [ - Align( - alignment: FractionalOffset(0.2, 0.6), - child: logo, - ), - Align( - alignment: FractionalOffset(-0.1, -0.1), - child: origin, - ), + Align(alignment: FractionalOffset(0.2, 0.6), child: logo), + Align(alignment: FractionalOffset(-0.1, -0.1), child: origin), ], ); break; @@ -80,10 +66,11 @@ class AlignDiagram extends StatelessWidget with DiagramMetadata { child: heading, ), Container( - height: 120.0, - width: 120.0, - color: Colors.blue[50], - child: containerChild), + height: 120.0, + width: 120.0, + color: Colors.blue[50], + child: containerChild, + ), ], ), ), @@ -97,8 +84,8 @@ class AlignDiagramStep extends DiagramStep { @override Future> get diagrams async => [ - const AlignDiagram('align_constant'), - const AlignDiagram('align_alignment'), - const AlignDiagram('align_fractional_offset'), - ]; + const AlignDiagram('align_constant'), + const AlignDiagram('align_alignment'), + const AlignDiagram('align_fractional_offset'), + ]; } diff --git a/packages/diagrams/lib/src/animated_icons.dart b/packages/diagrams/lib/src/animated_icons.dart index 300f7f6b..a08225b7 100644 --- a/packages/diagrams/lib/src/animated_icons.dart +++ b/packages/diagrams/lib/src/animated_icons.dart @@ -90,61 +90,61 @@ class AnimatedIconsStep extends DiagramStep { @override Future> get diagrams async => [ - const AnimatedIconsDiagram( - iconName: add_event, - iconData: AnimatedIcons.add_event, - ), - const AnimatedIconsDiagram( - iconName: arrow_menu, - iconData: AnimatedIcons.arrow_menu, - ), - const AnimatedIconsDiagram( - iconName: close_menu, - iconData: AnimatedIcons.close_menu, - ), - const AnimatedIconsDiagram( - iconName: ellipsis_search, - iconData: AnimatedIcons.ellipsis_search, - ), - const AnimatedIconsDiagram( - iconName: event_add, - iconData: AnimatedIcons.event_add, - ), - const AnimatedIconsDiagram( - iconName: home_menu, - iconData: AnimatedIcons.home_menu, - ), - const AnimatedIconsDiagram( - iconName: list_view, - iconData: AnimatedIcons.list_view, - ), - const AnimatedIconsDiagram( - iconName: menu_arrow, - iconData: AnimatedIcons.menu_arrow, - ), - const AnimatedIconsDiagram( - iconName: menu_close, - iconData: AnimatedIcons.menu_close, - ), - const AnimatedIconsDiagram( - iconName: menu_home, - iconData: AnimatedIcons.menu_home, - ), - const AnimatedIconsDiagram( - iconName: pause_play, - iconData: AnimatedIcons.pause_play, - ), - const AnimatedIconsDiagram( - iconName: play_pause, - iconData: AnimatedIcons.play_pause, - ), - const AnimatedIconsDiagram( - iconName: search_ellipsis, - iconData: AnimatedIcons.search_ellipsis, - ), - const AnimatedIconsDiagram( - iconName: view_list, - iconData: AnimatedIcons.view_list, - ), - ]; + const AnimatedIconsDiagram( + iconName: add_event, + iconData: AnimatedIcons.add_event, + ), + const AnimatedIconsDiagram( + iconName: arrow_menu, + iconData: AnimatedIcons.arrow_menu, + ), + const AnimatedIconsDiagram( + iconName: close_menu, + iconData: AnimatedIcons.close_menu, + ), + const AnimatedIconsDiagram( + iconName: ellipsis_search, + iconData: AnimatedIcons.ellipsis_search, + ), + const AnimatedIconsDiagram( + iconName: event_add, + iconData: AnimatedIcons.event_add, + ), + const AnimatedIconsDiagram( + iconName: home_menu, + iconData: AnimatedIcons.home_menu, + ), + const AnimatedIconsDiagram( + iconName: list_view, + iconData: AnimatedIcons.list_view, + ), + const AnimatedIconsDiagram( + iconName: menu_arrow, + iconData: AnimatedIcons.menu_arrow, + ), + const AnimatedIconsDiagram( + iconName: menu_close, + iconData: AnimatedIcons.menu_close, + ), + const AnimatedIconsDiagram( + iconName: menu_home, + iconData: AnimatedIcons.menu_home, + ), + const AnimatedIconsDiagram( + iconName: pause_play, + iconData: AnimatedIcons.pause_play, + ), + const AnimatedIconsDiagram( + iconName: play_pause, + iconData: AnimatedIcons.play_pause, + ), + const AnimatedIconsDiagram( + iconName: search_ellipsis, + iconData: AnimatedIcons.search_ellipsis, + ), + const AnimatedIconsDiagram( + iconName: view_list, + iconData: AnimatedIcons.view_list, + ), + ]; } diff --git a/packages/diagrams/lib/src/animation_diagram.dart b/packages/diagrams/lib/src/animation_diagram.dart index ce4cc4db..fb0af806 100644 --- a/packages/diagrams/lib/src/animation_diagram.dart +++ b/packages/diagrams/lib/src/animation_diagram.dart @@ -21,26 +21,20 @@ String _getCaption(Type type) { /// Convert the caption CamelCase name into lower_with_underscores. String getName(Type type) { final RegExp uppercase = RegExp(r'([A-Z])'); - return _getCaption(type).replaceAllMapped( - uppercase, - (Match match) { - if (match.start != 0) { - return '_${match.group(1)!.toLowerCase()}'; - } else { - return match.group(1)!.toLowerCase(); - } - }, - ); + return _getCaption(type).replaceAllMapped(uppercase, (Match match) { + if (match.start != 0) { + return '_${match.group(1)!.toLowerCase()}'; + } else { + return match.group(1)!.toLowerCase(); + } + }); } /// A base class for diagrams that show explicit animation transitions, like /// [FadeTransition]. See transitions.dart for more examples. abstract class TransitionDiagram extends StatefulWidget with DiagramMetadata { - const TransitionDiagram({ - super.key, - this.decorate = true, - }); + const TransitionDiagram({super.key, this.decorate = true}); /// Whether or not to decorate this diagram with an animation curve and top label. final bool decorate; @@ -73,11 +67,11 @@ class TransitionDiagramState extends State> duration: _kAnimationDuration, vsync: this, )..addListener(() { - setState(() { - // The animation controller is changing the animation value, so we - // need to redraw. - }); + setState(() { + // The animation controller is changing the animation value, so we + // need to redraw. }); + }); animation = widget.buildAnimation(_controller); } @@ -239,26 +233,30 @@ class SparklinePainter extends CustomPainter { final Curve curve; final double position; - static final Paint _axisPaint = Paint() - ..color = Colors.black45 - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0; - - static final Paint _sparklinePaint = Paint() - ..color = Colors.blue.shade900 - ..style = PaintingStyle.stroke - ..strokeCap = StrokeCap.round - ..strokeWidth = 4.0; - - static final Paint _graphProgressPaint = Paint() - ..color = Colors.black26 - ..style = PaintingStyle.stroke - ..strokeCap = StrokeCap.round - ..strokeWidth = 4.0; - - static final Paint _positionCirclePaint = Paint() - ..color = Colors.blue.shade900 - ..style = PaintingStyle.fill; + static final Paint _axisPaint = + Paint() + ..color = Colors.black45 + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0; + + static final Paint _sparklinePaint = + Paint() + ..color = Colors.blue.shade900 + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round + ..strokeWidth = 4.0; + + static final Paint _graphProgressPaint = + Paint() + ..color = Colors.black26 + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round + ..strokeWidth = 4.0; + + static final Paint _positionCirclePaint = + Paint() + ..color = Colors.blue.shade900 + ..style = PaintingStyle.fill; @override void paint(Canvas canvas, Size size) { @@ -274,10 +272,11 @@ class SparklinePainter extends CustomPainter { size.width - rightMargin, size.height - topMargin, ); - final Path axes = Path() - ..moveTo(area.left, area.top) // vertical axis - ..lineTo(area.left, area.bottom) // origin - ..lineTo(area.right, area.bottom); // horizontal axis + final Path axes = + Path() + ..moveTo(area.left, area.top) // vertical axis + ..lineTo(area.left, area.bottom) // origin + ..lineTo(area.right, area.bottom); // horizontal axis canvas.drawPath(axes, _axisPaint); final Offset activePoint = FractionalOffset( position, @@ -286,14 +285,17 @@ class SparklinePainter extends CustomPainter { // The sparkline itself. final Path sparkline = Path()..moveTo(area.left, area.bottom); - final double stepSize = 1.0 / + final double stepSize = + 1.0 / (area.width * (ui.PlatformDispatcher.instance.implicitView?.devicePixelRatio ?? 1.0)); void lineToPoint(Path path, double t) { - final Offset point = - FractionalOffset(t, 1.0 - curve.transform(t)).withinRect(area); + final Offset point = FractionalOffset( + t, + 1.0 - curve.transform(t), + ).withinRect(area); path.lineTo(point.dx, point.dy); } @@ -316,7 +318,10 @@ class SparklinePainter extends CustomPainter { lineToPoint(graphProgress, 1.0); canvas.drawPath(graphProgress, _graphProgressPaint); canvas.drawCircle( - Offset(activePoint.dx, activePoint.dy), 4.0, _positionCirclePaint); + Offset(activePoint.dx, activePoint.dy), + 4.0, + _positionCirclePaint, + ); } @override diff --git a/packages/diagrams/lib/src/animation_status_value.dart b/packages/diagrams/lib/src/animation_status_value.dart index dc114d32..5e073ffc 100644 --- a/packages/diagrams/lib/src/animation_status_value.dart +++ b/packages/diagrams/lib/src/animation_status_value.dart @@ -8,7 +8,8 @@ import 'package:flutter/material.dart'; import '../diagrams.dart'; -final Duration _kTotalDuration = _kBreakDuration + +final Duration _kTotalDuration = + _kBreakDuration + _kAnimationDuration + _kBreakDuration + _kAnimationDuration; @@ -38,8 +39,10 @@ class AnimationStatusValueDiagramState @override void initState() { super.initState(); - _controller = - AnimationController(vsync: this, duration: _kAnimationDuration); + _controller = AnimationController( + vsync: this, + duration: _kAnimationDuration, + ); waitLockstep(_kBreakDuration).then((_) => _controller.forward()); waitLockstep( _kBreakDuration + _kAnimationDuration + _kBreakDuration, @@ -148,7 +151,5 @@ class AnimationStatusValueDiagramStep extends DiagramStep { @override Future> get diagrams async => - const [ - AnimationStatusValueDiagram(), - ]; + const [AnimationStatusValueDiagram()]; } diff --git a/packages/diagrams/lib/src/app_bar.dart b/packages/diagrams/lib/src/app_bar.dart index a81fdee7..4a077bb4 100644 --- a/packages/diagrams/lib/src/app_bar.dart +++ b/packages/diagrams/lib/src/app_bar.dart @@ -32,14 +32,9 @@ class _DiagramState extends State { Widget build(BuildContext context) { return ConstrainedBox( key: UniqueKey(), - constraints: BoxConstraints.tight(const Size( - 540.0, - 260.0, - )), + constraints: BoxConstraints.tight(const Size(540.0, 260.0)), child: Theme( - data: ThemeData( - primarySwatch: Colors.blue, - ), + data: ThemeData(primarySwatch: Colors.blue), child: Material( color: const Color(0xFFFFFFFF), child: MediaQuery( @@ -67,7 +62,7 @@ class _DiagramState extends State { end: const FractionalOffset(0.48, 1.0), colors: [ Colors.blue.shade500, - Colors.blue.shade800 + Colors.blue.shade800, ], ), ), @@ -78,9 +73,7 @@ class _DiagramState extends State { child: Container( height: 50.0, padding: const EdgeInsets.all(4.0), - child: const Placeholder( - color: Color(0xFFFFFFFF), - ), + child: const Placeholder(color: Color(0xFFFFFFFF)), ), ), ), @@ -90,15 +83,27 @@ class _DiagramState extends State { child: LabelPainterWidget( key: canvasKey, labels: