Skip to content

Commit

Permalink
CLI Command that generates all flavors with single flag (#752)
Browse files Browse the repository at this point in the history
* - added support to generate all flavors with a single command

* - added more helpers and tests

* - updated the readme and changelog
  • Loading branch information
vlazdra authored Dec 29, 2024
1 parent 62ee636 commit 52bb16c
Show file tree
Hide file tree
Showing 10 changed files with 396 additions and 23 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## [2.4.4] - ()

- Added the ability to generate all flavors with a single flag. Closes [#751](https://github.com/jonbhanson/flutter_native_splash/issues/751)

## [2.4.3] - (2024-Nov-17)

- Add Swift Package Manager support. Closes [#749](https://github.com/jonbhanson/flutter_native_splash/issues/749).
Expand Down
40 changes: 34 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,20 @@ When the package finishes running, your splash screen is ready.

(Optionally), If you added your config to a separate YAML file instead of `pubspec.yaml`, just add --path with the command in the terminal:

```
```bash
dart run flutter_native_splash:create --path=path/to/my/file.yaml
```

| Command | Description |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| -h, --[no-]help | Show help |
| -p, --path | Path to the flutter project, if the project is not in it's default location. |
| -f, --flavor | Flavor to create the splash for. The flavor must match the pattern flutter_native_splash-*.yaml (where * is the flavor name). |
| -F, --flavors | Comma separated list of flavors to create the splash screens for. Match the pattern flutter_native_splash-*.yaml (where * is the flavor name). |
| -A, --[no-]all-flavors | Create the splash screens for all flavors that match the pattern flutter_native_splash-*.yaml (where * is the flavor name). |

> Note: Only one flavor option is allowed.

## 3. Set up app initialization (optional)

By default, the splash screen will be removed when Flutter has drawn the first frame. If you would like the splash screen to remain while your app initializes, you can use the `preserve()` and `remove()` methods together. Pass the `preserve()` method the value returned from `WidgetsFlutterBinding.ensureInitialized()` to keep the splash on screen. Later, when your app has initialized, make a call to `remove()` to remove the splash screen.
Expand Down Expand Up @@ -255,23 +265,23 @@ If you have a project setup that contains multiple flavors or environments, and

Instead of maintaining multiple files and copy/pasting images, you can now, using this tool, create different splash screens for different environments.

### Pre-requirements
## Pre-requirements

In order to use the new feature, and generate the desired splash images for you app, a couple of changes are required.
In order to use this feature, and generate the desired splash images for your app, a couple of changes are required.

If you want to generate just one flavor and one file you would use either options as described in Step 1. But in order to setup the flavors, you will then be required to move all your setup values to the `flutter_native_splash.yaml` file, but with a prefix.

Let's assume for the rest of the setup that you have 3 different flavors, `Production`, `Acceptance`, `Development`.

First this you will need to do is to create a different setup file for all 3 flavors with a suffix like so:
First thing you will need to do is to create a different setup file for all 3 flavors with a suffix like so:

```bash
flutter_native_splash-production.yaml
flutter_native_splash-acceptance.yaml
flutter_native_splash-development.yaml
```

You would setup those 3 files the same way as you would the one, but with different assets depending on which environment you would be generating. For example (Note: these are just examples, you can use whatever setup you need for your project that is already supported by the package):
You would setup those 3 files the same way as you would the one, but with different assets depending on which environment you would be generating. For example:

```yaml
# flutter_native_splash-development.yaml
Expand Down Expand Up @@ -328,6 +338,10 @@ flutter_native_splash:
web: false
```

> Note: these are just example values. You should substitute them with real values.

## One by one

If you'd like to generate only a single flavor (maybe you are
testing something out), you can use only the single command like this:

Expand All @@ -342,14 +356,28 @@ dart run flutter_native_splash:create --flavor acceptance
dart run flutter_native_splash:create --flavor development
```

## More than one

You also have the ability to specify all the flavors in one command
as shown bellow:

```bash
dart run flutter_native_splash:create --flavors development,staging,production
```

Note: the available flavors need to be comma separated for this option to work.
> Note: the available flavors need to be comma separated for this option to work.

## All flavors

And if you have many different flavors available in your project, and wish to generate the splash screen for all of them, you can use this command (starting from 2.4.4):

```bash
dart run flutter_native_splash:create --all-flavors
# OR you can use the shorthand option
dart run flutter_native_splash:create -A
```

This will take all files from the root of the project, scan through them and match for the pattern `flutter_native_splash-*.yaml` where the value at the place of the star will be used as the flavor name and will be consumed to generate the files.

### Android setup

Expand Down
89 changes: 76 additions & 13 deletions bin/create.dart
Original file line number Diff line number Diff line change
@@ -1,35 +1,98 @@
import 'dart:io';

import 'package:args/args.dart';
import 'package:flutter_native_splash/cli_commands.dart';
import 'package:flutter_native_splash/enums.dart';
import 'package:flutter_native_splash/helper_utils.dart';

void main(List<String> args) {
final parser = ArgParser();

parser.addOption('path');
parser.addOption('flavor');
parser.addOption('flavors');
parser
..addFlag(
ArgEnums.help.name,
abbr: ArgEnums.help.abbr,
help: 'Show help',
)
..addOption(
ArgEnums.path.name,
abbr: ArgEnums.path.abbr,
help:
'Path to the flutter project, if the project is not in it\'s default location.',
)
..addOption(
ArgEnums.flavor.name,
abbr: ArgEnums.flavor.abbr,
help:
'Flavor to create the splash for. The flavor must match the pattern flutter_native_splash-*.yaml (where * is the flavor name).',
)
..addOption(
ArgEnums.flavors.name,
abbr: ArgEnums.flavors.abbr,
help:
'Comma separated list of flavors to create the splash screens for. Match the pattern flutter_native_splash-*.yaml (where * is the flavor name).',
)
..addFlag(
ArgEnums.allFlavors.name,
abbr: ArgEnums.allFlavors.abbr,
help:
'Create the splash screens for all flavors that match the pattern flutter_native_splash-*.yaml (where * is the flavor name).',
);

final parsedArgs = parser.parse(args);

if (parsedArgs['flavor'] != null && parsedArgs['flavors'] != null) {
throw Exception('Cannot use both flavor and flavors arguments');
final helpArg = parsedArgs[ArgEnums.help.name] as bool?;

if (helpArg == true) {
print(parser.usage);
return;
}

if (parsedArgs['flavor'] != null) {
final pathArg = parsedArgs[ArgEnums.path.name]?.toString();
final flavorArg = parsedArgs[ArgEnums.flavor.name]?.toString();
final flavorsArg = parsedArgs[ArgEnums.flavors.name]?.toString();
final allFlavorsArg = parsedArgs[ArgEnums.allFlavors.name] as bool?;

// Validate the flavor arguments
HelperUtils.validateFlavorArgs(
flavorArg: flavorArg,
flavorsArg: flavorsArg,
allFlavorsArg: allFlavorsArg,
);

if (flavorArg != null) {
createSplash(
path: parsedArgs['path']?.toString(),
flavor: parsedArgs['flavor']?.toString(),
path: pathArg,
flavor: flavorArg,
);
} else if (parsedArgs['flavors'] != null) {
final flavors = parsedArgs['flavors']?.toString().split(',');
for (final flavor in flavors!) {
} else if (flavorsArg != null) {
for (final flavor in flavorsArg.split(',')) {
createSplash(
path: pathArg,
flavor: flavor,
);
}
} else if (allFlavorsArg == true) {
// Find all flavor configurations in current project directory
final flavors = Directory.current
.listSync()
.whereType<File>()
.map((entity) => entity.path.split(Platform.pathSeparator).last)
.where(HelperUtils.isValidFlavorConfigFileName)
.map(HelperUtils.getFlavorNameFromFileName)
.toList();

print('Found ${flavors.length} flavor configurations: $flavors');

for (final flavor in flavors) {
createSplash(
path: parsedArgs['path']?.toString(),
path: pathArg,
flavor: flavor,
);
}
} else {
createSplash(
path: parsedArgs['path']?.toString(),
path: pathArg,
flavor: null,
);
}
Expand Down
31 changes: 27 additions & 4 deletions bin/remove.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,39 @@
import 'package:args/args.dart';
import 'package:flutter_native_splash/cli_commands.dart';
import 'package:flutter_native_splash/enums.dart';

void main(List<String> args) {
final parser = ArgParser();

parser.addOption('path');
parser.addOption('flavor');
parser
..addFlag(
ArgEnums.help.name,
abbr: ArgEnums.help.abbr,
help: 'Show help',
)
..addOption(
ArgEnums.path.name,
abbr: ArgEnums.path.abbr,
help:
'Path to the flutter project, if the project is not in it\'s default location.',
)
..addOption(
ArgEnums.flavor.name,
abbr: ArgEnums.flavor.abbr,
help: 'Flavor to remove the splash for.',
);

final parsedArgs = parser.parse(args);

final helpArg = parsedArgs[ArgEnums.help.name];

if (helpArg != null) {
print(parser.usage);
return;
}

removeSplash(
path: parsedArgs['path']?.toString(),
flavor: parsedArgs['flavor']?.toString(),
path: parsedArgs[ArgEnums.path.name]?.toString(),
flavor: parsedArgs[ArgEnums.flavor.name]?.toString(),
);
}
1 change: 1 addition & 0 deletions example/ios/Flutter/Debug.xcconfig
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
1 change: 1 addition & 0 deletions example/ios/Flutter/Release.xcconfig
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
44 changes: 44 additions & 0 deletions example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '12.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}

def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end

File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end

require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)

flutter_ios_podfile_setup

target 'Runner' do
use_frameworks!
use_modular_headers!

flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end

post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
12 changes: 12 additions & 0 deletions lib/enums.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
enum ArgEnums {
help(name: 'help', abbr: 'h'),
path(name: 'path', abbr: 'p'),
flavor(name: 'flavor', abbr: 'f'),
flavors(name: 'flavors', abbr: 'F'),
allFlavors(name: 'all-flavors', abbr: 'A');

final String name;
final String abbr;

const ArgEnums({required this.name, required this.abbr});
}
46 changes: 46 additions & 0 deletions lib/helper_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import 'package:flutter_native_splash/enums.dart';

class HelperUtils {
const HelperUtils._();

/// Checks if a given filename matches the flutter native splash flavor config pattern
/// The pattern is: flutter_native_splash-*.yaml where * is the flavor name
///
/// Returns true if the filename matches the pattern, false otherwise
static bool isValidFlavorConfigFileName(String fileName) {
return RegExp(r'^flutter_native_splash-[^-]+\.yaml$').hasMatch(fileName);
}

/// Extracts the flavor name from a valid flavor config filename
///
/// Throws an exception if the filename is not a valid flavor config filename
static String getFlavorNameFromFileName(String fileName) {
final flavorMatch =
RegExp(r'^flutter_native_splash-(.+)\.yaml$').firstMatch(fileName);

final flavorName = flavorMatch?.group(1);

if (flavorName == null) {
throw Exception('Invalid flavor config filename: $fileName');
}

return flavorName;
}

/// Validate the flavor arguments
///
/// Throws an exception if the arguments are invalid.
static void validateFlavorArgs({
required String? flavorArg,
required String? flavorsArg,
required bool? allFlavorsArg,
}) {
if ((flavorArg != null && flavorsArg != null) ||
(flavorArg != null && allFlavorsArg == true) ||
(flavorsArg != null && allFlavorsArg == true)) {
throw Exception(
'Cannot use multiple flavor options together. Please use only one of: --${ArgEnums.flavor.name}, --${ArgEnums.flavors.name}, or --${ArgEnums.allFlavors.name}.',
);
}
}
}
Loading

0 comments on commit 52bb16c

Please sign in to comment.