This document describes the Crossbar home screen widget implementation on Android.
Crossbar uses Android's AppWidgetProvider along with the home_widget Flutter package to display plugin data on the home screen. The implementation supports three widget sizes:
- Small (1x1): Icon + Value
- Medium (2x1): Icon + Title + Value + Refresh
- Large (2x2+): Multiple plugins list
┌─────────────────┐ ┌─────────────────┐
│ Flutter App │────▶│ SharedPreferences│
│ (WidgetService)│ │ (home_widget) │
└─────────────────┘ └────────┬─────────┘
│
▼
┌──────────────────────┐
│ CrossbarWidgetProvider │
│ (Kotlin) │
└──────────────────────┘
│
▼
┌──────────────────────┐
│ RemoteViews │
│ (XML Layouts) │
└──────────────────────┘
android/app/src/main/
├── kotlin/com/verseles/crossbar/
│ ├── MainActivity.kt
│ └── CrossbarWidgetProvider.kt # Widget logic
├── res/
│ ├── layout/
│ │ ├── crossbar_widget_small.xml # 1x1 layout
│ │ ├── crossbar_widget_medium.xml # 2x1 layout
│ │ └── crossbar_widget_large.xml # 2x2+ layout
│ ├── drawable/
│ │ ├── widget_background.xml # Light theme
│ │ └── widget_background_dark.xml # Dark theme
│ ├── xml/
│ │ └── crossbar_widget_info.xml # Widget metadata
│ └── values/
│ └── strings.xml # Widget strings
└── AndroidManifest.xml # Receiver registration
- Flutter SchedulerService runs plugins periodically
- WidgetService.updateWidget() saves data via
home_widget home_widgetstores data in SharedPreferences- CrossbarWidgetProvider.onUpdate() reads from SharedPreferences
- Data is mapped to RemoteViews and displayed on home screen
The CrossbarWidgetProvider extends HomeWidgetProvider from the home_widget package:
class CrossbarWidgetProvider : HomeWidgetProvider() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray,
widgetData: SharedPreferences // Data from Flutter
) {
// Read plugin data and update RemoteViews
}
}The widget automatically selects the appropriate layout based on size:
| Dimensions | Layout |
|---|---|
| < 110dp wide | crossbar_widget_small.xml |
| ≥ 110dp wide | crossbar_widget_medium.xml |
| ≥ 180dp wide, ≥ 100dp tall | crossbar_widget_large.xml |
The Flutter app saves data in JSON format:
// Key: "plugin_ids"
["cpu.10s.sh", "memory.10s.sh"]
// Key: "plugin_cpu.10s.sh"
{
"pluginId": "cpu.10s.sh",
"text": "45%",
"icon": "⚡",
"color": "ff5733",
"tooltip": "Current CPU usage"
}- Long-press on Android home screen
- Select "Widgets"
- Find "Crossbar Plugin"
- Drag to home screen
- Resize as needed (supports horizontal and vertical resize)
- Automatic: Every 30 minutes (configurable in
crossbar_widget_info.xml) - On Plugin Run: When SchedulerService executes a plugin
- Manual: Tap the refresh icon on medium/large widgets
- Widget Container: Opens the Crossbar app
- Refresh Button: Triggers widget update
In crossbar_widget_info.xml:
android:updatePeriodMillis="1800000" <!-- 30 minutes -->Note: Android limits minimum to 30 minutes for battery optimization.
The widget uses @drawable/widget_background for the container.
To support dark theme, you can:
- Create
res/drawable-night/widget_background.xmlwith dark colors - Or use
AppCompatDelegateto detect theme in the Provider
# List available widgets
adb shell appwidget list
# Update all Crossbar widgets
adb shell am broadcast -a android.appwidget.action.APPWIDGET_UPDATE \
--es appwidget_provider com.verseles.crossbar.CrossbarWidgetProvider
# View widget logs
adb logcat | grep CrossbarWidget// Request widget pin (adds to home screen)
await WidgetService().requestWidgetPin('cpu.10s.sh');
// Force update
await WidgetService().updateWidget(pluginId, output);- Ensure the Flutter app has run at least once
- Check that plugins have executed successfully
- Verify SharedPreferences contains
plugin_idskey
- Check logcat for errors:
adb logcat | grep Crossbar - Ensure receiver is registered in AndroidManifest.xml
- Try removing and re-adding the widget
- Sync Gradle:
./gradlew clean build - Ensure
home_widgetpackage is properly configured - Check that Kotlin version matches (1.9.0+)