Repository: flutter
Issue: Everything builds from a Flutter module
Labels: tool,a: existing-apps,a: build,P3,team-tool,triaged-tool

This is a brainstorm proposal to restructure the modularity of Flutter projects to increase modular reuse between add-to-app cases and full-Flutter-app cases and to make the project structure generally more compositional. 

Consider that there are conceptually really 3 things involved when building a Flutter project

0. Flutter engine. Binaries that include the Flutter C++ engine + platform specific bits, Skia, Dart VM etc. These are pre-built and independent of the user's project. 

1. A Dart package. The inputs are .dart files, a pubspec and Flutter dependent assets such as fonts etc. The outputs are JIT/AOT binaries/assets and project assets. There's a single Dart package across all platforms. 

2. A platform library wrapping the dart package. The inputs are the output of 0 and 1 and some minimal platform specific bootstrapping scaffolding such as some .gradles and .xcodeproj. The output is a Flutter-tool agnostic platform specific library package such as .aars + POMs on Android, .frameworks on iOS, .dll + assets etc. This is a library and is not runnable. There is 1 platform library per platform per dart package. 

3. A runnable platform application that runs the Flutter platform library. The inputs are the output of 2 plus all things relevant for the application and platform. With Android as an example, this is the .gradle package names, flavors, AndroidManifests, .java files, resources etc. The output is a runnable APK, exe, IPA etc etc. There can be many apps that depends on 1 platform library + dart package combo per platform. This is a leaf node and no reverse dependency exists. This could be your standard `flutter create` app which is then just a simple bootstrap for your platform library in 2. This could be your own existing app for add-to-app cases. This could be a test bootstrap for the Flutter library you're developing but don't want to test by embedding it inside your full add-to-app app because the Flutter screen is 10 screens deep in your outer app. 

Historically, 2 didn't exist and 1 and 3 are highly coupled together since we only had full-Flutter app cases. But it's unmodular and hard to deal with add-to-app cases which are theoretically a strict subset of full-app cases but in practice become parallel implementations because full-app cases are too tightly coupled. 

In this proposal, 1 doesn't know about 2 and 2 doesn't know about 3. 

## What does `flutter run` do then

`flutter run` is currently run inside (1). If (1) doesn't know about (3), how does it launch the platform specific bootstrap? 

This convenience mechanism should still be a modular mechanism built on top of the proposal above and itself using more modular mechanisms. 

The Dart package's pubspec should still list only its own dependencies such as assets and plugins. Convenience features like flutter run can be mapped via a separate mapping mechanism such as a app-map.yaml file:

```yaml
android:
  simple_test:
    path: ../my_flutter_bootstrap
    command: ./gradlew assembleDebug  # optional, has reasonable default
  test_against_staging:
    path: ../my_flutter_bootstrap
    command: ./gradlew assembleFirebaseStaging
  real_app:
    path: /Volumes/corp_src/app1
    command: flutter build aar; ln -s ./build/host/outputs/repo 
             /Volumes/corp_src/app1/dependencies; ./gradlew assembleDebug
ios:
  ...
web:
  ...
```

Then we can `flutter run simple_test`. No need to -d someAndroidDevice since it knows its Android. We also won't need to hardcode the Flutter<->platform app's relative directory structure since anything can live anywhere relative to each other. 

We also remove the need for anything to be called Runner.app or for .app. to be anywhere in the package namespace on Android since the app is the leaf node and nothing depends on it. 

Behind the scene, flutter run is broken down into running the `command` in app-map.yaml (which in-turn depends on the platform library (2) which in turn builds the Dart code in (1)) and running `flutter attach` (which is what it conceptually does anyway). 

## What does `flutter create` do then

For convenience, it would do the exact same thing as it does today from the surface. But behind the scene, it's shimmed out by 2 things (which we can rename to make them more first class etc). A `flutter create -t module my_flutter_module` and a `flutter create-wrapper-project my_test_launcher --from-module ../my_flutter_module`. But you can independently do just one of the 2. e.g. you would call flutter create -t module to make a new Flutter module. You would call `flutter create-wrapper-project` if you're trying to create another project around an existing module etc. 

## How do plugins work

This is another case where we amalgamated 2 things (1- the resolution of the pub dependencies and the retrieval of all platforms' native components of the plugin and 2- the creation of the platform's generated registration and the retrieval of the plugin's platform side transient native dependencies, such as firebase, and the packaging of everything). 

I propose that we split the plugin processing into 2. `flutter packages get` only operates in Flutter modules and only does Flutter things. At the end of this, you can import the plugin's Dart code but nothing for the platform side has been done. 

Only upon trying to build the platform library's artifacts (such as by flutter build aar in the library) do the native code generation and transient dependency resolution happen.