Reputation: 31
i am try to create PIP in flutter app, there is no package or plugin that fulfil my requirement. so i decided to create my own logic. what i am doing i am create a new activity in android and start that activity by method call activity start successfully but when i am try update the UI with setState Ui is not update
my Flutter Code:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
static final GlobalKey<NavigatorState> navigatorKey =
GlobalKey<NavigatorState>();
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
navigatorKey: MyApp.navigatorKey,
title: 'Flutter PIP Demo',
initialRoute: "/",
onGenerateInitialRoutes: (route){
List<Route<dynamic>> pageStack = [];
pageStack.add(MaterialPageRoute(builder: (context) => MyHomePage()));
return pageStack;
},
onGenerateRoute: (route) {
switch (route.name) {
case "/":
return MaterialPageRoute(builder: (BuildContext context) => MyHomePage());
case "live_stream":
return MaterialPageRoute(builder: (BuildContext context) => MyHomePage2(route.arguments as String ));
default:
return MaterialPageRoute(builder: (BuildContext context) => MyHomePage());
}
},
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String name = "PIP";
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: ElevatedButton(
onPressed: () {
setState(() {
name = "clicked";
});
Navigator.pushNamed(
context,
"live_stream",
arguments: "PIP Screen"
);
const MethodChannel("live_stream").invokeMethod("start_new_activity").then((value){
bool result = value as bool;
if(result){
// Navigator.pop(context);
}
});
},
child: Text(name)),
)),
);
}
}
class MyHomePage2 extends StatefulWidget {
final String? name;
const MyHomePage2(this.name, {Key? key}) : super(key: key);
@override
State<MyHomePage2> createState() => _MyHomePage2State();
}
class _MyHomePage2State extends State<MyHomePage2> {
var methodcall = MethodChannel("live_stream");
String? name;
bool con = false;
@override
void initState() {
name = widget.name;
methodcall.setMethodCallHandler((call){
if(call.method == "onBackPressed"){
Navigator.pop(context);
}
return call.arguments;
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: ElevatedButton(onPressed: () {
setState(() {
name = "button clicked";
con = !con;
});
MethodChannel("live_stream").invokeMethod("back_to_main_activity");
}, child: Text(con? "test" : name!)),
)),
);
}
}
my Main Activity:
package com.example.pip
import android.app.PictureInPictureParams
import android.content.Intent
import android.graphics.Rect
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.util.Rational
import android.widget.FrameLayout
import androidx.annotation.RequiresApi
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.FlutterView
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity: FlutterActivity() {
private val CHANNEL = "live_stream"
private var flutterEngine: FlutterEngine? = null
private var flutterView : FlutterView? = null
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
Log.d("flutter_engine", "flutter")
super.configureFlutterEngine(flutterEngine)
this.flutterEngine = flutterEngine
FlutterEngineCache.getInstance().put("main_flutter_engine", flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
.setMethodCallHandler { call, result ->
Log.d("flutter", call.method)
if(call.method == "start_new_activity"){
// val flutterView = FlutterView(this)
// flutterView.detachFromFlutterEngine()
//val intent = Intent(this@MainActivity, NewActivity::class.java)
startActivity(Intent(this@MainActivity, NewActivity::class.java))
result.success(true)
}else{
result.notImplemented()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("Flutter New" , "onCreate")
flutterView = FlutterView(this)
flutterEngine = FlutterEngineCache.getInstance().get("main_flutter_engine")
flutterEngine!!.let {flutterEngine->
flutterEngine.navigationChannel.setInitialRoute("live_stream")
FlutterEngineCache.getInstance().put("main_flutter_engine", flutterEngine)
flutterView!!.attachToFlutterEngine(flutterEngine)
val flutterLayoutView = findViewById<FrameLayout>(R.id.main)
flutterLayoutView.addView(flutterView!!)
}
}
override fun onStart() {
super.onStart()
Log.d("Flutter Main" , "onStart")
}
override fun onResume() {
super.onResume()
Log.d("Flutter Main" , "onResume")
}
override fun onPause() {
super.onPause()
Log.d("Flutter Main" , "onPause")
}
override fun onRestart() {
super.onRestart()
Log.d("Flutter Main" , "onRestart")
}
override fun onStop() {
super.onStop()
Log.d("Flutter Main" , "onStop")
}
override fun onDestroy() {
super.onDestroy()
Log.d("Flutter Main" , "onDestroy")
}
}
my New Activity:
package com.example.pip
import android.app.PictureInPictureParams
import android.content.res.Configuration
import android.graphics.Rect
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.util.Rational
import android.view.Surface
import android.view.SurfaceControl
import android.widget.FrameLayout
import androidx.annotation.RequiresApi
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.FlutterView
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener
import io.flutter.plugin.common.MethodChannel
class NewActivity : FlutterActivity() {
private var flutterEngine: FlutterEngine? = null
private var flutterView : FlutterView? = null
private val CHANNEL = "live_stream"
// @RequiresApi(Build.VERSION_CODES.O)
@RequiresApi(Build.VERSION_CODES.Q)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_new)
Log.d("Flutter New" , "onCreate")
flutterView = FlutterView(this)
flutterEngine = FlutterEngineCache.getInstance().get("main_flutter_engine")
flutterEngine!!.let {
it.navigationChannel.setInitialRoute("live_stream")
FlutterEngineCache.getInstance().put("main_flutter_engine", it)
flutterView!!.attachToFlutterEngine(it)
flutterLi(flutterView!!)
MethodChannel(it.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, _ ->
Log.d("flutter", call.method)
if(call.method == "back_to_main_activity"){
Log.d("flutter", "onBackPressed")
flutterView!!.invalidate()
// flutterView!!.detachFromFlutterEngine()
// flutterView!!.attachToFlutterEngine(flutterEngine!!)
// MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger,CHANNEL).invokeMethod("onBackPressed","Hello")
// finish()
}
}
val flutterLayoutView = findViewById<FrameLayout>(R.id.liveStream)
flutterLayoutView.addView(flutterView!!)
}
}
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration?) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
if(isInPictureInPictureMode){
flutterView!!.attachToFlutterEngine(flutterEngine!!)
}
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onUserLeaveHint() {
FlutterEngineCache.getInstance().put("main_flutter_engine", flutterEngine)
val aspectRatio =
Rational(9, 16) // Set the desired aspect ratio for PiP mode
val sourceRectHint = Rect(0, 0, 400, 300)
val params = PictureInPictureParams.Builder()
params.setAspectRatio(aspectRatio)
params.setSourceRectHint(sourceRectHint)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
params.setAutoEnterEnabled(true)
params.setSeamlessResizeEnabled(true)
}
enterPictureInPictureMode(params.build())
flutterView!!.detachFromFlutterEngine()
super.onUserLeaveHint()
}
private fun flutterLi(flutterView: FlutterView){
flutterView.addFlutterEngineAttachmentListener(object : FlutterView.FlutterEngineAttachmentListener{
override fun onFlutterEngineAttachedToFlutterView(engine: FlutterEngine) {
flutterView.invalidate()
}
override fun onFlutterEngineDetachedFromFlutterView() {
flutterView!!.attachToFlutterEngine(flutterEngine!!)
flutterView.invalidate()
Log.d("Flutter New" , "onFlutterEngineDetachedFromFlutterView")
}
})
}
override fun onStart() {
super.onStart()
Log.d("Flutter New" , "onStart")
}
override fun onResume() {
flutterView!!.attachToFlutterEngine(flutterEngine!!)
super.onResume()
Log.d("Flutter New" , "onResume")
flutterView!!.addOnFirstFrameRenderedListener(object : FlutterUiDisplayListener {
override fun onFlutterUiDisplayed() {
flutterView!!.invalidate()
Log.d("Flutter New" , "onFlutterUiDisplayed")
}
override fun onFlutterUiNoLongerDisplayed() {
Log.d("Flutter New" , "onFlutterUiNoLongerDisplayed")
// flutterView!!.detachFromFlutterEngine()
//
// // Reattach the Flutter view to the Flutter engine
// flutterView!!.attachToFlutterEngine(flutterEngine!!)
//
// // Optionally, you can force a repaint of the Flutter UI
// flutterView!!.invalidate()
}
})
}
override fun onPause() {
super.onPause()
Log.d("Flutter New" , "onPause")
}
override fun onRestart() {
super.onRestart()
Log.d("Flutter New" , "onRestart")
}
override fun onStop() {
super.onStop()
Log.d("Flutter New" , "onStop")
}
override fun onDestroy() {
super.onDestroy()
flutterView!!.detachFromFlutterEngine()
Log.d("Flutter New" , "onDestroy")
}
}
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:label="pip">
<activity
android:supportsPictureInPicture="true"
android:theme="@style/LaunchTheme"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:name="io.flutter.embedding.android.FlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true"
android:windowSoftInputMode="adjustResize">
</activity>
<activity
android:supportsPictureInPicture="true"
android:theme="@style/LaunchTheme"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:name=".NewActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true"
android:windowSoftInputMode="adjustResize"/>
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize">
<!--
Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI.
-->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--
Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java
-->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
i try detach and attach the flutter view from flutter engine, its update the UI. But this is not good practice.
Upvotes: 0
Views: 149