mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Add module template for Android (#18697)
This commit is contained in:
parent
f5f055113a
commit
d89a6b544e
@ -141,16 +141,13 @@ void handleKnownGradleExceptions(String exceptionString) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _locateProjectGradlew({ bool ensureExecutable = true }) {
|
String _locateGradlewExecutable(Directory directory) {
|
||||||
final String path = fs.path.join(
|
final File gradle = directory.childFile(
|
||||||
'android',
|
|
||||||
platform.isWindows ? 'gradlew.bat' : 'gradlew',
|
platform.isWindows ? 'gradlew.bat' : 'gradlew',
|
||||||
);
|
);
|
||||||
|
|
||||||
if (fs.isFileSync(path)) {
|
if (gradle.existsSync()) {
|
||||||
final File gradle = fs.file(path);
|
os.makeExecutable(gradle);
|
||||||
if (ensureExecutable)
|
|
||||||
os.makeExecutable(gradle);
|
|
||||||
return gradle.absolute.path;
|
return gradle.absolute.path;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
@ -165,11 +162,12 @@ Future<String> _ensureGradle() async {
|
|||||||
// Note: Gradle may be bootstrapped and possibly downloaded as a side-effect
|
// Note: Gradle may be bootstrapped and possibly downloaded as a side-effect
|
||||||
// of validating the Gradle executable. This may take several seconds.
|
// of validating the Gradle executable. This may take several seconds.
|
||||||
Future<String> _initializeGradle() async {
|
Future<String> _initializeGradle() async {
|
||||||
|
final Directory android = fs.directory('android');
|
||||||
final Status status = logger.startProgress('Initializing gradle...', expectSlowOperation: true);
|
final Status status = logger.startProgress('Initializing gradle...', expectSlowOperation: true);
|
||||||
String gradle = _locateProjectGradlew();
|
String gradle = _locateGradlewExecutable(android);
|
||||||
if (gradle == null) {
|
if (gradle == null) {
|
||||||
_injectGradleWrapper();
|
injectGradleWrapper(android);
|
||||||
gradle = _locateProjectGradlew();
|
gradle = _locateGradlewExecutable(android);
|
||||||
}
|
}
|
||||||
if (gradle == null)
|
if (gradle == null)
|
||||||
throwToolExit('Unable to locate gradlew script');
|
throwToolExit('Unable to locate gradlew script');
|
||||||
@ -181,11 +179,13 @@ Future<String> _initializeGradle() async {
|
|||||||
return gradle;
|
return gradle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _injectGradleWrapper() {
|
/// Injects the Gradle wrapper into the specified directory.
|
||||||
copyDirectorySync(cache.getArtifactDirectory('gradle_wrapper'), fs.directory('android'));
|
void injectGradleWrapper(Directory directory) {
|
||||||
final String propertiesPath = fs.path.join('android', 'gradle', 'wrapper', 'gradle-wrapper.properties');
|
copyDirectorySync(cache.getArtifactDirectory('gradle_wrapper'), directory);
|
||||||
if (!fs.file(propertiesPath).existsSync()) {
|
_locateGradlewExecutable(directory);
|
||||||
fs.file(propertiesPath).writeAsStringSync('''
|
final File propertiesFile = directory.childFile(fs.path.join('gradle', 'wrapper', 'gradle-wrapper.properties'));
|
||||||
|
if (!propertiesFile.existsSync()) {
|
||||||
|
propertiesFile.writeAsStringSync('''
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
@ -196,14 +196,31 @@ distributionUrl=https\\://services.gradle.org/distributions/gradle-$gradleVersio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create android/local.properties if needed, and update Flutter settings.
|
/// Overwrite android/local.properties in the specified Flutter project, if needed.
|
||||||
|
///
|
||||||
|
/// Throws, if `pubspec.yaml` or Android SDK cannot be located.
|
||||||
Future<void> updateLocalProperties({String projectPath, BuildInfo buildInfo}) async {
|
Future<void> updateLocalProperties({String projectPath, BuildInfo buildInfo}) async {
|
||||||
final File localProperties = (projectPath == null)
|
final Directory android = (projectPath == null)
|
||||||
? fs.file(fs.path.join('android', 'local.properties'))
|
? fs.directory('android')
|
||||||
: fs.file(fs.path.join(projectPath, 'android', 'local.properties'));
|
: fs.directory(fs.path.join(projectPath, 'android'));
|
||||||
final String flutterManifest = (projectPath == null)
|
final String flutterManifest = (projectPath == null)
|
||||||
? fs.path.join(bundle.defaultManifestPath)
|
? fs.path.join(bundle.defaultManifestPath)
|
||||||
: fs.path.join(projectPath, bundle.defaultManifestPath);
|
: fs.path.join(projectPath, bundle.defaultManifestPath);
|
||||||
|
if (androidSdk == null) {
|
||||||
|
throwToolExit('Unable to locate Android SDK. Please run `flutter doctor` for more details.');
|
||||||
|
}
|
||||||
|
FlutterManifest manifest;
|
||||||
|
try {
|
||||||
|
manifest = await FlutterManifest.createFromPath(flutterManifest);
|
||||||
|
} catch (error) {
|
||||||
|
throwToolExit('Failed to load pubspec.yaml: $error');
|
||||||
|
}
|
||||||
|
updateLocalPropertiesSync(android, manifest, buildInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overwrite local.properties in the specified directory, if needed.
|
||||||
|
void updateLocalPropertiesSync(Directory android, FlutterManifest manifest, [BuildInfo buildInfo]) {
|
||||||
|
final File localProperties = android.childFile('local.properties');
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
||||||
SettingsFile settings;
|
SettingsFile settings;
|
||||||
@ -211,40 +228,27 @@ Future<void> updateLocalProperties({String projectPath, BuildInfo buildInfo}) as
|
|||||||
settings = new SettingsFile.parseFromFile(localProperties);
|
settings = new SettingsFile.parseFromFile(localProperties);
|
||||||
} else {
|
} else {
|
||||||
settings = new SettingsFile();
|
settings = new SettingsFile();
|
||||||
if (androidSdk == null) {
|
changed = true;
|
||||||
throwToolExit('Unable to locate Android SDK. Please run `flutter doctor` for more details.');
|
}
|
||||||
|
|
||||||
|
void changeIfNecessary(String key, String value) {
|
||||||
|
if (settings.values[key] != value) {
|
||||||
|
settings.values[key] = value;
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
settings.values['sdk.dir'] = escapePath(androidSdk.directory);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
final String escapedRoot = escapePath(Cache.flutterRoot);
|
|
||||||
if (changed || settings.values['flutter.sdk'] != escapedRoot) {
|
|
||||||
settings.values['flutter.sdk'] = escapedRoot;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if (buildInfo != null && settings.values['flutter.buildMode'] != buildInfo.modeName) {
|
|
||||||
settings.values['flutter.buildMode'] = buildInfo.modeName;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
FlutterManifest manifest;
|
|
||||||
try {
|
|
||||||
manifest = await FlutterManifest.createFromPath(flutterManifest);
|
|
||||||
} catch (error) {
|
|
||||||
throwToolExit('Failed to load pubspec.yaml: $error');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (androidSdk != null)
|
||||||
|
changeIfNecessary('sdk.dir', escapePath(androidSdk.directory));
|
||||||
|
changeIfNecessary('flutter.sdk', escapePath(Cache.flutterRoot));
|
||||||
|
if (buildInfo != null)
|
||||||
|
changeIfNecessary('flutter.buildMode', buildInfo.modeName);
|
||||||
final String buildName = buildInfo?.buildName ?? manifest.buildName;
|
final String buildName = buildInfo?.buildName ?? manifest.buildName;
|
||||||
if (buildName != null) {
|
if (buildName != null)
|
||||||
settings.values['flutter.versionName'] = buildName;
|
changeIfNecessary('flutter.versionName', buildName);
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int buildNumber = buildInfo?.buildNumber ?? manifest.buildNumber;
|
final int buildNumber = buildInfo?.buildNumber ?? manifest.buildNumber;
|
||||||
if (buildNumber != null) {
|
if (buildNumber != null)
|
||||||
settings.values['flutter.versionCode'] = '$buildNumber';
|
changeIfNecessary('flutter.versionCode', '$buildNumber');
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed)
|
if (changed)
|
||||||
settings.writeContents(localProperties);
|
settings.writeContents(localProperties);
|
||||||
|
@ -44,7 +44,7 @@ class CreateCommand extends FlutterCommand {
|
|||||||
argParser.addOption(
|
argParser.addOption(
|
||||||
'template',
|
'template',
|
||||||
abbr: 't',
|
abbr: 't',
|
||||||
allowed: <String>['app', 'package', 'plugin'],
|
allowed: <String>['app', 'module', 'package', 'plugin'],
|
||||||
help: 'Specify the type of project to create.',
|
help: 'Specify the type of project to create.',
|
||||||
valueHelp: 'type',
|
valueHelp: 'type',
|
||||||
allowedHelp: <String, String>{
|
allowedHelp: <String, String>{
|
||||||
@ -124,6 +124,7 @@ class CreateCommand extends FlutterCommand {
|
|||||||
throwToolExit('Unable to find package:flutter_driver in $flutterDriverPackagePath', exitCode: 2);
|
throwToolExit('Unable to find package:flutter_driver in $flutterDriverPackagePath', exitCode: 2);
|
||||||
|
|
||||||
final String template = argResults['template'];
|
final String template = argResults['template'];
|
||||||
|
final bool generateModule = template == 'module';
|
||||||
final bool generatePlugin = template == 'plugin';
|
final bool generatePlugin = template == 'plugin';
|
||||||
final bool generatePackage = template == 'package';
|
final bool generatePackage = template == 'package';
|
||||||
|
|
||||||
@ -172,6 +173,9 @@ class CreateCommand extends FlutterCommand {
|
|||||||
case 'app':
|
case 'app':
|
||||||
generatedFileCount += await _generateApp(dirPath, templateContext);
|
generatedFileCount += await _generateApp(dirPath, templateContext);
|
||||||
break;
|
break;
|
||||||
|
case 'module':
|
||||||
|
generatedFileCount += await _generateModule(dirPath, templateContext);
|
||||||
|
break;
|
||||||
case 'package':
|
case 'package':
|
||||||
generatedFileCount += await _generatePackage(dirPath, templateContext);
|
generatedFileCount += await _generatePackage(dirPath, templateContext);
|
||||||
break;
|
break;
|
||||||
@ -185,6 +189,9 @@ class CreateCommand extends FlutterCommand {
|
|||||||
if (generatePackage) {
|
if (generatePackage) {
|
||||||
final String relativePath = fs.path.relative(dirPath);
|
final String relativePath = fs.path.relative(dirPath);
|
||||||
printStatus('Your package code is in lib/${templateContext['projectName']}.dart in the $relativePath directory.');
|
printStatus('Your package code is in lib/${templateContext['projectName']}.dart in the $relativePath directory.');
|
||||||
|
} else if (generateModule) {
|
||||||
|
final String relativePath = fs.path.relative(dirPath);
|
||||||
|
printStatus('Your module code is in lib/main.dart in the $relativePath directory.');
|
||||||
} else {
|
} else {
|
||||||
// Run doctor; tell the user the next steps.
|
// Run doctor; tell the user the next steps.
|
||||||
final String relativeAppPath = fs.path.relative(appPath);
|
final String relativeAppPath = fs.path.relative(appPath);
|
||||||
@ -226,6 +233,25 @@ To edit platform code in an IDE see https://flutter.io/developing-packages/#edit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<int> _generateModule(String dirPath, Map<String, dynamic> templateContext) async {
|
||||||
|
int generatedCount = 0;
|
||||||
|
final String description = argResults.wasParsed('description')
|
||||||
|
? argResults['description']
|
||||||
|
: 'A new flutter module project.';
|
||||||
|
templateContext['description'] = description;
|
||||||
|
generatedCount += _renderTemplate(fs.path.join('module', 'common'), dirPath, templateContext);
|
||||||
|
if (argResults['pub']) {
|
||||||
|
await pubGet(
|
||||||
|
context: PubContext.create,
|
||||||
|
directory: dirPath,
|
||||||
|
offline: argResults['offline'],
|
||||||
|
);
|
||||||
|
final FlutterProject project = new FlutterProject(fs.directory(dirPath));
|
||||||
|
await project.ensureReadyForPlatformSpecificTooling();
|
||||||
|
}
|
||||||
|
return generatedCount;
|
||||||
|
}
|
||||||
|
|
||||||
Future<int> _generatePackage(String dirPath, Map<String, dynamic> templateContext) async {
|
Future<int> _generatePackage(String dirPath, Map<String, dynamic> templateContext) async {
|
||||||
int generatedCount = 0;
|
int generatedCount = 0;
|
||||||
final String description = argResults.wasParsed('description')
|
final String description = argResults.wasParsed('description')
|
||||||
|
@ -87,6 +87,23 @@ class FlutterManifest {
|
|||||||
return _flutterDescriptor['uses-material-design'] ?? false;
|
return _flutterDescriptor['uses-material-design'] ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Properties defining how to expose this Flutter project as a module
|
||||||
|
/// for integration into an unspecified host app.
|
||||||
|
Map<String, dynamic> get moduleDescriptor {
|
||||||
|
return _flutterDescriptor.containsKey('module')
|
||||||
|
? _flutterDescriptor['module'] ?? const <String, dynamic>{}
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// True if this manifest declares a Flutter module project.
|
||||||
|
///
|
||||||
|
/// A Flutter project is considered a module when it has a `module:`
|
||||||
|
/// descriptor. A Flutter module project supports integration into an
|
||||||
|
/// existing host app.
|
||||||
|
///
|
||||||
|
/// Such a project can be created using `flutter create -t module`.
|
||||||
|
bool get isModule => moduleDescriptor != null;
|
||||||
|
|
||||||
List<Map<String, dynamic>> get fontsDescriptor {
|
List<Map<String, dynamic>> get fontsDescriptor {
|
||||||
return _flutterDescriptor['fonts'] ?? const <Map<String, dynamic>>[];
|
return _flutterDescriptor['fonts'] ?? const <Map<String, dynamic>>[];
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,8 @@ Future<void> generateXcodeProperties(String projectPath) async {
|
|||||||
///
|
///
|
||||||
/// targetOverride: Optional parameter, if null or unspecified the default value
|
/// targetOverride: Optional parameter, if null or unspecified the default value
|
||||||
/// from xcode_backend.sh is used 'lib/main.dart'.
|
/// from xcode_backend.sh is used 'lib/main.dart'.
|
||||||
|
///
|
||||||
|
/// Returns the number of files written.
|
||||||
Future<void> updateGeneratedXcodeProperties({
|
Future<void> updateGeneratedXcodeProperties({
|
||||||
@required String projectPath,
|
@required String projectPath,
|
||||||
@required BuildInfo buildInfo,
|
@required BuildInfo buildInfo,
|
||||||
|
@ -148,7 +148,7 @@ void _writeAndroidPluginRegistrant(String directory, List<Plugin> plugins) {
|
|||||||
|
|
||||||
final String pluginRegistry =
|
final String pluginRegistry =
|
||||||
new mustache.Template(_androidPluginRegistryTemplate).renderString(context);
|
new mustache.Template(_androidPluginRegistryTemplate).renderString(context);
|
||||||
final String javaSourcePath = fs.path.join(directory, 'android', 'app', 'src', 'main', 'java');
|
final String javaSourcePath = fs.path.join(directory, 'src', 'main', 'java');
|
||||||
final Directory registryDirectory =
|
final Directory registryDirectory =
|
||||||
fs.directory(fs.path.join(javaSourcePath, 'io', 'flutter', 'plugins'));
|
fs.directory(fs.path.join(javaSourcePath, 'io', 'flutter', 'plugins'));
|
||||||
registryDirectory.createSync(recursive: true);
|
registryDirectory.createSync(recursive: true);
|
||||||
@ -233,9 +233,11 @@ void injectPlugins({String directory}) {
|
|||||||
directory ??= fs.currentDirectory.path;
|
directory ??= fs.currentDirectory.path;
|
||||||
final List<Plugin> plugins = _findPlugins(directory);
|
final List<Plugin> plugins = _findPlugins(directory);
|
||||||
final bool changed = _writeFlutterPluginsList(directory, plugins);
|
final bool changed = _writeFlutterPluginsList(directory, plugins);
|
||||||
|
if (fs.isDirectorySync(fs.path.join(directory, '.android', 'Flutter'))) {
|
||||||
if (fs.isDirectorySync(fs.path.join(directory, 'android')))
|
_writeAndroidPluginRegistrant(fs.path.join(directory, '.android', 'Flutter'), plugins);
|
||||||
_writeAndroidPluginRegistrant(directory, plugins);
|
} else if (fs.isDirectorySync(fs.path.join(directory, 'android', 'app'))) {
|
||||||
|
_writeAndroidPluginRegistrant(fs.path.join(directory, 'android', 'app'), plugins);
|
||||||
|
}
|
||||||
if (fs.isDirectorySync(fs.path.join(directory, 'ios'))) {
|
if (fs.isDirectorySync(fs.path.join(directory, 'ios'))) {
|
||||||
_writeIOSPluginRegistrant(directory, plugins);
|
_writeIOSPluginRegistrant(directory, plugins);
|
||||||
final CocoaPods cocoaPods = new CocoaPods();
|
final CocoaPods cocoaPods = new CocoaPods();
|
||||||
|
@ -5,10 +5,14 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'base/file_system.dart';
|
|
||||||
import 'ios/xcodeproj.dart';
|
|
||||||
import 'plugins.dart';
|
|
||||||
|
|
||||||
|
import 'android/gradle.dart' as gradle;
|
||||||
|
import 'base/file_system.dart';
|
||||||
|
import 'cache.dart';
|
||||||
|
import 'flutter_manifest.dart';
|
||||||
|
import 'ios/xcodeproj.dart' as xcode;
|
||||||
|
import 'plugins.dart';
|
||||||
|
import 'template.dart';
|
||||||
|
|
||||||
/// Represents the contents of a Flutter project at the specified [directory].
|
/// Represents the contents of a Flutter project at the specified [directory].
|
||||||
class FlutterProject {
|
class FlutterProject {
|
||||||
@ -47,6 +51,9 @@ class FlutterProject {
|
|||||||
/// The Android sub project of this project.
|
/// The Android sub project of this project.
|
||||||
AndroidProject get android => new AndroidProject(directory.childDirectory('android'));
|
AndroidProject get android => new AndroidProject(directory.childDirectory('android'));
|
||||||
|
|
||||||
|
/// The generated AndroidModule sub project of this module project.
|
||||||
|
AndroidModuleProject get androidModule => new AndroidModuleProject(directory.childDirectory('.android'));
|
||||||
|
|
||||||
/// Returns true if this project has an example application
|
/// Returns true if this project has an example application
|
||||||
bool get hasExampleApp => directory.childDirectory('example').childFile('pubspec.yaml').existsSync();
|
bool get hasExampleApp => directory.childDirectory('example').childFile('pubspec.yaml').existsSync();
|
||||||
|
|
||||||
@ -54,13 +61,19 @@ class FlutterProject {
|
|||||||
FlutterProject get example => new FlutterProject(directory.childDirectory('example'));
|
FlutterProject get example => new FlutterProject(directory.childDirectory('example'));
|
||||||
|
|
||||||
/// Generates project files necessary to make Gradle builds work on Android
|
/// Generates project files necessary to make Gradle builds work on Android
|
||||||
/// and CocoaPods+Xcode work on iOS, for app projects only
|
/// and CocoaPods+Xcode work on iOS, for app and module projects only.
|
||||||
|
///
|
||||||
|
/// Returns the number of files written.
|
||||||
Future<void> ensureReadyForPlatformSpecificTooling() async {
|
Future<void> ensureReadyForPlatformSpecificTooling() async {
|
||||||
if (!directory.existsSync() || hasExampleApp) {
|
if (!directory.existsSync() || hasExampleApp) {
|
||||||
return;
|
return 0;
|
||||||
|
}
|
||||||
|
final FlutterManifest manifest = await FlutterManifest.createFromPath(directory.childFile('pubspec.yaml').path);
|
||||||
|
if (manifest.isModule) {
|
||||||
|
await androidModule.ensureReadyForPlatformSpecificTooling(manifest);
|
||||||
}
|
}
|
||||||
injectPlugins(directory: directory.path);
|
injectPlugins(directory: directory.path);
|
||||||
await generateXcodeProperties(directory.path);
|
await xcode.generateXcodeProperties(directory.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,6 +110,36 @@ class AndroidProject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents the contents of the .android-generated/ folder of a Flutter module
|
||||||
|
/// project.
|
||||||
|
class AndroidModuleProject {
|
||||||
|
AndroidModuleProject(this.directory);
|
||||||
|
|
||||||
|
final Directory directory;
|
||||||
|
|
||||||
|
Future<void> ensureReadyForPlatformSpecificTooling(FlutterManifest manifest) async {
|
||||||
|
if (_shouldRegenerate()) {
|
||||||
|
final Template template = new Template.fromName(fs.path.join('module', 'android'));
|
||||||
|
template.render(directory, <String, dynamic>{
|
||||||
|
'androidIdentifier': manifest.moduleDescriptor['androidPackage'],
|
||||||
|
}, printStatusWhenWriting: false);
|
||||||
|
gradle.injectGradleWrapper(directory);
|
||||||
|
}
|
||||||
|
gradle.updateLocalPropertiesSync(directory, manifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _shouldRegenerate() {
|
||||||
|
final File flutterToolsStamp = Cache.instance.getStampFileFor('flutter_tools');
|
||||||
|
final File buildDotGradleFile = directory.childFile('build.gradle');
|
||||||
|
if (!buildDotGradleFile.existsSync())
|
||||||
|
return true;
|
||||||
|
return flutterToolsStamp.existsSync() &&
|
||||||
|
flutterToolsStamp
|
||||||
|
.lastModifiedSync()
|
||||||
|
.isAfter(buildDotGradleFile.lastModifiedSync());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Asynchronously returns the first line-based match for [regExp] in [file].
|
/// Asynchronously returns the first line-based match for [regExp] in [file].
|
||||||
///
|
///
|
||||||
/// Assumes UTF8 encoding.
|
/// Assumes UTF8 encoding.
|
||||||
|
@ -66,6 +66,7 @@ class Template {
|
|||||||
Directory destination,
|
Directory destination,
|
||||||
Map<String, dynamic> context, {
|
Map<String, dynamic> context, {
|
||||||
bool overwriteExisting = true,
|
bool overwriteExisting = true,
|
||||||
|
bool printStatusWhenWriting = true,
|
||||||
}) {
|
}) {
|
||||||
destination.createSync(recursive: true);
|
destination.createSync(recursive: true);
|
||||||
int fileCount = 0;
|
int fileCount = 0;
|
||||||
@ -117,14 +118,17 @@ class Template {
|
|||||||
if (finalDestinationFile.existsSync()) {
|
if (finalDestinationFile.existsSync()) {
|
||||||
if (overwriteExisting) {
|
if (overwriteExisting) {
|
||||||
finalDestinationFile.deleteSync(recursive: true);
|
finalDestinationFile.deleteSync(recursive: true);
|
||||||
printStatus(' $relativePathForLogging (overwritten)');
|
if (printStatusWhenWriting)
|
||||||
|
printStatus(' $relativePathForLogging (overwritten)');
|
||||||
} else {
|
} else {
|
||||||
// The file exists but we cannot overwrite it, move on.
|
// The file exists but we cannot overwrite it, move on.
|
||||||
printTrace(' $relativePathForLogging (existing - skipped)');
|
if (printStatusWhenWriting)
|
||||||
|
printTrace(' $relativePathForLogging (existing - skipped)');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
printStatus(' $relativePathForLogging (created)');
|
if (printStatusWhenWriting)
|
||||||
|
printStatus(' $relativePathForLogging (created)');
|
||||||
}
|
}
|
||||||
|
|
||||||
fileCount++;
|
fileCount++;
|
||||||
|
@ -43,6 +43,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"module": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"androidPackage": { "type": "string" }
|
||||||
|
}
|
||||||
|
},
|
||||||
"plugin": {
|
"plugin": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
// Generated file. Do not edit.
|
||||||
|
|
||||||
|
def localProperties = new Properties()
|
||||||
|
def localPropertiesFile = new File(buildscript.sourceFile.parentFile.parentFile, 'local.properties')
|
||||||
|
if (localPropertiesFile.exists()) {
|
||||||
|
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||||
|
localProperties.load(reader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.")
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||||
|
if (flutterVersionCode == null) {
|
||||||
|
throw new GradleException("versionCode not found. Define flutter.versionCode in the local.properties file.")
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||||
|
if (flutterVersionName == null) {
|
||||||
|
throw new GradleException("versionName not found. Define flutter.versionName in the local.properties file.")
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 27
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 16
|
||||||
|
targetSdkVersion 27
|
||||||
|
versionCode flutterVersionCode.toInteger()
|
||||||
|
versionName flutterVersionName
|
||||||
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flutter {
|
||||||
|
source '../..'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation 'junit:junit:4.12'
|
||||||
|
implementation 'com.android.support:support-v13:27.1.1'
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
<!-- Generated file. Do not edit. -->
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="{{androidIdentifier}}">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
@ -0,0 +1,98 @@
|
|||||||
|
package io.flutter.facade;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.arch.lifecycle.Lifecycle;
|
||||||
|
import android.arch.lifecycle.LifecycleObserver;
|
||||||
|
import android.arch.lifecycle.OnLifecycleEvent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import io.flutter.app.FlutterActivityDelegate;
|
||||||
|
import io.flutter.view.FlutterMain;
|
||||||
|
import io.flutter.view.FlutterNativeView;
|
||||||
|
import io.flutter.view.FlutterView;
|
||||||
|
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main entry point for using Flutter in Android applications.
|
||||||
|
*
|
||||||
|
* <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling. Do not edit.
|
||||||
|
* It may be moved into flutter.jar or another library dependency of the Flutter module project
|
||||||
|
* at a later point.</p>
|
||||||
|
*/
|
||||||
|
public final class Flutter {
|
||||||
|
private Flutter() {
|
||||||
|
// to prevent instantiation
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startInitialization(Context applicationContext) {
|
||||||
|
FlutterMain.startInitialization(applicationContext, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Fragment createFragment(String route) {
|
||||||
|
final FlutterFragment fragment = new FlutterFragment();
|
||||||
|
final Bundle args = new Bundle();
|
||||||
|
args.putString(FlutterFragment.ARG_ROUTE, route);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static View createView(final Activity activity, final Lifecycle lifecycle, final String route) {
|
||||||
|
FlutterMain.startInitialization(activity.getApplicationContext());
|
||||||
|
FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), null);
|
||||||
|
final FlutterActivityDelegate delegate = new FlutterActivityDelegate(activity, new FlutterActivityDelegate.ViewFactory() {
|
||||||
|
@Override
|
||||||
|
public FlutterView createFlutterView(Context context) {
|
||||||
|
final FlutterNativeView nativeView = new FlutterNativeView(context);
|
||||||
|
final FlutterView flutterView = new FlutterView(activity, null, nativeView);
|
||||||
|
flutterView.setInitialRoute(route);
|
||||||
|
return flutterView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainFlutterNativeView() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FlutterNativeView createFlutterNativeView() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
lifecycle.addObserver(new LifecycleObserver() {
|
||||||
|
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
|
||||||
|
public void onCreate() {
|
||||||
|
delegate.onCreate(null);
|
||||||
|
GeneratedPluginRegistrant.registerWith(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnLifecycleEvent(Lifecycle.Event.ON_START)
|
||||||
|
public void onStart() {
|
||||||
|
delegate.onStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||||
|
public void onResume() {
|
||||||
|
delegate.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
|
||||||
|
public void onPause() {
|
||||||
|
delegate.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
|
||||||
|
public void onStop() {
|
||||||
|
delegate.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
|
||||||
|
public void onDestroy() {
|
||||||
|
delegate.onDestroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return delegate.getFlutterView();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package io.flutter.facade;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Fragment} managing a Flutter view.
|
||||||
|
*
|
||||||
|
* <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling. Do not edit.
|
||||||
|
* It may be moved into flutter.jar or another library dependency of the Flutter module project
|
||||||
|
* at a later point.</p>
|
||||||
|
*/
|
||||||
|
public class FlutterFragment extends Fragment {
|
||||||
|
public static final String ARG_ROUTE = "route";
|
||||||
|
private String mRoute = "/";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (getArguments() != null) {
|
||||||
|
mRoute = getArguments().getString(ARG_ROUTE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState) {
|
||||||
|
super.onInflate(context, attrs, savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
return Flutter.createView(getActivity(), getLifecycle(), mRoute);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
// Generated file. Do not edit.
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:3.1.3'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.buildDir = '../build'
|
||||||
|
subprojects {
|
||||||
|
project.buildDir = "${rootProject.buildDir}/android_gen"
|
||||||
|
}
|
||||||
|
subprojects {
|
||||||
|
project.evaluationDependsOn(':flutter')
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx1536M
|
@ -0,0 +1,6 @@
|
|||||||
|
#Fri Jun 23 08:50:38 CEST 2017
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
@ -0,0 +1,19 @@
|
|||||||
|
// Generated file. Do not edit.
|
||||||
|
|
||||||
|
def scriptFile = getClass().protectionDomain.codeSource.location.path
|
||||||
|
def flutterProjectRoot = new File(scriptFile).parentFile.parentFile
|
||||||
|
|
||||||
|
gradle.include ':flutter'
|
||||||
|
gradle.project(':flutter').projectDir = new File(flutterProjectRoot, '.android/Flutter')
|
||||||
|
|
||||||
|
def plugins = new Properties()
|
||||||
|
def pluginsFile = new File(flutterProjectRoot, '.flutter-plugins')
|
||||||
|
if (pluginsFile.exists()) {
|
||||||
|
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins.each { name, path ->
|
||||||
|
def pluginDirectory = flutterProjectRoot.toPath().resolve(path).resolve('android').toFile()
|
||||||
|
gradle.include ":$name"
|
||||||
|
gradle.project(":$name").projectDir = pluginDirectory
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
// Generated file. Do not edit.
|
||||||
|
|
||||||
|
rootProject.name = 'android_generated'
|
||||||
|
setBinding(new Binding([gradle: this]))
|
||||||
|
evaluate(new File('include_flutter.groovy'))
|
@ -0,0 +1,40 @@
|
|||||||
|
.DS_Store
|
||||||
|
.dart_tool/
|
||||||
|
|
||||||
|
.packages
|
||||||
|
.pub/
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
.vagrant/
|
||||||
|
.sconsign.dblite
|
||||||
|
.svn/
|
||||||
|
|
||||||
|
*.swp
|
||||||
|
profile
|
||||||
|
|
||||||
|
DerivedData/
|
||||||
|
|
||||||
|
.generated/
|
||||||
|
|
||||||
|
*.pbxuser
|
||||||
|
*.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
*.perspectivev3
|
||||||
|
|
||||||
|
!default.pbxuser
|
||||||
|
!default.mode1v3
|
||||||
|
!default.mode2v3
|
||||||
|
!default.perspectivev3
|
||||||
|
|
||||||
|
xcuserdata
|
||||||
|
|
||||||
|
*.moved-aside
|
||||||
|
|
||||||
|
*.pyc
|
||||||
|
*sync/
|
||||||
|
Icon?
|
||||||
|
.tags*
|
||||||
|
|
||||||
|
build/
|
||||||
|
android_gen/
|
||||||
|
.flutter-plugins
|
@ -0,0 +1,8 @@
|
|||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: {{flutterRevision}}
|
||||||
|
channel: {{flutterChannel}}
|
@ -0,0 +1,8 @@
|
|||||||
|
# {{projectName}}
|
||||||
|
|
||||||
|
{{description}}
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
For help getting started with Flutter, view our online
|
||||||
|
[documentation](https://flutter.io/).
|
@ -0,0 +1,38 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:package_info/package_info.dart';
|
||||||
|
|
||||||
|
Future<void> main() async {
|
||||||
|
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||||
|
runApp(Directionality(textDirection: TextDirection.ltr, child: Router(packageInfo)));
|
||||||
|
}
|
||||||
|
|
||||||
|
class Router extends StatelessWidget {
|
||||||
|
Router(this.packageInfo);
|
||||||
|
|
||||||
|
final PackageInfo packageInfo;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final String route = window.defaultRouteName;
|
||||||
|
switch (route) {
|
||||||
|
case 'route1':
|
||||||
|
return Container(
|
||||||
|
child: Center(child: Text('Route 1\n${packageInfo.appName}')),
|
||||||
|
color: Colors.green,
|
||||||
|
);
|
||||||
|
case 'route2':
|
||||||
|
return Container(
|
||||||
|
child: Center(child: Text('Route 2\n${packageInfo.packageName}')),
|
||||||
|
color: Colors.blue,
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return Container(
|
||||||
|
child: Center(child: Text('Unknown route: $route')),
|
||||||
|
color: Colors.red,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
name: {{projectName}}
|
||||||
|
description: {{description}}
|
||||||
|
version: 1.0.0+1
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
package_info:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
||||||
|
module:
|
||||||
|
androidPackage: {{androidIdentifier}}
|
@ -186,6 +186,59 @@ void main() {
|
|||||||
);
|
);
|
||||||
}, timeout: allowForRemotePubInvocation);
|
}, timeout: allowForRemotePubInvocation);
|
||||||
|
|
||||||
|
testUsingContext('module', () async {
|
||||||
|
return _createProject(
|
||||||
|
projectDir,
|
||||||
|
<String>['--no-pub', '--template=module'],
|
||||||
|
<String>[
|
||||||
|
'.gitignore',
|
||||||
|
'.metadata',
|
||||||
|
'lib/main.dart',
|
||||||
|
'pubspec.yaml',
|
||||||
|
'README.md',
|
||||||
|
],
|
||||||
|
unexpectedPaths: <String>[
|
||||||
|
'.android/',
|
||||||
|
'android/',
|
||||||
|
'ios/',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}, timeout: allowForCreateFlutterProject);
|
||||||
|
|
||||||
|
testUsingContext('module with pub', () async {
|
||||||
|
return _createProject(
|
||||||
|
projectDir,
|
||||||
|
<String>['-t', 'module'],
|
||||||
|
<String>[
|
||||||
|
'.gitignore',
|
||||||
|
'.metadata',
|
||||||
|
'lib/main.dart',
|
||||||
|
'pubspec.lock',
|
||||||
|
'pubspec.yaml',
|
||||||
|
'README.md',
|
||||||
|
'.packages',
|
||||||
|
'.android/build.gradle',
|
||||||
|
'.android/Flutter/build.gradle',
|
||||||
|
'.android/Flutter/src/main/java/io/flutter/facade/Flutter.java',
|
||||||
|
'.android/Flutter/src/main/java/io/flutter/facade/FlutterFragment.java',
|
||||||
|
'.android/Flutter/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java',
|
||||||
|
'.android/Flutter/src/main/AndroidManifest.xml',
|
||||||
|
'.android/gradle.properties',
|
||||||
|
'.android/gradle/wrapper/gradle-wrapper.jar',
|
||||||
|
'.android/gradle/wrapper/gradle-wrapper.properties',
|
||||||
|
'.android/gradlew',
|
||||||
|
'.android/gradlew.bat',
|
||||||
|
'.android/local.properties',
|
||||||
|
'.android/include_flutter.groovy',
|
||||||
|
'.android/settings.gradle',
|
||||||
|
],
|
||||||
|
unexpectedPaths: <String>[
|
||||||
|
'android/',
|
||||||
|
'ios/',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}, timeout: allowForRemotePubInvocation);
|
||||||
|
|
||||||
// Verify content and formatting
|
// Verify content and formatting
|
||||||
testUsingContext('content', () async {
|
testUsingContext('content', () async {
|
||||||
Cache.flutterRoot = '../..';
|
Cache.flutterRoot = '../..';
|
||||||
@ -423,11 +476,16 @@ Future<Null> _createProject(
|
|||||||
args.add(dir.path);
|
args.add(dir.path);
|
||||||
await runner.run(args);
|
await runner.run(args);
|
||||||
|
|
||||||
|
bool pathExists(String path) {
|
||||||
|
final String fullPath = fs.path.join(dir.path, path);
|
||||||
|
return fs.typeSync(fullPath) != FileSystemEntityType.notFound;
|
||||||
|
}
|
||||||
|
|
||||||
for (String path in expectedPaths) {
|
for (String path in expectedPaths) {
|
||||||
expect(fs.file(fs.path.join(dir.path, path)).existsSync(), true, reason: '$path does not exist');
|
expect(pathExists(path), true, reason: '$path does not exist');
|
||||||
}
|
}
|
||||||
for (String path in unexpectedPaths) {
|
for (String path in unexpectedPaths) {
|
||||||
expect(fs.file(fs.path.join(dir.path, path)).existsSync(), false, reason: '$path exists');
|
expect(pathExists(path), false, reason: '$path exists');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user