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 }) {
|
||||
final String path = fs.path.join(
|
||||
'android',
|
||||
String _locateGradlewExecutable(Directory directory) {
|
||||
final File gradle = directory.childFile(
|
||||
platform.isWindows ? 'gradlew.bat' : 'gradlew',
|
||||
);
|
||||
|
||||
if (fs.isFileSync(path)) {
|
||||
final File gradle = fs.file(path);
|
||||
if (ensureExecutable)
|
||||
os.makeExecutable(gradle);
|
||||
if (gradle.existsSync()) {
|
||||
os.makeExecutable(gradle);
|
||||
return gradle.absolute.path;
|
||||
} else {
|
||||
return null;
|
||||
@ -165,11 +162,12 @@ Future<String> _ensureGradle() async {
|
||||
// Note: Gradle may be bootstrapped and possibly downloaded as a side-effect
|
||||
// of validating the Gradle executable. This may take several seconds.
|
||||
Future<String> _initializeGradle() async {
|
||||
final Directory android = fs.directory('android');
|
||||
final Status status = logger.startProgress('Initializing gradle...', expectSlowOperation: true);
|
||||
String gradle = _locateProjectGradlew();
|
||||
String gradle = _locateGradlewExecutable(android);
|
||||
if (gradle == null) {
|
||||
_injectGradleWrapper();
|
||||
gradle = _locateProjectGradlew();
|
||||
injectGradleWrapper(android);
|
||||
gradle = _locateGradlewExecutable(android);
|
||||
}
|
||||
if (gradle == null)
|
||||
throwToolExit('Unable to locate gradlew script');
|
||||
@ -181,11 +179,13 @@ Future<String> _initializeGradle() async {
|
||||
return gradle;
|
||||
}
|
||||
|
||||
void _injectGradleWrapper() {
|
||||
copyDirectorySync(cache.getArtifactDirectory('gradle_wrapper'), fs.directory('android'));
|
||||
final String propertiesPath = fs.path.join('android', 'gradle', 'wrapper', 'gradle-wrapper.properties');
|
||||
if (!fs.file(propertiesPath).existsSync()) {
|
||||
fs.file(propertiesPath).writeAsStringSync('''
|
||||
/// Injects the Gradle wrapper into the specified directory.
|
||||
void injectGradleWrapper(Directory directory) {
|
||||
copyDirectorySync(cache.getArtifactDirectory('gradle_wrapper'), directory);
|
||||
_locateGradlewExecutable(directory);
|
||||
final File propertiesFile = directory.childFile(fs.path.join('gradle', 'wrapper', 'gradle-wrapper.properties'));
|
||||
if (!propertiesFile.existsSync()) {
|
||||
propertiesFile.writeAsStringSync('''
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
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 {
|
||||
final File localProperties = (projectPath == null)
|
||||
? fs.file(fs.path.join('android', 'local.properties'))
|
||||
: fs.file(fs.path.join(projectPath, 'android', 'local.properties'));
|
||||
final Directory android = (projectPath == null)
|
||||
? fs.directory('android')
|
||||
: fs.directory(fs.path.join(projectPath, 'android'));
|
||||
final String flutterManifest = (projectPath == null)
|
||||
? fs.path.join(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;
|
||||
|
||||
SettingsFile settings;
|
||||
@ -211,40 +228,27 @@ Future<void> updateLocalProperties({String projectPath, BuildInfo buildInfo}) as
|
||||
settings = new SettingsFile.parseFromFile(localProperties);
|
||||
} else {
|
||||
settings = new SettingsFile();
|
||||
if (androidSdk == null) {
|
||||
throwToolExit('Unable to locate Android SDK. Please run `flutter doctor` for more details.');
|
||||
changed = true;
|
||||
}
|
||||
|
||||
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;
|
||||
if (buildName != null) {
|
||||
settings.values['flutter.versionName'] = buildName;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (buildName != null)
|
||||
changeIfNecessary('flutter.versionName', buildName);
|
||||
final int buildNumber = buildInfo?.buildNumber ?? manifest.buildNumber;
|
||||
if (buildNumber != null) {
|
||||
settings.values['flutter.versionCode'] = '$buildNumber';
|
||||
changed = true;
|
||||
}
|
||||
if (buildNumber != null)
|
||||
changeIfNecessary('flutter.versionCode', '$buildNumber');
|
||||
|
||||
if (changed)
|
||||
settings.writeContents(localProperties);
|
||||
|
@ -44,7 +44,7 @@ class CreateCommand extends FlutterCommand {
|
||||
argParser.addOption(
|
||||
'template',
|
||||
abbr: 't',
|
||||
allowed: <String>['app', 'package', 'plugin'],
|
||||
allowed: <String>['app', 'module', 'package', 'plugin'],
|
||||
help: 'Specify the type of project to create.',
|
||||
valueHelp: 'type',
|
||||
allowedHelp: <String, String>{
|
||||
@ -124,6 +124,7 @@ class CreateCommand extends FlutterCommand {
|
||||
throwToolExit('Unable to find package:flutter_driver in $flutterDriverPackagePath', exitCode: 2);
|
||||
|
||||
final String template = argResults['template'];
|
||||
final bool generateModule = template == 'module';
|
||||
final bool generatePlugin = template == 'plugin';
|
||||
final bool generatePackage = template == 'package';
|
||||
|
||||
@ -172,6 +173,9 @@ class CreateCommand extends FlutterCommand {
|
||||
case 'app':
|
||||
generatedFileCount += await _generateApp(dirPath, templateContext);
|
||||
break;
|
||||
case 'module':
|
||||
generatedFileCount += await _generateModule(dirPath, templateContext);
|
||||
break;
|
||||
case 'package':
|
||||
generatedFileCount += await _generatePackage(dirPath, templateContext);
|
||||
break;
|
||||
@ -185,6 +189,9 @@ class CreateCommand extends FlutterCommand {
|
||||
if (generatePackage) {
|
||||
final String relativePath = fs.path.relative(dirPath);
|
||||
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 {
|
||||
// Run doctor; tell the user the next steps.
|
||||
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 {
|
||||
int generatedCount = 0;
|
||||
final String description = argResults.wasParsed('description')
|
||||
|
@ -87,6 +87,23 @@ class FlutterManifest {
|
||||
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 {
|
||||
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
|
||||
/// from xcode_backend.sh is used 'lib/main.dart'.
|
||||
///
|
||||
/// Returns the number of files written.
|
||||
Future<void> updateGeneratedXcodeProperties({
|
||||
@required String projectPath,
|
||||
@required BuildInfo buildInfo,
|
||||
|
@ -148,7 +148,7 @@ void _writeAndroidPluginRegistrant(String directory, List<Plugin> plugins) {
|
||||
|
||||
final String pluginRegistry =
|
||||
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 =
|
||||
fs.directory(fs.path.join(javaSourcePath, 'io', 'flutter', 'plugins'));
|
||||
registryDirectory.createSync(recursive: true);
|
||||
@ -233,9 +233,11 @@ void injectPlugins({String directory}) {
|
||||
directory ??= fs.currentDirectory.path;
|
||||
final List<Plugin> plugins = _findPlugins(directory);
|
||||
final bool changed = _writeFlutterPluginsList(directory, plugins);
|
||||
|
||||
if (fs.isDirectorySync(fs.path.join(directory, 'android')))
|
||||
_writeAndroidPluginRegistrant(directory, plugins);
|
||||
if (fs.isDirectorySync(fs.path.join(directory, '.android', 'Flutter'))) {
|
||||
_writeAndroidPluginRegistrant(fs.path.join(directory, '.android', 'Flutter'), 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'))) {
|
||||
_writeIOSPluginRegistrant(directory, plugins);
|
||||
final CocoaPods cocoaPods = new CocoaPods();
|
||||
|
@ -5,10 +5,14 @@
|
||||
import 'dart:async';
|
||||
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].
|
||||
class FlutterProject {
|
||||
@ -47,6 +51,9 @@ class FlutterProject {
|
||||
/// The Android sub project of this project.
|
||||
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
|
||||
bool get hasExampleApp => directory.childDirectory('example').childFile('pubspec.yaml').existsSync();
|
||||
|
||||
@ -54,13 +61,19 @@ class FlutterProject {
|
||||
FlutterProject get example => new FlutterProject(directory.childDirectory('example'));
|
||||
|
||||
/// 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 {
|
||||
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);
|
||||
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].
|
||||
///
|
||||
/// Assumes UTF8 encoding.
|
||||
|
@ -66,6 +66,7 @@ class Template {
|
||||
Directory destination,
|
||||
Map<String, dynamic> context, {
|
||||
bool overwriteExisting = true,
|
||||
bool printStatusWhenWriting = true,
|
||||
}) {
|
||||
destination.createSync(recursive: true);
|
||||
int fileCount = 0;
|
||||
@ -117,14 +118,17 @@ class Template {
|
||||
if (finalDestinationFile.existsSync()) {
|
||||
if (overwriteExisting) {
|
||||
finalDestinationFile.deleteSync(recursive: true);
|
||||
printStatus(' $relativePathForLogging (overwritten)');
|
||||
if (printStatusWhenWriting)
|
||||
printStatus(' $relativePathForLogging (overwritten)');
|
||||
} else {
|
||||
// The file exists but we cannot overwrite it, move on.
|
||||
printTrace(' $relativePathForLogging (existing - skipped)');
|
||||
if (printStatusWhenWriting)
|
||||
printTrace(' $relativePathForLogging (existing - skipped)');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
printStatus(' $relativePathForLogging (created)');
|
||||
if (printStatusWhenWriting)
|
||||
printStatus(' $relativePathForLogging (created)');
|
||||
}
|
||||
|
||||
fileCount++;
|
||||
|
@ -43,6 +43,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"androidPackage": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"plugin": {
|
||||
"type": "object",
|
||||
"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);
|
||||
|
||||
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
|
||||
testUsingContext('content', () async {
|
||||
Cache.flutterRoot = '../..';
|
||||
@ -423,11 +476,16 @@ Future<Null> _createProject(
|
||||
args.add(dir.path);
|
||||
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) {
|
||||
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) {
|
||||
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