Skip to content

Conversation

@camsim99
Copy link
Contributor

@camsim99 camsim99 commented Nov 11, 2024

Important

Messed up this branch :') Landed in #167815.

Overview

Implements setting content sensitivity on Android via a new widget called SensitiveContent that currently will only function on Android.

How it's implemented

There are three different content sensitivity levels: autoSensitive, sensitive, notSensitive. All except autoSensitive behave as their Android counterpart level behaves (see links on the levels); for Flutter, autoSensitive will behave the same as notSensitive though it is implemented as if it works (see #160879 for more details.

In any given Flutter view, each SensitiveContent widget sets a content sensitivity level and there may be multiple SensitiveContent widgets in the widget tree at a time, but only the most severe content sensitivity level in the tree will be maintained in order to ensure secure content remains obscured during screen projection. This means that even widgets that are not visible may still impact the overall content sensitivity of the entire Flutter view (see #160051 for more details).

Under the hood, the implementation uses method channels. Theoretically, this could cause a delay in setting the content sensitivity level and a Flutter view being obscured during screen projection on time, so we will investigate using JNIgen in the future (see #160050 for more details). Over the method channel, the framework will send a Flutter view ID and content sensitivity level to the engine to set the expected content sensitivity level for the view.

Timeline

Required for feature shipping

  • Land this PR with the method channel implementation of content sensitivity.

Short-term follow up tasks

Long-term follow up tasks

Stretch goals

Open questions

  • Is it okay to hard-code the FlutterActivity and FlutterFragment view IDs? Does this impact add-to-app in any way? I assume we would need extenders of those classes to use a different view ID than the hardcoded one. Approach not used.
  • Should we expose autoSensitive for now? If so, what behavior should it have? Exposing autoSensitive as if it works.

One way to test this PR (rough steps)

  1. Create a Flutter app that has some number of SensitiveContent widgets.
  2. Run the app on an emulator/device that runs API 35 and has the Google meets app downloaded.
  3. Start a Google meets meeting and screen share (works with full screen and single app mode).
  4. Join the Google meets meeting from another device and witness a blacked out screen if content has been marked sensitive.

Pre-launch Checklist

@github-actions github-actions bot added the framework flutter/packages/flutter repository. See also f: labels. label Nov 11, 2024
@camsim99 camsim99 changed the title [Proof of concept] Implement setting content sensitivity on Android Implement setting content sensitivity on Android Nov 20, 2024
@camsim99 camsim99 changed the title Implement setting content sensitivity on Android [Android] Implement setting content sensitivity on Android Nov 20, 2024
@github-actions github-actions bot added the engine flutter/engine related. See also e: labels. label Dec 26, 2024
@github-actions github-actions bot added the platform-android Android applications specifically label Jan 7, 2025
@camsim99
Copy link
Contributor Author

camsim99 commented Jan 7, 2025

Looking for feedback on the approach presented in this PR and the open questions I have before writing tests. Looks like there are some linting issues, but this should run, though I've been testing with the non-monorepo engine up until this point.

@camsim99 camsim99 requested review from justinmc and reidbaker January 7, 2025 22:48
Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive-by review here. I still want to look more closely at the SensitiveContent widget and maybe play with it locally. At a high level though, this approach seems good and it looks like things worked as we thought they would. I'll post back tomorrow.

One thing I remember from your Dash Forum session was we thought we would need to use FFI to communicate with the engine. Is that requirement out of date now?

Oh and I struggled in getting one of my engine/framework projects to run in the monorepo when I got back from break. I updated some docs in the process, maybe check that or feel free to reach out if you need to try it in the monorepo and get stuck. #161184

@reidbaker
Copy link
Contributor

"so we will investigate using JNIgen in the future" I believe we should say that we do not plan to release the feature without evaluating if frame drops can occur.

@reidbaker
Copy link
Contributor

"Implements setting content sensitivity on Android via a new plugin called SensitiveContent"

Most flutter users would view plugin in this context as something that should probably be in flutter/packages.
Maybe call it a FrameworkPlugin or just say we are adding a new way to mark content sensitive without mentioning the implementation detail of a plugin.

@reidbaker
Copy link
Contributor

In the pr description consider trying to more cleanly describe what is work that needs to happen in this pr but has intentionally not happened. What work is future work but still required before we ship the feature and what is work that is future work that is unplanned.

Copy link
Contributor

@reidbaker reidbaker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed android code will do a different pass on dart code later.

* see {@link android.view.View#findViewById}.
*/
public static final int FLUTTER_VIEW_ID = View.generateViewId();
public static final int FLUTTER_VIEW_ID = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is a dangerous implementation to change.

From the docs on View.generateViewId()

Generate a value suitable for use in [setId(int)](https://developer.android.com/reference/android/view/View#setId(int)). This value will not collide with ID values generated at build time by aapt for R.id.```

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that this is dangerous; I didn't realize the implications went beyond debugging before.

As of now, I changed my approach to identifying the View we want to set the content sensitivity of by initializing SensitiveContentPlugins with the ID of the View that is created by a FlutterActivity or FlutterFragment. My understanding is that every FlutterActivity or FlutterFragment will (via the FlutterActivityAndFragmentDelegate code I added) provide a SensitiveContentPlugin to handle the View created for each FlutterActivity and FlutterFragment created.

This should cover regular Flutter apps as well as add-to-app use cases where a FlutterActivity or FlutterFragment is used. The only case I think left uncovered is adding a FlutterView to a native app, but it's my understanding that we are not required to handle FlutterActivityAndFragmentDelegate plumbing there (see https://docs.flutter.dev/add-to-app/android/add-flutter-view#general-approach).

Copy link
Contributor Author

@camsim99 camsim99 Feb 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@reidbaker and I spoke about my new approach offline and agree it should work. In summary, we should be able to assume that each FlutterActivity and FlutterFragment should map to one FlutterView in all cases (minus the FlutterView add-to-app case) and so one SensitiveContentPlugin per FlutterActivity or FlutterFragment should be safe. @justinmc this actually will allow us to remove the singleton approach because the SensitiveContent widget no longer needs to be aware of the FlutterView that it is setting the content sensitivity of.

The only exception to that is when one FlutterEngine is used for multiple FlutterActivitys or FlutterFragments. https://docs.flutter.dev/add-to-app/android/add-flutter-screen suggests that this approach is at least possible when using a pre-warmed FlutterEngine. I think this can be fixed by not making FLUTTER_VIEW_ID static and final so that each FlutterView created gets a different ID. I will now work on making this fix, removing the singleton approach, and polishing this PR for a full review.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok small misunderstanding...we still need the singleton approach because we need to be able to track all SensitiveContent widgets in a Flutter app.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to track all sensitive content widgets in a flutter app for a flutter view. It seems to me like that singleton can/should be in dart but the android side could not be a singleton.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah it would have been cool to get rid of the Dart singleton but that sounds good anyway.

Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of nits and questions here but I think the overall approach is sound. The biggest architectural thing is possibly finding a way to avoid the SensitiveContentSetting singleton (commented below), but that is not a blocker.

I'm not sure about hardcoding the view ids, hopefully someone who knows more about Android and add-to-app scenarios will know the answer. If not, maybe it's something we could expose to the framework via MediaQuery or something like that.

About autoSensitive, how do you see the distinction between Android's CONTENT_SENSITIVITY_AUTO and the feature where we automatically add a SensitiveContent widget to password fields? Maybe we should allow the user to set CONTENT_SENSITIVITY_AUTO if they really want to. Or will that literally have no effect?

Edit: I posted this review without seeing Reid's review from a few hours earlier (thanks GitHub), so sorry if there is redundancy.

Comment on lines 187 to 189
/// By default, this is 0. On Android, this is the the ID of the native
/// `View` that is created by the default `FlutterActivity`, which is used by
/// default in Flutter Android apps.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When might a user want to change this to something other than 0?

Comment on lines 174 to 175
/// Widget to set the [ContentSensitivity] level of content in a particular
/// Flutter view.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Maybe explain here what happens when multiple SensitiveContent widgets are in the tree.

Comment on lines 209 to 219
@override
void initState() {
super.initState();
SensitiveContentSetting.register(widget.viewId, widget.sensitivityLevel);
}

@override
void dispose() {
SensitiveContentSetting.unregister(widget.viewId, widget.sensitivityLevel);
super.dispose();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some scenarios to try:

  • What if SensitiveContent is part of an item in ListView.builder, and that item is scrolled off-screen. Will it be disposed and unregister itself? Is that desirable?
  • What happens when a new full screen route is pushed on top of the route containing SensitiveContent? Does it get disposed, and is that desirable or not?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • What if SensitiveContent is part of an item in ListView.builder, and that item is scrolled off-screen. Will it be disposed and unregister itself? Is that desirable?

I don't see it unregistered and I assume that the SensitiveContent widget is actually not getting disposed, keeping the screen obscured. Will need to double check this theory by debugging. I think ideally, the screen is only obscured while the SensitiveContent widget is visible on screen; fixing this may be part of #160051 which I intend to do in a follow up PR.

  • What happens when a new full screen route is pushed on top of the route containing SensitiveContent? Does it get disposed, and is that desirable or not?

If a route is pushed then the screen stays obscured. If a route is pushReplaced, then the SensitiveContent widget gets disposed and the screen does not stay obscured. As I mentioned in the first case, if we want the content sensitivity of the view to rely on widget visibility versus being disposed (meaning push and pushReplace would behave the same), this can be done as a part of #160051.

final Map<int, ViewContentSensitivityState> _contentSensitivityStates = <int, ViewContentSensitivityState> {};
final SensitiveContentService _sensitiveContentService = SensitiveContentService();

static final SensitiveContentSetting _instance = SensitiveContentSetting._();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One downside to this singleton pattern is that it doesn't get reset between test runs or hot reloads. I know it's currently used in many places though. But I wonder if there is a better way here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@justinmc do you have a good example of a plugin that is not a singleton that keeps track of android state?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't able to find a good example. Maybe WidgetsBinding, but that is for communicating from the engine to widgets.

I'll ask around, but if I don't come up with anything then we can disregard this comment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another question I asked that may have implications here is whether we need to use FFI or not for this feature.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just now seeing the issue about switching to JNIgen in the future: #160050. What's the reason behind not wanting to do that as a part of the initial PR?

I was talking to @chunhtai about how to get around using method channels and he suggested going through dart:ui as in FlutterView.updateSemantics (which I believe is handled here in the Android embedding). I've never used this approach before but it seems doable today.

Copy link
Contributor

@chunhtai chunhtai Jan 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by injecting Java code from their embedding?

In case if they implement their custom embedding, how would they handle the FFI call?

I remember the engine team alternatively suggesting checking the Java code into the engine and do something similar to dart:ui

I think they are referring to something like this

@Native<Void Function(Bool)>(symbol: 'PlatformConfigurationNativeApi::SetNeedsReportTimings')

create an one-off API and drill it through the engine shell to embedding

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case if they implement their custom embedding, how would they handle the FFI call?

I've been giving this some thought. One solution could be keeping the sensitive content service that makes calls right now to a method channel (later will be FFI) overridable such that someone can override it to call into their custom embedding. This is not a great solution because it would require modifying the framework. On one hand, I'm not sure that this would be a huge leap for someone maintaining a custom embedding. On the other, I understand that sensitive content would be one of potentially many features switching from using a method channel to JNI/FFI, so we need to consider the larger scale impact of what that world would look like.

Frankly, I need to read up more on how custom embedders work (https://github.com/flutter/flutter/blob/main/engine/src/flutter/docs/Custom-Flutter-Engine-Embedders.md for my future reference). We'll need to set up some system like that for people to be able to override the behavior of any features that rely on FFI/JNI. I still plan to work on landing this with method channels for now, but I'll add a note to #160050 to make sure that answering this questions is part of that work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that requiring framework changes is not a good approach. Currently I'm under the assumption that a custom embedder can simply listen for the method channel call just like we do in our embedders, so overriding something in the framework wouldn't be necessary.

And my assumption about the dart:ui FFI approach is that custom embedders need to implement some interface exposed in dart:ui, including whatever we add for sensitive content, so again no other changes would be needed. But I don't have firsthand experience with this. Maybe @stuartmorgan can clarify here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have enough context from this thread to know what we are talking about exactly. It sounds like it's about how new Framework->embedding communication systems should work?

If so:

Currently I'm under the assumption that a custom embedder can simply listen for the method channel call just like we do in our embedders

We should absolutely not be building new features that require this behavior by embedders. Having a de-facto secondary embedding API of a bunch of method channels, which aren't tightly controlled, can much more easily be accidentally broken for third-party embeddings, and are untyped, is a terrible situation, and it should not be exacerbated with new public channels.

If some new feature is being exposed to arbitrary embeddings, it needs to be added to the formal API for embeddings, not require embedding authors to find, reverse engineer, and use a method channel.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One solution could be keeping the sensitive content service that makes calls right now to a method channel (later will be FFI) overridable such that someone can override it to call into their custom embedding

I think this will be more complex then just handle the message in custom embedding.

Also any solution that require changes to framework to support a certain embedding is a big no-no.

Usually things like this we will create a new API surface drill through native dart:ui -> runtimeController -> shell -> each platform views. You can trace through FlutterView.updateSemantics to see how it is drill throw these layer.

For custom embedding, you will add a new API in embedder.h. I think the mentality is as long as the custom embedding implement function handler properly in embedder.h. Their embedding should work correctly.

@camsim99
Copy link
Contributor Author

"Implements setting content sensitivity on Android via a new plugin called SensitiveContent"

Most flutter users would view plugin in this context as something that should probably be in flutter/packages. Maybe call it a FrameworkPlugin or just say we are adding a new way to mark content sensitive without mentioning the implementation detail of a plugin.

Oops I meant widget not plugin :)


void main() {
// Default content sensitivity setting for testing.
final int defaultContentSensitivitySettingId = ContentSensitivity.autoSensitive.id;
Copy link
Contributor Author

@camsim99 camsim99 Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only tested with this as the default as of right now because I couldn't convince myself that there was greater value in testing other defaults besides the method channel setContentSensitivity method getting called (which is already tested with the other non-default modes in this test). If reviewers think that is valuable, I can make new test files to test the other modes as defaults (I think I have to do this because of the singleton pattern I used for SensitiveContentSetting).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's not a big deal, in the code it looks like there is no special behavior for different default values. If you wanted to though, you could quickly do it by looping over ContentSensitivity.values and running the same test(s) for each value.

@camsim99 camsim99 marked this pull request as ready for review February 24, 2025 19:02
@camsim99
Copy link
Contributor Author

camsim99 commented Feb 24, 2025

@justinmc @reidbaker This is ready for a full review! I know this PR is getting lengthy but I tried to address all your feedback + added tests + updated the PR description.

@camsim99
Copy link
Contributor Author

Just found a small refactor I'd like to make while working on something else -- still ready for review!

Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some ideas in my comments below about the dart classes in sensitive_content.dart, but most other stuff is nits.

I know we talked about mutliwindow already but I keep forgetting how that will work, is there something you can link me to that explains that?


void main() {
// Default content sensitivity setting for testing.
final int defaultContentSensitivitySettingId = ContentSensitivity.autoSensitive.id;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's not a big deal, in the code it looks like there is no special behavior for different default values. If you wanted to though, you could quickly do it by looping over ContentSensitivity.values and running the same test(s) for each value.

expect(setContentSensitivityCallCount, 1);
},
);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super nit: Just as a thought to reduce lines of code in these tests, you could write a single test and put it in a loop over ContentSensitivity.values, and use that value as the sensitivity for the widget that gets disposed, or something like that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually prefer having the tests loop-less so it's clear the exact use case that each one is testing. I think reading the looped version could get kinda hard to understand.

final SensitiveContentSetting sensitiveContentSetting = SensitiveContentSetting.instance;

// The number of method channel calls to `SensitiveContent.setContentSensitivity`.
int setContentSensitivityCallCount = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Instead of a count, you could do a List, where the length of the list is the same as the count. Then you could also test what it gets called with. Not sure if this is actually valuable or not here though, up to you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this idea and I think it's useful! Helps us verify what we actually requested the native side set. Done.

@camsim99
Copy link
Contributor Author

camsim99 commented Mar 4, 2025

To address the multiwindow/multiview concerns:

I know we talked about mutliwindow already but I keep forgetting how that will work, is there something you can link me to that explains that?

I chatted a bit about this with reidbaker@ offline, and we concluded that we don't need to plan ahead for an unplanned feature; that is, multiwindow won't work with this PR out of the box.

That is because multiwindow would involve multiple native Android FlutterViews and as of today, they do not have unique IDs such that the SensitiveContentPlugin could differentiate which View set content sensitivity for. This is a problem because this ID is most likely what someone would need to link native FlutterViews to the Dart ones to implement multiwindow.

I am making a PR to fix this, though, because this impacts the add-to-app multi-FlutterView use case as well, so it'd be nice to land as part of this feature work: #162685.

@camsim99
Copy link
Contributor Author

camsim99 commented Mar 5, 2025

@justinmc @reidbaker This is ready for another look!

@camsim99 camsim99 requested a review from justinmc March 5, 2025 18:45
@reidbaker
Copy link
Contributor

Lets assume that we add this widget to a password field in the material library. How does a user turn off sensitive content for an integration test they have already built after updating to this version of flutter and the new version of material.

I assume we would add a field to TextField that would act as a switch for setting the sensitivity of the wrapper SensitiveContent widget in its implementation. By default, we can set it to notSensitive to not break people. Even if we don't, though, they can then just use the field to notSensitive to fix any other breakages.

That would mean that in test runs they would need to build a way to override the value.

This value is sensitive and they want to protect their users but they dont want the protection during debug mode during testing.

I think this user journey, the one where you want it off during testing is the one that I am the most worried about.

@camsim99
Copy link
Contributor Author

If the TextField is baked deep into their implementation, then yes, they would need to expose a way to override the field we add from a test. Maybe I have to think deeper, but I think that's unavoidable; the only other alternative is to not bake it into Flutter widgets like TextField to begin with.

I think it's fair to ask developers to build that functionality, and I don't think that making them do so is a good enough reason to decide against baking SensitiveContent into relevant Flutter widgets. Building that functionality is likely not super difficult and certainly wouldn't outweigh the added security benefits.

Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I caught a place where errors can be silently swallowed, see comment below.

About the integration test discussion above, does that situation only apply to integration tests that are actually testing sharing their screen to another device?

Comment on lines 145 to 149
} catch (e) {
// If this call fails or unexpectedly returns null, then consider setting content
// sensitivity unsupported.
return false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method looks like it will swallow errors and app developers might not be aware that something went wrong. They probably do want to assume that an error or null means false, but in addition I would probably do FlutterError.reportError.

You may want to do the error handling at the level of the caller of this method in order to align with how error handling is done for the other methods in this file, but it's up to you. The important part is just that errors don't get swallowed.

@reidbaker
Copy link
Contributor

I think I caught a place where errors can be silently swallowed, see comment below.

About the integration test discussion above, does that situation only apply to integration tests that are actually testing sharing their screen to another device?

Yes it only applies to integration tests where the output of the screen is streamed.

Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍 . Thanks for the error handling fix, looks great now.

About the integration test thing. Ok at least it's a rare case we're talking about here, but I agree we should provide a way to do this.

Would it be possible to mock isSupported and fake that sensitive content is not supported at all? It's easy to do this in a unit test (example) but I'm not sure about in an integration test.

@camsim99
Copy link
Contributor Author

Would it be possible to mock isSupported and fake that sensitive content is not supported at all? It's easy to do this in a unit test (example) but I'm not sure about in an integration test.

That's what I'm thinking! For integration tests, developers would just need to expose a way to change their widget in an integration test to turn sensitive content sensitivity off.

Copy link
Contributor

@chunhtai chunhtai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

throw FlutterError('Failed to retrieve content sensitivity: $e');
}

final ContentSensitivity contentSensitivity = ContentSensitivity.values.firstWhere(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can just be ContentSensitivity.values[result];

@reidbaker
Copy link
Contributor

My force push was after running git rebase --onto main HEAD

@reidbaker
Copy link
Contributor

Well @camsim99 I messed this up. Now that the pr is closed I cant push to this branch anymore.

github-merge-queue bot pushed a commit that referenced this pull request Apr 29, 2025
> [!NOTE]  
> This PR lands #158473 (closed
due to confusing git-isms). Please see that PR for all comments and
discussion on landing this feature.

### Overview
Implements setting content sensitivity on Android via a new widget
called `SensitiveContent` that currently will only function on Android.

### How it's implemented
There are three different content sensitivity levels:
[`autoSensitive`](https://developer.android.com/reference/android/view/View#CONTENT_SENSITIVITY_AUTO),
[`sensitive`](https://developer.android.com/reference/android/view/View#CONTENT_SENSITIVITY_SENSITIVE),
[`notSensitive`](https://developer.android.com/reference/android/view/View#CONTENT_SENSITIVITY_NOT_SENSITIVE).
All except `autoSensitive` behave as their Android counterpart level
behaves (see links on the levels); for Flutter, `autoSensitive` will
behave the same as `notSensitive` though it is implemented as if it
works (see #160879 for more
details.

In any given Flutter view, each `SensitiveContent` widget sets a content
sensitivity level and there may be multiple `SensitiveContent` widgets
in the widget tree at a time, but only the most severe content
sensitivity level in the tree will be maintained in order to ensure
secure content remains obscured during screen projection. This means
that even widgets that are not visible may still impact the overall
content sensitivity of the entire Flutter view (see
#160051 for more details).

Under the hood, the implementation uses method channels. Theoretically,
this could cause a delay in setting the content sensitivity level and a
Flutter view being obscured during screen projection on time, so we will
investigate using JNIgen in the future (see
#160050 for more details). Over
the method channel, the framework will send a Flutter view ID and
content sensitivity level to the engine to set the expected content
sensitivity level for the view.

### Timeline

#### Required for feature shipping
- Land this PR with the method channel implementation of content
sensitivity.

#### Short-term follow up tasks
- Ensure every `FlutterView` has a unique ID for the native
`SensitiveContentPlugin` to identify them by:
#162685
- Add `SensitiveContent` widget to relevant Flutter widgets:
#167302

#### Long-term follow up tasks
- Convert this PR to use JNIgen:
#160050. Verify no frames are
dropped, revealing sensitive content during media projection:
#164820.

#### Stretch goals
- Make the `SensitiveContent` widget sensitive to the visibility of
child widgets: #160051
- Implement `autoSensitivity` mode:
#160879
- Investigate backwards compatibility for APIs < 35:
#159208

### Open questions
- ~Is it okay to hard-code the `FlutterActivity` and `FlutterFragment`
view IDs? Does this impact add-to-app in any way? I assume we would need
extenders of those classes to use a different view ID than the hardcoded
one.~ Approach not used.
- ~Should we expose `autoSensitive` for now? If so, what behavior should
it have?~ Exposing `autoSensitive` as if it works.

### One way to test this PR (rough steps)
1. Create a Flutter app that has some number of `SensitiveContent`
widgets.
9. Run the app on an emulator/device that runs API 35 and has the Google
meets app downloaded.
10. Start a Google meets meeting and screen share (works with full
screen and single app mode).
11. Join the Google meets meeting from another device and witness a
blacked out screen if content has been marked sensitive.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
github-merge-queue bot pushed a commit that referenced this pull request Jul 2, 2025
## Motivation

We'd like to let users opt-in to experimental features so that they can
give early feedback while we iterate on the feature. For example:

Example feature flags:

1. Android sensitive content:
#158473. When enabled, Flutter
will tell Android when the view contains sensitive content like a
password.
2. Desktop multi-window. When enabled, Flutter will use child windows to
allow things like a context menu to "escape" outside of the current
window.

### Use case

Users will be able to turn on features by:

* **Option 1**: Run `flutter config --enable-my-feature`. This enables
the feature for all projects on the machine
* **Option 2**: Add `enable-my-feature: true` in their `pubspec.yaml`,
under the `flutter` section. This would enable the for a single project
on the machine.

Turning on a feature affects _both_ development-time (`flutter run`) and
deployment-time (`flutter build x`). For example, I can `flutter build
windows` to create an `.exe` with multi-window features enabled.

## How this works

This adds a new
[`runtimeId`](https://github.com/flutter/flutter/pull/168437/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R243-R247)
property to the tool's `Feature` class. If a feature is on and has a
`runtimeId`, its `runtimeId` will be [stamped into the Dart application
as a Dart
define](https://github.com/flutter/flutter/pull/168437/files#diff-bd662448bdc2e6f50e47cd3b20b22b41a828561bce65cb4d54ea4f5011cc604eR293-R327).
The framework uses this Dart define to [determine which features are
enabled](https://github.com/flutter/flutter/pull/168437/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45).

### Multi-window example

#168697 shows how this new
feature flag system can be used to add a multi-window feature flag:

1. It adds a new [multi-window
feature](https://github.com/flutter/flutter/pull/168697/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R189-R198)
to the Flutter tool. This can be turned on using `flutter config
--enable-multi-window` or by putting `enable-multi-window: true` in an
app's .pubspec, under the `flutter` section.
2. It adds a new
[`isMultiWindowEnabled`](https://github.com/flutter/flutter/pull/168697/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45R7-R11)
property to the framework.
3. The Material library can use this new property to determine whether
it should create a new window.
[Example](https://github.com/flutter/flutter/pull/168697/files#diff-2cbc1634ed6b61d61dfa090e7bfbbb7c60b74c8abc3a28df6f79eee691fd1b73).

## Limitations

### Tool and framework only

For now, these feature flags are available only to the Flutter tool and
Flutter framework. The flags are not automatically available to the
embedder or the engine.

For example, embedders need to configure their surfaces differently if
Impeller is enabled. This configuration must happen before the Dart
isolate is launched. As a result, the framework's feature flags is not a
viable solution for this scenario for now. For these kinds of scenarios,
we should continue to use platform-specific configuration like the
`AndroidManifest.xml` or `Info.plist` files.

This is a fixable limitation, we just need to invest in this plumbing :)

### Tree shaking

Feature flags are not designed to help tree shaking. For example, you
cannot conditionally import Dart code depending on the enabled feature
flags. Code that is feature flagged off will still be imported into
user's apps.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
github-merge-queue bot pushed a commit that referenced this pull request Jul 2, 2025
<!-- start_original_pr_link -->
Reverts: #168437
<!-- end_original_pr_link -->
<!-- start_initiating_author -->
Initiated by: chingjun
<!-- end_initiating_author -->
<!-- start_revert_reason -->
Reason for reverting: broke internal customers
<!-- end_revert_reason -->
<!-- start_original_pr_author -->
Original PR Author: loic-sharma
<!-- end_original_pr_author -->

<!-- start_reviewers -->
Reviewed By: {bkonyi, justinmc, matanlurey}
<!-- end_reviewers -->

<!-- start_revert_body -->
This change reverts the following previous change:
## Motivation

We'd like to let users opt-in to experimental features so that they can
give early feedback while we iterate on the feature. For example:

Example feature flags:

1. Android sensitive content:
#158473. When enabled, Flutter
will tell Android when the view contains sensitive content like a
password.
2. Desktop multi-window. When enabled, Flutter will use child windows to
allow things like a context menu to "escape" outside of the current
window.

### Use case

Users will be able to turn on features by:

* **Option 1**: Run `flutter config --enable-my-feature`. This enables
the feature for all projects on the machine
* **Option 2**: Add `enable-my-feature: true` in their `pubspec.yaml`,
under the `flutter` section. This would enable the for a single project
on the machine.

Turning on a feature affects _both_ development-time (`flutter run`) and
deployment-time (`flutter build x`). For example, I can `flutter build
windows` to create an `.exe` with multi-window features enabled.

## How this works

This adds a new
[`runtimeId`](https://github.com/flutter/flutter/pull/168437/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R243-R247)
property to the tool's `Feature` class. If a feature is on and has a
`runtimeId`, its `runtimeId` will be [stamped into the Dart application
as a Dart
define](https://github.com/flutter/flutter/pull/168437/files#diff-bd662448bdc2e6f50e47cd3b20b22b41a828561bce65cb4d54ea4f5011cc604eR293-R327).
The framework uses this Dart define to [determine which features are
enabled](https://github.com/flutter/flutter/pull/168437/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45).

### Multi-window example

#168697 shows how this new
feature flag system can be used to add a multi-window feature flag:

1. It adds a new [multi-window
feature](https://github.com/flutter/flutter/pull/168697/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R189-R198)
to the Flutter tool. This can be turned on using `flutter config
--enable-multi-window` or by putting `enable-multi-window: true` in an
app's .pubspec, under the `flutter` section.
2. It adds a new
[`isMultiWindowEnabled`](https://github.com/flutter/flutter/pull/168697/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45R7-R11)
property to the framework.
3. The Material library can use this new property to determine whether
it should create a new window.
[Example](https://github.com/flutter/flutter/pull/168697/files#diff-2cbc1634ed6b61d61dfa090e7bfbbb7c60b74c8abc3a28df6f79eee691fd1b73).

## Limitations

### Tool and framework only

For now, these feature flags are available only to the Flutter tool and
Flutter framework. The flags are not automatically available to the
embedder or the engine.

For example, embedders need to configure their surfaces differently if
Impeller is enabled. This configuration must happen before the Dart
isolate is launched. As a result, the framework's feature flags is not a
viable solution for this scenario for now. For these kinds of scenarios,
we should continue to use platform-specific configuration like the
`AndroidManifest.xml` or `Info.plist` files.

This is a fixable limitation, we just need to invest in this plumbing :)

### Tree shaking

Feature flags are not designed to help tree shaking. For example, you
cannot conditionally import Dart code depending on the enabled feature
flags. Code that is feature flagged off will still be imported into
user's apps.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

<!-- end_revert_body -->

Co-authored-by: auto-submit[bot] <[email protected]>
loic-sharma added a commit to loic-sharma/flutter that referenced this pull request Jul 12, 2025
## Motivation

We'd like to let users opt-in to experimental features so that they can
give early feedback while we iterate on the feature. For example:

Example feature flags:

1. Android sensitive content:
flutter#158473. When enabled, Flutter
will tell Android when the view contains sensitive content like a
password.
2. Desktop multi-window. When enabled, Flutter will use child windows to
allow things like a context menu to "escape" outside of the current
window.

### Use case

Users will be able to turn on features by:

* **Option 1**: Run `flutter config --enable-my-feature`. This enables
the feature for all projects on the machine
* **Option 2**: Add `enable-my-feature: true` in their `pubspec.yaml`,
under the `flutter` section. This would enable the for a single project
on the machine.

Turning on a feature affects _both_ development-time (`flutter run`) and
deployment-time (`flutter build x`). For example, I can `flutter build
windows` to create an `.exe` with multi-window features enabled.

## How this works

This adds a new
[`runtimeId`](https://github.com/flutter/flutter/pull/168437/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R243-R247)
property to the tool's `Feature` class. If a feature is on and has a
`runtimeId`, its `runtimeId` will be [stamped into the Dart application
as a Dart
define](https://github.com/flutter/flutter/pull/168437/files#diff-bd662448bdc2e6f50e47cd3b20b22b41a828561bce65cb4d54ea4f5011cc604eR293-R327).
The framework uses this Dart define to [determine which features are
enabled](https://github.com/flutter/flutter/pull/168437/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45).

### Multi-window example

flutter#168697 shows how this new
feature flag system can be used to add a multi-window feature flag:

1. It adds a new [multi-window
feature](https://github.com/flutter/flutter/pull/168697/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R189-R198)
to the Flutter tool. This can be turned on using `flutter config
--enable-multi-window` or by putting `enable-multi-window: true` in an
app's .pubspec, under the `flutter` section.
2. It adds a new
[`isMultiWindowEnabled`](https://github.com/flutter/flutter/pull/168697/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45R7-R11)
property to the framework.
3. The Material library can use this new property to determine whether
it should create a new window.
[Example](https://github.com/flutter/flutter/pull/168697/files#diff-2cbc1634ed6b61d61dfa090e7bfbbb7c60b74c8abc3a28df6f79eee691fd1b73).

## Limitations

### Tool and framework only

For now, these feature flags are available only to the Flutter tool and
Flutter framework. The flags are not automatically available to the
embedder or the engine.

For example, embedders need to configure their surfaces differently if
Impeller is enabled. This configuration must happen before the Dart
isolate is launched. As a result, the framework's feature flags is not a
viable solution for this scenario for now. For these kinds of scenarios,
we should continue to use platform-specific configuration like the
`AndroidManifest.xml` or `Info.plist` files.

This is a fixable limitation, we just need to invest in this plumbing :)

### Tree shaking

Feature flags are not designed to help tree shaking. For example, you
cannot conditionally import Dart code depending on the enabled feature
flags. Code that is feature flagged off will still be imported into
user's apps.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
github-merge-queue bot pushed a commit that referenced this pull request Jul 15, 2025
This relands #168437. The google3
fixes were landed in: #171547,
https://critique.corp.google.com/cl/781275353,
#171933.

This PR is split into two commits:

1. d625379, code from
#168437 without any changes
2. f35d29e, updates the PR to omit
obvious types.

Original PR description:

## Motivation

We'd like to let users opt-in to experimental features so that they can
give early feedback while we iterate on the feature. For example:

Example feature flags:

1. Android sensitive content:
#158473. When enabled, Flutter
will tell Android when the view contains sensitive content like a
password.
3. Desktop multi-window. When enabled, Flutter will use child windows to
allow things like a context menu to "escape" outside of the current
window.

### Use case

Users will be able to turn on features by:

* **Option 1**: Run `flutter config --enable-my-feature`. This enables
the feature for all projects on the machine
* **Option 2**: Add `enable-my-feature: true` in their `pubspec.yaml`,
under the `flutter` section. This would enable the for a single project
on the machine.

Turning on a feature affects _both_ development-time (`flutter run`) and
deployment-time (`flutter build x`). For example, I can `flutter build
windows` to create an `.exe` with multi-window features enabled.

## How this works

This adds a new
[`runtimeId`](https://github.com/flutter/flutter/pull/168437/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R243-R247)
property to the tool's `Feature` class. If a feature is on and has a
`runtimeId`, its `runtimeId` will be [stamped into the Dart application
as a Dart
define](https://github.com/flutter/flutter/pull/168437/files#diff-bd662448bdc2e6f50e47cd3b20b22b41a828561bce65cb4d54ea4f5011cc604eR293-R327).
The framework uses this Dart define to [determine which features are
enabled](https://github.com/flutter/flutter/pull/168437/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45).

### Multi-window example

#168697 shows how this new
feature flag system can be used to add a multi-window feature flag:

1. It adds a new [multi-window
feature](https://github.com/flutter/flutter/pull/168697/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R189-R198)
to the Flutter tool. This can be turned on using `flutter config
--enable-multi-window` or by putting `enable-multi-window: true` in an
app's .pubspec, under the `flutter` section.
2. It adds a new
[`isMultiWindowEnabled`](https://github.com/flutter/flutter/pull/168697/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45R7-R11)
property to the framework.
4. The Material library can use this new property to determine whether
it should create a new window.
[Example](https://github.com/flutter/flutter/pull/168697/files#diff-2cbc1634ed6b61d61dfa090e7bfbbb7c60b74c8abc3a28df6f79eee691fd1b73).

## Limitations

### Tool and framework only

For now, these feature flags are available only to the Flutter tool and
Flutter framework. The flags are not automatically available to the
embedder or the engine.

For example, embedders need to configure their surfaces differently if
Impeller is enabled. This configuration must happen before the Dart
isolate is launched. As a result, the framework's feature flags is not a
viable solution for this scenario for now. For these kinds of scenarios,
we should continue to use platform-specific configuration like the
`AndroidManifest.xml` or `Info.plist` files.

This is a fixable limitation, we just need to invest in this plumbing :)

### Tree shaking

Feature flags are not designed to help tree shaking. For example, you
cannot conditionally import Dart code depending on the enabled feature
flags. Code that is feature flagged off will still be imported into
user's apps.
mboetger pushed a commit to mboetger/flutter that referenced this pull request Jul 21, 2025
## Motivation

We'd like to let users opt-in to experimental features so that they can
give early feedback while we iterate on the feature. For example:

Example feature flags:

1. Android sensitive content:
flutter#158473. When enabled, Flutter
will tell Android when the view contains sensitive content like a
password.
2. Desktop multi-window. When enabled, Flutter will use child windows to
allow things like a context menu to "escape" outside of the current
window.

### Use case

Users will be able to turn on features by:

* **Option 1**: Run `flutter config --enable-my-feature`. This enables
the feature for all projects on the machine
* **Option 2**: Add `enable-my-feature: true` in their `pubspec.yaml`,
under the `flutter` section. This would enable the for a single project
on the machine.

Turning on a feature affects _both_ development-time (`flutter run`) and
deployment-time (`flutter build x`). For example, I can `flutter build
windows` to create an `.exe` with multi-window features enabled.

## How this works

This adds a new
[`runtimeId`](https://github.com/flutter/flutter/pull/168437/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R243-R247)
property to the tool's `Feature` class. If a feature is on and has a
`runtimeId`, its `runtimeId` will be [stamped into the Dart application
as a Dart
define](https://github.com/flutter/flutter/pull/168437/files#diff-bd662448bdc2e6f50e47cd3b20b22b41a828561bce65cb4d54ea4f5011cc604eR293-R327).
The framework uses this Dart define to [determine which features are
enabled](https://github.com/flutter/flutter/pull/168437/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45).

### Multi-window example

flutter#168697 shows how this new
feature flag system can be used to add a multi-window feature flag:

1. It adds a new [multi-window
feature](https://github.com/flutter/flutter/pull/168697/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R189-R198)
to the Flutter tool. This can be turned on using `flutter config
--enable-multi-window` or by putting `enable-multi-window: true` in an
app's .pubspec, under the `flutter` section.
2. It adds a new
[`isMultiWindowEnabled`](https://github.com/flutter/flutter/pull/168697/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45R7-R11)
property to the framework.
3. The Material library can use this new property to determine whether
it should create a new window.
[Example](https://github.com/flutter/flutter/pull/168697/files#diff-2cbc1634ed6b61d61dfa090e7bfbbb7c60b74c8abc3a28df6f79eee691fd1b73).

## Limitations

### Tool and framework only

For now, these feature flags are available only to the Flutter tool and
Flutter framework. The flags are not automatically available to the
embedder or the engine.

For example, embedders need to configure their surfaces differently if
Impeller is enabled. This configuration must happen before the Dart
isolate is launched. As a result, the framework's feature flags is not a
viable solution for this scenario for now. For these kinds of scenarios,
we should continue to use platform-specific configuration like the
`AndroidManifest.xml` or `Info.plist` files.

This is a fixable limitation, we just need to invest in this plumbing :)

### Tree shaking

Feature flags are not designed to help tree shaking. For example, you
cannot conditionally import Dart code depending on the enabled feature
flags. Code that is feature flagged off will still be imported into
user's apps.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
mboetger pushed a commit to mboetger/flutter that referenced this pull request Jul 21, 2025
…r#171542)

<!-- start_original_pr_link -->
Reverts: flutter#168437
<!-- end_original_pr_link -->
<!-- start_initiating_author -->
Initiated by: chingjun
<!-- end_initiating_author -->
<!-- start_revert_reason -->
Reason for reverting: broke internal customers
<!-- end_revert_reason -->
<!-- start_original_pr_author -->
Original PR Author: loic-sharma
<!-- end_original_pr_author -->

<!-- start_reviewers -->
Reviewed By: {bkonyi, justinmc, matanlurey}
<!-- end_reviewers -->

<!-- start_revert_body -->
This change reverts the following previous change:
## Motivation

We'd like to let users opt-in to experimental features so that they can
give early feedback while we iterate on the feature. For example:

Example feature flags:

1. Android sensitive content:
flutter#158473. When enabled, Flutter
will tell Android when the view contains sensitive content like a
password.
2. Desktop multi-window. When enabled, Flutter will use child windows to
allow things like a context menu to "escape" outside of the current
window.

### Use case

Users will be able to turn on features by:

* **Option 1**: Run `flutter config --enable-my-feature`. This enables
the feature for all projects on the machine
* **Option 2**: Add `enable-my-feature: true` in their `pubspec.yaml`,
under the `flutter` section. This would enable the for a single project
on the machine.

Turning on a feature affects _both_ development-time (`flutter run`) and
deployment-time (`flutter build x`). For example, I can `flutter build
windows` to create an `.exe` with multi-window features enabled.

## How this works

This adds a new
[`runtimeId`](https://github.com/flutter/flutter/pull/168437/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R243-R247)
property to the tool's `Feature` class. If a feature is on and has a
`runtimeId`, its `runtimeId` will be [stamped into the Dart application
as a Dart
define](https://github.com/flutter/flutter/pull/168437/files#diff-bd662448bdc2e6f50e47cd3b20b22b41a828561bce65cb4d54ea4f5011cc604eR293-R327).
The framework uses this Dart define to [determine which features are
enabled](https://github.com/flutter/flutter/pull/168437/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45).

### Multi-window example

flutter#168697 shows how this new
feature flag system can be used to add a multi-window feature flag:

1. It adds a new [multi-window
feature](https://github.com/flutter/flutter/pull/168697/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R189-R198)
to the Flutter tool. This can be turned on using `flutter config
--enable-multi-window` or by putting `enable-multi-window: true` in an
app's .pubspec, under the `flutter` section.
2. It adds a new
[`isMultiWindowEnabled`](https://github.com/flutter/flutter/pull/168697/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45R7-R11)
property to the framework.
3. The Material library can use this new property to determine whether
it should create a new window.
[Example](https://github.com/flutter/flutter/pull/168697/files#diff-2cbc1634ed6b61d61dfa090e7bfbbb7c60b74c8abc3a28df6f79eee691fd1b73).

## Limitations

### Tool and framework only

For now, these feature flags are available only to the Flutter tool and
Flutter framework. The flags are not automatically available to the
embedder or the engine.

For example, embedders need to configure their surfaces differently if
Impeller is enabled. This configuration must happen before the Dart
isolate is launched. As a result, the framework's feature flags is not a
viable solution for this scenario for now. For these kinds of scenarios,
we should continue to use platform-specific configuration like the
`AndroidManifest.xml` or `Info.plist` files.

This is a fixable limitation, we just need to invest in this plumbing :)

### Tree shaking

Feature flags are not designed to help tree shaking. For example, you
cannot conditionally import Dart code depending on the enabled feature
flags. Code that is feature flagged off will still be imported into
user's apps.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

<!-- end_revert_body -->

Co-authored-by: auto-submit[bot] <[email protected]>
azatech pushed a commit to azatech/flutter that referenced this pull request Jul 28, 2025
This relands flutter#168437. The google3
fixes were landed in: flutter#171547,
https://critique.corp.google.com/cl/781275353,
flutter#171933.

This PR is split into two commits:

1. d625379, code from
flutter#168437 without any changes
2. f35d29e, updates the PR to omit
obvious types.

Original PR description:

## Motivation

We'd like to let users opt-in to experimental features so that they can
give early feedback while we iterate on the feature. For example:

Example feature flags:

1. Android sensitive content:
flutter#158473. When enabled, Flutter
will tell Android when the view contains sensitive content like a
password.
3. Desktop multi-window. When enabled, Flutter will use child windows to
allow things like a context menu to "escape" outside of the current
window.

### Use case

Users will be able to turn on features by:

* **Option 1**: Run `flutter config --enable-my-feature`. This enables
the feature for all projects on the machine
* **Option 2**: Add `enable-my-feature: true` in their `pubspec.yaml`,
under the `flutter` section. This would enable the for a single project
on the machine.

Turning on a feature affects _both_ development-time (`flutter run`) and
deployment-time (`flutter build x`). For example, I can `flutter build
windows` to create an `.exe` with multi-window features enabled.

## How this works

This adds a new
[`runtimeId`](https://github.com/flutter/flutter/pull/168437/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R243-R247)
property to the tool's `Feature` class. If a feature is on and has a
`runtimeId`, its `runtimeId` will be [stamped into the Dart application
as a Dart
define](https://github.com/flutter/flutter/pull/168437/files#diff-bd662448bdc2e6f50e47cd3b20b22b41a828561bce65cb4d54ea4f5011cc604eR293-R327).
The framework uses this Dart define to [determine which features are
enabled](https://github.com/flutter/flutter/pull/168437/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45).

### Multi-window example

flutter#168697 shows how this new
feature flag system can be used to add a multi-window feature flag:

1. It adds a new [multi-window
feature](https://github.com/flutter/flutter/pull/168697/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R189-R198)
to the Flutter tool. This can be turned on using `flutter config
--enable-multi-window` or by putting `enable-multi-window: true` in an
app's .pubspec, under the `flutter` section.
2. It adds a new
[`isMultiWindowEnabled`](https://github.com/flutter/flutter/pull/168697/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45R7-R11)
property to the framework.
4. The Material library can use this new property to determine whether
it should create a new window.
[Example](https://github.com/flutter/flutter/pull/168697/files#diff-2cbc1634ed6b61d61dfa090e7bfbbb7c60b74c8abc3a28df6f79eee691fd1b73).

## Limitations

### Tool and framework only

For now, these feature flags are available only to the Flutter tool and
Flutter framework. The flags are not automatically available to the
embedder or the engine.

For example, embedders need to configure their surfaces differently if
Impeller is enabled. This configuration must happen before the Dart
isolate is launched. As a result, the framework's feature flags is not a
viable solution for this scenario for now. For these kinds of scenarios,
we should continue to use platform-specific configuration like the
`AndroidManifest.xml` or `Info.plist` files.

This is a fixable limitation, we just need to invest in this plumbing :)

### Tree shaking

Feature flags are not designed to help tree shaking. For example, you
cannot conditionally import Dart code depending on the enabled feature
flags. Code that is feature flagged off will still be imported into
user's apps.
romanejaquez pushed a commit to romanejaquez/flutter that referenced this pull request Aug 14, 2025
> [!NOTE]  
> This PR lands flutter#158473 (closed
due to confusing git-isms). Please see that PR for all comments and
discussion on landing this feature.

### Overview
Implements setting content sensitivity on Android via a new widget
called `SensitiveContent` that currently will only function on Android.

### How it's implemented
There are three different content sensitivity levels:
[`autoSensitive`](https://developer.android.com/reference/android/view/View#CONTENT_SENSITIVITY_AUTO),
[`sensitive`](https://developer.android.com/reference/android/view/View#CONTENT_SENSITIVITY_SENSITIVE),
[`notSensitive`](https://developer.android.com/reference/android/view/View#CONTENT_SENSITIVITY_NOT_SENSITIVE).
All except `autoSensitive` behave as their Android counterpart level
behaves (see links on the levels); for Flutter, `autoSensitive` will
behave the same as `notSensitive` though it is implemented as if it
works (see flutter#160879 for more
details.

In any given Flutter view, each `SensitiveContent` widget sets a content
sensitivity level and there may be multiple `SensitiveContent` widgets
in the widget tree at a time, but only the most severe content
sensitivity level in the tree will be maintained in order to ensure
secure content remains obscured during screen projection. This means
that even widgets that are not visible may still impact the overall
content sensitivity of the entire Flutter view (see
flutter#160051 for more details).

Under the hood, the implementation uses method channels. Theoretically,
this could cause a delay in setting the content sensitivity level and a
Flutter view being obscured during screen projection on time, so we will
investigate using JNIgen in the future (see
flutter#160050 for more details). Over
the method channel, the framework will send a Flutter view ID and
content sensitivity level to the engine to set the expected content
sensitivity level for the view.

### Timeline

#### Required for feature shipping
- Land this PR with the method channel implementation of content
sensitivity.

#### Short-term follow up tasks
- Ensure every `FlutterView` has a unique ID for the native
`SensitiveContentPlugin` to identify them by:
flutter#162685
- Add `SensitiveContent` widget to relevant Flutter widgets:
flutter#167302

#### Long-term follow up tasks
- Convert this PR to use JNIgen:
flutter#160050. Verify no frames are
dropped, revealing sensitive content during media projection:
flutter#164820.

#### Stretch goals
- Make the `SensitiveContent` widget sensitive to the visibility of
child widgets: flutter#160051
- Implement `autoSensitivity` mode:
flutter#160879
- Investigate backwards compatibility for APIs < 35:
flutter#159208

### Open questions
- ~Is it okay to hard-code the `FlutterActivity` and `FlutterFragment`
view IDs? Does this impact add-to-app in any way? I assume we would need
extenders of those classes to use a different view ID than the hardcoded
one.~ Approach not used.
- ~Should we expose `autoSensitive` for now? If so, what behavior should
it have?~ Exposing `autoSensitive` as if it works.

### One way to test this PR (rough steps)
1. Create a Flutter app that has some number of `SensitiveContent`
widgets.
9. Run the app on an emulator/device that runs API 35 and has the Google
meets app downloaded.
10. Start a Google meets meeting and screen share (works with full
screen and single app mode).
11. Join the Google meets meeting from another device and witness a
blacked out screen if content has been marked sensitive.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
ksokolovskyi pushed a commit to ksokolovskyi/flutter that referenced this pull request Aug 19, 2025
This relands flutter#168437. The google3
fixes were landed in: flutter#171547,
https://critique.corp.google.com/cl/781275353,
flutter#171933.

This PR is split into two commits:

1. d625379, code from
flutter#168437 without any changes
2. f35d29e, updates the PR to omit
obvious types.

Original PR description:

## Motivation

We'd like to let users opt-in to experimental features so that they can
give early feedback while we iterate on the feature. For example:

Example feature flags:

1. Android sensitive content:
flutter#158473. When enabled, Flutter
will tell Android when the view contains sensitive content like a
password.
3. Desktop multi-window. When enabled, Flutter will use child windows to
allow things like a context menu to "escape" outside of the current
window.

### Use case

Users will be able to turn on features by:

* **Option 1**: Run `flutter config --enable-my-feature`. This enables
the feature for all projects on the machine
* **Option 2**: Add `enable-my-feature: true` in their `pubspec.yaml`,
under the `flutter` section. This would enable the for a single project
on the machine.

Turning on a feature affects _both_ development-time (`flutter run`) and
deployment-time (`flutter build x`). For example, I can `flutter build
windows` to create an `.exe` with multi-window features enabled.

## How this works

This adds a new
[`runtimeId`](https://github.com/flutter/flutter/pull/168437/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R243-R247)
property to the tool's `Feature` class. If a feature is on and has a
`runtimeId`, its `runtimeId` will be [stamped into the Dart application
as a Dart
define](https://github.com/flutter/flutter/pull/168437/files#diff-bd662448bdc2e6f50e47cd3b20b22b41a828561bce65cb4d54ea4f5011cc604eR293-R327).
The framework uses this Dart define to [determine which features are
enabled](https://github.com/flutter/flutter/pull/168437/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45).

### Multi-window example

flutter#168697 shows how this new
feature flag system can be used to add a multi-window feature flag:

1. It adds a new [multi-window
feature](https://github.com/flutter/flutter/pull/168697/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R189-R198)
to the Flutter tool. This can be turned on using `flutter config
--enable-multi-window` or by putting `enable-multi-window: true` in an
app's .pubspec, under the `flutter` section.
2. It adds a new
[`isMultiWindowEnabled`](https://github.com/flutter/flutter/pull/168697/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45R7-R11)
property to the framework.
4. The Material library can use this new property to determine whether
it should create a new window.
[Example](https://github.com/flutter/flutter/pull/168697/files#diff-2cbc1634ed6b61d61dfa090e7bfbbb7c60b74c8abc3a28df6f79eee691fd1b73).

## Limitations

### Tool and framework only

For now, these feature flags are available only to the Flutter tool and
Flutter framework. The flags are not automatically available to the
embedder or the engine.

For example, embedders need to configure their surfaces differently if
Impeller is enabled. This configuration must happen before the Dart
isolate is launched. As a result, the framework's feature flags is not a
viable solution for this scenario for now. For these kinds of scenarios,
we should continue to use platform-specific configuration like the
`AndroidManifest.xml` or `Info.plist` files.

This is a fixable limitation, we just need to invest in this plumbing :)

### Tree shaking

Feature flags are not designed to help tree shaking. For example, you
cannot conditionally import Dart code depending on the enabled feature
flags. Code that is feature flagged off will still be imported into
user's apps.
mboetger pushed a commit to mboetger/flutter that referenced this pull request Sep 18, 2025
This relands flutter#168437. The google3
fixes were landed in: flutter#171547,
https://critique.corp.google.com/cl/781275353,
flutter#171933.

This PR is split into two commits:

1. d625379, code from
flutter#168437 without any changes
2. f35d29e, updates the PR to omit
obvious types.

Original PR description:

## Motivation

We'd like to let users opt-in to experimental features so that they can
give early feedback while we iterate on the feature. For example:

Example feature flags:

1. Android sensitive content:
flutter#158473. When enabled, Flutter
will tell Android when the view contains sensitive content like a
password.
3. Desktop multi-window. When enabled, Flutter will use child windows to
allow things like a context menu to "escape" outside of the current
window.

### Use case

Users will be able to turn on features by:

* **Option 1**: Run `flutter config --enable-my-feature`. This enables
the feature for all projects on the machine
* **Option 2**: Add `enable-my-feature: true` in their `pubspec.yaml`,
under the `flutter` section. This would enable the for a single project
on the machine.

Turning on a feature affects _both_ development-time (`flutter run`) and
deployment-time (`flutter build x`). For example, I can `flutter build
windows` to create an `.exe` with multi-window features enabled.

## How this works

This adds a new
[`runtimeId`](https://github.com/flutter/flutter/pull/168437/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R243-R247)
property to the tool's `Feature` class. If a feature is on and has a
`runtimeId`, its `runtimeId` will be [stamped into the Dart application
as a Dart
define](https://github.com/flutter/flutter/pull/168437/files#diff-bd662448bdc2e6f50e47cd3b20b22b41a828561bce65cb4d54ea4f5011cc604eR293-R327).
The framework uses this Dart define to [determine which features are
enabled](https://github.com/flutter/flutter/pull/168437/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45).

### Multi-window example

flutter#168697 shows how this new
feature flag system can be used to add a multi-window feature flag:

1. It adds a new [multi-window
feature](https://github.com/flutter/flutter/pull/168697/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R189-R198)
to the Flutter tool. This can be turned on using `flutter config
--enable-multi-window` or by putting `enable-multi-window: true` in an
app's .pubspec, under the `flutter` section.
2. It adds a new
[`isMultiWindowEnabled`](https://github.com/flutter/flutter/pull/168697/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45R7-R11)
property to the framework.
4. The Material library can use this new property to determine whether
it should create a new window.
[Example](https://github.com/flutter/flutter/pull/168697/files#diff-2cbc1634ed6b61d61dfa090e7bfbbb7c60b74c8abc3a28df6f79eee691fd1b73).

## Limitations

### Tool and framework only

For now, these feature flags are available only to the Flutter tool and
Flutter framework. The flags are not automatically available to the
embedder or the engine.

For example, embedders need to configure their surfaces differently if
Impeller is enabled. This configuration must happen before the Dart
isolate is launched. As a result, the framework's feature flags is not a
viable solution for this scenario for now. For these kinds of scenarios,
we should continue to use platform-specific configuration like the
`AndroidManifest.xml` or `Info.plist` files.

This is a fixable limitation, we just need to invest in this plumbing :)

### Tree shaking

Feature flags are not designed to help tree shaking. For example, you
cannot conditionally import Dart code depending on the enabled feature
flags. Code that is feature flagged off will still be imported into
user's apps.
lucaantonelli pushed a commit to lucaantonelli/flutter that referenced this pull request Nov 21, 2025
## Motivation

We'd like to let users opt-in to experimental features so that they can
give early feedback while we iterate on the feature. For example:

Example feature flags:

1. Android sensitive content:
flutter#158473. When enabled, Flutter
will tell Android when the view contains sensitive content like a
password.
2. Desktop multi-window. When enabled, Flutter will use child windows to
allow things like a context menu to "escape" outside of the current
window.

### Use case

Users will be able to turn on features by:

* **Option 1**: Run `flutter config --enable-my-feature`. This enables
the feature for all projects on the machine
* **Option 2**: Add `enable-my-feature: true` in their `pubspec.yaml`,
under the `flutter` section. This would enable the for a single project
on the machine.

Turning on a feature affects _both_ development-time (`flutter run`) and
deployment-time (`flutter build x`). For example, I can `flutter build
windows` to create an `.exe` with multi-window features enabled.

## How this works

This adds a new
[`runtimeId`](https://github.com/flutter/flutter/pull/168437/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R243-R247)
property to the tool's `Feature` class. If a feature is on and has a
`runtimeId`, its `runtimeId` will be [stamped into the Dart application
as a Dart
define](https://github.com/flutter/flutter/pull/168437/files#diff-bd662448bdc2e6f50e47cd3b20b22b41a828561bce65cb4d54ea4f5011cc604eR293-R327).
The framework uses this Dart define to [determine which features are
enabled](https://github.com/flutter/flutter/pull/168437/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45).

### Multi-window example

flutter#168697 shows how this new
feature flag system can be used to add a multi-window feature flag:

1. It adds a new [multi-window
feature](https://github.com/flutter/flutter/pull/168697/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R189-R198)
to the Flutter tool. This can be turned on using `flutter config
--enable-multi-window` or by putting `enable-multi-window: true` in an
app's .pubspec, under the `flutter` section.
2. It adds a new
[`isMultiWindowEnabled`](https://github.com/flutter/flutter/pull/168697/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45R7-R11)
property to the framework.
3. The Material library can use this new property to determine whether
it should create a new window.
[Example](https://github.com/flutter/flutter/pull/168697/files#diff-2cbc1634ed6b61d61dfa090e7bfbbb7c60b74c8abc3a28df6f79eee691fd1b73).

## Limitations

### Tool and framework only

For now, these feature flags are available only to the Flutter tool and
Flutter framework. The flags are not automatically available to the
embedder or the engine.

For example, embedders need to configure their surfaces differently if
Impeller is enabled. This configuration must happen before the Dart
isolate is launched. As a result, the framework's feature flags is not a
viable solution for this scenario for now. For these kinds of scenarios,
we should continue to use platform-specific configuration like the
`AndroidManifest.xml` or `Info.plist` files.

This is a fixable limitation, we just need to invest in this plumbing :)

### Tree shaking

Feature flags are not designed to help tree shaking. For example, you
cannot conditionally import Dart code depending on the enabled feature
flags. Code that is feature flagged off will still be imported into
user's apps.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
lucaantonelli pushed a commit to lucaantonelli/flutter that referenced this pull request Nov 21, 2025
…r#171542)

<!-- start_original_pr_link -->
Reverts: flutter#168437
<!-- end_original_pr_link -->
<!-- start_initiating_author -->
Initiated by: chingjun
<!-- end_initiating_author -->
<!-- start_revert_reason -->
Reason for reverting: broke internal customers
<!-- end_revert_reason -->
<!-- start_original_pr_author -->
Original PR Author: loic-sharma
<!-- end_original_pr_author -->

<!-- start_reviewers -->
Reviewed By: {bkonyi, justinmc, matanlurey}
<!-- end_reviewers -->

<!-- start_revert_body -->
This change reverts the following previous change:
## Motivation

We'd like to let users opt-in to experimental features so that they can
give early feedback while we iterate on the feature. For example:

Example feature flags:

1. Android sensitive content:
flutter#158473. When enabled, Flutter
will tell Android when the view contains sensitive content like a
password.
2. Desktop multi-window. When enabled, Flutter will use child windows to
allow things like a context menu to "escape" outside of the current
window.

### Use case

Users will be able to turn on features by:

* **Option 1**: Run `flutter config --enable-my-feature`. This enables
the feature for all projects on the machine
* **Option 2**: Add `enable-my-feature: true` in their `pubspec.yaml`,
under the `flutter` section. This would enable the for a single project
on the machine.

Turning on a feature affects _both_ development-time (`flutter run`) and
deployment-time (`flutter build x`). For example, I can `flutter build
windows` to create an `.exe` with multi-window features enabled.

## How this works

This adds a new
[`runtimeId`](https://github.com/flutter/flutter/pull/168437/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R243-R247)
property to the tool's `Feature` class. If a feature is on and has a
`runtimeId`, its `runtimeId` will be [stamped into the Dart application
as a Dart
define](https://github.com/flutter/flutter/pull/168437/files#diff-bd662448bdc2e6f50e47cd3b20b22b41a828561bce65cb4d54ea4f5011cc604eR293-R327).
The framework uses this Dart define to [determine which features are
enabled](https://github.com/flutter/flutter/pull/168437/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45).

### Multi-window example

flutter#168697 shows how this new
feature flag system can be used to add a multi-window feature flag:

1. It adds a new [multi-window
feature](https://github.com/flutter/flutter/pull/168697/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R189-R198)
to the Flutter tool. This can be turned on using `flutter config
--enable-multi-window` or by putting `enable-multi-window: true` in an
app's .pubspec, under the `flutter` section.
2. It adds a new
[`isMultiWindowEnabled`](https://github.com/flutter/flutter/pull/168697/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45R7-R11)
property to the framework.
3. The Material library can use this new property to determine whether
it should create a new window.
[Example](https://github.com/flutter/flutter/pull/168697/files#diff-2cbc1634ed6b61d61dfa090e7bfbbb7c60b74c8abc3a28df6f79eee691fd1b73).

## Limitations

### Tool and framework only

For now, these feature flags are available only to the Flutter tool and
Flutter framework. The flags are not automatically available to the
embedder or the engine.

For example, embedders need to configure their surfaces differently if
Impeller is enabled. This configuration must happen before the Dart
isolate is launched. As a result, the framework's feature flags is not a
viable solution for this scenario for now. For these kinds of scenarios,
we should continue to use platform-specific configuration like the
`AndroidManifest.xml` or `Info.plist` files.

This is a fixable limitation, we just need to invest in this plumbing :)

### Tree shaking

Feature flags are not designed to help tree shaking. For example, you
cannot conditionally import Dart code depending on the enabled feature
flags. Code that is feature flagged off will still be imported into
user's apps.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

<!-- end_revert_body -->

Co-authored-by: auto-submit[bot] <[email protected]>
lucaantonelli pushed a commit to lucaantonelli/flutter that referenced this pull request Nov 21, 2025
This relands flutter#168437. The google3
fixes were landed in: flutter#171547,
https://critique.corp.google.com/cl/781275353,
flutter#171933.

This PR is split into two commits:

1. d625379, code from
flutter#168437 without any changes
2. f35d29e, updates the PR to omit
obvious types.

Original PR description:

## Motivation

We'd like to let users opt-in to experimental features so that they can
give early feedback while we iterate on the feature. For example:

Example feature flags:

1. Android sensitive content:
flutter#158473. When enabled, Flutter
will tell Android when the view contains sensitive content like a
password.
3. Desktop multi-window. When enabled, Flutter will use child windows to
allow things like a context menu to "escape" outside of the current
window.

### Use case

Users will be able to turn on features by:

* **Option 1**: Run `flutter config --enable-my-feature`. This enables
the feature for all projects on the machine
* **Option 2**: Add `enable-my-feature: true` in their `pubspec.yaml`,
under the `flutter` section. This would enable the for a single project
on the machine.

Turning on a feature affects _both_ development-time (`flutter run`) and
deployment-time (`flutter build x`). For example, I can `flutter build
windows` to create an `.exe` with multi-window features enabled.

## How this works

This adds a new
[`runtimeId`](https://github.com/flutter/flutter/pull/168437/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R243-R247)
property to the tool's `Feature` class. If a feature is on and has a
`runtimeId`, its `runtimeId` will be [stamped into the Dart application
as a Dart
define](https://github.com/flutter/flutter/pull/168437/files#diff-bd662448bdc2e6f50e47cd3b20b22b41a828561bce65cb4d54ea4f5011cc604eR293-R327).
The framework uses this Dart define to [determine which features are
enabled](https://github.com/flutter/flutter/pull/168437/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45).

### Multi-window example

flutter#168697 shows how this new
feature flag system can be used to add a multi-window feature flag:

1. It adds a new [multi-window
feature](https://github.com/flutter/flutter/pull/168697/files#diff-0ded384225f19a4c34d43c7c11f7cb084ff3db947cfa82d8d52fc94c112bb2a7R189-R198)
to the Flutter tool. This can be turned on using `flutter config
--enable-multi-window` or by putting `enable-multi-window: true` in an
app's .pubspec, under the `flutter` section.
2. It adds a new
[`isMultiWindowEnabled`](https://github.com/flutter/flutter/pull/168697/files#diff-c8dbd5cd3103bc5be53c4ac5be8bdb9bf73e10cd5d8e4ac34e737fd1f8602d45R7-R11)
property to the framework.
4. The Material library can use this new property to determine whether
it should create a new window.
[Example](https://github.com/flutter/flutter/pull/168697/files#diff-2cbc1634ed6b61d61dfa090e7bfbbb7c60b74c8abc3a28df6f79eee691fd1b73).

## Limitations

### Tool and framework only

For now, these feature flags are available only to the Flutter tool and
Flutter framework. The flags are not automatically available to the
embedder or the engine.

For example, embedders need to configure their surfaces differently if
Impeller is enabled. This configuration must happen before the Dart
isolate is launched. As a result, the framework's feature flags is not a
viable solution for this scenario for now. For these kinds of scenarios,
we should continue to use platform-specific configuration like the
`AndroidManifest.xml` or `Info.plist` files.

This is a fixable limitation, we just need to invest in this plumbing :)

### Tree shaking

Feature flags are not designed to help tree shaking. For example, you
cannot conditionally import Dart code depending on the enabled feature
flags. Code that is feature flagged off will still be imported into
user's apps.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

engine flutter/engine related. See also e: labels. framework flutter/packages/flutter repository. See also f: labels. platform-android Android applications specifically

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants