New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instance state not saved when app is killed by OS #6827

Open
LouisCAD opened this Issue Nov 12, 2016 · 65 comments

Comments

@LouisCAD

LouisCAD commented Nov 12, 2016

What is instance state, and why it exists

On Android, an Activity can be killed at any time by the system. This happens usually when Android needs memory when your Activity is not in the foreground, or because of a non-handled configuration change, such as a locale change.
To avoid the user having to restart what he did from scratch when Android killed the Activity, the system calls onSaveInstanceState(…) when the Activity is paused, where the app is supposed to save it's data in a Bundle, and passes the saved bundle in both onCreate(…) and onRestoreInstanceState(…) when the task is resumed if the activity has been killed by the system.

The issue about it in flutter

In the flutter apps I tried (Flutter Gallery, and the base project with the FAB tap counter), if I open enough apps to make Android kill the flutter app's Activity, all the state is lost when I come back to the flutter activity (while not having remove the task from recents).

Steps to Reproduce

  1. Install Flutter Gallery
  2. Open the device's settings, and in developer options, switch "Don't keep activities" on. (see the screenshot)
    dev_option
    This will allow to simulate when Android kills Activities because it lacks memory and you're not in the foreground.
  3. Open the Flutter Gallery app and go anywhere other than the main screen.
  4. Go to the launcher by pressing the device's home button.
  5. Press the overview button and return to Flutter Gallery. Here's the bug.

What's expected: The app is in the same state that where we left off, with untouched UI.
What happens: The activity is restarted from scratch, losing all the UI state, even really really long forms.

@eseidelGoogle

This comment has been minimized.

Show comment
Hide comment
@eseidelGoogle

eseidelGoogle Nov 12, 2016

Contributor

#3427 is also likely related.

Contributor

eseidelGoogle commented Nov 12, 2016

#3427 is also likely related.

@LouisCAD

This comment has been minimized.

Show comment
Hide comment
@LouisCAD

LouisCAD Nov 12, 2016

@eseidelGoogle That's right. In which format could the flutter Activity state be saved, if it can be at all as of the current version?

LouisCAD commented Nov 12, 2016

@eseidelGoogle That's right. In which format could the flutter Activity state be saved, if it can be at all as of the current version?

@Hixie

This comment has been minimized.

Show comment
Hide comment
@Hixie

Hixie Nov 12, 2016

Contributor

Right now we don't do anything to save anything.

The framework itself has very little state worth saving -- it's all animation and stuff like that -- so it may be that we always leave this up to the app to do. We should probably expose it at the Dart level though. (Right now it's only exposed at the Java level.)

Contributor

Hixie commented Nov 12, 2016

Right now we don't do anything to save anything.

The framework itself has very little state worth saving -- it's all animation and stuff like that -- so it may be that we always leave this up to the app to do. We should probably expose it at the Dart level though. (Right now it's only exposed at the Java level.)

@LouisCAD

This comment has been minimized.

Show comment
Hide comment
@LouisCAD

LouisCAD Nov 12, 2016

@Hixie On Android, all framework Views have their state automatically saved and restored by the system, which only requires the developer to save manually the non UI part of the instance state. Shouldn't flutter work the same way for all UI widgets to prevent developers from having to write all the boilerplate each time to save where the user was, the scroll position, the enabled state of some button, the state of the previous screen and so on…?

LouisCAD commented Nov 12, 2016

@Hixie On Android, all framework Views have their state automatically saved and restored by the system, which only requires the developer to save manually the non UI part of the instance state. Shouldn't flutter work the same way for all UI widgets to prevent developers from having to write all the boilerplate each time to save where the user was, the scroll position, the enabled state of some button, the state of the previous screen and so on…?

@Hixie

This comment has been minimized.

Show comment
Hide comment
@Hixie

Hixie Nov 14, 2016

Contributor

