The following sections describe how to update GlanceAppWidget
and manage its
state.
Manage GlanceAppWidget
state
The provided GlanceAppWidget
class is instantiated whenever the widget is
created or requires an update, so it should be stateless and passive.
The concept of state can be divided into the following:
- Application state: The state or content of the app that is required by the widget. For example, a list of stored destinations (i.e., database) defined by the user.
- Glance state: The specific state that is only relevant to the app widget and does not necessarily modify or affect the app's state. For example, a checkbox was selected in the widget or a counter was increased.
Use application state
App widgets should be passive. Each application is responsible for managing the data layer and handling the states, such as idle, loading, and error reflecting in the widget UI.
For example, the following code retrieves the destinations from the in-memory cache from the repository layer, provides the stored list of destinations, and displays a different UI depending on its state:
class DestinationAppWidget : GlanceAppWidget() { // ... @Composable fun MyContent() { val repository = remember { DestinationsRepository.getInstance() } // Retrieve the cache data everytime the content is refreshed val destinations by repository.destinations.collectAsState(State.Loading) when (destinations) { is State.Loading -> { // show loading content } is State.Error -> { // show widget error content } is State.Completed -> { // show the list of destinations } } } }
Whenever the state or the data changes, it is the app's responsibility to notify and update the widget. See Update GlanceAppWidget for more information.
Update GlanceAppWidget
As explained in the Manage GlanceAppWidget
state section, app
widgets are hosted in a different process. Glance translates the content into
actual RemoteViews
and sends them to the host. To update the content, Glance
must recreate the RemoteViews
and send them again.
To send the update, call the update
method of the GlanceAppWidget
instance,
providing the context
and the glanceId
:
MyAppWidget().update(context, glanceId)
To obtain the glanceId
, query the GlanceAppWidgetManager
:
val manager = GlanceAppWidgetManager(context) val widget = GlanceSizeModeWidget() val glanceIds = manager.getGlanceIds(widget.javaClass) glanceIds.forEach { glanceId -> widget.update(context, glanceId) }
Alternatively, use one of the GlanceAppWidget update
extensions:
// Updates all placed instances of MyAppWidget MyAppWidget().updateAll(context) // Iterate over all placed instances of MyAppWidget and update if the state of // the instance matches the given predicate MyAppWidget().updateIf<State>(context) { state -> state == State.Completed }
These methods can be called from any part of your application. Because they are
suspend
functions, we recommend launching them outside of the main thread
scope. In the following example, they are launched in a CoroutineWorker
:
class DataSyncWorker( val context: Context, val params: WorkerParameters, ) : CoroutineWorker(context, params) { override suspend fun doWork(): Result { // Fetch data or do some work and then update all instance of your widget MyAppWidget().updateAll(context) return Result.success() } }
See Kotlin Coroutines on Android for more details on coroutines.