Commit 10f64830 authored by Mikkel Nygaard Ravn's avatar Mikkel Nygaard Ravn Committed by GitHub

Add Swift and Kotlin templates (#10259)

parent 230f1081
...@@ -46,6 +46,18 @@ class CreateCommand extends FlutterCommand { ...@@ -46,6 +46,18 @@ class CreateCommand extends FlutterCommand {
defaultsTo: 'A new flutter project.', defaultsTo: 'A new flutter project.',
help: 'The description to use for your new flutter project. This string ends up in the pubspec.yaml file.' help: 'The description to use for your new flutter project. This string ends up in the pubspec.yaml file.'
); );
argParser.addOption(
'ios-language',
abbr: 'i',
defaultsTo: 'objc',
allowed: <String>['objc', 'swift'],
);
argParser.addOption(
'android-language',
abbr: 'a',
defaultsTo: 'java',
allowed: <String>['java', 'kotlin'],
);
} }
@override @override
...@@ -109,9 +121,14 @@ class CreateCommand extends FlutterCommand { ...@@ -109,9 +121,14 @@ class CreateCommand extends FlutterCommand {
throwToolExit(error); throwToolExit(error);
final Map<String, dynamic> templateContext = _templateContext( final Map<String, dynamic> templateContext = _templateContext(
projectName, argResults['description'], dirPath, projectName: projectName,
flutterRoot, renderDriverTest: argResults['with-driver-test'], projectDescription: argResults['description'],
withPluginHook: generatePlugin, dirPath: dirPath,
flutterRoot: flutterRoot,
renderDriverTest: argResults['with-driver-test'],
withPluginHook: generatePlugin,
androidLanguage: argResults['android-language'],
iosLanguage: argResults['ios-language'],
); );
printStatus('Creating project ${fs.path.relative(dirPath)}...'); printStatus('Creating project ${fs.path.relative(dirPath)}...');
...@@ -206,9 +223,16 @@ To edit platform code in an IDE see https://flutter.io/platform-plugins/#edit-co ...@@ -206,9 +223,16 @@ To edit platform code in an IDE see https://flutter.io/platform-plugins/#edit-co
} }
} }
Map<String, dynamic> _templateContext(String projectName, Map<String, dynamic> _templateContext({
String projectDescription, String dirPath, String flutterRoot, String projectName,
{ bool renderDriverTest: false, bool withPluginHook: false }) { String projectDescription,
String androidLanguage,
String iosLanguage,
String dirPath,
String flutterRoot,
bool renderDriverTest: false,
bool withPluginHook: false,
}) {
flutterRoot = fs.path.normalize(flutterRoot); flutterRoot = fs.path.normalize(flutterRoot);
final String pluginDartClass = _createPluginClassName(projectName); final String pluginDartClass = _createPluginClassName(projectName);
...@@ -229,6 +253,8 @@ To edit platform code in an IDE see https://flutter.io/platform-plugins/#edit-co ...@@ -229,6 +253,8 @@ To edit platform code in an IDE see https://flutter.io/platform-plugins/#edit-co
'pluginClass': pluginClass, 'pluginClass': pluginClass,
'pluginDartClass': pluginDartClass, 'pluginDartClass': pluginDartClass,
'withPluginHook': withPluginHook, 'withPluginHook': withPluginHook,
'androidLanguage': androidLanguage,
'iosLanguage': iosLanguage,
}; };
} }
......
...@@ -126,10 +126,6 @@ const String _iosPluginRegistryHeaderTemplate = '''// ...@@ -126,10 +126,6 @@ const String _iosPluginRegistryHeaderTemplate = '''//
#import <Flutter/Flutter.h> #import <Flutter/Flutter.h>
{{#plugins}}
#import "{{class}}.h"
{{/plugins}}
@interface GeneratedPluginRegistrant : NSObject @interface GeneratedPluginRegistrant : NSObject
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry; + (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry;
@end @end
...@@ -142,6 +138,9 @@ const String _iosPluginRegistryImplementationTemplate = '''// ...@@ -142,6 +138,9 @@ const String _iosPluginRegistryImplementationTemplate = '''//
// //
#import "GeneratedPluginRegistrant.h" #import "GeneratedPluginRegistrant.h"
{{#plugins}}
#import <{{name}}/{{class}}.h>
{{/plugins}}
@implementation GeneratedPluginRegistrant @implementation GeneratedPluginRegistrant
......
...@@ -10,6 +10,7 @@ import 'globals.dart'; ...@@ -10,6 +10,7 @@ import 'globals.dart';
const String _kTemplateExtension = '.tmpl'; const String _kTemplateExtension = '.tmpl';
const String _kCopyTemplateExtension = '.copy.tmpl'; const String _kCopyTemplateExtension = '.copy.tmpl';
final Pattern _kTemplateLanguageVariant = new RegExp(r"(\w+)-(\w+)\.tmpl.*");
/// Expands templates in a directory to a destination. All files that must /// Expands templates in a directory to a destination. All files that must
/// undergo template expansion should end with the '.tmpl' extension. All other /// undergo template expansion should end with the '.tmpl' extension. All other
...@@ -20,8 +21,11 @@ const String _kCopyTemplateExtension = '.copy.tmpl'; ...@@ -20,8 +21,11 @@ const String _kCopyTemplateExtension = '.copy.tmpl';
/// without template expansion (images, data files, etc.), the '.copy.tmpl' /// without template expansion (images, data files, etc.), the '.copy.tmpl'
/// extension may be used. /// extension may be used.
/// ///
/// Files in the destination will not contain either the '.tmpl' or '.copy.tmpl' /// Folders with platform/language-specific content must be named
/// extensions. /// '<platform>-<language>.tmpl'.
///
/// Files in the destination will contain none of the '.tmpl', '.copy.tmpl'
/// or '-<language>.tmpl' extensions.
class Template { class Template {
Template(Directory templateSource, Directory baseDir) { Template(Directory templateSource, Directory baseDir) {
_templateFilePaths = <String, String>{}; _templateFilePaths = <String, String>{};
...@@ -66,19 +70,38 @@ class Template { ...@@ -66,19 +70,38 @@ class Template {
destination.createSync(recursive: true); destination.createSync(recursive: true);
int fileCount = 0; int fileCount = 0;
final String projectName = context['projectName']; /// Returns the resolved destination path corresponding to the specified
final String pluginClass = context['pluginClass']; /// raw destination path, after performing language filtering and template
final String destinationDirPath = destination.absolute.path; /// expansion on the path itself.
///
_templateFilePaths.forEach((String relativeDestPath, String absoluteSrcPath) { /// Returns null if the given raw destination path has been filtered.
String renderPath(String relativeDestinationPath) {
final Match match = _kTemplateLanguageVariant.matchAsPrefix(relativeDestinationPath);
if (match != null) {
final String platform = match.group(1);
final String language = context['${platform}Language'];
if (language != match.group(2))
return null;
relativeDestinationPath = relativeDestinationPath.replaceAll('$platform-$language.tmpl', platform);
}
final String projectName = context['projectName'];
final String pluginClass = context['pluginClass'];
final String destinationDirPath = destination.absolute.path;
String finalDestinationPath = fs.path String finalDestinationPath = fs.path
.join(destinationDirPath, relativeDestPath) .join(destinationDirPath, relativeDestinationPath)
.replaceAll(_kCopyTemplateExtension, '') .replaceAll(_kCopyTemplateExtension, '')
.replaceAll(_kTemplateExtension, ''); .replaceAll(_kTemplateExtension, '');
if (projectName != null) if (projectName != null)
finalDestinationPath = finalDestinationPath.replaceAll('projectName', projectName); finalDestinationPath = finalDestinationPath.replaceAll('projectName', projectName);
if (pluginClass != null) if (pluginClass != null)
finalDestinationPath = finalDestinationPath.replaceAll('pluginClass', pluginClass); finalDestinationPath = finalDestinationPath.replaceAll('pluginClass', pluginClass);
return finalDestinationPath;
}
_templateFilePaths.forEach((String relativeDestinationPath, String absoluteSourcePath) {
final String finalDestinationPath = renderPath(relativeDestinationPath);
if (finalDestinationPath == null)
return;
final File finalDestinationFile = fs.file(finalDestinationPath); final File finalDestinationFile = fs.file(finalDestinationPath);
final String relativePathForLogging = fs.path.relative(finalDestinationFile.path); final String relativePathForLogging = fs.path.relative(finalDestinationFile.path);
...@@ -100,7 +123,7 @@ class Template { ...@@ -100,7 +123,7 @@ class Template {
fileCount++; fileCount++;
finalDestinationFile.createSync(recursive: true); finalDestinationFile.createSync(recursive: true);
final File sourceFile = fs.file(absoluteSrcPath); final File sourceFile = fs.file(absoluteSourcePath);
// Step 2: If the absolute paths ends with a 'copy.tmpl', this file does // Step 2: If the absolute paths ends with a 'copy.tmpl', this file does
// not need mustache rendering but needs to be directly copied. // not need mustache rendering but needs to be directly copied.
......
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withInputStream { stream ->
localProperties.load(stream)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 25
buildToolsVersion '25.0.3'
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "{{androidIdentifier}}"
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
androidTestCompile 'com.android.support:support-annotations:25.0.0'
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'
compile 'org.jetbrains.kotlin:kotlin-stdlib-jre7:1.1.2-4'
}
package {{androidIdentifier}}
import android.os.Bundle
import io.flutter.app.FlutterActivity
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity(): FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this);
}
}
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.2-4'
}
}
allprojects {
repositories {
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}
task wrapper(type: Wrapper) {
gradleVersion = '2.14.1'
}
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<component name="NewModuleRootManager" inherit-compiler-output="true"> <component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$/android"> <content url="file://$MODULE_DIR$/android">
<sourceFolder url="file://$MODULE_DIR$/android/app/src/main/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/android/app/src/main/{{androidLanguage}}" isTestSource="false" />
</content> </content>
<orderEntry type="jdk" jdkName="Android API {{androidSdkVersion}} Platform" jdkType="Android SDK" /> <orderEntry type="jdk" jdkName="Android API {{androidSdkVersion}} Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
......
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
if ENV['FLUTTER_FRAMEWORK_DIR'] == nil
abort('Please set FLUTTER_FRAMEWORK_DIR to the directory containing Flutter.framework')
end
target 'Runner' do
# Pods for Runner
# Flutter Pods
pod 'Flutter', :path => ENV['FLUTTER_FRAMEWORK_DIR']
if File.exists? '../.flutter-plugins'
flutter_root = File.expand_path('..')
File.foreach('../.flutter-plugins') { |line|
plugin = line.split(pattern='=')
if plugin.length == 2
name = plugin[0].strip()
path = plugin[1].strip()
resolved_path = File.expand_path("#{path}/ios", flutter_root)
pod name, :path => resolved_path
else
puts "Invalid plugin specification: #{line}"
end
}
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
end
end
...@@ -6,9 +6,7 @@ if ENV['FLUTTER_FRAMEWORK_DIR'] == nil ...@@ -6,9 +6,7 @@ if ENV['FLUTTER_FRAMEWORK_DIR'] == nil
end end
target 'Runner' do target 'Runner' do
# Uncomment this line if you want to use Swift in your app. use_frameworks!
# Note that some Flutter plugins are not compatible with use_frameworks!
# use_frameworks!
# Pods for Runner # Pods for Runner
......
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; };
9740EEBB1CF902C7004384FC /* app.flx in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB71CF902C7004384FC /* app.flx */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
9740EEB71CF902C7004384FC /* app.flx */ = {isa = PBXFileReference; lastKnownFileType = file; name = app.flx; path = Flutter/app.flx; sourceTree = "<group>"; };
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
9740EEB71CF902C7004384FC /* app.flx */,
3B80C3931E831B6300D905FE /* App.framework */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEBA1CF902C7004384FC /* Flutter.framework */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
97C146F11CF9000F007C117D /* Supporting Files */ = {
isa = PBXGroup;
children = (
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0830;
ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 0830;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9740EEBB1CF902C7004384FC /* app.flx in Resources */,
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ARCHS = arm64;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}};
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ARCHS = arm64;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}};
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 3.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
GeneratedPluginRegistrant.register(with: self);
return super.application(application, didFinishLaunchingWithOptions: launchOptions);
}
}
#import "GeneratedPluginRegistrant.h"
\ No newline at end of file
group '{{androidIdentifier}}'
version '1.0-SNAPSHOT'
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
}
}
allprojects {
repositories {
jcenter()
}
}
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 25
buildToolsVersion '25.0.3'
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
lintOptions {
disable 'InvalidPackage'
}
}
dependencies {
compile 'org.jetbrains.kotlin:kotlin-stdlib-jre7:1.1.2-4'
}
package {{androidIdentifier}}
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.PluginRegistry.Registrar
class {{pluginClass}}(): MethodCallHandler {
companion object {
@JvmStatic
fun registerWith(registrar: Registrar): Unit {
val channel = MethodChannel(registrar.messenger(), "{{projectName}}")
channel.setMethodCallHandler({{pluginClass}}())
}
}
override fun onMethodCall(call: MethodCall, result: Result): Unit {
if (call.method.equals("getPlatformVersion")) {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
}
import Flutter
import UIKit
public class Swift{{pluginClass}}: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "{{projectName}}", binaryMessenger: registrar.messenger());
let instance = Swift{{pluginClass}}();
registrar.addMethodCallDelegate(instance, channel: channel);
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
result("iOS " + UIDevice.current.systemVersion);
}
}
\ No newline at end of file
#import <Flutter/Flutter.h>
@interface {{pluginClass}} : NSObject<FlutterPlugin>
@end
#import "{{pluginClass}}.h"
#import <{{projectName}}/{{projectName}}-Swift.h>
@implementation {{pluginClass}}
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
[Swift{{pluginClass}} registerWithRegistrar:registrar];
}
@end
...@@ -10,6 +10,7 @@ void main() { ...@@ -10,6 +10,7 @@ void main() {
test('error', () async { test('error', () async {
final BufferLogger mockLogger = new BufferLogger(); final BufferLogger mockLogger = new BufferLogger();
final VerboseLogger verboseLogger = new VerboseLogger(mockLogger); final VerboseLogger verboseLogger = new VerboseLogger(mockLogger);
verboseLogger.supportsColor = false;
verboseLogger.printStatus('Hey Hey Hey Hey'); verboseLogger.printStatus('Hey Hey Hey Hey');
verboseLogger.printTrace('Oooh, I do I do I do'); verboseLogger.printTrace('Oooh, I do I do I do');
......
...@@ -39,15 +39,32 @@ void main() { ...@@ -39,15 +39,32 @@ void main() {
return _createAndAnalyzeProject( return _createAndAnalyzeProject(
projectDir, projectDir,
<String>[], <String>[],
fs.path.join(projectDir.path, 'lib', 'main.dart'), <String>[
'android/app/src/main/java/com/yourcompany/flutter_project/MainActivity.java',
'ios/Runner/AppDelegate.h',
'ios/Runner/AppDelegate.m',
'ios/Runner/main.m',
'lib/main.dart',
],
); );
}); });
testUsingContext('project with-driver-test', () async { testUsingContext('kotlin/swift project', () async {
return _createAndAnalyzeProject( return _createAndAnalyzeProject(
projectDir, projectDir,
<String>['--with-driver-test'], <String>['--android-language', 'kotlin', '--ios-language', 'swift'],
fs.path.join(projectDir.path, 'lib', 'main.dart'), <String>[
'android/app/src/main/kotlin/com/yourcompany/flutter_project/MainActivity.kt',
'ios/Runner/AppDelegate.swift',
'ios/Runner/Runner-Bridging-Header.h',
'lib/main.dart',
],
<String>[
'android/app/src/main/java/com/yourcompany/flutter_project/MainActivity.java',
'ios/Runner/AppDelegate.h',
'ios/Runner/AppDelegate.m',
'ios/Runner/main.m',
],
); );
}); });
...@@ -55,7 +72,50 @@ void main() { ...@@ -55,7 +72,50 @@ void main() {
return _createAndAnalyzeProject( return _createAndAnalyzeProject(
projectDir, projectDir,
<String>['--plugin'], <String>['--plugin'],
fs.path.join(projectDir.path, 'example', 'lib', 'main.dart'), <String>[
'android/src/main/java/com/yourcompany/flutter_project/FlutterProjectPlugin.java',
'ios/Classes/FlutterProjectPlugin.h',
'ios/Classes/FlutterProjectPlugin.m',
'lib/flutter_project.dart',
'example/android/app/src/main/java/com/yourcompany/flutter_project_example/MainActivity.java',
'example/ios/Runner/AppDelegate.h',
'example/ios/Runner/AppDelegate.m',
'example/ios/Runner/main.m',
'example/lib/main.dart',
],
);
});
testUsingContext('kotlin/swift plugin project', () async {
return _createAndAnalyzeProject(
projectDir,
<String>['--plugin', '--android-language', 'kotlin', '--ios-language', 'swift'],
<String>[
'android/src/main/kotlin/com/yourcompany/flutter_project/FlutterProjectPlugin.kt',
'ios/Classes/FlutterProjectPlugin.h',
'ios/Classes/FlutterProjectPlugin.m',
'ios/Classes/SwiftFlutterProjectPlugin.swift',
'lib/flutter_project.dart',
'example/android/app/src/main/kotlin/com/yourcompany/flutter_project_example/MainActivity.kt',
'example/ios/Runner/AppDelegate.swift',
'example/ios/Runner/Runner-Bridging-Header.h',
'example/lib/main.dart',
],
<String>[
'android/src/main/java/com/yourcompany/flutter_project/FlutterProjectPlugin.java',
'example/android/app/src/main/java/com/yourcompany/flutter_project_example/MainActivity.java',
'example/ios/Runner/AppDelegate.h',
'example/ios/Runner/AppDelegate.m',
'example/ios/Runner/main.m',
],
);
});
testUsingContext('project with-driver-test', () async {
return _createAndAnalyzeProject(
projectDir,
<String>['--with-driver-test'],
<String>['lib/main.dart'],
); );
}); });
...@@ -82,19 +142,16 @@ void main() { ...@@ -82,19 +142,16 @@ void main() {
<String>[file.path], <String>[file.path],
workingDirectory: projectDir.path, workingDirectory: projectDir.path,
); );
final String formatted = final String formatted = await process.stdout.transform(UTF8.decoder).join();
await process.stdout.transform(UTF8.decoder).join();
expect(original, formatted, reason: file.path); expect(original, formatted, reason: file.path);
} }
} }
// Generated Xcode settings // Generated Xcode settings
final String xcodeConfigPath = final String xcodeConfigPath = fs.path.join('ios', 'Flutter', 'Generated.xcconfig');
fs.path.join('ios', 'Flutter', 'Generated.xcconfig');
expectExists(xcodeConfigPath); expectExists(xcodeConfigPath);
final File xcodeConfigFile = final File xcodeConfigFile = fs.file(fs.path.join(projectDir.path, xcodeConfigPath));
fs.file(fs.path.join(projectDir.path, xcodeConfigPath));
final String xcodeConfig = xcodeConfigFile.readAsStringSync(); final String xcodeConfig = xcodeConfigFile.readAsStringSync();
expect(xcodeConfig, contains('FLUTTER_ROOT=')); expect(xcodeConfig, contains('FLUTTER_ROOT='));
expect(xcodeConfig, contains('FLUTTER_APPLICATION_PATH=')); expect(xcodeConfig, contains('FLUTTER_APPLICATION_PATH='));
...@@ -121,8 +178,8 @@ void main() { ...@@ -121,8 +178,8 @@ void main() {
final CommandRunner<Null> runner = createTestCommandRunner(command); final CommandRunner<Null> runner = createTestCommandRunner(command);
expect( expect(
runner.run(<String>['create', projectDir.path, '--pub']), runner.run(<String>['create', projectDir.path, '--pub']),
throwsToolExit(exitCode: 2, message: 'Try moving --pub') throwsToolExit(exitCode: 2, message: 'Try moving --pub'),
); );
}); });
...@@ -135,8 +192,8 @@ void main() { ...@@ -135,8 +192,8 @@ void main() {
if (!existingFile.existsSync()) if (!existingFile.existsSync())
existingFile.createSync(recursive: true); existingFile.createSync(recursive: true);
expect( expect(
runner.run(<String>['create', existingFile.path]), runner.run(<String>['create', existingFile.path]),
throwsToolExit(message: 'file exists') throwsToolExit(message: 'file exists'),
); );
}); });
...@@ -145,18 +202,16 @@ void main() { ...@@ -145,18 +202,16 @@ void main() {
final CreateCommand command = new CreateCommand(); final CreateCommand command = new CreateCommand();
final CommandRunner<Null> runner = createTestCommandRunner(command); final CommandRunner<Null> runner = createTestCommandRunner(command);
expect( expect(
runner.run(<String>['create', fs.path.join(projectDir.path, 'invalidName')]), runner.run(<String>['create', fs.path.join(projectDir.path, 'invalidName')]),
throwsToolExit(message: '"invalidName" is not a valid Dart package name.') throwsToolExit(message: '"invalidName" is not a valid Dart package name.'),
); );
}); });
}); });
} }
Future<Null> _createAndAnalyzeProject( Future<Null> _createAndAnalyzeProject(
Directory dir, Directory dir, List<String> createArgs, List<String> expectedPaths,
List<String> createArgs, [List<String> unexpectedPaths = const <String>[]]) async {
String mainPath,
) async {
Cache.flutterRoot = '../..'; Cache.flutterRoot = '../..';
final CreateCommand command = new CreateCommand(); final CreateCommand command = new CreateCommand();
final CommandRunner<Null> runner = createTestCommandRunner(command); final CommandRunner<Null> runner = createTestCommandRunner(command);
...@@ -165,7 +220,12 @@ Future<Null> _createAndAnalyzeProject( ...@@ -165,7 +220,12 @@ Future<Null> _createAndAnalyzeProject(
args.add(dir.path); args.add(dir.path);
await runner.run(args); await runner.run(args);
expect(fs.file(mainPath).existsSync(), true); for (String path in expectedPaths) {
expect(fs.file(fs.path.join(dir.path, path)).existsSync(), true, reason: '$path does not exist');
}
for (String path in unexpectedPaths) {
expect(fs.file(fs.path.join(dir.path, path)).existsSync(), false, reason: '$path exists');
}
final String flutterToolsPath = fs.path.absolute(fs.path.join( final String flutterToolsPath = fs.path.absolute(fs.path.join(
'bin', 'bin',
'flutter_tools.dart', 'flutter_tools.dart',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment