Skip to content

Commit

Permalink
feat(windows): add support for windows closes #380
Browse files Browse the repository at this point in the history
  • Loading branch information
RatakondalaArun committed Jul 23, 2022
1 parent a69a4e9 commit c22a950
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 1 deletion.
3 changes: 3 additions & 0 deletions lib/abs/icon_generator.dart
Expand Up @@ -55,6 +55,9 @@ class IconGeneratorContext {

/// Shortcut for `config.webConfig`
WebConfig? get webConfig => config.webConfig;

/// Shortcut for `config.windowsConfig`
WindowsConfig? get windowsConfig => config.windowsConfig;
}

/// Generates Icon for given platforms
Expand Down
14 changes: 14 additions & 0 deletions lib/constants.dart
Expand Up @@ -42,6 +42,20 @@ String webIndexFilePath = path.join(webDirPath, 'index.html');
/// Relative pubspec.yaml path
String pubspecFilePath = path.join('pubspec.yaml');

// Windows
/// Relative path to windows directory
String windowsDirPath = path.join('windows');

/// Relative path to windows resources directory
String windowsResourcesDirPath = path.join(windowsDirPath, 'runner', 'resources');

/// Relative path to windows icon file path
String windowsIconFilePath = path.join(windowsResourcesDirPath, 'app_icon.ico');

/// Default windows icon size for flutter
///
const int kWindowsIconSize = 48;

const String errorMissingImagePath =
'Missing "image_path" or "image_path_android" + "image_path_ios" within configuration';
const String errorMissingPlatform = 'No platform specified within config to generate icons for.';
Expand Down
39 changes: 39 additions & 0 deletions lib/flutter_launcher_icons_config.dart
Expand Up @@ -46,6 +46,10 @@ class FlutterLauncherIconsConfig {
@JsonKey(name: 'web')
final WebConfig? webConfig;

/// Windows platform config
@JsonKey(name: 'windows')
final WindowsConfig? windowsConfig;

/// Creates an instance of [FlutterLauncherIconsConfig]
const FlutterLauncherIconsConfig({
this.imagePath,
Expand All @@ -56,6 +60,7 @@ class FlutterLauncherIconsConfig {
this.adaptiveIconForeground,
this.adaptiveIconBackground,
this.webConfig,
this.windowsConfig,
});

/// Creates [FlutterLauncherIconsConfig] icons from [json]
Expand Down Expand Up @@ -161,3 +166,37 @@ class WebConfig {
@override
String toString() => 'WebConfig: ${toJson()}';
}

/// A Configs for Windows
@JsonSerializable(
anyMap: true,
checked: true,
)
class WindowsConfig {
/// Specifies weather to generate icons for web
final bool generate;

/// Image path for web
@JsonKey(name: 'image_path')
final String? imagePath;

/// Size of the icon to generate
@JsonKey(name: 'icon_size')
final int? iconSize;

/// Creates a instance of [WindowsConfig]
const WindowsConfig({
this.generate = false,
this.imagePath,
this.iconSize,
});

/// Creates [WindowsConfig] from [json]
factory WindowsConfig.fromJson(Map json) => _$WindowsConfigFromJson(json);

/// Creates [Map] from [WindowsConfig]
Map toJson() => _$WindowsConfigToJson(this);

@override
String toString() => 'WindowsConfig: ${toJson()}';
}
27 changes: 26 additions & 1 deletion lib/flutter_launcher_icons_config.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions lib/main.dart
Expand Up @@ -10,6 +10,7 @@ import 'package:flutter_launcher_icons/flutter_launcher_icons_config.dart';
import 'package:flutter_launcher_icons/ios.dart' as ios_launcher_icons;
import 'package:flutter_launcher_icons/logger.dart';
import 'package:flutter_launcher_icons/web/web_icon_generator.dart';
import 'package:flutter_launcher_icons/windows/windows_icon_generator.dart';
import 'package:path/path.dart' as path;
import 'package:yaml/yaml.dart';

Expand Down Expand Up @@ -151,6 +152,7 @@ Future<void> createIconsFromConfig(
flavor: flavor,
platforms: (context) => [
WebIconGenerator(context),
WindowsIconGenerator(context),
// todo: add other platforms
],
);
Expand Down
2 changes: 2 additions & 0 deletions lib/utils.dart
Expand Up @@ -33,6 +33,8 @@ String generateError(Exception e, String? error) {
return '\n✗ ERROR: ${(e).runtimeType.toString()}$errorOutput';
}

// TODO(RatakondalaArun): Remove nullable return type
// this can never return null value since it already throws exception
Image? decodeImageFile(String filePath) {
final image = decodeImage(File(filePath).readAsBytesSync());
if (image == null) {
Expand Down
75 changes: 75 additions & 0 deletions lib/windows/windows_icon_generator.dart
@@ -0,0 +1,75 @@
import 'package:image/image.dart';
import 'package:path/path.dart' as path;

import '../abs/icon_generator.dart';
import '../constants.dart' as constants;
import '../custom_exceptions.dart';
import '../utils.dart' as utils;

/// A Implementation of [IconGenerator] for Windows
class WindowsIconGenerator extends IconGenerator {
/// Creates a instance of [WindowsIconGenerator]
WindowsIconGenerator(IconGeneratorContext context) : super(context, 'Windows');

@override
void createIcons() {
final imgFilePath = path.join(
context.prefixPath,
context.windowsConfig!.imagePath ?? context.config.imagePath,
);

context.logger.verbose('Decoding and loading image file from $imgFilePath...');
final imgFile = utils.decodeImageFile(imgFilePath);
// TODO(RatakondalaArun): remove null check
// #utils.decodeImageFile never returns null instead it throws Exception
if (imgFile == null) {
context.logger.error('Image File not found at given path $imgFilePath...');
throw FileNotFoundException(imgFilePath);
}

context.logger.verbose('Generating icon from $imgFilePath...');
_generateIcon(imgFile);
}

@override
bool validateRequirements() {
context.logger.verbose('Validating windows config...');
final windowsConfig = context.windowsConfig;
if (windowsConfig == null || !windowsConfig.generate) {
context.logger.error('Windows config is not provided or windows.generate is false. Skipped...');
return false;
}

if (windowsConfig.imagePath == null && context.config.imagePath == null) {
context.logger.error('Invalid config. Either provide windows.image_path or image_path');
return false;
}

// if icon_size is given it should be between 48<=icon_size<=256
// because .ico only supports this size
if (windowsConfig.iconSize != null && (windowsConfig.iconSize! < 48 || windowsConfig.iconSize! > 256)) {
context.logger.error(
'Invalid windows.icon_size=${windowsConfig.iconSize}. Icon size should be between 48<=icon_size<=256',
);
return false;
}
final entitesToCheck = [
path.join(context.prefixPath, constants.windowsDirPath),
path.join(context.prefixPath, windowsConfig.imagePath ?? context.config.imagePath),
];

final failedEntityPath = utils.areFSEntiesExist(entitesToCheck);
if (failedEntityPath != null) {
context.logger.error('$failedEntityPath this file or folder is required to generate web icons');
return false;
}

return true;
}

void _generateIcon(Image image) {
final favIcon = utils.createResizedImage(context.windowsConfig!.iconSize ?? constants.kWindowsIconSize, image);
final favIconFile = utils.createFileIfNotExist(path.join(context.prefixPath, constants.windowsIconFilePath));
favIconFile.writeAsBytesSync(encodeIco(favIcon));
}
}

0 comments on commit c22a950

Please sign in to comment.