In Flutter, there's very little framework state to save. For example, the enabled state of a button is not state, it's input provided by the application. The current route history is stored in the framework, but not stored in a state that the framework can rebuild (since it's all instances of objects provided by the application code). The scroll position is about the only thing we could actually store (and we do save that in a store currently, just not one that survives the app).

But in any case we should definitely do better than today.

Contributor

Hixie commented Nov 14, 2016

In Flutter, there's very little framework state to save. For example, the enabled state of a button is not state, it's input provided by the application. The current route history is stored in the framework, but not stored in a state that the framework can rebuild (since it's all instances of objects provided by the application code). The scroll position is about the only thing we could actually store (and we do save that in a store currently, just not one that survives the app).

But in any case we should definitely do better than today.

@LouisCAD

This comment has been minimized.

Show comment
Hide comment
@LouisCAD

LouisCAD Nov 25, 2016

As an experienced Android Developer, I'd like to take part to the conversation with iOS developers too when it'll be discussed so flutter can be the perfect framework to build iOS and Android apps.
I think the persistence ability of the framework may be important to save data, preferences, cache data and states in the most developer friendly possible way.

LouisCAD commented Nov 25, 2016

As an experienced Android Developer, I'd like to take part to the conversation with iOS developers too when it'll be discussed so flutter can be the perfect framework to build iOS and Android apps.
I think the persistence ability of the framework may be important to save data, preferences, cache data and states in the most developer friendly possible way.

@Hixie Hixie added this to the 4: Make shippers happy milestone Feb 27, 2017

@Takhion

This comment has been minimized.

Show comment
Hide comment
@Takhion

Takhion Sep 24, 2017

Whoa I didn't realise this was the case? This is actually critical, and it's worth mentioning that on devices with less memory the process could be killed quite easily!

What about saving the PageStore (or related) as a byte stream through serialization.dart in onSaveInstanceState?

Takhion commented Sep 24, 2017

Whoa I didn't realise this was the case? This is actually critical, and it's worth mentioning that on devices with less memory the process could be killed quite easily!

What about saving the PageStore (or related) as a byte stream through serialization.dart in onSaveInstanceState?

@LouisCAD

This comment has been minimized.

Show comment
Hide comment
@LouisCAD

LouisCAD Sep 27, 2017

@Takhion Could you link "PageStore" you're mentioning? I'm interested in trying to workaround this issue since it seems it won't be fixed any soon (31st december of 2029 is a liiittle far IMHO)

LouisCAD commented Sep 27, 2017

@Takhion Could you link "PageStore" you're mentioning? I'm interested in trying to workaround this issue since it seems it won't be fixed any soon (31st december of 2029 is a liiittle far IMHO)

@Takhion

This comment has been minimized.

Show comment
Hide comment
@Takhion

Takhion Sep 27, 2017

@LouisCAD sure it's here: https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/page_storage.dart

I think you'll need to save more than that though: at least the current route(s) and maybe some state?
What do you think @Hixie?

Takhion commented Sep 27, 2017

@LouisCAD sure it's here: https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/page_storage.dart

I think you'll need to save more than that though: at least the current route(s) and maybe some state?
What do you think @Hixie?

@Hixie

This comment has been minimized.

Show comment
Hide comment
@Hixie

Hixie Sep 29, 2017

Contributor

We don't currently do anything to make this easy. We haven't studied this problem in detail yet. For now I recommend storing the information you want to persist manually, and applying it afresh when the app is restored.

Contributor

Hixie commented Sep 29, 2017

We don't currently do anything to make this easy. We haven't studied this problem in detail yet. For now I recommend storing the information you want to persist manually, and applying it afresh when the app is restored.

@sethladd

This comment has been minimized.

Show comment
Hide comment
@sethladd

sethladd Oct 7, 2017

Contributor

Do we expose the necessary lifecycle events ("you're about to be killed!", "congrats, you're now restored") into Dart code, so developers can handle persisting state? That seems like the sufficient capabilities to unlock devs to explore solutions. Thoughts?

Contributor

sethladd commented Oct 7, 2017

Do we expose the necessary lifecycle events ("you're about to be killed!", "congrats, you're now restored") into Dart code, so developers can handle persisting state? That seems like the sufficient capabilities to unlock devs to explore solutions. Thoughts?

@LouisCAD

This comment has been minimized.

Show comment
Hide comment
@LouisCAD

LouisCAD Oct 7, 2017

@sethladd Just exposing it to developers is not enough IMHO. Flutter needs to save UI state too, without developers having to do it manually repeatedly for each app with dirty and hardly maintainable verbose boilerplate code.

LouisCAD commented Oct 7, 2017

@sethladd Just exposing it to developers is not enough IMHO. Flutter needs to save UI state too, without developers having to do it manually repeatedly for each app with dirty and hardly maintainable verbose boilerplate code.

@sethladd

This comment has been minimized.

Show comment
Hide comment
@sethladd

sethladd Oct 7, 2017

Contributor

@LouisCAD thanks for the feedback. I'm thinking in steps... what's step one? Do we have the lifecycle events exposed yet?

Contributor

sethladd commented Oct 7, 2017

@LouisCAD thanks for the feedback. I'm thinking in steps... what's step one? Do we have the lifecycle events exposed yet?

@Takhion

This comment has been minimized.

Show comment
Hide comment
@Takhion

Takhion Oct 7, 2017

@sethladd all I could find is this, which doesn't expose any callback for saving/restoring instance state

Takhion commented Oct 7, 2017

@sethladd all I could find is this, which doesn't expose any callback for saving/restoring instance state

@mit-mit

This comment has been minimized.

Show comment
Hide comment
@mit-mit

mit-mit Oct 9, 2017

Member

@Hixie you mentioned over in #3427 that we do have hooks for lifecycle. Did you mean https://docs.flutter.io/flutter/widgets/WidgetsBindingObserver-class.html ?

Member

mit-mit commented Oct 9, 2017

@Hixie you mentioned over in #3427 that we do have hooks for lifecycle. Did you mean https://docs.flutter.io/flutter/widgets/WidgetsBindingObserver-class.html ?

@DanielNovak

This comment has been minimized.

Show comment
Hide comment
@DanielNovak

DanielNovak Oct 10, 2017

Is Flutter using multiple Activities by default? (e.g. if you go to a new screen).

Process death is fairly common, the user hits the Home button and launches some other memory/CPU intensive application and your application will be killed in the background (or your application has been in the background for too long). It's an integral part of Android OS - every screen (Activity) should be able to persist and restore its' state and the process can be killed at any time after onStop().

Android will recreate the backstack of Activities if the user returns to a killed app, the top Activity is created first and then activities in the backstack are recreated on-demand if you go back in the backstack history. This can be a bigger issue in case Flutter is using multiple Activities (not sure if it does).

This means that if you don't have state saving implemented then the system will recreate the Activities but everything else is lost (process was killed), thus leading to inconsistency and crashes.

I wrote an article about process death on Android https://medium.com/inloop/android-process-kill-and-the-big-implications-for-your-app-1ecbed4921cb

DanielNovak commented Oct 10, 2017

Is Flutter using multiple Activities by default? (e.g. if you go to a new screen).

Process death is fairly common, the user hits the Home button and launches some other memory/CPU intensive application and your application will be killed in the background (or your application has been in the background for too long). It's an integral part of Android OS - every screen (Activity) should be able to persist and restore its' state and the process can be killed at any time after onStop().

Android will recreate the backstack of Activities if the user returns to a killed app, the top Activity is created first and then activities in the backstack are recreated on-demand if you go back in the backstack history. This can be a bigger issue in case Flutter is using multiple Activities (not sure if it does).

This means that if you don't have state saving implemented then the system will recreate the Activities but everything else is lost (process was killed), thus leading to inconsistency and crashes.

I wrote an article about process death on Android https://medium.com/inloop/android-process-kill-and-the-big-implications-for-your-app-1ecbed4921cb

@DanielNovak

This comment has been minimized.

Show comment
Hide comment
@DanielNovak

DanielNovak Oct 10, 2017

Also there is no (clean) way to prevent Android from killing your application once it's past the onStop() state (after clicking Home or if the app is just not the current foreground app). So somehow you have to deal with it. Default Android widgets are saving their state (e.g. entered text in EditText) automatically into the Bundle instance. Your Activity will notify you that it's necessary to store your state by calling onSaveInstanceState(Bundle). So maybe Flutter should be able to forward this onSaveInstanceState callback from the Activity to your "screens". You will get the Bundle with the saved state back in the onCreate(Bundle savedInstanceState) or onRestoreInstanceState(Bundle savedInstanceState) lifecycle callback in your activity.

So to recap - Flutter could maybe forward the onSaveInstanceState() and onRestoreInstanceState() callbacks to the developer. Ideally you would also wrap the Android Bundle object into something that can be also used in Flutter. The next step would be that all Flutter widgets inside the screen would be also notified about these callbacks and use them to persist their current state.
The Android OS will then take the Bundle and actually persist it on disk so that it's not lost in case the process is killed and can be deserialized again.

Good luck with that :-). Please take care and don't introduce too much of this Android state / lifecycle hell to Flutter.

I am not sure how that works on iOS - but I think there is something similar but it's not "necessary" to use it (?).

DanielNovak commented Oct 10, 2017

Also there is no (clean) way to prevent Android from killing your application once it's past the onStop() state (after clicking Home or if the app is just not the current foreground app). So somehow you have to deal with it. Default Android widgets are saving their state (e.g. entered text in EditText) automatically into the Bundle instance. Your Activity will notify you that it's necessary to store your state by calling onSaveInstanceState(Bundle). So maybe Flutter should be able to forward this onSaveInstanceState callback from the Activity to your "screens". You will get the Bundle with the saved state back in the onCreate(Bundle savedInstanceState) or onRestoreInstanceState(Bundle savedInstanceState) lifecycle callback in your activity.

So to recap - Flutter could maybe forward the onSaveInstanceState() and onRestoreInstanceState() callbacks to the developer. Ideally you would also wrap the Android Bundle object into something that can be also used in Flutter. The next step would be that all Flutter widgets inside the screen would be also notified about these callbacks and use them to persist their current state.
The Android OS will then take the Bundle and actually persist it on disk so that it's not lost in case the process is killed and can be deserialized again.

Good luck with that :-). Please take care and don't introduce too much of this Android state / lifecycle hell to Flutter.

I am not sure how that works on iOS - but I think there is something similar but it's not "necessary" to use it (?).

@zoechi

This comment has been minimized.

Show comment
Hide comment
@zoechi

zoechi Oct 10, 2017

Contributor

A lifecycle callback would be fine for me. I would just store/load the serialized redux state.

Contributor

zoechi commented Oct 10, 2017

A lifecycle callback would be fine for me. I would just store/load the serialized redux state.

@sethladd

This comment has been minimized.

Show comment
Hide comment
@sethladd

sethladd Oct 10, 2017

Contributor

Thanks for all the feedback! @Takhion also offered to help here. Perhaps an API design and a library is a good start? If that library works, we can look to integrate more formally. Also, the library will help identify what we need to do at the low-level engine (if anything). Basically: what's the concrete API proposal?

Contributor

sethladd commented Oct 10, 2017

Thanks for all the feedback! @Takhion also offered to help here. Perhaps an API design and a library is a good start? If that library works, we can look to integrate more formally. Also, the library will help identify what we need to do at the low-level engine (if anything). Basically: what's the concrete API proposal?

@Takhion

This comment has been minimized.

Show comment
Hide comment
@Takhion

Takhion Oct 11, 2017

Is Flutter using multiple Activities by default? (e.g. if you go to a new screen)

@DanielNovak Flutter uses it's own "routing" system and by default it integrates with Android through a single View in a single Activity. You can have a hybrid Android/Flutter app and as such potentially multiple Activities and Flutter Views, but in that case you could easily save/restore instance state through Android directly.

I would just store/load the serialized redux state

@zoechi you probably don't want to do that because the saved instance state Bundle has to go through IPC with a hard limit of 1MB for your entire Android app state. Instance state, by definition, should only be the pieces of data that would allow you to recreate the same conditions of whatever in-progress activity the user is performing, so: text input, scroll position, current page, etc. Anything else should either be persisted on disk or derived from other state.

Takhion commented Oct 11, 2017

Is Flutter using multiple Activities by default? (e.g. if you go to a new screen)

@DanielNovak Flutter uses it's own "routing" system and by default it integrates with Android through a single View in a single Activity. You can have a hybrid Android/Flutter app and as such potentially multiple Activities and Flutter Views, but in that case you could easily save/restore instance state through Android directly.

I would just store/load the serialized redux state

@zoechi you probably don't want to do that because the saved instance state Bundle has to go through IPC with a hard limit of 1MB for your entire Android app state. Instance state, by definition, should only be the pieces of data that would allow you to recreate the same conditions of whatever in-progress activity the user is performing, so: text input, scroll position, current page, etc. Anything else should either be persisted on disk or derived from other state.

@raju-bitter

This comment has been minimized.

Show comment
Hide comment
@raju-bitter

raju-bitter Oct 18, 2017

Contributor

Flutter exposes the Android onPause lifecycle event. The Android onDestroy() lifecycle event is not exposed. I guess the right approach would be to hook into the onPause() event for storing instance related state.
To be compatible with Android's onSaveInstanceState() and onRestoreInstanceState(), maybe it makes sense to add similar methods to Flutter widgets.
@sethladd @LouisCAD Did anyone create a design proposal?

Contributor

raju-bitter commented Oct 18, 2017

Flutter exposes the Android onPause lifecycle event. The Android onDestroy() lifecycle event is not exposed. I guess the right approach would be to hook into the onPause() event for storing instance related state.
To be compatible with Android's onSaveInstanceState() and onRestoreInstanceState(), maybe it makes sense to add similar methods to Flutter widgets.
@sethladd @LouisCAD Did anyone create a design proposal?

@DanielNovak

This comment has been minimized.

Show comment
Hide comment
@DanielNovak

DanielNovak Oct 18, 2017

@raju-bitter onPause() is not optimal, it's better to hook into onSaveInstanceState(). Here is the javadoc from onSaveInstanceState which mentions that onPause() is called more regularly than onSaveInstanceState (you would be triggering state saving more often than necessary or when it's not necessary at all):

Do not confuse this method with activity lifecycle callbacks such as onPause(), which is always called when an activity is being placed in the background or on its way to destruction, or onStop() which is called before destruction. One example of when onPause() and onStop() is called and not this method is when a user navigates back from activity B to activity A: there is no need to call onSaveInstanceState(Bundle) on B because that particular instance will never be restored, so the system avoids calling it. An example when onPause() is called and not onSaveInstanceState(Bundle) is when activity B is launched in front of activity A: the system may avoid calling onSaveInstanceState(Bundle) on activity A if it isn't killed during the lifetime of B since the state of the user interface of A will stay intact.

DanielNovak commented Oct 18, 2017

@raju-bitter onPause() is not optimal, it's better to hook into onSaveInstanceState(). Here is the javadoc from onSaveInstanceState which mentions that onPause() is called more regularly than onSaveInstanceState (you would be triggering state saving more often than necessary or when it's not necessary at all):

Do not confuse this method with activity lifecycle callbacks such as onPause(), which is always called when an activity is being placed in the background or on its way to destruction, or onStop() which is called before destruction. One example of when onPause() and onStop() is called and not this method is when a user navigates back from activity B to activity A: there is no need to call onSaveInstanceState(Bundle) on B because that particular instance will never be restored, so the system avoids calling it. An example when onPause() is called and not onSaveInstanceState(Bundle) is when activity B is launched in front of activity A: the system may avoid calling onSaveInstanceState(Bundle) on activity A if it isn't killed during the lifetime of B since the state of the user interface of A will stay intact.

@zoechi

This comment has been minimized.

Show comment
Hide comment
@zoechi

zoechi Oct 18, 2017

Contributor

@Takhion

instance state Bundle has to go through IPC with a hard limit of 1MB for your entire Android app state

That's not my issue.
I just need to know when to persist/restore, persisting myself on disk is fine for me.

Contributor

zoechi commented Oct 18, 2017

@Takhion

instance state Bundle has to go through IPC with a hard limit of 1MB for your entire Android app state

That's not my issue.
I just need to know when to persist/restore, persisting myself on disk is fine for me.

@mravn-google

This comment has been minimized.

Show comment
Hide comment
@mravn-google

mravn-google Nov 14, 2017

Contributor

@Takhion Please take a look at flutter/engine#4358

Contributor

mravn-google commented Nov 14, 2017

@Takhion Please take a look at flutter/engine#4358

@stanmots

This comment has been minimized.

Show comment
Hide comment
@stanmots

stanmots Dec 3, 2017

I see you are mostly talking about Android platform here. But, actually, iOS also has somewhat similar mechanism to save/restore UI state when the app was killed for some reason. For example, when the user switches to a different app the current app is suspended and can be even killed because of memory constraints. You can find more info about this process in the official docs.

iOS developers expect this preserving/restoring procedure to be almost automatic. I guess, if your goal is to make truly cross-platform framework you should definitely take this into account.

stanmots commented Dec 3, 2017

I see you are mostly talking about Android platform here. But, actually, iOS also has somewhat similar mechanism to save/restore UI state when the app was killed for some reason. For example, when the user switches to a different app the current app is suspended and can be even killed because of memory constraints. You can find more info about this process in the official docs.

iOS developers expect this preserving/restoring procedure to be almost automatic. I guess, if your goal is to make truly cross-platform framework you should definitely take this into account.

@Hixie Hixie added the f: routes label Feb 6, 2018

@Zhuinden

This comment has been minimized.

Show comment
Hide comment
@Zhuinden

Zhuinden Feb 24, 2018

You'd think something that intends to target Android as a platform at least attempts to obey the basics of the Activity contract....

Zhuinden commented Feb 24, 2018

You'd think something that intends to target Android as a platform at least attempts to obey the basics of the Activity contract....

@saket

This comment has been minimized.

Show comment
Hide comment
@saket

saket Feb 28, 2018

@Zhuinden let's not complain about things that are still in beta? :)

saket commented Feb 28, 2018

@Zhuinden let's not complain about things that are still in beta? :)

@aegis123

This comment has been minimized.

Show comment
Hide comment
@aegis123

aegis123 Feb 28, 2018

Other synchronous api's are onLowMemory and onTrimMemory and onConfigurationChanged don't know if IOS also has similar api's or mechanisms

aegis123 commented Feb 28, 2018

Other synchronous api's are onLowMemory and onTrimMemory and onConfigurationChanged don't know if IOS also has similar api's or mechanisms

@lukaspili

This comment has been minimized.

Show comment
Hide comment
@lukaspili

lukaspili Feb 28, 2018

Contributor

We may not want to reproduce the flawed android save&restoration mechanism on Flutter: You cannot save the whole state in the bundle because of the transaction size limit.

On android, you end up having multiple state save/restoration logics:

  • developer's savedInstanceState, but you can't store large objects inside, like a list of elements
  • database, where you can store large objects
  • most widgets also manage their own state
  • activity/fragment stack

It's really easy for developers to implement messy and buggy logic. In my own experience, it's really rare to see state save&restoration done right.

On flutter, the developer's state is the single source of truth.
Developers should persist this state into disk when it changes, without going trough the limited native platform mechanics.

Sure, you won't be able to easily save the scroll position, but who cares.
Most of the time, good UX for restoration means that the user should see the same screen than he was on previously. You can already do this with Flutter today.

Bottom point: there is room for improvement on the Flutter side in order to make state save/restoration less boilerplate (for things like Navigator). But I don't think it should provide hook to platform save/restore state.

Contributor

lukaspili commented Feb 28, 2018

We may not want to reproduce the flawed android save&restoration mechanism on Flutter: You cannot save the whole state in the bundle because of the transaction size limit.

