@@ -68,13 +68,15 @@ Pin Bazel with `.bazelversion`:
68689.2.0
6969```
7070
71- Use this complete ` .bazelrc ` in an outside consumer (the examples import the
71+ Use this complete ` .bazelrc ` in an outside consumer; the examples import
72+ repository-shared cache settings instead:
7273```
7374common --enable_bzlmod
7475
7576build:android --merge_android_manifest_permissions
7677build:android --tool_java_language_version=17 --tool_java_runtime_version=remotejdk_17
7778build:android --java_language_version=17 --java_runtime_version=remotejdk_17
79+ common:android --repo_env=ANDROID_NDK_HOME
7880common --config=android
7981```
8082
@@ -376,27 +378,24 @@ Future<void> _checkDocumentsDirectory() async {
376378This is the runtime assertion: after installation it must resolve a directory,
377379not report ` MissingPluginException ` .
378380
379- ### Seed generated state before changing the module graph
381+ ### Create generated state and wire the plugin graph
380382
381- The ordering is required. First create the Dart registrant placeholder, but do
382- ** not** create an empty ` plugin_deps.MODULE.bazel ` :
383+ After ` flutter pub get ` , create both generated-state files as zero-byte
384+ placeholders. The plugin guards require their committed files to exist, and
385+ ` include() ` requires its target file to exist while the module is evaluated:
383386
384387``` sh
385- touch lib/dart_plugin_registrant.dart
386- # NOT `touch plugin_deps.MODULE.bazel`.
388+ touch plugin_deps.MODULE.bazel lib/dart_plugin_registrant.dart
387389```
388390
389- Move the complete ` maven = use_extension(...) ` through
390- ` use_repo(maven, "flutter_maven") ` block from the plugin-free ` MODULE.bazel `
391- above into ` plugin_deps.MODULE.bazel ` unchanged. It is a valid seed for the
392- module include; an empty included file does not create ` @flutter_maven ` , so
393- ` plugins.project(maven_repo = "@flutter_maven//:pin") ` cannot resolve.
394-
395- Then remove that Maven block from ` MODULE.bazel ` and replace it with the real
396- plugin graph. Keep the module declaration, ` rules_flutter ` override, and three
397- direct Bazel dependencies unchanged.
391+ In the same editing pass, replace the hand-written Maven install in
392+ ` android/config.MODULE.bazel ` with the Consumer Module's permanent extension
393+ proxy and import, then add the plugin extension and generated segment:
398394
399395``` python
396+ maven = use_extension(" @rules_jvm_external//:extensions.bzl" , " maven" )
397+ use_repo(maven, " flutter_maven" )
398+
400399plugins = use_extension(" @rules_flutter//tools/flutter:plugins.bzl" , " flutter_plugins_ext" )
401400plugins.project(
402401 abis = [" arm64-v8a" , " x86_64" ],
@@ -409,9 +408,16 @@ use_repo(plugins, "flutter_plugins")
409408include(" //:plugin_deps.MODULE.bazel" )
410409```
411410
412- ` use_repo ` makes the generated per-package targets and
413- ` @flutter_plugins//:all ` visible. ` include ` creates the one Maven repository in
414- the consumer module. They are different, and both are required.
411+ The Consumer Module owns the Maven proxy and ` use_repo(maven, "flutter_maven") ` ;
412+ the generated segment owns the single ` maven.install ` . The position of
413+ ` include() ` relative to ` plugins.project() ` does not matter, provided the
414+ segment declaring the repository name has been evaluated. Do not leave a
415+ second ` use_repo ` in the generated segment: declaring the name in both places
416+ fails with:
417+
418+ ``` text
419+ Error in use_repo: The repo name 'flutter_maven' cannot be defined by a use_repo() call at ... as it is already defined by a use_repo() call at ...
420+ ```
415421
416422### Turn on the Dart and Android plugin graph
417423
@@ -425,9 +431,9 @@ flutter_app(
425431)
426432```
427433
428- Apply that change in the same edit as the module extension above: enabling the
429- Dart guard without creating ` @flutter_plugins ` leaves its expected file
430- unresolvable .
434+ This BUILD edit and the module wiring above are one transition; apply both
435+ before running a guard. With a graph, ` flutter_android_binary ` derives the
436+ conventional registrant target .
431437
432438In ` android/app/BUILD.bazel ` , retain the existing imports, embedding, and
433439` main_activity ` , then replace the opt-out Android setup with the plugin-aware
@@ -467,21 +473,24 @@ registrant target.
467473
468474### Generate, commit, build, and prove the transition
469475
470- Use the guards' generated outputs rather than hand-editing either committed
471- file. The first guard fails against the seed by design; copy its expected file
472- from the generated repository :
476+ The placeholders make the plugin guard fail once, as expected. Its failure
477+ output names the updater command; run the updater rather than copying from
478+ Bazel's output tree :
473479
474480``` sh
475481bazel build //:plugins_check
476- cp " $( bazel info output_base) /$( bazel cquery --output=files @flutter_plugins//:plugin_deps.MODULE.bazel) " plugin_deps.MODULE.bazel
477- bazel build //:plugins_check
482+ bazel run //:plugins_update
478483
479484bazel build //:dart_registrant_check
480- cp " $( bazel info output_base) /$( bazel cquery --output=files @flutter_plugins//:dart_plugin_registrant.dart) " lib/dart_plugin_registrant.dart
481- bazel build //:dart_registrant_check
485+ # Run the `bazel run //:dart_registrant_update` command printed above.
486+ bazel run //:dart_registrant_update
487+
488+ bazel build //:plugins_check //:dart_registrant_check
482489bazel test //:guards_test
483490```
484491
492+ The guard prints this command because it owns the generated artifact's identity.
493+
485494Commit ` plugin_deps.MODULE.bazel ` and ` lib/dart_plugin_registrant.dart ` . Then
486495build and install the APK:
487496
@@ -495,15 +504,16 @@ adb install -r bazel-bin/android/app/hello_bazel.apk
495504Launch the app and make the ` path_provider ` call. It should return the
496505application documents directory without throwing ` MissingPluginException ` .
497506If the app opens but the call throws that exception, the APK build and install
498- succeeded but plugin registration is stale or missing; refresh the generated
499- registrants and rebuild.
507+ succeeded but plugin registration is stale or missing; rerun the registrant
508+ guard and its printed updater, then rebuild.
500509
501510For every later pub plugin addition, removal, or upgrade, run ` flutter pub get ` .
502- Flutter refreshes ` GeneratedPluginRegistrant.java ` ; keep its BUILD target dependent
503- on ` @flutter_plugins//:all ` , rather than maintaining a per-plugin Bazel list.
504- Rerun the two guards, copy their generated outputs when they drift, commit them,
505- rebuild, install, and exercise the changed plugin. Standard CMake-backed plugins
506- are generated automatically. If a plugin's Maven coordinate cannot be read statically,
511+ Flutter refreshes ` GeneratedPluginRegistrant.java ` ; keep its BUILD target
512+ dependent on ` @flutter_plugins//:all ` , rather than maintaining a per-plugin
513+ list. Run both guards; when a file drifts, use the updater command printed
514+ by its guard and commit the resulting files. Rebuild, install, and
515+ exercise the changed plugin. Standard CMake-backed plugins are generated
516+ automatically. If a plugin's Maven coordinate cannot be read statically,
507517declare it on that package instead of adding a second Maven install:
508518
509519``` python
@@ -552,19 +562,17 @@ flutter pub get
552562cd ../..
553563```
554564
555- Before enabling ` plugins.project() ` , bootstrap the two committed generated-state
556- files. From the module root, create the Dart placeholder but do ** not** create an
557- empty Maven segment:
565+ Create both committed generated-state files as zero-byte placeholders from the
566+ module root:
558567
559568``` sh
560- touch packages/host_app/lib/dart_plugin_registrant.dart
561- # NOT `touch plugin_deps.MODULE.bazel`.
569+ touch plugin_deps.MODULE.bazel packages/host_app/lib/dart_plugin_registrant.dart
562570```
563571
564- Seed the root ` plugin_deps.MODULE.bazel ` by moving the complete
565- ` maven = use_extension(...) ` through ` use_repo(maven, "flutter_maven") ` block
566- from the plugin-free module above into that file, unchanged. This valid seed
567- makes ` @flutter_maven//:pin ` available while ` plugins.project() ` is evaluated .
572+ The root ` include() ` requires the Maven segment to exist, and the plugin
573+ guards' committed-file attributes are mandatory. The placeholders carry no
574+ Maven content; the Consumer Module's wiring below makes ` @flutter_maven `
575+ visible before the generated segment is populated .
568576
569577Keep the root module as a composition point and delegate Android configuration:
570578
@@ -614,6 +622,9 @@ android_ndk = use_extension("@rules_flutter//tools/flutter:ndk.bzl", "android_nd
614622use_repo(android_ndk, " androidndk" , " androidndk_cmake" )
615623register_toolchains(" @androidndk//:all" )
616624
625+ maven = use_extension(" @rules_jvm_external//:extensions.bzl" , " maven" )
626+ use_repo(maven, " flutter_maven" )
627+
617628plugins = use_extension(" @rules_flutter//tools/flutter:plugins.bzl" , " flutter_plugins_ext" )
618629plugins.project(
619630 abis = [" arm64-v8a" ],
@@ -626,6 +637,12 @@ use_repo(plugins, "flutter_plugins")
626637include(" //:plugin_deps.MODULE.bazel" )
627638```
628639
640+ The Maven proxy and import are owned by this Consumer Module; the generated
641+ segment contributes the single install. ` include() ` may appear before or after
642+ ` plugins.project() ` because its position does not matter once the segment
643+ declaring the repository name has been evaluated. Do not duplicate the
644+ ` use_repo ` declaration in the generated segment.
645+
629646The root needs a Bazel package solely so it can export the included generated
630647segment:
631648
@@ -700,21 +717,24 @@ the generated Android app; the canonical full file is
700717[ ` packages/host_app/android/app/BUILD.bazel ` ] ( examples/local_plugin/packages/host_app/android/app/BUILD.bazel ) .
701718
702719Generate the root Maven segment and the app's Dart registrant through their
703- guards; the first build is expected to fail against the seed and print the
704- expected generated content :
720+ updater targets. The empty placeholders make the plugin guard fail once, and
721+ its failure output names the Maven updater :
705722
706723``` sh
707724bazel build //packages/host_app:plugins_check
708- cp " $( bazel info output_base) /$( bazel cquery --output=files @flutter_plugins//:plugin_deps.MODULE.bazel) " plugin_deps.MODULE.bazel
709- bazel build //packages/host_app:plugins_check
725+ bazel run //packages/host_app:plugins_update
710726
711727bazel build //packages/host_app:dart_registrant_check
712- cp " $( bazel info output_base) /$( bazel cquery --output=files @flutter_plugins//:dart_plugin_registrant.dart) " packages/host_app/lib/dart_plugin_registrant.dart
713- bazel build //packages/host_app:dart_registrant_check
728+ # Run the `bazel run //packages/host_app:dart_registrant_update` command printed above.
729+ bazel run //packages/host_app:dart_registrant_update
730+
731+ bazel build //packages/host_app:plugins_check //packages/host_app:dart_registrant_check
714732bazel test //packages/host_app:guards_test
715733bazel build //packages/host_app/android/app:host_app
716734```
717735
736+ The guard prints this command because it owns the generated artifact's identity.
737+
718738Adapt all three label families together when your layout differs:
719739` metadata = "//<app-package>:.flutter-plugins-dependencies" ` ,
720740` embedding = "//<app-package>/android/app:flutter_embedding" ` ,
@@ -999,10 +1019,11 @@ at the monorepo root for the subpackage shape), and map the real
9991019the explicit root-vs-named ` app ` label rule.
10001020
10011021After every pub resolution change, run ` flutter pub get ` . For any plugin graph,
1002- then run ` :plugins_check ` and ` :dart_registrant_check ` , copy the generator's
1003- expected files with the ` bazel cquery --output=files ` commands above, and commit
1004- them with the lockfile changes. For ` path: ` dependencies, keep the local Dart
1005- filegroup in ` path_deps ` ; its content is not represented by a pub lock hash.
1022+ run ` :plugins_check ` and ` :dart_registrant_check ` ; when either guard reports
1023+ drift, run the updater command it prints and commit the generated files with
1024+ the lockfile changes. For ` path: ` dependencies,
1025+ keep the local Dart filegroup in ` path_deps ` ; its content is not represented by
1026+ a pub lock hash.
10061027
10071028Finally, treat building as artifact production, not runtime proof. Discover the
10081029APK, install it on a device or emulator for a shipped ABI, launch it, and
0 commit comments