Throwed a NullPointerException: The fragment has been removed from FragmentManager in testing - android-testing

#Test
fun testBottomSheetShown(){
val args = Bundle().apply {
putString("EXTRA_TITLE", "title")
putString("EXTRA_CURRENT_VALUE", "choiceOne")
}
val scenario = launchFragmentInContainer<ChoicesBottomSheet>(args, R.style.BottomSheetDialog)
scenario.onFragment {
assertEquals(Lifecycle.State.RESUMED, it.lifecycle.currentState)
}
}
I tried to test my Bottomsheet with fragment scenario but it throws the error below:
java.lang.NullPointerException: The fragment has been removed from FragmentManager already.

launchFragmentInContainer will cause the ChoicesBottomSheet to reach the RESUME state.
The fragment has been removed from FragmentManager already
The above error is thrown if the Fragment is already destroyed, and you are trying to access it. So I believe that your BottomSheet fragment is dismissed during its flow to RESUME state. Please check if you have a dismiss() call somewhere in your BottomSheet code, that is getting called before your fragment reaches the onResume() call.

Related

Compose - pass composable block in function after coroutine result

I have a problem that I still can't solve and it just doesn't want to work. Basically I have to convert a function into a composable.
In the old function I launched a Coroutine and at the result, I changed context and then continued with my processes. In compose I don't understand how I have to "change context" in order to continue.
Old code:
fun getMyView( activity: Activity
) {
backgroundCoroutineScope.launch {
//some stuff here
withContext(coroutineContext) {
startSearchView(
activity
)
}
}
}
New not working code:
#Composable
fun getMyView( content: #Composable() () -> Unit) {
LaunchedEffect(key1 = Unit) {
//some stuff here like old funciont
//here I don't know how to change context, wait the end and go ahead. startSearchViewis a composable function too
// i want to use it to populate my screen
startSearchView(
content
)
}
}
How can I solve it? Thanks
Seems like you are trying to asynchronously "create" composable function, but UI emitting doesn't work this way. Like #PylypDukhov suggested, you should keep a mutable state holding nullable result of your async action. After loading the data set this state. Then in composable just do something like:
if (data != null) {
SearchComposable(data)
}
This way the composable will be emitted after the data is loaded

How to verify state change in Compose?

Say in a composable I have two states:
var stateA by remember { mutableStateOf(varA) }
var stateB by remember { mutableStateOf(varB) }
varA and varB are class variables of type Int and are set elsewhere in the code.
Then somewhere in the composable, in the same scope, I have
processA(stateA)
processB(stateB)
processA and processB are not composable functions.
So after initial rendering, if neither state changes, then nothing is further processed, that is cool.
Then if say stateB is changed, then both process statements get called. But I hope only to call processB in the case and not processA. How can I detect which of the states has changed?
You should not run heavy processing directly from Composable functions. More information can be found in side effects documentation.
In this case, you can use LaunchedEffect. Using snapshotFlow, you can create a flow that emits values every time a state changes, so you can process it. You can have a flow for each state, so they will be processed independently.
LaunchedEffect(Unit) {
launch {
snapshotFlow { stateA }
.collect(::processA)
}
launch {
snapshotFlow { stateB }
.collect(::processB)
}
}

Compose java.lang.IllegalStateException: pending composition has not been applied

I just want to run the simple test
class Exa {
#get:Rule
val composeTestRule = createAndroidComposeRule<MainActivity>()
// createComposeRule() if you don't need access to the activityTestRule
#Test
fun MyTest() {
// Start the app
composeTestRule.setContent {
Greeting2("Nurs")
}
composeTestRule.onNodeWithText("Hello Nurs!").assertIsDisplayed()
}
}
#Composable
fun Greeting2(name: String) {
Text(text = "Hello $name!")
}
but it gives me the following error:
java.lang.IllegalStateException: pending composition has not been applied
the strange thing that if I run it in another project it works
Wanted to add some detail for anyone who comes here from Google. The exception listed in the title:
java.lang.IllegalStateException: pending composition has not been applied
is thrown if a Composable throws an exception while composing or recomposing the view. In the case that the original poster was noticing, the exception was likely thrown because the activity itself was not able to host the composition, but other code may cause this if an exception is thrown within a composable method.
This happend to me when I put images to my drawable folder from the outside. In a release variant of app I had the same exception, but not in debug one. I just cleaned project(Build -> Clean Project) and everything worked fine.
If you are using Canvas Composable then give it some size. I was facing the same issue which was resolved after setting a fixed size.
Canvas(modifier = Modifier.size(400.dp)) {
...
}
You may use fillMaxWidth() or something similar.
From Documentation :
Canvas Composable is a component that allow you to specify an area on the screen and perform canvas drawing on this area. You MUST specify size with modifier, whether with exact sizes via Modifier.size modifier, or relative to parent, via Modifier.fillMaxSize, ColumnScope.weight, etc. If parent wraps this child, only exact sizes must be specified.
the problem was in MainActivity:
class MainActivity3 :
ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
Greeting("Android")
}
}
}
}
#Composable
fun Greeting(name: String)
you have to use clear activity without any dependency

How to "loosely" trigger a function?

I have the following async recursive code:
func syncData() {
dal.getList(...) { [unowned self] list, error in
if let objects = list {
if oneTime {
oneTime = false
syncOtherStuffNow()
}
syncData() // recurse until all data synced
} else if let error = error {... }
func syncOtherStuffNow() { } // with its own recursion
My understanding is that the recursion will build the call stack until all the function calls complete, at which point they will all unwind and free up the heap.
I also want to trigger another function (syncOtherStuffNow) from within the closure. But don't want to bind it to the closure with a strong reference waiting for it's return (even though it's async too).
How can I essentially trigger the syncOtherStuffNow() selector to run, and not affect the current closure with hanging on to its return call?
I thought of using Notifications, but that seems overkill given the two functions are in the same class.
Since dal.getList() takes a callback I guess it is asynchronous and so the the first syncData starts the async call and then returns immediately which lets syncData() return.
If syncOtherStuffNow() is async it will return immediately and so dataSync() will not wait on it finishing its job and so continue with its execution to the end.
You can test whether sth builds a callstack by putting a breakpoint on every recursion and look on the callstack how many calls of the same function are ontop.
What I do is recurse with asyncAfter, which unwinds the call stack.

dart, is it possible to execute code on library load?

I have a library in my lib/sndbx.dart file:
library sndbx;
bool foo(){
print('hey hey!');
return true;
}
bool bar = foo();
and I have a main in my bin/sndbx_cmd.dart:
import 'package:sndbx_cmd/sndbx.dart';
void main() {
print("Hello, World!");
}
but I don't see "hey hey!" printed to the commandline, how come when the library gets loaded it doesn't define the top level bool variable bar?
I see that if I get the bar value:
import 'package:sndbx_cmd/sndbx.dart';
void main() {
print("Hello, World!");
print(bar);
print(bar);
}
I get:
Hello World!
hey hey!
true
true
so why does it only call foo once? what if I changed it to:
library sndbx;
bool called = false;
bool foo(){
print('hey hey!');
return called = !called;
}
bool bar = foo();
because bar doesn't get defined on project load it feels like it acts as a getter but it only gets the value on the first call and the value is cached there after where I might expect it to keep calling foo every time I access it since that's what it does on the first access, as opposed to being defined when the library is initially loaded.
If you want it to be called every time you read it you actually have it to make a getter.
bar is lazy evaluated (on first access).
As far as I know there is no way to execute code on library load. You have to invoke it from main() (or a method called form main() of course).

Resources