On android, you end up having multiple state save/restoration logics:

  • developer's savedInstanceState, but you can't store large objects inside, like a list of elements
  • database, where you can store large objects
  • most widgets also manage their own state
  • activity/fragment stack

It's really easy for developers to implement messy and buggy logic. In my own experience, it's really rare to see state save&restoration done right.

On flutter, the developer's state is the single source of truth.
Developers should persist this state into disk when it changes, without going trough the limited native platform mechanics.

Sure, you won't be able to easily save the scroll position, but who cares.
Most of the time, good UX for restoration means that the user should see the same screen than he was on previously. You can already do this with Flutter today.

Bottom point: there is room for improvement on the Flutter side in order to make state save/restoration less boilerplate (for things like Navigator). But I don't think it should provide hook to platform save/restore state.

@saket

This comment has been minimized.

Show comment
Hide comment
@saket

saket Mar 1, 2018

Sure, you won't be able to easily save the scroll position, but who cares.

Everyone? I'd be really frustrated if the app resets its list everytime I accidentally trigger an orientation change. :p

saket commented Mar 1, 2018

Sure, you won't be able to easily save the scroll position, but who cares.

Everyone? I'd be really frustrated if the app resets its list everytime I accidentally trigger an orientation change. :p

@mravn-google

This comment has been minimized.

Show comment
Hide comment
@mravn-google

mravn-google Mar 1, 2018

Contributor

@Saketme FWIW, Flutter does not reset scroll positions on orientation change.

Contributor

mravn-google commented Mar 1, 2018

@Saketme FWIW, Flutter does not reset scroll positions on orientation change.

@LouisCAD

This comment has been minimized.

Show comment
Hide comment
@LouisCAD

LouisCAD Mar 1, 2018

Using a Flutter app with a bit of multitasking should be fun on an Android Go device! Progress lost everytime the 1GB of RAM or less are under pressure

LouisCAD commented Mar 1, 2018

Using a Flutter app with a bit of multitasking should be fun on an Android Go device! Progress lost everytime the 1GB of RAM or less are under pressure

@lukaspili

This comment has been minimized.

Show comment
Hide comment
@lukaspili

lukaspili Mar 1, 2018

Contributor

@Saketme: as @mravn-google said, Flutter is not bound to configuration changes like android Activities.
The only state management needed is when app is killed by the OS while being in background.

@LouisCAD Sure state save&restoration is important.
Flutter developers can already implement most of this logic themselves, and Flutter should probably provide ways to make it less boilerplate. However the mechanism offered by Android is flawed and we could take the opportunity with Flutter to design something better, rather than hacking into Activity onSaveInstanceState / onRestoreInstanceState.

Contributor

lukaspili commented Mar 1, 2018

@Saketme: as @mravn-google said, Flutter is not bound to configuration changes like android Activities.
The only state management needed is when app is killed by the OS while being in background.

@LouisCAD Sure state save&restoration is important.
Flutter developers can already implement most of this logic themselves, and Flutter should probably provide ways to make it less boilerplate. However the mechanism offered by Android is flawed and we could take the opportunity with Flutter to design something better, rather than hacking into Activity onSaveInstanceState / onRestoreInstanceState.

@nsreenath

This comment has been minimized.

Show comment
Hide comment
@nsreenath

nsreenath Mar 5, 2018

What's current the status of this issue?

Consider the case of a chat app, where I am typing a long message in the TextField.
Suddenly I receives a phone call and after some time, the OS kills the chat app due to low memory.
After the phone call, I come back to the chat app. Will Flutter automatically persist and recreate the message I was typing? or The app has to persist and recreate it manually?

Other than scroll position and route, what else are "states" that are not usually managed by developers?

nsreenath commented Mar 5, 2018

What's current the status of this issue?

Consider the case of a chat app, where I am typing a long message in the TextField.
Suddenly I receives a phone call and after some time, the OS kills the chat app due to low memory.
After the phone call, I come back to the chat app. Will Flutter automatically persist and recreate the message I was typing? or The app has to persist and recreate it manually?

Other than scroll position and route, what else are "states" that are not usually managed by developers?

@LouisCAD

This comment has been minimized.

Show comment
Hide comment
@LouisCAD

LouisCAD Mar 5, 2018

@nsreenath Any chat app should persist messages draft by itself every few characters and when going to background. You may type a long message, and if the device shutdown for some reason, you'd not want to lose your progress on this. Android instance state is for light user input and progress into the screens

LouisCAD commented Mar 5, 2018

@nsreenath Any chat app should persist messages draft by itself every few characters and when going to background. You may type a long message, and if the device shutdown for some reason, you'd not want to lose your progress on this. Android instance state is for light user input and progress into the screens

@Zhuinden

This comment has been minimized.

Show comment
Hide comment
@Zhuinden

