This guide describes how to add a single Flutter screen to an
existing Android app. A Flutter screen can be added as a normal,
opaque screen, or as a see-through, translucent screen.
Both options are described in this guide.
Note:
Make sure to use the following import:
import io . flutter . embedding . android . FlutterActivity ;
< ul class =" nav nav - tabs sample - code - tabs " id=" default - activity - launch - language " role=" tablist ">
<li class=" nav - item ">
<a class=" nav - link active " id=" default - activity - launch - java " href=" #default - activity - launch - java - tab " role=" tab " aria-controls=" default - activity - launch - java " aria-selected=" true ">Java</a>
</li>
<li class=" nav - item ">
<a class=" nav - link " id=" default - activity - launch - kotlin " href=" #default - activity - launch - kotlin - tab " role=" tab " aria-controls=" default - activity - launch - kotlin " aria-selected=" true ">Kotlin</a>
</li>
</ul>
<div class=" tab - content ">
<div class=" tab - pane active " id=" default - activity - launch - java - tab " role=" tabpanel " aria-labelledby=" default - activity - launch - java - tab " markdown=" 1 ">
<!--code-excerpt " ExistingActivity . java " title-->
```java
myButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(
FlutterActivity.createDefaultIntent(currentActivity)
);
}
});
</div>
myButton . setOnClickListener {
startActivity (
FlutterActivity . createDefaultIntent ( this )
)
}
</div>
The previous example assumes that your Dart entrypoint
is called main()
, and your initial Flutter route is ‘/’.
The Dart entrypoint can’t be changed using Intent
,
but the initial route can be changed using Intent
.
The following example demonstrates how to launch a
FlutterActivity
that initially renders a custom
route in Flutter.
myButton . addOnClickListener ( new OnClickListener () {
@Override
public void onClick ( View v ) {
startActivity (
FlutterActivity
. withNewEngine ()
. initialRoute ( "/my_route" )
. build ( currentActivity )
);
}
});
myButton . setOnClickListener {
startActivity (
FlutterActivity
. withNewEngine ()
. initialRoute ( "/my_route" )
. build ( this )
)
}
Replace "/my_route"
with your desired initial route.
The use of the withNewEngine()
factory method
configures a FlutterActivity
that internally create
its own FlutterEngine
instance. This comes with a
non-trivial initialization time. The alternative approach
is to instruct FlutterActivity
to use a pre-warmed,
cached FlutterEngine
, which minimizes Flutter’s
initialization time. That approach is discussed next.
Step 3: (Optional) Use a cached FlutterEngine
Every FlutterActivity
creates its own FlutterEngine
by default. Each FlutterEngine
has a non-trivial
warm-up time. This means that launching a standard
FlutterActivity
comes with a brief delay before your Flutter
experience becomes visible. To minimize this delay,
you can warm up a FlutterEngine
before arriving at
your FlutterActivity
, and then you can use
your pre-warmed FlutterEngine
instead.
To pre-warm a FlutterEngine
, find a reasonable
location in your app to instantiate a FlutterEngine
.
The following example arbitrarily pre-warms a
FlutterEngine
in the Application
class:
public class MyApplication extends Application {
@Override
public void onCreate () {
super . onCreate ();
// Instantiate a FlutterEngine.
flutterEngine = new FlutterEngine ( this );
// Start executing Dart code to pre-warm the FlutterEngine.
flutterEngine . getDartExecutor (). executeDartEntrypoint (
DartEntrypoint . createDefault ()
);
// Cache the FlutterEngine to be used by FlutterActivity.
FlutterEngineCache
. getInstance ()
. put ( "my_engine_id" , flutterEngine );
}
}
class MyApplication : Application () {
lateinit var flutterEngine : FlutterEngine
override fun onCreate () {
super . onCreate ()
// Instantiate a FlutterEngine.
flutterEngine = FlutterEngine ( this )
// Start executing Dart code to pre-warm the FlutterEngine.
flutterEngine . dartExecutor . executeDartEntrypoint (
DartExecutor . DartEntrypoint . createDefault ()
)
// Cache the FlutterEngine to be used by FlutterActivity.
FlutterEngineCache
. getInstance ()
. put ( "my_engine_id" , flutterEngine )
}
}
The ID passed to the FlutterEngineCache
can be whatever you want.
Make sure that you pass the same ID to any FlutterActivity
or FlutterFragment
that should use the cached FlutterEngine
.
Using FlutterActivity
with a cached FlutterEngine
is discussed next.
Note:
To warm up a FlutterEngine
, you must execute a Dart
entrypoint. Keep in mind that the moment
executeDartEntrypoint()
is invoked,
your Dart entrypoint method begins executing.
If your Dart entrypoint invokes runApp()
to run a Flutter app, then your Flutter app behaves as if it
were running in a window of zero size until this
FlutterEngine
is attached to a FlutterActivity
,
FlutterFragment
, or FlutterView
. Make sure that your app
behaves appropriately between the time you warm it up and
the time you display Flutter content.
With a pre-warmed, cached FlutterEngine
, you now need
to instruct your FlutterActivity
to use the cached
FlutterEngine
instead of creating a new one.
To accomplish this, use FlutterActivity
’s withCachedEngine()
builder:
myButton . addOnClickListener ( new OnClickListener () {
@Override
public void onClick ( View v ) {
startActivity (
FlutterActivity
. withCachedEngine ( "my_engine_id" )
. build ( currentActivity )
);
}
});
myButton . setOnClickListener {
startActivity (
FlutterActivity
. withCachedEngine ( "my_engine_id" )
. build ( this )
)
}
When using the withCachedEngine()
factory method,
pass the same ID that you used when caching the desired
FlutterEngine
.
Now, when you launch FlutterActivity
,
there is significantly less delay in
the display of Flutter content.
Note:
When using a cached FlutterEngine
, that FlutterEngine
outlives any
FlutterActivity
or FlutterFragment
that displays it. Keep in
mind that Dart code begins executing as soon as you pre-warm the
FlutterEngine
, and continues executing after the destruction of your
FlutterActivity
/FlutterFragment
. To stop executing and clear resources,
obtain your FlutterEngine
from the FlutterEngineCache
and destroy the
FlutterEngine
with FlutterEngine.destroy()
.
Note:
Runtime performance isn’t the only reason that you might
pre-warm and cache a FlutterEngine
.
A pre-warmed FlutterEngine
executes Dart code independent
from a FlutterActivity
, which allows such a FlutterEngine
to be used to execute arbitrary Dart code at any moment.
Non-UI application logic can be executed in a FlutterEngine
,
like networking and data caching, and in background behavior
within a Service
or elsewhere. When using a FlutterEngine
to execute behavior in the background, be sure to adhere to all
Android restrictions on background execution.
Note:
Flutter’s debug/release builds have drastically different
performance characteristics. To evaluate the performance
of Flutter, use a release build.
Initial route with a cached engine
The concept of an initial route is available when configuring a
FlutterActivity
or a FlutterFragment
with a new FlutterEngine
.
However, FlutterActivity
and FlutterFragment
don’t offer the
concept of an initial route when using a cached engine.
This is because a cached engine is expected to already be
running Dart code, which means it’s too late to configure the
initial route.
Developers that would like their cached engine to begin
with a custom initial route can configure their cached
FlutterEngine
to use a custom initial route just before
executing the Dart entrypoint. The following example
demonstrates the use of an initial route with a cached engine:
public class MyApplication extends Application {
@Override
public void onCreate () {
super . onCreate ();
// Instantiate a FlutterEngine.
flutterEngine = new FlutterEngine ( this );
// Configure an initial route.
flutterEngine . getNavigationChannel (). setInitialRoute ( "your/route/here" );
// Start executing Dart code to pre-warm the FlutterEngine.
flutterEngine . getDartExecutor (). executeDartEntrypoint (
DartEntrypoint . createDefault ()
);
// Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.
FlutterEngineCache
. getInstance ()
. put ( "my_engine_id" , flutterEngine );
}
}
class MyApplication : Application () {
lateinit var flutterEngine : FlutterEngine
override fun onCreate () {
super . onCreate ()
// Instantiate a FlutterEngine.
flutterEngine = FlutterEngine ( this )
// Configure an initial route.
flutterEngine . navigationChannel . initialRoute = "your/route/here" ;
// Start executing Dart code to pre-warm the FlutterEngine.
flutterEngine . dartExecutor . executeDartEntrypoint (
DartExecutor . DartEntrypoint . createDefault ()
)
// Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.
FlutterEngineCache
. getInstance ()
. put ( "my_engine_id" , flutterEngine )
}
}
By setting the initial route of the navigation channel, the associated
FlutterEngine
displays the desired route upon initial execution of the
runApp()
Dart function.
Changing the initial route property of the navigation channel
after the initial execution of runApp()
has no effect.
Developers who would like to use the same FlutterEngine
between different Activity
s and Fragment
s and switch
the route between those displays need to setup a method channel and
explicitly instruct their Dart code to change Navigator
routes.
Add a translucent Flutter screen
Most full-screen Flutter experiences are opaque.
However, some apps would like to deploy a Flutter
screen that looks like a modal, for example,
a dialog or bottom sheet. Flutter supports translucent
FlutterActivity
s out of the box.
To make your FlutterActivity
translucent,
make the following changes to the regular process of
creating and launching a FlutterActivity
.
Step 1: Use a theme with translucency
Android requires a special theme property for Activity
s that render
with a translucent background. Create or update an Android theme with the
following property:
<style name= "MyTheme" parent= "@style/MyParentTheme" >
<item name= "android:windowIsTranslucent" > true</item>
</style>
Then, apply the translucent theme to your FlutterActivity
.
<activity
android:name= "io.flutter.embedding.android.FlutterActivity"
android:theme= "@style/MyTheme"
android:configChanges= "orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated= "true"
android:windowSoftInputMode= "adjustResize"
/>
Your FlutterActivity
now supports translucency.
Next, you need to launch your FlutterActivity
with explicit transparency support.
Step 2: Start FlutterActivity with transparency
To launch your FlutterActivity
with a transparent background,
pass the appropriate BackgroundMode
to the IntentBuilder
:
// Using a new FlutterEngine.
startActivity (
FlutterActivity
. withNewEngine ()
. backgroundMode ( FlutterActivityLaunchConfigs . BackgroundMode . transparent )
. build ( context )
);
// Using a cached FlutterEngine.
startActivity (
FlutterActivity
. withCachedEngine ( "my_engine_id" )
. backgroundMode ( FlutterActivityLaunchConfigs . BackgroundMode . transparent )
. build ( context )
);
// Using a new FlutterEngine.
startActivity (
FlutterActivity
. withNewEngine ()
. backgroundMode ( FlutterActivityLaunchConfigs . BackgroundMode . transparent )
. build ( this )
);
// Using a cached FlutterEngine.
startActivity (
FlutterActivity
. withCachedEngine ( "my_engine_id" )
. backgroundMode ( FlutterActivityLaunchConfigs . BackgroundMode . transparent )
. build ( this )
);
You now have a FlutterActivity
with a transparent background.
Note:
Make sure that your Flutter content also includes a
translucent background. If your Flutter UI paints a
solid background color, then it still appears as
though your FlutterActivity
has an opaque background.