I haven't done a whole lot of this sort of thing that's actually come to completion in a practical way, so others will hopefully be able to do more. I have however written an Android app in Scala, which had several of your requirements; the UI is interactive, and the "state" is all stored in an SQLite database. Both the database and the UI require interfacing with the Android framework, which are very much Java-oriented and so not an easy fit for either Scala or functional programming.
What I did was to adopt something like an MVC design, where the model part was implemented as a set of ADTs supporting only pure operations. This had the additional advantage that the model code was completely independent of the Android framework, so it could be tested outside the emulator any way I liked.
This left me with the controller (the view was a very thin layer that was mostly just configuration, with the way Android works), plus the additional operations of "load the model from the database" and "save the model to the database". Being Scala, I simply implemented these parts with impure code that calls the pure model code to do actual data manipulation. In Haskell these parts would probably have been entirely in the IO monad[1].
All up, this allowed me to think about my problem domain in pure functional terms, without having to "go against the grain" when interfacing with the external systems. The database layer becomes the problem of mapping between a database schema and the ADTs I used for my data model; dealing with the fact that these operations could fail is the responsibility of the controller (which initiated the DB operation), and doesn't impact on the model. The controller operations become conceptually very simple like "when this button is pressed, set the current state to the result of calling a function on the current state, then update the display table". In the end there was much more of this impure "glue" code than the actual model code, but I still think it was a win to do the program this way, because the core of my app was manipulation of complex structured data, and so getting that right was the trickiest bit. The rest was mostly tedious to write rather than difficult to design.
This works when you have a substantial amount of computation in your program (not necessarily large amounts of data, just that you actually compute things on it). If the program is almost entirely gluing different external systems together, then you don't necessarily have as much to gain.
[1] Remember the IO monad isn't just for reading/writing from the console. Modelling operations whose result is affected by the state of something external to your program is exactly what the IO monad is for; generally Haskellers would try to avoid using the IO monad for almost the entirety of their program if it isn't interacting with an external system all the time (or preferably even if it is). You can do pure computation on data in response to events coming from "outside", or even do pure computation of the IO actions that need to be performed in response to an external event, if that's complex.