Zhuinden Mar 5, 2018

I actually slept on this and I came to the realization that it's actually quite easy for Flutter devs to detect if the application came back from process death or is completely new instance.

Very simple, as long as they check for savedInstanceState == null (assuming they put at least 1 item in the onSaveInstanceState(bundle)). If a static boolean flag isFirstInit == false and savedInstanceState != null, then it's after process death.

Then they can create any kind of serialization / deserialization callback for complex state which could be cleared automatically when they detect savedInstanceState == null. That way they only need to persist some random boolean, but can manage their own persistence system.

Zhuinden commented Mar 5, 2018

I actually slept on this and I came to the realization that it's actually quite easy for Flutter devs to detect if the application came back from process death or is completely new instance.

Very simple, as long as they check for savedInstanceState == null (assuming they put at least 1 item in the onSaveInstanceState(bundle)). If a static boolean flag isFirstInit == false and savedInstanceState != null, then it's after process death.

Then they can create any kind of serialization / deserialization callback for complex state which could be cleared automatically when they detect savedInstanceState == null. That way they only need to persist some random boolean, but can manage their own persistence system.

@stanmots

This comment has been minimized.

Show comment
Hide comment
@stanmots

stanmots Mar 5, 2018

Very simple, as long as they check for savedInstanceState == null (assuming they put at least 1 item in the onSaveInstanceState(bundle)).

There is no savedInstanceState and onSaveInstanceState on iOS. But the app can still be killed by OS.

Consider a typical workflow:

  • A user goes to a specific screen inside the app
  • Then the user switches to another app
  • Suddenly the wise OS decides to kill the previous app to preserve the memory
  • The user returns to the previous app but she sees the home screen. Not the screen that was open when she decided to switch the app

So, what is currently the recommended way to preserve the screen history in Flutter?

P.S. Of course, this is not important if your app has just one screen 😏

stanmots commented Mar 5, 2018

Very simple, as long as they check for savedInstanceState == null (assuming they put at least 1 item in the onSaveInstanceState(bundle)).

There is no savedInstanceState and onSaveInstanceState on iOS. But the app can still be killed by OS.

Consider a typical workflow:

  • A user goes to a specific screen inside the app
  • Then the user switches to another app
  • Suddenly the wise OS decides to kill the previous app to preserve the memory
  • The user returns to the previous app but she sees the home screen. Not the screen that was open when she decided to switch the app

So, what is currently the recommended way to preserve the screen history in Flutter?

P.S. Of course, this is not important if your app has just one screen 😏

@lukaspili

This comment has been minimized.

Show comment
Hide comment
@lukaspili

lukaspili Mar 5, 2018

Contributor

@Zhuinden I fail to see many scenarios where that's needed anyway.

a) Chat example:
Always save and restore previous content written in the text field that was not sent.

b) Long form example:

  1. Always save the content
  2. When user comes back to the form screen, notify the user there is a draft version that was saved but not submitted, asking if he wants to restore it. It does not make any difference if user exited the form manually (maybe ask if user wants to save draft) or if the app was killed while in background.

There are probably some cases where specific knowledge of "fresh new screen" vs "new screen after process death" is useful, but that might not be so common when you design your app's content to be always backed by a database.

@storix You need to manually persist the current route and restore it when app launches again. Too much boilerplate for a common use case, I agree. But still doable.
Also: Many android devs would be surprised by how few iOS apps/devs bother with state restoration. Majority of devs I know is not even aware that's an issue. Guess it's the luxury of working on higher end devices only.

Contributor

lukaspili commented Mar 5, 2018

@Zhuinden I fail to see many scenarios where that's needed anyway.

a) Chat example:
Always save and restore previous content written in the text field that was not sent.

b) Long form example:

  1. Always save the content
  2. When user comes back to the form screen, notify the user there is a draft version that was saved but not submitted, asking if he wants to restore it. It does not make any difference if user exited the form manually (maybe ask if user wants to save draft) or if the app was killed while in background.

There are probably some cases where specific knowledge of "fresh new screen" vs "new screen after process death" is useful, but that might not be so common when you design your app's content to be always backed by a database.

@storix You need to manually persist the current route and restore it when app launches again. Too much boilerplate for a common use case, I agree. But still doable.
Also: Many android devs would be surprised by how few iOS apps/devs bother with state restoration. Majority of devs I know is not even aware that's an issue. Guess it's the luxury of working on higher end devices only.

@zoechi

This comment has been minimized.

Show comment
Hide comment
@zoechi

zoechi Mar 5, 2018

Contributor

@Zhuinden the lifecycle callbacks from #6827 (comment) worked for my chat application. I just save some state every time when the app is switched to background. See also https://docs.flutter.io/flutter/dart-ui/AppLifecycleState-class.html

Contributor

zoechi commented Mar 5, 2018

