Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b3883f8
fix: add package_name to rpm and appimage
prateekmedia Sep 24, 2024
5caf46f
fix: update appname for exe too
prateekmedia Sep 24, 2024
6cd0efc
fix: update flutter_app_packager
prateekmedia Sep 24, 2024
eb1315d
fix: really resize the image
prateekmedia Sep 26, 2024
1a50de8
feat: include_build_number parameter
prateekmedia Sep 26, 2024
9bf0e15
Merge remote-tracking branch 'origin/main' into develop
prateekmedia Sep 26, 2024
52622ac
fix: set include build number to true by default
prateekmedia Sep 28, 2024
0ebb016
fix: set include build number to true by default
prateekmedia Sep 28, 2024
fc44798
fix: add include build number to other build methods
prateekmedia Sep 28, 2024
f4f8d04
Merge remote-tracking branch 'origin/main' into develop
prateekmedia Sep 28, 2024
8a827a2
fix(linux): remove version field from desktop filess
prateekmedia Sep 29, 2024
762c404
Merge remote-tracking branch 'origin/main' into develop
prateekmedia Sep 29, 2024
94534db
fix: package three icon sizes
prateekmedia Oct 28, 2024
67b6fe7
chore: remove unused variables
prateekmedia Oct 28, 2024
00c4b82
Merge my/develop into develop
prateekmedia Dec 3, 2025
76a1274
feat(linux): add startup_wm_class parameter for desktop files
prateekmedia Dec 3, 2025
f004a64
fix: add error handling for image decode in Linux makers
prateekmedia Dec 3, 2025
7296a9e
fix(appimage): use appName consistently for icon naming
prateekmedia Dec 3, 2025
62e4678
allow custom desktop files
prateekmedia Jan 11, 2026
7447e23
bump ver
prateekmedia Jan 11, 2026
e05bdbe
use local path dependencies for testing
prateekmedia Jan 11, 2026
1c48202
add pubspec_overrides for all local path dependencies
prateekmedia Jan 11, 2026
7b97815
override all internal deps to local paths in unified_distributor
prateekmedia Jan 11, 2026
5b48abf
only override flutter_app_packager to local path
prateekmedia Jan 11, 2026
71392e8
remove pubspec_overrides.yaml
prateekmedia Jan 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/en/makers/appimage.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ mv appimagetool /usr/local/bin/
Add `make_config.yaml` to your project `linux/packaging/appimage` directory.

```yaml
package_name: hello_world
display_name: Hello World

icon: assets/logo.png
Expand Down
1 change: 1 addition & 0 deletions docs/en/makers/rpm.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Install requirements:
Add `make_config.yaml` to your project `linux/packaging/rpm` directory.

```yaml
package_name: hello-world
icon: assets/logo.png
summary: A really cool application
group: Application/Emulator
Expand Down
1 change: 1 addition & 0 deletions docs/zh/makers/appimage.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ mv appimagetool /usr/local/bin/
将 `make_config.yaml` 添加到您的项目 `linux/packaging/appimage` 目录。

```yaml
package_name: hello_world
display_name: Hello World

icon: assets/logo.png
Expand Down
1 change: 1 addition & 0 deletions docs/zh/makers/rpm.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
将 `make_config.yaml` 添加到您的项目 `linux/packaging/rpm` 目录。

```yaml
package_name: hello-world
icon: assets/logo.png
summary: A really cool application
group: Application/Emulator
Expand Down
5 changes: 3 additions & 2 deletions packages/fastforge/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: fastforge
description: A powerful and efficient tool for packaging and publishing your applications with ease.
version: 0.6.6
version: 0.6.7
homepage: https://fastforge.dev
repository: https://github.com/fastforgedev/fastforge/tree/main/packages/fastforge
issue_tracker: https://github.com/fastforgedev/fastforge/issues
Expand All @@ -14,7 +14,8 @@ environment:
sdk: ">=2.16.0 <4.0.0"

dependencies:
unified_distributor: ^0.2.6
unified_distributor:
path: ../unified_distributor

dev_dependencies:
dependency_validator: ^3.0.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:io';

import 'package:flutter_app_packager/src/api/app_package_maker.dart';
import 'package:flutter_app_packager/src/makers/appimage/make_appimage_config.dart';
import 'package:image/image.dart' as img;
import 'package:path/path.dart' as path;
import 'package:shell_executor/shell_executor.dart';

Expand Down Expand Up @@ -85,7 +86,16 @@ class AppPackageMakerAppImage extends AppPackageMaker {
),
)..createSync(recursive: true);

