diff --git a/experimental/add_to_app/README.md b/experimental/add_to_app/README.md index 23ba03747..3eaee4c99 100644 --- a/experimental/add_to_app/README.md +++ b/experimental/add_to_app/README.md @@ -10,6 +10,10 @@ a standalone Flutter module called `flutter_module`. ## Goals for this sample * Show developers how to add Flutter to their existing applications. +* Show the following options: + - Whether to build the Flutter module from source each time the app builds or + rely on a separately pre-built module. + - Whether plugins are needed by the Flutter module used in the app. ## The important bits @@ -20,13 +24,12 @@ There are two Flutter modules included in the codebase: * `flutter_module` displays the dimensions of the screen, a button that increments a simple counter, and an optional exit button. * `flutter_module_using_plugin` does everything `flutter_module` does, and adds - another button that will save the counter's value to a file using the - [`path_provider`](https://pub.dev/packages/path_povider) Flutter plugin. + another button that will open the Flutter documentation in a browser using the + [`url_launcher`](https://pub.dev/packages/url_launcher) Flutter plugin. -Before running any of the Android or iOS apps included in this sample project, -you first need to resolve the Flutter modules' depencies. Do so by running this -command from within the `flutter_module` and `flutter_module_using_plugin` -directories: +Before using them, you need to resolve the Flutter modules' dependencies. Do so +by running this command from within the `flutter_module` and +`flutter_module_using_plugin` directories: ```bash flutter packages get @@ -38,14 +41,15 @@ In addition to the Flutter modules, this repo also includes a number of Android and iOS applications that demonstrate different ways of importing them. -The Android apps are ready to run once you've completed the -`flutter packages get` commands listed above. The iOS apps use CocoaPods, -so you need to run this command to install the dependencies listed in their -Podfiles prior to running them the first time. +With the exception of `android_using_prebuilt_module`, the Android apps are +ready to run once you've completed the `flutter packages get` commands listed +above. The iOS apps use CocoaPods, though, so you need to run this command to +install the dependencies listed in their Podfiles prior to running them the +first time. This should be done in the individual project directories themselves. For -example, prior to running `ios_fullscreen` for the first time, you need to run -these commands: +example, prior to running `ios_fullscreen` or `ios_using_plugin` for the first +time, you need to run these commands: ```bash cd ios_fullscreen @@ -71,6 +75,55 @@ These apps showcase a relatively straightforward integration of If you are new to Flutter's add-to-app APIs, these projects are a great place to begin learning how to use them. +### `android_using_plugin` and `ios_plugin` + +These apps are similar to `android_fullscreen` and `ios_fullscreen`, with the +following differences: + +* Rather than importing `flutter_module`, they import + `flutter_module_using_plugin`. +* They include the native code (Kotlin or Swift) required to initialize plugins + at Flutter engine creation time. +* Their Flutter view includes an additional button that opens the Flutter docs + in the mobile device's browser. + +If you're interested in learning what additional steps an app needs to take in +order to use a Flutter module that relies on plugins, these projects can help. + +### `android_using_prebuilt_module` + +This app is essentially identical to `android_fullscreen` with one key +difference: + +* The Flutter module is *not* built automatically when the app builds. Instead, + it's built separately into an `aar`. The Android app is configured to import + that `aar` along with its other gradle dependencies. + +This can be useful for teams that don't want to require every developer working +on the app to have the Flutter toolchain installed on their local machines. + +Prior to building `android_using_prebuilt_module` for the first time, the +Flutter module should be built into an `aar`. The build can be done in a debug +or release configuration. To build a debug `aar`, run this command from the +`flutter_module` directory: + +``` +flutter build aar --debug +``` + +To build a release version of the `aar`, simply omit the debug flag: + +``` +flutter build aar +``` + +The Android app is configured to import the appropriate `aar` based on its own +build configuration, so if you build a debug version of the app, it will look +for the debug `aar`, and likewise for a release build. + +If the `flutter_module` project is updated, the `aar` must be rebuilt via one of +the commands above in order for those changes to appear in the app. + ## Questions/issues If you have a general question about incorporating Flutter into an existing diff --git a/experimental/add_to_app/android_fullscreen/app/src/main/java/dev/flutter/example/androidfullscreen/MainActivity.kt b/experimental/add_to_app/android_fullscreen/app/src/main/java/dev/flutter/example/androidfullscreen/MainActivity.kt index 2f822254c..dc32af349 100644 --- a/experimental/add_to_app/android_fullscreen/app/src/main/java/dev/flutter/example/androidfullscreen/MainActivity.kt +++ b/experimental/add_to_app/android_fullscreen/app/src/main/java/dev/flutter/example/androidfullscreen/MainActivity.kt @@ -11,7 +11,7 @@ import androidx.appcompat.app.AppCompatActivity import io.flutter.embedding.android.FlutterActivity class MainActivity : AppCompatActivity() { - private var counterLabel: TextView? = null + private lateinit var counterLabel: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -32,6 +32,6 @@ class MainActivity : AppCompatActivity() { override fun onResume() { super.onResume() val app = application as MyApplication - counterLabel?.text = "Current count: ${app.count}" + counterLabel.text = "Current count: ${app.count}" } } diff --git a/experimental/add_to_app/android_fullscreen/app/src/main/java/dev/flutter/example/androidfullscreen/MyApplication.kt b/experimental/add_to_app/android_fullscreen/app/src/main/java/dev/flutter/example/androidfullscreen/MyApplication.kt index 28069f889..bf65b3f68 100644 --- a/experimental/add_to_app/android_fullscreen/app/src/main/java/dev/flutter/example/androidfullscreen/MyApplication.kt +++ b/experimental/add_to_app/android_fullscreen/app/src/main/java/dev/flutter/example/androidfullscreen/MyApplication.kt @@ -15,7 +15,7 @@ const val ENGINE_ID = "1" class MyApplication : Application() { var count = 0 - private var channel: MethodChannel? = null + private lateinit var channel: MethodChannel override fun onCreate() { super.onCreate() @@ -31,7 +31,7 @@ class MyApplication : Application() { channel = MethodChannel(flutterEngine.dartExecutor, "dev.flutter.example/counter") - channel?.setMethodCallHandler { call, _ -> + channel.setMethodCallHandler { call, _ -> when (call.method) { "incrementCounter" -> { count++ @@ -45,6 +45,6 @@ class MyApplication : Application() { } private fun reportCounter() { - channel?.invokeMethod("reportCounter", count) + channel.invokeMethod("reportCounter", count) } } diff --git a/experimental/add_to_app/android_fullscreen/build.gradle b/experimental/add_to_app/android_fullscreen/build.gradle index 327160ade..e796b11e6 100644 --- a/experimental/add_to_app/android_fullscreen/build.gradle +++ b/experimental/add_to_app/android_fullscreen/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:3.5.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/experimental/add_to_app/android_using_prebuilt_module/app/src/main/java/dev/flutter/example/androidusingprebuiltmodule/MainActivity.kt b/experimental/add_to_app/android_using_prebuilt_module/app/src/main/java/dev/flutter/example/androidusingprebuiltmodule/MainActivity.kt index f7f6f8997..30d56992e 100644 --- a/experimental/add_to_app/android_using_prebuilt_module/app/src/main/java/dev/flutter/example/androidusingprebuiltmodule/MainActivity.kt +++ b/experimental/add_to_app/android_using_prebuilt_module/app/src/main/java/dev/flutter/example/androidusingprebuiltmodule/MainActivity.kt @@ -11,7 +11,7 @@ import androidx.appcompat.app.AppCompatActivity import io.flutter.embedding.android.FlutterActivity class MainActivity : AppCompatActivity() { - private var counterLabel: TextView? = null + private lateinit var counterLabel: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -32,6 +32,6 @@ class MainActivity : AppCompatActivity() { override fun onResume() { super.onResume() val app = application as MyApplication - counterLabel?.text = "Current count: ${app.count}" + counterLabel.text = "Current count: ${app.count}" } } diff --git a/experimental/add_to_app/android_using_prebuilt_module/app/src/main/java/dev/flutter/example/androidusingprebuiltmodule/MyApplication.kt b/experimental/add_to_app/android_using_prebuilt_module/app/src/main/java/dev/flutter/example/androidusingprebuiltmodule/MyApplication.kt index 48070c53c..2bca6f536 100644 --- a/experimental/add_to_app/android_using_prebuilt_module/app/src/main/java/dev/flutter/example/androidusingprebuiltmodule/MyApplication.kt +++ b/experimental/add_to_app/android_using_prebuilt_module/app/src/main/java/dev/flutter/example/androidusingprebuiltmodule/MyApplication.kt @@ -15,7 +15,7 @@ const val ENGINE_ID = "1" class MyApplication : Application() { var count = 0 - private var channel: MethodChannel? = null + private lateinit var channel: MethodChannel override fun onCreate() { super.onCreate() @@ -31,7 +31,7 @@ class MyApplication : Application() { channel = MethodChannel(flutterEngine.dartExecutor, "dev.flutter.example/counter") - channel?.setMethodCallHandler { call, _ -> + channel.setMethodCallHandler { call, _ -> when (call.method) { "incrementCounter" -> { count++ @@ -45,6 +45,6 @@ class MyApplication : Application() { } private fun reportCounter() { - channel?.invokeMethod("reportCounter", count) + channel.invokeMethod("reportCounter", count) } } diff --git a/experimental/add_to_app/flutter_module/lib/main.dart b/experimental/add_to_app/flutter_module/lib/main.dart index 354371cc1..a44604698 100644 --- a/experimental/add_to_app/flutter_module/lib/main.dart +++ b/experimental/add_to_app/flutter_module/lib/main.dart @@ -6,7 +6,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; +/// The entrypoint for the flutter module. void main() { + // This call ensures the Flutter binding has been set up before creating the + // MethodChannel-based model. WidgetsFlutterBinding.ensureInitialized(); final model = CounterModel(); @@ -19,6 +22,13 @@ void main() { ); } +/// A simple model that uses a [MethodChannel] as the source of truth for the +/// state of the counter. +/// +/// Rather than storing app state data within the Flutter module itself (where +/// the native portions of the app can't access it), this module passes messages +/// back to the containing app whenever it needs to increment or retrieve the +/// value of the counter. class CounterModel extends ChangeNotifier { CounterModel() { _channel.setMethodCallHandler(_handleMessage); @@ -43,6 +53,10 @@ class CounterModel extends ChangeNotifier { } } +/// The "app" displayed by this module. +/// +/// It offers two routes, one suitable for displaying as a full screen and +/// another designed to be part of a larger UI.class MyApp extends StatelessWidget { class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { @@ -56,6 +70,8 @@ class MyApp extends StatelessWidget { } } +/// Wraps [Contents] in a Material [Scaffold] so it looks correct when displayed +/// full-screen. class FullScreenView extends StatelessWidget { @override Widget build(BuildContext context) { @@ -68,6 +84,11 @@ class FullScreenView extends StatelessWidget { } } +/// The actual content displayed by the module. +/// +/// This widget displays info about the state of a counter and how much room (in +/// logical pixels) it's been given. It also offers buttons to increment the +/// counter and (optionally) close the Flutter view. class Contents extends StatelessWidget { final bool showExit; diff --git a/experimental/add_to_app/flutter_module/pubspec.lock b/experimental/add_to_app/flutter_module/pubspec.lock index 5a2a7b187..7a25da03a 100644 --- a/experimental/add_to_app/flutter_module/pubspec.lock +++ b/experimental/add_to_app/flutter_module/pubspec.lock @@ -115,7 +115,7 @@ packages: name: provider url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" + version: "3.1.0+1" quiver: dependency: transitive description: @@ -192,4 +192,4 @@ packages: source: hosted version: "3.5.0" sdks: - dart: ">=2.5.0 <3.0.0" + dart: ">=2.6.0-dev <3.0.0" diff --git a/experimental/add_to_app/flutter_module/pubspec.yaml b/experimental/add_to_app/flutter_module/pubspec.yaml index 4ae72de98..3d2738496 100644 --- a/experimental/add_to_app/flutter_module/pubspec.yaml +++ b/experimental/add_to_app/flutter_module/pubspec.yaml @@ -4,7 +4,7 @@ description: An example Flutter module. version: 1.0.0+1 environment: - sdk: ">=2.5.0 <3.0.0" + sdk: ">=2.6.0-dev <3.0.0" dependencies: flutter: diff --git a/experimental/add_to_app/flutter_module_using_plugin/build.gradle b/experimental/add_to_app/flutter_module_using_plugin/build.gradle deleted file mode 100644 index 68f3d142b..000000000 --- a/experimental/add_to_app/flutter_module_using_plugin/build.gradle +++ /dev/null @@ -1 +0,0 @@ -buildscript {} \ No newline at end of file diff --git a/experimental/add_to_app/flutter_module_using_plugin/lib/main.dart b/experimental/add_to_app/flutter_module_using_plugin/lib/main.dart index 89ff855bd..576740773 100644 --- a/experimental/add_to_app/flutter_module_using_plugin/lib/main.dart +++ b/experimental/add_to_app/flutter_module_using_plugin/lib/main.dart @@ -7,7 +7,10 @@ import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart' as launcher; +/// The entrypoint for the flutter module. void main() { + // This call ensures the Flutter binding has been set up before creating the + // MethodChannel-based model. WidgetsFlutterBinding.ensureInitialized(); final model = CounterModel(); @@ -20,6 +23,13 @@ void main() { ); } +/// A simple model that uses a [MethodChannel] as the source of truth for the +/// state of the counter. +/// +/// Rather than storing app state data within the Flutter module itself (where +/// the native portions of the app can't access it), this module passes messages +/// back to the containing app whenever it needs to increment or retrieve the +/// value of the counter. class CounterModel extends ChangeNotifier { CounterModel() { _channel.setMethodCallHandler(_handleMessage); @@ -44,6 +54,10 @@ class CounterModel extends ChangeNotifier { } } +/// The "app" displayed by this module. +/// +/// It offers two routes, one suitable for displaying as a full screen and +/// another designed to be part of a larger UI. class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { @@ -57,6 +71,8 @@ class MyApp extends StatelessWidget { } } +/// Wraps [Contents] in a Material [Scaffold] so it looks correct when displayed +/// full-screen. class FullScreenView extends StatelessWidget { @override Widget build(BuildContext context) { @@ -69,6 +85,12 @@ class FullScreenView extends StatelessWidget { } } +/// The actual content displayed by the module. +/// +/// This widget displays info about the state of a counter and how much room (in +/// logical pixels) it's been given. It also offers buttons to increment the +/// counter, opening the Flutter documentation via the url_launcher plugin, and +/// (optionally) close the Flutter view. class Contents extends StatelessWidget { final bool showExit; @@ -126,6 +148,8 @@ class Contents extends StatelessWidget { ), RaisedButton( onPressed: () async { + // Use the url_launcher plugin to open the Flutter docs in + // a browser. final url = 'https://flutter.dev/docs'; if (await launcher.canLaunch(url)) { launcher.launch(url); diff --git a/experimental/add_to_app/flutter_module_using_plugin/pubspec.lock b/experimental/add_to_app/flutter_module_using_plugin/pubspec.lock index e54744a05..ee3d7195e 100644 --- a/experimental/add_to_app/flutter_module_using_plugin/pubspec.lock +++ b/experimental/add_to_app/flutter_module_using_plugin/pubspec.lock @@ -115,7 +115,7 @@ packages: name: provider url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" + version: "3.1.0+1" quiver: dependency: transitive description: @@ -183,7 +183,14 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "5.2.0" + version: "5.2.5" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" vector_math: dependency: transitive description: @@ -199,5 +206,5 @@ packages: source: hosted version: "3.5.0" sdks: - dart: ">=2.5.0 <3.0.0" - flutter: ">=1.5.0 <2.0.0" + dart: ">=2.6.0-dev <3.0.0" + flutter: ">=1.9.1+hotfix.4 <2.0.0" diff --git a/experimental/add_to_app/flutter_module_using_plugin/pubspec.yaml b/experimental/add_to_app/flutter_module_using_plugin/pubspec.yaml index 4b3ae94de..70c68fb55 100644 --- a/experimental/add_to_app/flutter_module_using_plugin/pubspec.yaml +++ b/experimental/add_to_app/flutter_module_using_plugin/pubspec.yaml @@ -4,13 +4,13 @@ description: An example Flutter module that uses a plugin. version: 1.0.0+1 environment: - sdk: ">=2.5.0 <3.0.0" + sdk: ">=2.6.0-dev <3.0.0" dependencies: flutter: sdk: flutter provider: ^3.1.0 - url_launcher: ^5.1.6 + url_launcher: ^5.2.5 dev_dependencies: flutter_test: diff --git a/experimental/add_to_app/ios_fullscreen/IOSFullScreen.xcodeproj/project.pbxproj b/experimental/add_to_app/ios_fullscreen/IOSFullScreen.xcodeproj/project.pbxproj index 0a4a4922e..416393650 100644 --- a/experimental/add_to_app/ios_fullscreen/IOSFullScreen.xcodeproj/project.pbxproj +++ b/experimental/add_to_app/ios_fullscreen/IOSFullScreen.xcodeproj/project.pbxproj @@ -494,6 +494,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -555,6 +556,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_BITCODE = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; diff --git a/experimental/add_to_app/ios_using_plugin/IOSUsingPlugin.xcodeproj/project.pbxproj b/experimental/add_to_app/ios_using_plugin/IOSUsingPlugin.xcodeproj/project.pbxproj index 4de89bb12..ce55385f9 100644 --- a/experimental/add_to_app/ios_using_plugin/IOSUsingPlugin.xcodeproj/project.pbxproj +++ b/experimental/add_to_app/ios_using_plugin/IOSUsingPlugin.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ @@ -152,7 +152,6 @@ BF8095192EC0A98CBABFC968 /* Pods-IOSUsingPluginUITests.debug.xcconfig */, 256E81164BC118D2D32C497F /* Pods-IOSUsingPluginUITests.release.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -495,6 +494,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -556,6 +556,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_BITCODE = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; diff --git a/experimental/add_to_app/ios_using_plugin/IOSUsingPlugin/AppDelegate.swift b/experimental/add_to_app/ios_using_plugin/IOSUsingPlugin/AppDelegate.swift index d458eea32..bb5ca8776 100644 --- a/experimental/add_to_app/ios_using_plugin/IOSUsingPlugin/AppDelegate.swift +++ b/experimental/add_to_app/ios_using_plugin/IOSUsingPlugin/AppDelegate.swift @@ -16,7 +16,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Instantiate Flutter engine self.flutterEngine = FlutterEngine(name: "io.flutter", project: nil) self.flutterEngine?.run(withEntrypoint: nil) - GeneratedPluginRegistrant.register(with: self.flutterEngine) + GeneratedPluginRegistrant.register(with: self.flutterEngine!) + return true } } diff --git a/experimental/add_to_app/ios_using_plugin/Podfile.lock b/experimental/add_to_app/ios_using_plugin/Podfile.lock index efdbb192e..25a6544bf 100644 --- a/experimental/add_to_app/ios_using_plugin/Podfile.lock +++ b/experimental/add_to_app/ios_using_plugin/Podfile.lock @@ -28,8 +28,8 @@ SPEC CHECKSUMS: Flutter: 0e3d915762c693b495b44d77113d4970485de6ec flutter_module_using_plugin: ab21df6109f463a98496d1b37ad061e4fd4e3664 FlutterPluginRegistrant: 5f023d9f4a14f5b49cc6bb950327099916b47859 - url_launcher: 0067ddb8f10d36786672aa0722a21717dba3a298 + url_launcher: a1c0cc845906122c4784c542523d8cacbded5626 -PODFILE CHECKSUM: f4d84c8c6c48bfdf0126e770a3e88f6f1ef3ffe8 +PODFILE CHECKSUM: 357b6c94e1edb8289fba0dbdd79daa105ab9d88c COCOAPODS: 1.7.5