Android Widget Development: RemoteViews, Glance, and FCM

TRUETECH is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.

Development and support of all types of mobile applications:

Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1All 1734 services
Android Widget Development: RemoteViews, Glance, and FCM
Medium
~3-5 days
Frequently Asked Questions

Our competencies:

Development stages

Latest works

  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    858
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    743
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1159
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    1034
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    968
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    562

You built a widget updating every 30 minutes, but users complain about stale data. Currency rates change every minute, order status needs real-time updates. The standard updatePeriodMillis minimum of 30 minutes won't cut it. The solution is to use WorkManager for periodic tasks or push notifications from the server. We have extensive experience developing Android widgets, with over 50 projects in Google Play. For example, in a trading platform project, we replaced pull-based updates with push via FCM, reducing latency from 30 minutes to 2 seconds. Users immediately noticed the difference. Our Android widget development expertise covers RemoteViews, AppWidgetProvider, and Glance for creating polished app widgets. We build widgets of any complexity: from informational dashboards to interactive collections with instant updates. We guarantee stability and compliance with guidelines. Get a consultation for your task.

How to Update Widgets Faster Than 30 Minutes?

The system allows android:updatePeriodMillis no less than 1800000 ms. For more frequent updates, use WorkManager with PeriodicWorkRequest (minimum 15 minutes, set in the API) or AlarmManager. Inside WorkManager, call AppWidgetManager.updateAppWidget(). For push updates — FirebaseMessagingService.onMessageReceived with a direct updateAppWidget call. WorkManager automatically respects Doze Mode and Standby Buckets to preserve battery. Here is a comparison of approaches:

Method Minimum interval Recommendation
updatePeriodMillis 30 minutes Only for low-frequency data
WorkManager 15 minutes For regular data (weather, rates)
Push (FCM) Instant For event-driven updates (order status, messages)

For push updates, add server-side integration that sends data to the device. We use FCM — pushes arrive even when the app is backgrounded.

Example widget update implementation with WorkManager
class WidgetUpdateWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
    override suspend fun doWork(): Result {
        val widgetManager = AppWidgetManager.getInstance(applicationContext)
        val widgetIds = widgetManager.getAppWidgetIds(ComponentName(applicationContext, MyWidget::class.java))
        val views = buildRemoteViews(applicationContext)
        widgetManager.updateAppWidget(widgetIds, views)
        return Result.success()
    }
}

Why RemoteViews Limits Interactivity?

Android widgets run in the launcher process, so only RemoteViews are available. RemoteViews is limited to basic Views: TextView, ImageView, Button, LinearLayout, RelativeLayout, FrameLayout, GridLayout, ListView, GridView, StackView. RecyclerView, ConstraintLayout (before API 31), custom Views, and WebView are forbidden. For displaying lists, use ListView or GridView with RemoteViewsFactory. Starting from Android 12 (API 31), CheckBox, RadioButton, and Switch are added. Importantly, all elements use PendingIntent for click handling — assignOnClick does not work because direct View access is unavailable. A typical mistake is attempting to set onClickListener directly; this is impossible. Use setOnClickPendingIntent or setPendingIntentTemplate for collections.

Glance: A Declarative Approach

The Glance library offers a Compose-like syntax, hiding manual creation of RemoteViews. You write declaratively: Column, Text, Button. Glance automatically generates RemoteViews. This reduces the chance of errors and speeds up development. However, Glance does not yet support all components: for example, LazyColumn is unavailable; use Column with a fixed number of rows. State is managed via GlanceStateDefinition and updateAppWidgetState. For a new project with minSdkVersion 23+, this is the best choice. Here is a minimal example:

class MyGlanceWidget : GlanceAppWidget() {
    @Composable
    override fun Content() {
        val data = currentState<MyWidgetData>()
        Column(modifier = GlanceModifier.fillMaxSize().background(Color.White)) {
            Text(text = data.title, style = TextStyle(fontSize = 16.sp))
            Button(text = "Обновить", onClick = actionRunCallback<RefreshAction>())
        }
    }
}

Glance library - Official GitHub repository

Feature RemoteViews Glance
Syntax Imperative (XML + Java/Kotlin) Declarative (Compose-like)
Error likelihood Higher Lower
Collections support ListView/GridView Column (fixed number of rows)
Minimum API 17 (with restrictions) 23

AppWidgetProvider and Update Handling

AppWidgetProvider is a BroadcastReceiver that receives updates. In onUpdate(), create RemoteViews and call updateAppWidget(). A typical implementation:

class MyWidget : AppWidgetProvider() {
    override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray
    ) {
        appWidgetIds.forEach { widgetId ->
            val views = buildRemoteViews(context)
            appWidgetManager.updateAppWidget(widgetId, views)
        }
    }
}

For widgets with data from the internet, use a background loader in onReceive or via WorkManager. Never block onUpdate — it is the UI thread. Handle loading errors: show a placeholder (e.g., a TextView with "Error loading") and schedule a retry via WorkManager.

Working with Collections via RemoteViewsFactory

ListView or GridView in a widget require a RemoteViewsFactory. The factory creates RemoteViews for each item. Clicks on items are implemented using setOnClickFillInIntent and setPendingIntentTemplate. The template is a PendingIntent that will be launched with fill data. Example:

class WidgetListFactory(private val context: Context) : RemoteViewsService.RemoteViewsFactory {
    private var items: List<WidgetItem> = emptyList()

    override fun onDataSetChanged() {
        items = loadDataFromSharedPrefs(context)
    }

    override fun getViewAt(position: Int): RemoteViews {
        val item = items[position]
        return RemoteViews(context.packageName, R.layout.widget_list_item).apply {
            setTextViewText(R.id.item_title, item.title)
            val fillIntent = Intent().putExtra("item_id", item.id)
            setOnClickFillInIntent(R.id.item_container, fillIntent)
        }
    }
}

Important: onDataSetChanged() is called on a background thread, but the factory itself may be cached. Clear the cache when necessary.

Widget Configuration by User

An AppWidgetConfigure Activity opens when the widget is added. The user selects parameters (city, theme, update frequency). After selection, be sure to call:

setResult(Activity.RESULT_OK, intent.putExtra(EXTRA_APPWIDGET_ID, widgetId))

Without this, the widget will not be added. In Glance, launch configuration via GlanceAppWidgetManager().startConfigureActivityIntent.

Development Process

  1. Requirements analysis: define functionality, update frequency, target sizes (2x2, 4x2, 4x4).
  2. Layout design: choose between RemoteViews and Glance, create XML layout or Compose interface.
  3. Implementation: write AppWidgetProvider logic with WorkManager or FCM.
  4. Data integration: connect to API, database (Room) or SharedPreferences.
  5. Testing: on emulators and real devices, including Doze Mode.
  6. Publication: prepare metadata, code signing, submit to Google Play.

What's Included

  • RemoteViews layout adapted to sizes (2x2, 4x2, 4x4).
  • AppWidgetProvider with update support (WorkManager, FCM).
  • Configuration screen if needed.
  • Integration with database (Room) or network API.
  • Publication in Google Play with metadata, code signing and provisioning profile setup.
  • Documentation and source code.

Timelines and Pricing

Development of a single widget with configuration and regular updates takes 3 to 5 days. If a collection with push updates is required, up to 1 week. Typical cost: simple widget $800–$1500, complex widget with push $2000–$4000. Clients save 20% on development time using our optimized approach. Pricing is calculated individually: send a technical specification for a quote. Get a consultation on architecture — we'll help you choose the stack (RemoteViews or Glance) and avoid typical performance issues. Order widget development today — contact us to start. Optimize your budget — we offer flexible terms.

Common Mistakes in Widget Development

  • Using updatePeriodMillis for data that requires frequent updates. Switch to WorkManager or FCM.
  • Blocking onUpdate() with long operations. Move all background tasks to WorkManager.
  • Ignoring Doze Mode: use WorkManager with respect to Standby Buckets.
  • Incorrect configuration handling: don't forget to call setResult.
  • Missing placeholders for data loading errors.

We implement widgets in Java or Kotlin, including hybrid solutions. Over 50 successful projects confirm our expertise — with an average rating of 4.8 stars and over 100,000 active installations. Contact us for a project evaluation.

Development of Widgets, App Clips, and Live Activities: Entry Points Outside the App

We understand that users see your app not only when they open it. A widget on the home screen, a live score in Dynamic Island, a mini experience without installation — these are separate entry points that we implement within platform constraints. Over 5 years, we have developed more than 50 extensions for mobile apps, from simple informational widgets to App Clips with payment scenarios, saving clients up to 30% of time on repeat visits.

What entry points should you consider for your app?

WidgetKit Widget Development: Why You Can't Just "Add a Widget"

WidgetKit works via a Timeline Provider — the widget doesn't stay in memory continuously; it requests data snapshots in advance. The most common mistake: developers try to show real-time data via URLSession directly from getTimeline(). Apple doesn't prohibit this, but with aggressive updates, the system starts throttling requests, and the widget gets stuck on outdated data.

The correct approach: the main app updates data via WidgetCenter.shared.reloadTimelines(ofKind:) — after receiving a push notification or when the user returns to the foreground. The widget reads data from a shared App Group container using UserDefaults(suiteName:) or file storage. No direct network requests in the provider in production.

In the latest iOS versions, AppIntent-based interactive widgets have emerged — buttons and toggles directly on the widget without opening the app. This is implemented via Button(intent:) in the SwiftUI widget layout. Only works for simple actions; complex logic should transition to the app via widgetURL.

How Live Activities Change User Experience?

Live Activities are a mechanism for displaying live data on the Lock Screen and Dynamic Island (iPhone 14 Pro+). They are launched via ActivityKit, updated via push notifications of type liveactivity with a payload up to 4KB.

Architecturally, it's a separate SwiftUI target with two views: compact (Dynamic Island) and expanded (Lock Screen). Data is passed via ActivityAttributes — a strictly typed structure. The dynamic part is ContentState, while the static part (unchanged during the activity) is directly in ActivityAttributes.

A typical issue: Live Activity doesn't update on the device even though push is sent. The reason is that the app doesn't have permission for background push or apns-push-type is set incorrectly. In production, you need apns-push-type: liveactivity and a token from activity.pushToken. According to Apple documentation, without a correct push token, the Activity won't receive updates.

When to Use App Clips vs Instant Apps?

App Clips (iOS) and Instant Apps (Android) solve a similar problem — provide functionality without installing the full app. But the implementation is fundamentally different.

App Clip is a separate target in Xcode, max 15MB, launched via NFC tag, QR code, Safari Smart App Banner, or a link in Messages. Data access is limited: no Keychain sharing with the main app without explicit setup, no access to HealthKit, no push notifications (only ephemeral). The App Clip Card is configured in App Store Connect, and metadata errors are a common reason for rejection.

Android Instant Apps are built on a modular architecture: the app is divided into feature modules, each of which can be downloaded separately via Play Feature Delivery. An Instant App is a feature module with <dist:module dist:instant="true">. The limitation is no more than 15MB total for instant delivery.

Comparison shows that App Clips win in payment scenarios due to Apple Pay integration — conversion is 20% higher compared to Instant Apps in similar cases. Instant Apps are better suited for game demos and services requiring quick access via Google Search.

Parameter App Clips Instant Apps
Max size 15 MB 15 MB
Launch triggers NFC, QR, URL, Safari URL, Google Search, Play Store
Shared Keychain Via App Group Via SharedPreferences/Keystore
Recommended scenario Payment, boarding, demo Game demo, one-time services

What Does Our Work Include?

  • Audit of current architecture: determine which entry points your app needs — widget, Live Activity, App Clip, Instant App.
  • Prototyping: visual model of the extension following platform guidelines (Apple HIG, Material Design).
  • Development: implementation in Swift (iOS) or Kotlin (Android) using WidgetKit, ActivityKit, App Clip API, Play Feature Delivery.
  • Integration: setting up App Group, Keychain sharing, push certificates, provisioning profiles.
  • Testing: on real devices (iPhone, iPad, Android) and simulators. For Live Activities, test via xcrun simctl push.
  • Publication: preparing metadata for App Store Connect (App Clip Card) and Google Play Console (Instant App configuration).
  • Documentation and training: architecture description, widget update instructions, push notification troubleshooting.

How Does Our Development Process Work?

  1. Analytics: which app features are truly needed outside the app, and which mechanism fits. Widget for forecast — WidgetKit. Real-time delivery tracking — Live Activity. Payment at checkout — App Clip.
  2. Design: choosing stack, data update schemes (Timeline, push), UI layouts for compact and expanded views.
  3. Implementation: writing code in Swift/Kotlin, configuring App Group, push certificates, test schemes.
  4. Testing: each extension is tested in isolation. WidgetKit rendering is verified via Xcode Widget Gallery, Live Activities via simulator with forced push.
  5. Deployment: publishing to stores, monitoring metrics (update frequency, App Clip launch count).

Estimated Timeframes

Extension Type Timeframe (business days)
Simple informational widget 5 to 10
Interactive widget (AppIntent) 10 to 15
Live Activity with push 10 to 20
App Clip with payment 20 to 30
Instant App (Android) 15 to 25

Cost is calculated individually after audit. An estimate is provided within 2 business days.

What Are Typical Mistakes in Extension Development?

  • Too frequent widget updates — leads to throttling and empty state. We recommend an interval of at least 15 minutes (see Apple Human Interface Guidelines in WidgetKit documentation).
  • Ignoring shared container — the widget doesn't see data because it uses its own UserDefaults instead of App Group.
  • Lack of fallback for Live Activities — if push isn't delivered, the user sees outdated data. A periodic polling mechanism via Activity.update with pushType: nil is needed.
  • Incorrect App Clip Card metadata — a common reason for rejection in App Store Review. For example, incorrect URL or missing icon.

Contact us to assess which extension fits your app. Order an audit of current entry points — we'll find non-obvious scenarios for widgets and App Clips. Get an engineer consultation on architecture today.