await desktopFile.writeAsString(makeConfig.desktopFileContent);
// Use custom desktop file if specified, otherwise use generated content
if (makeConfig.desktopFile != null) {
final customDesktopFile = File(path.join(Directory.current.path, makeConfig.desktopFile!));
if (!customDesktopFile.existsSync()) {
throw MakeError("Desktop file ${makeConfig.desktopFile} path wasn't found");
}
await customDesktopFile.copy(desktopFile.path);
} else {
await desktopFile.writeAsString(makeConfig.desktopFileContent);
}

final appRunFile = File(
path.join(
Expand Down Expand Up @@ -116,38 +126,33 @@ class AppPackageMakerAppImage extends AppPackageMaker {
),
);

final icon256x256 = path.join(
makeConfig.packagingDirectory.path,
'${makeConfig.appName}.AppDir/usr/share/icons/hicolor/256x256/apps',
);
final icon128x128 = path.join(
makeConfig.packagingDirectory.path,
'${makeConfig.appName}.AppDir/usr/share/icons/hicolor/128x128/apps',
);

await $('mkdir', [
'-p',
icon128x128,
icon256x256,
]).then((value) {
if (value.exitCode != 0) {
throw MakeError(value.stderr as String);
}
});
for (final size in [128, 256, 512]) {
final iconDir = path.join(
makeConfig.packagingDirectory.path,
'${makeConfig.appName}.AppDir/usr/share/icons/hicolor/${size}x$size/apps',
);
final mkdirProcessRes = await $('mkdir', [
'-p',
iconDir,
]);

await iconFile.copy(
path.join(
icon128x128,
'${makeConfig.appName}${path.extension(makeConfig.icon)}',
),
);
if (mkdirProcessRes.exitCode != 0) throw MakeError();

await iconFile.copy(
path.join(
icon256x256,
'${makeConfig.appName}${path.extension(makeConfig.icon)}',
),
);
final iconBytes = await iconFile.readAsBytes();
final decodedIcon = img.decodeImage(iconBytes);
if (decodedIcon == null) {
throw MakeError('Failed to decode icon: ${makeConfig.icon}');
}
final icon = img.copyResize(
decodedIcon,
width: size,
height: size,
interpolation: img.Interpolation.average,
);
final newIconFile =
File(path.join(iconDir, '${makeConfig.appName}.png'));
await newIconFile.writeAsBytes(img.encodePng(icon));
}

if (makeConfig.metainfo != null) {
final metainfoDir = path.join(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,36 +22,38 @@ class AppImageAction {
List<String> arguments;

Map<String, dynamic> toJson() {
return {
'label': label,
'name': name,
'arguments': arguments,
};
return {'label': label, 'name': name, 'arguments': arguments};
}
}

class MakeAppImageConfig extends MakeConfig {
MakeAppImageConfig({
this.packageName,
required this.displayName,
required this.icon,
this.keywords = const [],
this.categories = const [],
this.actions = const [],
this.include = const [],
this.startupNotify = true,
this.startupWMClass,
this.genericName = 'A Flutter Application',
this.supportedMimeType,
this.metainfo,
this.desktopFile,
});
factory MakeAppImageConfig.fromJson(Map<String, dynamic> map) {
return MakeAppImageConfig(
packageName: map['package_name'] as String?,
displayName: map['display_name'] as String,
icon: map['icon'] as String,
metainfo: map['metainfo'] as String?,
desktopFile: map['desktop_file'] as String?,
include: (map['include'] as List<dynamic>? ?? []).cast<String>(),
keywords: (map['keywords'] as List<dynamic>? ?? []).cast<String>(),
categories: (map['categories'] as List<dynamic>? ?? []).cast<String>(),
startupNotify: map['startup_notify'] as bool? ?? false,
startupWMClass: map['startup_wm_class'] as String?,
genericName: map['generic_name'] as String? ?? 'A Flutter Application',
actions: (map['actions'] as List? ?? [])
.map(
Expand All @@ -66,13 +68,19 @@ class MakeAppImageConfig extends MakeConfig {
);
}

@override
String get appName => packageName ?? super.appName;

final String icon;
final String? metainfo;
final String? desktopFile;
final List<String> keywords;
final List<String> categories;
final List<AppImageAction> actions;
final bool startupNotify;
final String? startupWMClass;
final String genericName;
final String? packageName;
final String displayName;
final List<String> include;
List<String>? supportedMimeType;
Expand All @@ -85,6 +93,7 @@ class MakeAppImageConfig extends MakeConfig {
'Icon': appName,
'Type': 'Application',
'StartupNotify': startupNotify ? 'true' : 'false',
if (startupWMClass != null) 'StartupWMClass': startupWMClass,
if (supportedMimeType != null && supportedMimeType!.isNotEmpty)
'MimeType': '${supportedMimeType!.join(';')};',
if (categories.isNotEmpty) 'Categories': categories.join(';'),
Expand All @@ -93,14 +102,16 @@ class MakeAppImageConfig extends MakeConfig {
'Actions': this.actions.map((e) => e.label).join(';'),
}.entries.map((e) => '${e.key}=${e.value}').join('\n');

final actions = this.actions.map((action) {
final fields = {
'Name': action.name,
'Exec':
'LD_LIBRARY_PATH=usr/lib $appName ${action.arguments.join(' ')} %u',
};
return '[Desktop Action ${action.label}]\n${fields.entries.map((e) => '${e.key}=${e.value}').join('\n')}';
}).join('\n\n');
final actions = this.actions
.map((action) {
final fields = {
'Name': action.name,
'Exec':
'LD_LIBRARY_PATH=usr/lib $appName ${action.arguments.join(' ')} %u',
};
return '[Desktop Action ${action.label}]\n${fields.entries.map((e) => '${e.key}=${e.value}').join('\n')}';
})
.join('\n\n');

return '[Desktop Entry]\n$fields\n\n$actions';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:io';

import 'package:flutter_app_packager/src/api/app_package_maker.dart';
import 'package:flutter_app_packager/src/makers/deb/make_deb_config.dart';
import 'package:image/image.dart' as img;
import 'package:path/path.dart' as path;
import 'package:shell_executor/shell_executor.dart';

Expand Down Expand Up @@ -46,18 +47,11 @@ class AppPackageMakerDeb extends AppPackageMaker {
/// /usr/share/applications
/// /usr/share/icons/hicolor/128x128/apps
/// /usr/share/icons/hicolor/256x256/apps
/// /usr/share/icons/hicolor/512x512/apps

final debianDir = path.join(packagingDirectory.path, 'DEBIAN');
final applicationsDir =
path.join(packagingDirectory.path, 'usr/share/applications');
final icon128Dir = path.join(
packagingDirectory.path,
'usr/share/icons/hicolor/128x128/apps',
);
final icon256Dir = path.join(
packagingDirectory.path,
'usr/share/icons/hicolor/256x256/apps',
);
final metainfoDir =
path.join(packagingDirectory.path, 'usr/share/metainfo');
final mkdirProcessResult = await $('mkdir', [
Expand All @@ -66,7 +60,6 @@ class AppPackageMakerDeb extends AppPackageMaker {
path.join(packagingDirectory.path, 'usr/share', makeConfig.appBinaryName),
applicationsDir,
if (makeConfig.metainfo != null) metainfoDir,
if (makeConfig.icon != null) ...[icon128Dir, icon256Dir],
]);

if (mkdirProcessResult.exitCode != 0) throw MakeError();
Expand All @@ -77,18 +70,33 @@ class AppPackageMakerDeb extends AppPackageMaker {
throw MakeError("provided icon ${makeConfig.icon} path wasn't found");
}

await iconFile.copy(
path.join(
icon128Dir,
makeConfig.appBinaryName + path.extension(makeConfig.icon!),
),
);
await iconFile.copy(
path.join(
icon256Dir,
makeConfig.appBinaryName + path.extension(makeConfig.icon!),
),
);
for (final size in [128, 256, 512]) {
final iconDir = path.join(
packagingDirectory.path,
'usr/share/icons/hicolor/${size}x$size/apps',
);
final mkdirProcessRes = await $('mkdir', [
'-p',
iconDir,
]);

if (mkdirProcessRes.exitCode != 0) throw MakeError();

final iconBytes = await iconFile.readAsBytes();
final decodedIcon = img.decodeImage(iconBytes);
if (decodedIcon == null) {
throw MakeError('Failed to decode icon: ${makeConfig.icon}');
}
final icon = img.copyResize(
decodedIcon,
width: size,
height: size,
interpolation: img.Interpolation.average,
);
final newIconFile =
File(path.join(iconDir, '${makeConfig.appBinaryName}.png'));
await newIconFile.writeAsBytes(img.encodePng(icon));
}
}
if (makeConfig.metainfo != null) {
final metainfoPath =
Expand Down Expand Up @@ -118,7 +126,18 @@ class AppPackageMakerDeb extends AppPackageMaker {
if (!desktopEntryFile.existsSync()) desktopEntryFile.createSync();

await controlFile.writeAsString(files['CONTROL']!);
await desktopEntryFile.writeAsString(files['DESKTOP']!);

// Use custom desktop file if specified, otherwise use generated content
if (makeConfig.desktopFile != null) {
final customDesktopFile = File(path.join(Directory.current.path, makeConfig.desktopFile!));
if (!customDesktopFile.existsSync()) {
throw MakeError("Desktop file ${makeConfig.desktopFile} path wasn't found");
}
await customDesktopFile.copy(desktopEntryFile.path);
} else {
await desktopEntryFile.writeAsString(files['DESKTOP']!);
}

await postinstFile.writeAsString(files['postinst']!);
await postrmFile.writeAsString(files['postrm']!);

Expand Down
Loading
Loading