@Zhuinden the lifecycle callbacks from #6827 (comment) worked for my chat application. I just save some state every time when the app is switched to background. See also https://docs.flutter.io/flutter/dart-ui/AppLifecycleState-class.html

@Hixie Hixie self-assigned this Mar 6, 2018

@jules15

This comment has been minimized.

Show comment
Hide comment
@jules15

jules15 Mar 6, 2018

I would like to urge the Flutter team not to expose Android/iOS lifecycle methods as the canonical way of persisting/restoring UI state. Even in native apps, they were difficult to grok and code correctly, and the line between UI and application state was often blurry, with different mechanisms provided for each. It would be a shame to promulgate all of these disparate APIs into Flutter (especially if additional platforms are targeted in the future).

Flutter's stateless widgets have already migrated the responsibility for UI state out of the widgets and into the app, so the problem of persisting/restoring UI state is increasingly covered by persisting/restoring app state instead. I see this as a good thing, as opposed to treating UI and App state differently, as in iOS and Android.

I notice that some Flutter widgets, like ScrollView and TextField, provide "Controller" classes that encapsulate the dynamic state of the widget. In some ways they are acting as "memento" objects -- a snapshot of the widget's state -- which can be provided back to the widget at a later time to restore their state to some previous configuration. Sounds familiar. If these "mementos" were made easy to persist/restore, then an application could take responsibility for them as part of managing its own state. This would end the app/UI state dichotomy, would be opt-in, and would obviate the need for the Flutter framework to provide UI state preservation hooks (less is more!)

In other words, make it the developer's responsibility, but try to make it as easy as possible. If a developer thinks that a half-completed form is important enough for Flutter to persist across invocations, then one could argue that it's probably important enough to be considered application state. I realize that persisting the state of objects introduces versioning issues, but I think this is a more manageable problem.

jules15 commented Mar 6, 2018

I would like to urge the Flutter team not to expose Android/iOS lifecycle methods as the canonical way of persisting/restoring UI state. Even in native apps, they were difficult to grok and code correctly, and the line between UI and application state was often blurry, with different mechanisms provided for each. It would be a shame to promulgate all of these disparate APIs into Flutter (especially if additional platforms are targeted in the future).

Flutter's stateless widgets have already migrated the responsibility for UI state out of the widgets and into the app, so the problem of persisting/restoring UI state is increasingly covered by persisting/restoring app state instead. I see this as a good thing, as opposed to treating UI and App state differently, as in iOS and Android.

I notice that some Flutter widgets, like ScrollView and TextField, provide "Controller" classes that encapsulate the dynamic state of the widget. In some ways they are acting as "memento" objects -- a snapshot of the widget's state -- which can be provided back to the widget at a later time to restore their state to some previous configuration. Sounds familiar. If these "mementos" were made easy to persist/restore, then an application could take responsibility for them as part of managing its own state. This would end the app/UI state dichotomy, would be opt-in, and would obviate the need for the Flutter framework to provide UI state preservation hooks (less is more!)

In other words, make it the developer's responsibility, but try to make it as easy as possible. If a developer thinks that a half-completed form is important enough for Flutter to persist across invocations, then one could argue that it's probably important enough to be considered application state. I realize that persisting the state of objects introduces versioning issues, but I think this is a more manageable problem.

@gitspeak

This comment has been minimized.

Show comment
Hide comment
@gitspeak

gitspeak Mar 20, 2018

Has there been any progress on this matter? I think there is a real need to distinguish between 'first run' and 'restored' process state. How else can you program to show the right screen when the user navigates back to a killed process?

gitspeak commented Mar 20, 2018

Has there been any progress on this matter? I think there is a real need to distinguish between 'first run' and 'restored' process state. How else can you program to show the right screen when the user navigates back to a killed process?

@rooton

This comment has been minimized.

Show comment
Hide comment
@rooton

rooton Aug 14, 2018

@lukaspili you wrote: "The only state management needed is when app is killed by the OS while being in background."

Okey, then what about router stack, also save to disc? Also I dont find proper solution to determine that application was restored. Every time when app goes to background I need to save router stack, app data, state to disc? What if I dont use Redux?

rooton commented Aug 14, 2018

@lukaspili you wrote: "The only state management needed is when app is killed by the OS while being in background."

Okey, then what about router stack, also save to disc? Also I dont find proper solution to determine that application was restored. Every time when app goes to background I need to save router stack, app data, state to disc? What if I dont use Redux?

@sir-boformer

This comment has been minimized.

Show comment
Hide comment
@sir-boformer

sir-boformer Sep 26, 2018

Contributor

I think as a first step, we should give plugins the possibility to save and restore state. Here is my proposal: #22328

Contributor

sir-boformer commented Sep 26, 2018

I think as a first step, we should give plugins the possibility to save and restore state. Here is my proposal: #22328

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment