Skip to content

FlutterFragment fails to render after being overlaid by a FlutterActivity #31264

Closed

Description

@mjohnsullivan

When an AppCompatActivity contains a FlutterFragment, and an intent is fired to launch a FlutterActivity, when navigating back to the parent AppCompatActivity, the FlutterFragment is not rendered and is blank.

The Kotlin code for replicating this is:

// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.addfluttertoandroid

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.support.annotation.NonNull
import android.support.design.widget.BottomNavigationView
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.widget.TextView
import io.flutter.embedding.android.FlutterFragment
import io.flutter.embedding.engine.FlutterEngine
import kotlinx.android.synthetic.main.activity_main.*
import android.view.*
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.FlutterView


class MainActivity : AppCompatActivity() {

    private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
        when (item.itemId) {
            R.id.navigation_home -> {
                addFragment("I am an Android Fragment!!!")
                return@OnNavigationItemSelectedListener true
            }
            R.id.navigation_dashboard -> {
                addFragment("I am a second Android Fragment!!!")
                return@OnNavigationItemSelectedListener true
            }
            R.id.navigation_notifications -> {
                addFlutterFragment()
                return@OnNavigationItemSelectedListener true
            }
        }
        false
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_linear)
        addFragment("I am an Android Fragment!!!")
        navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.action_menu, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
        R.id.action_favorite -> {
            Log.i("onCreate", "Menu item selected")
            launchFlutterActivity()
            true
        }
        else -> {
            super.onOptionsItemSelected(item)
        }
    }

    private fun addFragment(text: String) {
        supportFragmentManager
            .beginTransaction()
            .replace(R.id.fragment_container,
                AndroidFragment.newInstance(text))
            .commit()
    }


    private fun addFlutterFragment() {
        supportFragmentManager.beginTransaction()
            .replace(R.id.fragment_container,
                MyFlutterFragment.newInstance())
            .commit()
    }

    private fun launchFlutterActivity() =
        // startActivity(Intent(this, MyFlutterActivity::class.java))
        startActivity(Intent(this, MyFlutterActivity::class.java))

}

class AndroidFragment : Fragment() {

    companion object {
        fun newInstance(text: String): AndroidFragment {
            val fragment = AndroidFragment()
            val args = Bundle()
            args.putString("text", text)
            fragment.arguments = args
            return fragment
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_main, container, false)
        arguments?.getString("text").let {
            view.findViewById<TextView>(R.id.fragment_message).setText(it)
        }
        return view
    }
}

class MyFlutterFragment : FlutterFragment() {

    companion object {
        @JvmStatic
        fun newInstance() =
            MyFlutterFragment().apply {
                return FlutterFragment.Builder(MyFlutterFragment::class.java)
                    .renderMode(FlutterView.RenderMode.surface)
                    .transparencyMode(FlutterView.TransparencyMode.transparent)
                    .build<MyFlutterFragment>()
            }
    }

    override fun createFlutterEngine(@NonNull context: Context): FlutterEngine =
         (context.applicationContext as MyApplication).engine


    override fun retainFlutterEngineAfterFragmentDestruction() = true
}

class MyFlutterActivity : FlutterActivity(), FlutterFragment.FlutterEngineProvider {

    override fun getFlutterEngine(context: Context): FlutterEngine? =
        (context.applicationContext as MyApplication).engine

}

If you navigate first to 'Notifications' in the bottom nav bar, and then tap on the action button in the app bar, then tap the back button to return to the first activity, the FlutterFragment will not render.

Activity

  1. jason-simmons commented on Apr 18, 2019

    @jason-simmons
    Member

    This app is trying to transfer a single FlutterEngine instance between FlutterViews in two different fragments. @matthew-carroll is that a supported scenario?

    The sequence is:

    • The first activity creates a FlutterFragment containing FlutterView A.. The fragment's onCreateView calls FlutterView.attachToFlutterEngine to bind the FlutterEngine to view A.
    • The second activity creates its FlutterFragment containing FlutterView B. B reuses the same engine instance and calls attachToFlutterEngine to bind the engine to B.
    • Press the back button. The second activity's FlutterFragment is destroyed, and detachFromFlutterEngine is called on FlutterView B.
    • During the transition back to the first activity onResume is called on the first FlutterFragment. But the first FlutterFragment does not reattach the shared engine instance to FlutterView A
  2. matthew-carroll commented on Apr 20, 2019

    @matthew-carroll
    Contributor

    @jason-simmons Yes, that scenario should be supported. I'll look into the attachment call as you mentioned and see if there is a more appropriate place for it to facilitate this use-case.

  3. added 4 commits that reference this issue on May 2, 2019
  4. github-actions commented on Aug 30, 2021

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  5. locked as resolved and limited conversation to collaborators on Aug 30, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

platform-androidAndroid applications specifically

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions