Benutzerdefinierte Widgets für iOS & Android mit React Native
Erfahren Sie, wie Sie native Widgets für iOS und Android mit React Native erstellen und Gesundheitsdaten auf dem Startbildschirm anzeigen.
Auf dieser Seite
In einer Welt der Informationsüberflutung bieten Startbildschirm-Widgets eine leistungsstarke Möglichkeit, Benutzern einen Überblick über relevante Daten aus Ihrer App zu verschaffen, ohne dass diese geöffnet werden muss. Für Gesundheits- und Fitness-Anwendungen ist dies ein Wendepunkt. Stellen Sie sich vor, Ihre Benutzer sehen ihre täglichen Schritte oder ihren Schlafscore jedes Mal, wenn sie ihr Handy entsperren – eine konstante, subtile Motivationsquelle.
Dieses Tutorial führt Sie durch den Prozess der Erstellung benutzerdefinierter Startbildschirm-Widgets für iOS und Android innerhalb einer React Native-Anwendung. Wir werden ein „Health Stats"-Widget erstellen, das die täglichen Schritte und den Schlafscore eines Benutzers anzeigt. Obwohl React Native keine integrierte „Widget"-Komponente hat, können wir native Module nutzen, um diese leistungsstarken Erweiterungen zu erstellen.
Voraussetzungen:
- Grundlegendes Verständnis von React Native.
- Node.js und npm/yarn installiert.
- Eine React Native-Entwicklungsumgebung für iOS und Android eingerichtet.
- Für die iOS-Entwicklung: Ein Mac mit installiertem Xcode.
- Für die Android-Entwicklung: Android Studio installiert.
- Ein aktives Apple Developer-Konto ist für App Group-Funktionen unter iOS erforderlich.
Warum dies für Entwickler wichtig ist:
Widgets können die Benutzerinteraktion und App-Bindung erheblich steigern. Am Ende dieses Tutorials verfügen Sie über die Fähigkeiten, benutzerdefinierte Widgets für Ihre React Native-Apps zu entwickeln und ein integrierteres und wertvolleres Benutzererlebnis zu bieten.
Das Problem verstehen
Die Hauptchallenge bei der Erstellung von Widgets mit React Native besteht darin, dass Widgets grundlegend native UI-Komponenten sind. Sie laufen in einem separaten Prozess von Ihrer Hauptanwendung und können JavaScript-Komponenten nicht direkt rendern. Daher müssen wir die UI des Widgets mit den nativen Tools der Plattform erstellen (SwiftUI für iOS und XML-Layouts für Android) und eine zuverlässige Methode etablieren, um Daten von unserer React Native-App an diese nativen Widgets weiterzugeben.
Unser Ansatz wird wie folgt sein:
- Die Widget-UI nativ für jede Plattform erstellen.
- Eine gemeinsame Datenschicht etablieren, auf die sowohl die React Native-App als auch das native Widget zugreifen können.
- Daten aus unserer React Native-App in diesen gemeinsamen Speicher schreiben.
- Die Daten in unseren nativen Widgets lesen und anzeigen.
Voraussetzungen
Bevor wir beginnen, vergewissern Sie sich, dass Sie ein React Native-Projekt eingerichtet haben. Falls nicht, können Sie eines mit dem folgenden Befehl erstellen:
npx react-native init HealthStatsWidgetApp
cd HealthStatsWidgetApp
Wir benötigen auch eine Bibliothek, um den Datenaustausch zwischen der React Native-App und den nativen Widgets zu erleichtern. Hierfür verwenden wir react-native-shared-group-preferences für iOS und ein benutzerdefiniertes natives Modul für Android.
Schritt 1: Das iOS-Widget erstellen
Was wir tun
Für iOS erstellen wir eine „Widget-Erweiterung" in Xcode und verwenden SwiftUI, um die UI unseres Widgets zu gestalten. Anschließend richten wir eine „App Group" ein, um den Datenaustausch zwischen unserer Haupt-App und der Widget-Erweiterung zu ermöglichen.
Implementierung
1. Die Widget-Erweiterung in Xcode erstellen:
- Öffnen Sie den
ios-Ordner Ihres Projekts in Xcode (open ios/HealthStatsWidgetApp.xcworkspace). - Gehen Sie zu
File > New > Target.... - Wählen Sie „Widget Extension" und klicken Sie auf „Next".
- Benennen Sie Ihr Widget (z.B. „HealthStatsWidget") und stellen Sie sicher, dass „Include Configuration Intent" für dieses einfache Beispiel deaktiviert ist.
- Klicken Sie auf „Finish". Aktivieren Sie das neue Schema, wenn Sie dazu aufgefordert werden.
2. App Groups konfigurieren:
- Wählen Sie in Xcode Ihr Haupt-App-Target (
HealthStatsWidgetApp) aus. - Gehen Sie zur Registerkarte „Signing & Capabilities" und klicken Sie auf „+ Capability", um „App Groups" hinzuzufügen.
- Klicken Sie auf die Schaltfläche „+" unter „App Groups" und fügen Sie eine neue Gruppe mit einem eindeutigen Bezeichner hinzu, typischerweise im Format
group.com.yourcompany.yourappname. - Wählen Sie nun Ihr Widget-Erweiterungs-Target (
HealthStatsWidget) aus und wiederholen Sie den gleichen Prozess, indem Sie genau dieselbe App Group hinzufügen.
3. Daten von React Native zum iOS-Widget teilen:
Installieren Sie zunächst das Paket für gemeinsame Einstellungen:
npm install react-native-shared-group-preferences
cd ios && pod install
Nun können Sie in Ihrem React Native-Code (z.B. App.js) Daten in den gemeinsamen Speicher schreiben:
// src/App.js
import React from 'react';
import { SafeAreaView, Button, View, Text } from 'react-native';
import SharedGroupPreferences from 'react-native-shared-group-preferences';
const App = () => {
const appGroupIdentifier = 'group.com.yourcompany.HealthStatsWidgetApp'; // Ersetzen Sie durch Ihren App Group-Bezeichner
const updateWidgetData = async () => {
const healthData = {
steps: Math.floor(Math.random() * 10000),
sleepScore: Math.floor(Math.random() * 100),
};
try {
await SharedGroupPreferences.setItem('healthData', healthData, appGroupIdentifier);
console.log('Daten im gemeinsamen Speicher gespeichert');
} catch (error) {
console.error('Fehler beim Speichern der Daten:', error);
}
};
return (
<SafeAreaView>
<View style={{ padding: 20 }}>
<Text style={{ fontSize: 18, marginBottom: 20 }}>Health Stats App</Text>
<Button title="Widget-Daten aktualisieren" onPress={updateWidgetData} />
</View>
</SafeAreaView>
);
};
export default App;
4. Daten im SwiftUI-Widget anzeigen:
Lassen Sie uns nun den Swift-Code des Widgets modifizieren, um diese Daten zu lesen und anzuzeigen. Öffnen Sie die Datei HealthStatsWidget.swift in Xcode.
// HealthStatsWidget/HealthStatsWidget.swift
import WidgetKit
import SwiftUI
struct HealthData: Codable {
let steps: Int
let sleepScore: Int
}
struct Provider: TimelineProvider {
let appGroupIdentifier = "group.com.yourcompany.HealthStatsWidgetApp" // Ersetzen Sie durch Ihren App Group-Bezeichner
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), healthData: HealthData(steps: 8000, sleepScore: 85))
}
func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), healthData: HealthData(steps: 8000, sleepScore: 85))
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
let userDefaults = UserDefaults(suiteName: appGroupIdentifier)
var entries: [SimpleEntry] = []
if let savedData = userDefaults?.object(forKey: "healthData") as? [String: Any] {
let steps = savedData["steps"] as? Int ?? 0
let sleepScore = savedData["sleepScore"] as? Int ?? 0
let entry = SimpleEntry(date: Date(), healthData: HealthData(steps: steps, sleepScore: sleepScore))
entries.append(entry)
} else {
let entry = SimpleEntry(date: Date(), healthData: HealthData(steps: 0, sleepScore: 0))
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let healthData: HealthData
}
struct HealthStatsWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
VStack {
Text("Health Stats")
.font(.headline)
Text("Steps: \(entry.healthData.steps)")
Text("Sleep Score: \(entry.healthData.sleepScore)")
}
}
}
@main
struct HealthStatsWidget: Widget {
let kind: String = "HealthStatsWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
HealthStatsWidgetEntryView(entry: entry)
}
.configurationDisplayName("Health Stats")
.description("Ihre täglichen Gesundheitsstatistiken.")
}
}
Wie es funktioniert
- App Groups erstenen einen sicheren, gemeinsamen Container auf dem Gerät, auf den sowohl Ihre Haupt-App als auch ihre Erweiterungen zugreifen können.
react-native-shared-group-preferencesbietet eine Brücke zu den nativenUserDefaults(für iOS) und ermöglicht es uns, Daten aus JavaScript in diesen gemeinsamen Container zu schreiben.- Der
TimelineProviderin unserem Swift-Code ist dafür verantwortlich, die Daten abzurufen und zu bestimmen, wann das Widget aktualisiert werden soll. - Die Funktion
getTimelineliest die Daten ausUserDefaultsunter Verwendung desselben App Group-Bezeichners und erstellt einen Timeline-Eintrag, der von der SwiftUI-Ansicht des Widgets angezeigt werden soll.
Häufige Fallstricke
- Stellen Sie sicher, dass der App Group-Bezeichner in Ihrem Haupt-App-Target, Widget-Erweiterungs-Target und Ihrem React Native-Code identisch ist.
- Sie müssen ein aktives Apple Developer-Konto haben, um App Groups zu verwenden.
- Widget-Aktualisierungen sind nicht sofortig und werden vom Betriebssystem verwaltet, um die Batterie zu schonen.
Schritt 2: Das Android-Widget erstellen
Was wir tun
Für Android verwenden wir Android Studio, um einen AppWidgetProvider und ein Layout für unser Widget in XML zu erstellen. Um Daten zu teilen, werden wir ein benutzerdefiniertes natives Modul erstellen, das SharedPreferences nutzt.
Implementierung
1. Das Widget in Android Studio erstellen:
- Öffnen Sie den
android-Ordner Ihres Projekts in Android Studio. - Klicken Sie mit der rechten Maustaste auf den
app-Ordner und gehen Sie dann zuNew > Widget > App Widget. - Setzen Sie den
Class NameaufHealthStatsWidgetProviderund denLayout Nameaufhealth_stats_widget. Klicken Sie auf „Finish".
2. Das Widget-Layout gestalten:
Android Studio erstellt health_stats_widget.xml im Verzeichnis res/layout. Öffnen Sie es und fügen Sie Folgendes hinzu:
<!-- res/layout/health_stats_widget.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:background="@android:color/white"
android:padding="16dp">
<TextView
android:id="@+id/widget_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Health Stats"
android:textColor="@android:color/black"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/widget_steps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Steps: 0"
android:textColor="@android:color/black" />
<TextView
android:id="@+id/widget_sleep"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sleep Score: 0"
android:textColor="@android:color/black" />
</LinearLayout>
3. Ein natives Modul für die Datenfreigabe erstellen:
Wir müssen eine Brücke erstellen, damit unsere React Native-App mit der nativen Android-Seite kommunizieren kann.
Erstellen Sie zunächst eine neue Java-Klasse SharedStorageModule.java:
// android/app/src/main/java/com/healthstatswidgetapp/SharedStorageModule.java
package com.healthstatswidgetapp;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import android.content.Context;
import android.content.SharedPreferences;
public class SharedStorageModule extends ReactContextBaseJavaModule {
ReactApplicationContext context;
public SharedStorageModule(ReactApplicationContext reactContext) {
super(reactContext);
context = reactContext;
}
@Override
public String getName() {
return "SharedStorage";
}
@ReactMethod
public void set(String data) {
SharedPreferences.Editor editor = context.getSharedPreferences("DATA", Context.MODE_PRIVATE).edit();
editor.putString("healthData", data);
editor.apply();
}
}
Erstellen Sie anschließend ein Paket zur Registrierung dieses Moduls, SharedStoragePackage.java:
// android/app/src/main/java/com/healthstatswidgetapp/SharedStoragePackage.java
package com.healthstatswidgetapp;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SharedStoragePackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new SharedStorageModule(reactContext));
return modules;
}
}
Registrieren Sie schließlich das Paket in MainApplication.java:
// android/app/src/main/java/com/healthstatswidgetapp/MainApplication.java
// ...
import com.healthstatswidgetapp.SharedStoragePackage; // Fügen Sie diesen Import hinzu
public class MainApplication extends Application implements ReactApplication {
// ...
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Pakete, die noch nicht automatisch verknüpft werden können, können hier manuell hinzugefügt werden, zum Beispiel:
packages.add(new SharedStoragePackage()); // Fügen Sie diese Zeile hinzu
return packages;
}
// ...
}
4. Den Widget Provider aktualisieren, um Daten anzuzeigen:
Modifizieren Sie HealthStatsWidgetProvider.java, um aus SharedPreferences zu lesen und die Ansichten des Widgets zu aktualisieren.
// android/app/src/main/java/com/healthstatswidgetapp/HealthStatsWidgetProvider.java
package com.healthstatswidgetapp;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.SharedPreferences;
import android.widget.RemoteViews;
import org.json.JSONObject;
public class HealthStatsWidgetProvider extends AppWidgetProvider {
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
try {
SharedPreferences sharedPref = context.getSharedPreferences("DATA", Context.MODE_PRIVATE);
String healthDataString = sharedPref.getString("healthData", "{\"steps\":0,\"sleepScore\":0}");
JSONObject healthData = new JSONObject(healthDataString);
// Konstruieren Sie das RemoteViews-Objekt
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.health_stats_widget);
views.setTextViewText(R.id.widget_steps, "Steps: " + healthData.getInt("steps"));
views.setTextViewText(R.id.widget_sleep, "Sleep Score: " + healthData.getInt("sleepScore"));
// Weisen Sie den Widget-Manager an, das Widget zu aktualisieren
appWidgetManager.updateAppWidget(appWidgetId, views);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// Es können mehrere Widgets aktiv sein, also aktualisieren wir alle
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
}
5. Daten von React Native zum Android-Widget senden:
Lassen Sie uns nun unseren React Native-Code aktualisieren, um das neue native Modul für Android zu verwenden.
// src/App.js (mit Android-Ergänzungen)
import React from 'react';
import { SafeAreaView, Button, View, Text, NativeModules, Platform } from 'react-native';
import SharedGroupPreferences from 'react-native-shared-group-preferences';
const { SharedStorage } = NativeModules;
const App = () => {
const appGroupIdentifier = 'group.com.yourcompany.HealthStatsWidgetApp'; // Für iOS
const updateWidgetData = async () => {
const healthData = {
steps: Math.floor(Math.random() * 10000),
sleepScore: Math.floor(Math.random() * 100),
};
if (Platform.OS === 'ios') {
try {
await SharedGroupPreferences.setItem('healthData', healthData, appGroupIdentifier);
console.log('iOS-Daten im gemeinsamen Speicher gespeichert');
} catch (error) {
console.error('Fehler beim Speichern der iOS-Daten:', error);
}
} else if (Platform.OS === 'android') {
try {
await SharedStorage.set(JSON.stringify(healthData));
console.log('Android-Daten im gemeinsamen Speicher gespeichert');
} catch (error) {
console.error('Fehler beim Speichern der Android-Daten:', error);
}
}
};
return (
<SafeAreaView>
<View style={{ padding: 20 }}>
<Text style={{ fontSize: 18, marginBottom: 20 }}>Health Stats App</Text>
<Button title="Widget-Daten aktualisieren" onPress={updateWidgetData} />
</View>
</SafeAreaView>
);
};
export default App;
Wie es funktioniert
- Wir haben ein benutzerdefiniertes natives Modul
SharedStorageerstellt, das eineset-Methode für unseren JavaScript-Code bereitstellt. - Diese Methode verwendet die
SharedPreferencesvon Android, um die Gesundheitsdaten als JSON-String zu speichern. - Der
HealthStatsWidgetProviderist einAppWidgetProvider, ein Broadcast-Receiver, der Widget-Updates verarbeitet. - In der
onUpdate-Methode lesen wir die Daten ausSharedPreferences, parsen das JSON und verwendenRemoteViews, um den Text in unserem Widget-Layout zu aktualisieren.
Alles zusammenführen
Nachdem Sie die Schritte für sowohl iOS als auch Android implementiert haben, können Sie Ihre App auf beiden Plattformen ausführen.
Testschritte:
- Führen Sie die App auf Ihrem Simulator oder Gerät aus:
- Für iOS:
npx react-native run-ios - Für Android:
npx react-native run-android
- Für iOS:
- Öffnen Sie die App und drücken Sie die Schaltfläche „Widget-Daten aktualisieren".
- Gehen Sie zu Ihrem Startbildschirm und fügen Sie das „Health Stats"-Widget hinzu.
- Sie sollten die Gesundheitsdaten auf Ihrem Widget angezeigt sehen.
- Gehen Sie zurück zur App, drücken Sie erneut die Schaltfläche, und das Widget sollte aktualisiert werden (Hinweis: Widget-Updates auf Android können eine leichte Verzögerung haben).
Best Practices für die Sicherheit
- Sensible Daten: Für dieses Beispiel verwenden wir einfache, nicht-sensible Daten. Wenn Sie sensible Informationen anzeigen müssen, sollten Sie die Daten vor der Speicherung in den gemeinsamen Einstellungen verschlüsseln.
- Datenvalidierung: Validieren Sie die Daten immer auf der nativen Seite, bevor Sie sie im Widget anzeigen, um Abstürze durch fehlerhafte Daten zu verhindern.
Fazit
Sie haben erfolgreich ein plattformübergreifendes Startbildschirm-Widget für Ihre React Native-Anwendung erstellt! Auch wenn es erforderlich ist, in nativen Code einzutauchen, ist der Prozess überschaubar und erschließt eine leistungsstarke Möglichkeit, Ihre Benutzer engagiert zu halten. Wir haben gesehen, wie man native UI für Widgets auf sowohl iOS als auch Android erstellt und wie man eine Datenbrücke etabliert, um Informationen von Ihrer React Native-App zu teilen.
Nächste Schritte für Leser:
- Erkunden Sie verschiedene Widget-Größen und -Layouts.
- Implementieren Sie Hintergrundabruf, um das Widget periodisch ohne Benutzerinteraktion zu aktualisieren.
- Fügen Sie Deep Linking hinzu, damit das Antippen des Widgets einen bestimmten Bildschirm in Ihrer App öffnet.
Ressourcen
- Apple Developer: Creating a Widget Extension
- Android Developer: App Widgets
- react-native-shared-group-preferences
Kurzübersicht
| Aspekt | Wichtige Informationen |
|---|---|
| Thema | Entwicklung |
| Relevanz | Patientenaufklärung und -sensibilisierung |
| Maßnahme | Besprechen Sie dies mit Ihrem Arzt |
| Häufigkeit | Wie von Ihrem Betreuungsteam empfohlen |
| Evidenz | Basierend auf aktuellen klinischen Richtlinien |
Häufig gestellte Fragen
F: Wie weiß ich, ob dies für mich relevant ist? A: Besprechen Sie Ihre persönliche und familiäre Krankengeschichte mit Ihrem Arzt, um Ihre individuellen Risikofaktoren zu bewerten und geeignete nächste Schritte zu bestimmen.
F: Was sollte ich meinen Arzt fragen? A: Gute Fragen sind unter anderem: „Was bedeuten meine Ergebnisse?", „Was sind meine Optionen?", „Welche Lebensstiländerungen würden helfen?" und „Wann sollte ich nachuntersuchen lassen?"
F: Gibt es Lebensstiländerungen, die helfen können? A: Die meisten Erkrankungen profitieren von grundlegenden Maßnahmen: ausgewogene Ernährung, regelmäßige körperliche Aktivität (150+ Minuten/Woche), qualitativ hochwertiger Schlaf (7-9 Stunden), Stressmanagement und Vermeidung von Tabak.
Haftungsausschluss
Dieser Artikel dient nur zu Bildungszwecken und stellt keine medizinische Beratung dar. Konsultieren Sie immer einen qualifizierten Arzt, bevor Sie Entscheidungen über Ihre Gesundheit, Behandlung oder Pflege treffen. Individuelle Umstände variieren, und was für eine Person angemessen ist, ist möglicherweise nicht für eine andere Person geeignet.
Wenn Sie einen medizinischen Notfall haben, rufen Sie sofort 911 (oder Ihre lokale Notrufnummer) an.