1. Introduction
Que sont les widgets ?
Pour les développeurs Flutter, la définition courante de widget fait référence aux composants d'interface utilisateur créés à l'aide du framework Flutter. Dans le contexte de cet atelier de programmation, un widget fait référence à une version miniature d'une application qui permet d'afficher des informations sur l'application sans l'ouvrir. Sur Android, les widgets se trouvent sur l'écran d'accueil. Sur iOS, ils peuvent être ajoutés à l'écran d'accueil, à l'écran de verrouillage ou à la vue "Aujourd'hui".

Quelle est la complexité maximale d'un widget ?
La plupart des widgets d'écran d'accueil sont simples. Ils peuvent contenir du texte de base, des graphiques simples ou, sur Android, des commandes de base. Android et iOS limitent les composants et fonctionnalités d'UI que vous pouvez utiliser.

Créer l'UI pour les widgets
En raison de ces limitations de l'UI, vous ne pouvez pas dessiner directement l'UI d'un widget d'écran d'accueil à l'aide du framework Flutter. Vous pouvez plutôt ajouter à votre application Flutter des widgets créés avec des frameworks de plate-forme tels que Jetpack Compose ou SwiftUI. Cet atelier de programmation présente des exemples de partage de ressources entre votre application et les widgets pour éviter de réécrire une UI complexe.
Objectifs de l'atelier
Dans cet atelier de programmation, vous allez créer des widgets d'écran d'accueil sur Android et iOS pour une application Flutter simple, à l'aide du package home_widget, qui permet aux utilisateurs de lire des articles. Vos widgets :
- Affichez les données de votre application Flutter.
- Affichez du texte à l'aide des composants de police partagés depuis l'application Flutter.
- Affichez une image d'un widget Flutter rendu.

Cette application Flutter comprend deux écrans (ou routes) :
- Le premier affiche une liste d'articles d'actualité avec des titres et des descriptions.
- La deuxième affiche l'article complet avec un graphique créé à l'aide de
CustomPaint.
.

Points abordés
- Découvrez comment créer des widgets sur l'écran d'accueil sur iOS et Android.
- Utiliser le package home_widget pour partager des données entre le widget de l'écran d'accueil et l'application Flutter
- Comment réduire la quantité de code à réécrire
- Comment mettre à jour le widget de l'écran d'accueil depuis votre application Flutter
2. Configurer l'environnement de développement
Pour les deux plates-formes, vous avez besoin du SDK Flutter et d'un EDI. Vous pouvez utiliser l'IDE de votre choix pour travailler avec Flutter. Il peut s'agir de Visual Studio Code avec les extensions Dart Code et Flutter, ou d'Android Studio ou IntelliJ avec les plug-ins Flutter et Dart installés.
Pour créer le widget de l'écran d'accueil iOS :
- Vous pouvez exécuter cet atelier de programmation sur un appareil iOS physique ou sur le simulateur iOS.
- Vous devez configurer un système macOS avec l'IDE Xcode. Cela installe le compilateur nécessaire pour créer la version iOS de votre application.
Pour créer le widget de l'écran d'accueil Android :
- Vous pouvez exécuter cet atelier de programmation sur un appareil Android physique ou sur l'émulateur Android.
- Vous devez configurer votre système de développement avec Android Studio. Cela installe le compilateur nécessaire pour créer la version Android de votre application.
Télécharger le code de démarrage
Télécharger la version initiale du projet sur GitHub
Dans la ligne de commande, clonez le dépôt GitHub dans un répertoire flutter-codelabs :
$ git clone https://github.com/flutter/codelabs.git flutter-codelabs
Après avoir cloné le dépôt, vous trouverez le code de cet atelier de programmation dans le répertoire flutter-codelabs/homescreen_codelab. Ce répertoire contient le code du projet finalisé pour chaque étape de l'atelier de programmation.
Ouvrir l'application de démarrage
Ouvrez le répertoire flutter-codelabs/homescreen_codelab/step_03 dans l'IDE de votre choix.
Installer des packages
Tous les packages requis ont été ajoutés au fichier pubspec.yaml du projet. Pour récupérer les dépendances du projet, exécutez la commande suivante :
$ flutter pub get
3. Ajouter un widget d'écran d'accueil de base
Commencez par ajouter le widget de l'écran d'accueil à l'aide des outils natifs de la plate-forme.
Créer un widget d'écran d'accueil iOS de base
L'ajout d'une extension d'application à votre application Flutter iOS est semblable à l'ajout d'une extension d'application à une application SwiftUI ou UIKit :
- Exécutez
open ios/Runner.xcworkspacedans une fenêtre de terminal à partir du répertoire de votre projet Flutter. Vous pouvez également effectuer un clic droit sur le dossier ios dans VSCode, puis sélectionner Open in Xcode (Ouvrir dans Xcode). L'espace de travail Xcode par défaut s'ouvre dans votre projet Flutter. - Dans le menu, sélectionnez File → New → Target (Fichier → Nouveau → Cible). Cela ajoute une cible au projet.
- Une liste de modèles s'affiche. Sélectionnez Widget Extension (Extension de widget).
- Saisissez "NewsWidgets" dans le champ Nom du produit pour ce widget. Décochez les cases Inclure l'activité en direct et Inclure l'intention de configuration.
Inspecter l'exemple de code
Lorsque vous ajoutez une cible, Xcode génère un exemple de code basé sur le modèle que vous avez sélectionné. Pour en savoir plus sur le code généré et WidgetKit, consultez la documentation d'Apple sur les extensions d'application .
Déboguer et tester votre widget exemple
- Commencez par mettre à jour la configuration de votre application Flutter. Vous devez effectuer cette opération lorsque vous ajoutez des packages à votre application Flutter et que vous prévoyez d'exécuter une cible dans le projet à partir de Xcode. Pour mettre à jour la configuration de votre application, exécutez la commande suivante dans le répertoire de votre application Flutter :
$ flutter build ios --config-only
- Cliquez sur Runner pour afficher la liste des cibles. Sélectionnez la cible du widget que vous venez de créer (NewsWidgets), puis cliquez sur Run (Exécuter). Exécutez la cible du widget à partir de Xcode lorsque vous modifiez le code du widget iOS.

- L'écran du simulateur ou de l'appareil doit afficher un widget d'écran d'accueil de base. Si vous ne la voyez pas, vous pouvez l'ajouter à l'écran. Cliquez de manière prolongée sur l'écran d'accueil, puis sur + en haut à gauche.

- Recherchez le nom de l'application. Pour cet atelier de programmation, recherchez "Homescreen Widgets" (Widgets de l'écran d'accueil).

- Une fois le widget Écran d'accueil ajouté, il devrait afficher un texte simple indiquant l'heure.
Créer un widget Android de base
- Pour ajouter un widget d'écran d'accueil dans Android, ouvrez le fichier de compilation du projet dans Android Studio. Vous trouverez ce fichier à l'adresse android/build.gradle. Vous pouvez également effectuer un clic droit sur le dossier android dans VSCode, puis sélectionner Open in Android Studio (Ouvrir dans Android Studio).
- Une fois le projet créé, recherchez le répertoire de l'application en haut à gauche. Ajoutez votre nouveau widget d'écran d'accueil à ce répertoire. Effectuez un clic droit sur le répertoire, puis sélectionnez New -> Widget -> App Widget (Nouveau -> Widget -> Widget d'application).

- Android Studio affiche un nouveau formulaire. Ajoutez des informations de base sur votre widget d'écran d'accueil, y compris son nom de classe, son emplacement, sa taille et sa langue source.
Pour cet atelier de programmation, définissez les valeurs suivantes :
- Dans le champ Nom de la classe, saisissez NewsWidget.
- Définissez le menu déroulant Largeur minimale (cellules) sur 3.
- Définissez le menu déroulant Hauteur minimale (cellules) sur 3.
Inspecter l'exemple de code
Lorsque vous envoyez le formulaire, Android Studio crée et met à jour plusieurs fichiers. Les modifications pertinentes pour cet atelier de programmation sont répertoriées dans le tableau ci-dessous.
Action | Fichier cible | Modifier |
Mettre à jour |
| Ajoute un récepteur qui enregistre NewsWidget. |
Créer |
| Définit l'UI du widget de l'écran d'accueil. |
Créer |
| Définit la configuration de votre widget d'écran d'accueil. Vous pouvez ajuster les dimensions ou le nom de votre widget dans ce fichier. |
Créer |
| Contient votre code Kotlin pour ajouter des fonctionnalités à votre widget d'écran d'accueil. |
Vous trouverez plus d'informations sur ces fichiers tout au long de cet atelier de programmation.
Déboguer et tester votre widget exemple
Exécutez maintenant votre application et affichez le widget de l'écran d'accueil. Une fois l'application créée, accédez à l'écran de sélection des applications de votre appareil Android et appuyez de manière prolongée sur l'icône de ce projet Flutter. Sélectionnez Widgets dans le menu pop-up.

L'appareil ou l'émulateur Android affiche votre widget d'écran d'accueil par défaut pour Android.
4. Envoyer des données de votre application Flutter à votre widget d'écran d'accueil
Vous pouvez personnaliser le widget d'écran d'accueil de base que vous avez créé. Mettez à jour le widget de l'écran d'accueil pour afficher un titre et un résumé d'un article d'actualité. La capture d'écran suivante montre un exemple du widget de l'écran d'accueil affichant un titre et un résumé.

Pour transmettre des données entre votre application et le widget de l'écran d'accueil, vous devez écrire du code Dart et natif. Cette section divise ce processus en trois parties :
- Écrire du code Dart dans votre application Flutter que les deux plates-formes (Android et iOS) peuvent utiliser
- Ajouter des fonctionnalités iOS natives
- Ajouter des fonctionnalités Android natives
Utiliser les groupes d'applications iOS
Pour partager des données entre une application parent iOS et une extension de widget, les deux cibles doivent appartenir au même groupe d'applications. Pour en savoir plus sur les groupes d'applications, consultez la documentation Apple sur les groupes d'applications.
Mettez à jour l'identifiant du bundle :
Dans Xcode, accédez aux paramètres de votre cible. Dans l'onglet Signing & Capabilities (Signature et capacités), vérifiez que votre équipe et l'identifiant du bundle sont définis.
Ajoutez le groupe d'applications aux cibles Runner et NewsWidgetExtension dans Xcode :
Sélectionnez + Capability > App Groups (+ Capacité > Groupes d'applications), puis ajoutez un groupe d'applications. Répétez l'opération pour la cible Runner (application parente) et la cible du widget.

Ajouter le code Dart
Les applications iOS et Android peuvent partager des données avec une application Flutter de différentes manières.Pour communiquer avec ces applications, utilisez le magasin key/value local de l'appareil. iOS appelle ce magasin UserDefaults et Android l'appelle SharedPreferences. Le package home_widget encapsule ces API pour simplifier l'enregistrement des données sur l'une ou l'autre plate-forme et permet aux widgets de l'écran d'accueil d'extraire les données mises à jour.

Les données de titre et de description proviennent du fichier news_data.dart. Ce fichier contient des données fictives et une classe de données NewsArticle.
lib/news_data.dart
class NewsArticle {
final String title;
final String description;
final String? articleText;
NewsArticle({
required this.title,
required this.description,
this.articleText = loremIpsum,
});
}
Mettre à jour les valeurs du titre et de la description
Pour ajouter la fonctionnalité permettant de mettre à jour le widget de votre écran d'accueil depuis votre application Flutter, accédez au fichier lib/home_screen.dart. Remplacez le contenu du fichier par le code suivant. Remplacez ensuite <YOUR APP GROUP> par l'identifiant de votre groupe d'applications.
lib/home_screen.dart
import 'package:flutter/material.dart';
import 'package:home_widget/home_widget.dart'; // Add this import
import 'article_screen.dart';
import 'news_data.dart';
// TODO: Replace with your App Group ID
const String appGroupId = '<YOUR APP GROUP>'; // Add from here
const String iOSWidgetName = 'NewsWidgets';
const String androidWidgetName = 'NewsWidget'; // To here.
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
void updateHeadline(NewsArticle newHeadline) { // Add from here
// Save the headline data to the widget
HomeWidget.saveWidgetData<String>('headline_title', newHeadline.title);
HomeWidget.saveWidgetData<String>(
'headline_description', newHeadline.description);
HomeWidget.updateWidget(
iOSName: iOSWidgetName,
androidName: androidWidgetName,
);
} // To here.
class _MyHomePageState extends State<MyHomePage> {
@override // Add from here
void initState() {
super.initState();
HomeWidget.setAppGroupId(appGroupId);
// Mock read in some data and update the headline
final newHeadline = getNewsStories()[0];
updateHeadline(newHeadline);
} // To here.
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Top Stories'),
centerTitle: false,
titleTextStyle: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
color: Colors.black)),
body: ListView.separated(
separatorBuilder: (context, idx) {
return const Divider();
},
itemCount: getNewsStories().length,
itemBuilder: (context, idx) {
final article = getNewsStories()[idx];
return ListTile(
key: Key('$idx ${article.hashCode}'),
title: Text(article.title!),
subtitle: Text(article.description!),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return ArticleScreen(article: article);
},
),
);
},
);
},
));
}
}
La fonction updateHeadline enregistre les paires clé/valeur dans l'espace de stockage local de votre appareil. La clé headline_title contient la valeur newHeadline.title. La clé headline_description contient la valeur de newHeadline.description. La fonction avertit également la plate-forme native que de nouvelles données pour les widgets de l'écran d'accueil peuvent être récupérées et affichées.
Modifier le floatingActionButton
Appelez la fonction updateHeadline lorsque l'utilisateur appuie sur floatingActionButton, comme indiqué :
lib/article_screen.dart
// New: import the updateHeadline function
import 'home_screen.dart';
...
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('Updating home screen widget...'),
));
// New: call updateHeadline
updateHeadline(widget.article);
},
label: const Text('Update Homescreen'),
),
...
Avec ce changement, lorsqu'un utilisateur appuie sur le bouton Mettre à jour le titre depuis la page d'un article, les informations du widget de l'écran d'accueil sont mises à jour.
Mettre à jour le code iOS pour afficher les données de l'article
Pour mettre à jour le widget de l'écran d'accueil pour iOS, utilisez Xcode.
Ouvrez le fichier NewsWidgets.swift dans Xcode :
Configurez le TimelineEntry.
Remplacez la structure SimpleEntry par le code suivant :
ios/NewsWidgets/NewsWidgets.swift
// The date and any data you want to pass into your app must conform to TimelineEntry
struct NewsArticleEntry: TimelineEntry {
let date: Date
let title: String
let description:String
}
Cette structure NewsArticleEntry définit les données entrantes à transmettre au widget de l'écran d'accueil lors de la mise à jour. Le type TimelineEntry nécessite un paramètre de date.Pour en savoir plus sur le protocole TimelineEntry, consultez la documentation d'Apple sur TimelineEntry.
Modifiez le View qui affiche le contenu.
Modifiez le widget de l'écran d'accueil pour afficher le titre et la description de l'article au lieu de la date. Pour afficher du texte dans SwiftUI, utilisez la vue Text. Pour empiler des vues les unes sur les autres dans SwiftUI, utilisez la vue VStack.
Remplacez la vue NewsWidgetEntryView générée par le code suivant :
ios/NewsWidgets/NewsWidgets.swift
//View that holds the contents of the widget
struct NewsWidgetsEntryView : View {
var entry: Provider.Entry
var body: some View {
VStack {
Text(entry.title)
Text(entry.description)
}
}
}
Modifiez le fournisseur pour indiquer au widget de l'écran d'accueil quand et comment effectuer la mise à jour.
Remplacez le Provider existant par le code suivant. Ensuite, remplacez <YOUR APP GROUP> par l'identifiant de votre groupe d'applications :
ios/NewsWidgets/NewsWidgets.swift
struct Provider: TimelineProvider {
// Placeholder is used as a placeholder when the widget is first displayed
func placeholder(in context: Context) -> NewsArticleEntry {
// Add some placeholder title and description, and get the current date
NewsArticleEntry(date: Date(), title: "Placeholder Title", description: "Placeholder description")
}
// Snapshot entry represents the current time and state
func getSnapshot(in context: Context, completion: @escaping (NewsArticleEntry) -> ()) {
let entry: NewsArticleEntry
if context.isPreview{
entry = placeholder(in: context)
}
else{
// Get the data from the user defaults to display
let userDefaults = UserDefaults(suiteName: <YOUR APP GROUP>)
let title = userDefaults?.string(forKey: "headline_title") ?? "No Title Set"
let description = userDefaults?.string(forKey: "headline_description") ?? "No Description Set"
entry = NewsArticleEntry(date: Date(), title: title, description: description)
}
completion(entry)
}
// getTimeline is called for the current and optionally future times to update the widget
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
// This just uses the snapshot function you defined earlier
getSnapshot(in: context) { (entry) in
// atEnd policy tells widgetkit to request a new entry after the date has passed
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
}
}
Le Provider du code précédent est conforme à un TimelineProvider. Provider comporte trois méthodes différentes :
- La méthode
placeholdergénère une entrée d'espace réservé lorsque l'utilisateur prévisualise le widget de l'écran d'accueil pour la première fois.

- La méthode
getSnapshotlit les données à partir des paramètres utilisateur par défaut et génère l'entrée pour l'heure actuelle. - La méthode
getTimelinerenvoie des entrées de chronologie. Cela s'avère utile lorsque vous avez des points temporels prévisibles pour mettre à jour votre contenu. Cet atelier de programmation utilise la fonction getSnapshot pour obtenir l'état actuel. La méthode.atEndindique au widget de l'écran d'accueil d'actualiser les données après le passage de l'heure actuelle.
Mettre en commentaire le NewsWidgets_Previews
L'utilisation des aperçus ne fait pas partie de cet atelier de programmation. Pour en savoir plus sur la prévisualisation des widgets SwiftUI sur l'écran d'accueil, consultez la documentation d'Apple sur le débogage des widgets.
Enregistrez tous les fichiers et réexécutez la cible de l'application et du widget.
Exécutez à nouveau les cibles pour vérifier que l'application et le widget de l'écran d'accueil fonctionnent.
- Sélectionnez le schéma de l'application dans Xcode pour exécuter la cible de l'application.
- Sélectionnez le schéma d'extension dans Xcode pour exécuter la cible de l'extension.
- Accédez à la page d'un article dans l'application.
- Cliquez sur le bouton pour modifier le titre. Le widget de l'écran d'accueil devrait également mettre à jour le titre.
Mettre à jour le code Android
Ajoutez le fichier XML du widget de l'écran d'accueil.
Dans Android Studio, mettez à jour les fichiers générés à l'étape précédente.Ouvrez le fichier res/layout/news_widget.xml. Il définit la structure et la mise en page du widget de votre écran d'accueil. Sélectionnez Code en haut à droite, puis remplacez le contenu de ce fichier par le code suivant :
android/app/res/layout/news_widget.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/widget_container"
style="@style/Widget.Android.AppWidget.Container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@android:color/white"
android:theme="@style/Theme.Android.AppWidgetContainer">
<TextView
android:id="@+id/headline_title"
style="@style/Widget.Android.AppWidget.InnerView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:background="@android:color/white"
android:text="Title"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/headline_description"
style="@style/Widget.Android.AppWidget.InnerView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/headline_title"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="4dp"
android:background="@android:color/white"
android:text="Title"
android:textSize="16sp" />
</RelativeLayout>
Ce fichier XML définit deux vues de texte : l'une pour le titre de l'article et l'autre pour sa description. Ces vues de texte définissent également le style. Vous reviendrez à ce fichier tout au long de cet atelier de programmation.
Mise à jour de la fonctionnalité NewsWidget
Ouvrez le fichier de code source Kotlin NewsWidget.kt. Ce fichier contient une classe générée appelée NewsWidget qui étend la classe AppWidgetProvider.
La classe NewsWidget contient trois méthodes de sa superclasse. Vous allez modifier la méthode onUpdate. Android appelle cette méthode pour les widgets à intervalles fixes.
Remplacez le contenu du fichier NewsWidget.kt par le code suivant :
android/app/java/com.mydomain.homescreen_widgets/NewsWidget.kt
// Import will depend on App ID.
package com.mydomain.homescreen_widgets
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.widget.RemoteViews
// New import.
import es.antonborri.home_widget.HomeWidgetPlugin
/**
* Implementation of App Widget functionality.
*/
class NewsWidget : AppWidgetProvider() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray,
) {
for (appWidgetId in appWidgetIds) {
// Get reference to SharedPreferences
val widgetData = HomeWidgetPlugin.getData(context)
val views = RemoteViews(context.packageName, R.layout.news_widget).apply {
val title = widgetData.getString("headline_title", null)
setTextViewText(R.id.headline_title, title ?: "No title set")
val description = widgetData.getString("headline_description", null)
setTextViewText(R.id.headline_description, description ?: "No description set")
}
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
}
Désormais, lorsque onUpdate est appelé, Android récupère les valeurs les plus récentes du stockage local à l'aide de la méthode the widgetData.getString(), puis appelle setTextViewText pour modifier le texte affiché sur le widget de l'écran d'accueil.
Tester les mises à jour
Testez l'application pour vous assurer que les widgets de l'écran d'accueil sont mis à jour avec les nouvelles données. Pour mettre à jour les données, utilisez Mettre à jour l'écran d'accueil FloatingActionButton sur les pages des articles. Le widget de votre écran d'accueil devrait se mettre à jour avec le titre de l'article.

5. Utiliser des polices personnalisées d'application Flutter dans votre widget d'écran d'accueil iOS
Jusqu'à présent, vous avez configuré le widget de l'écran d'accueil pour lire les données fournies par l'application Flutter. L'application Flutter inclut une police personnalisée que vous pouvez utiliser dans le widget de l'écran d'accueil. Vous pouvez utiliser la police personnalisée dans le widget de l'écran d'accueil iOS. L'utilisation de polices personnalisées dans les widgets de l'écran d'accueil n'est pas disponible sur Android.
Mettre à jour le code iOS
Flutter stocke ses composants dans le mainBundle des applications iOS. Vous pouvez accéder aux composants de ce bundle à partir du code du widget de votre écran d'accueil.
Dans la structure NewsWidgetsEntryView de votre fichier NewsWidgets.swift, apportez les modifications suivantes :
Créez une fonction d'assistance pour obtenir le chemin d'accès au répertoire des éléments Flutter :
ios/NewsWidgets/NewsWidgets.swift
struct NewsWidgetsEntryView : View {
...
// New: Add the helper function.
var bundle: URL {
let bundle = Bundle.main
if bundle.bundleURL.pathExtension == "appex" {
// Peel off two directory levels - MY_APP.app/PlugIns/MY_APP_EXTENSION.appex
var url = bundle.bundleURL.deletingLastPathComponent().deletingLastPathComponent()
url.append(component: "Frameworks/App.framework/flutter_assets")
return url
}
return bundle.bundleURL
}
...
}
Enregistrez la police à l'aide de l'URL de votre fichier de police personnalisée.
ios/NewsWidgets/NewsWidgets.swift
struct NewsWidgetsEntryView : View {
...
// New: Register the font.
init(entry: Provider.Entry){
self.entry = entry
CTFontManagerRegisterFontsForURL(bundle.appending(path: "/fonts/Chewy-Regular.ttf") as CFURL, CTFontManagerScope.process, nil)
}
...
}
Mettez à jour la vue Texte du titre pour utiliser votre police personnalisée.
ios/NewsWidgets/NewsWidgets.swift
struct NewsWidgetsEntryView : View {
...
var body: some View {
VStack {
// Update the following line.
Text(entry.title).font(Font.custom("Chewy", size: 13))
Text(entry.description)
}
}
...
}
Lorsque vous exécutez votre widget d'écran d'accueil, il utilise désormais la police personnalisée pour le titre, comme illustré dans l'image suivante :

6. Afficher des widgets Flutter en tant qu'image
Dans cette section, vous allez afficher un graphique de votre application Flutter en tant que widget sur l'écran d'accueil.
Ce widget offre un défi plus important que le texte que vous avez affiché sur l'écran d'accueil. Il est beaucoup plus facile d'afficher le graphique Flutter sous forme d'image que d'essayer de le recréer à l'aide de composants d'UI natifs.
Codez votre widget d'écran d'accueil pour afficher votre graphique Flutter sous forme de fichier PNG. Le widget de votre écran d'accueil peut afficher cette image.
Écrire le code Dart
Du côté de Dart, ajoutez la méthode renderFlutterWidget à partir du package home_widget. Cette méthode utilise un widget, un nom de fichier et une clé. Il renvoie une image du widget Flutter et l'enregistre dans un conteneur partagé. Indiquez le nom de l'image dans votre code et assurez-vous que le widget de l'écran d'accueil peut accéder au conteneur. key enregistre le chemin d'accès complet au fichier sous forme de chaîne dans l'espace de stockage local de l'appareil. Cela permet au widget de l'écran d'accueil de trouver le fichier si son nom change dans le code Dart.
Pour cet atelier de programmation, la classe LineChart du fichier lib/article_screen.dart représente le graphique. Sa méthode de compilation renvoie un CustomPainter qui peint ce graphique à l'écran.
Pour implémenter cette fonctionnalité, ouvrez le fichier lib/article_screen.dart. Importez le package home_widget. Ensuite, remplacez le code de la classe _ArticleScreenState par le code suivant :
lib/article_screen.dart
import 'package:flutter/material.dart';
// New: import the home_widget package.
import 'package:home_widget/home_widget.dart';
import 'home_screen.dart';
import 'news_data.dart';
...
class _ArticleScreenState extends State<ArticleScreen> {
// New: add this GlobalKey
final _globalKey = GlobalKey();
String? imagePath;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.article.title!),
),
// New: add this FloatingActionButton
floatingActionButton: FloatingActionButton.extended(
onPressed: () async {
if (_globalKey.currentContext != null) {
var path = await HomeWidget.renderFlutterWidget(
const LineChart(),
fileName: 'screenshot',
key: 'filename',
logicalSize: _globalKey.currentContext!.size,
pixelRatio:
MediaQuery.of(_globalKey.currentContext!).devicePixelRatio,
);
setState(() {
imagePath = path as String?;
});
}
updateHeadline(widget.article);
},
label: const Text('Update Homescreen'),
),
body: ListView(
padding: const EdgeInsets.all(16.0),
children: [
Text(
widget.article.description!,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 20.0),
Text(widget.article.articleText!),
const SizedBox(height: 20.0),
Center(
// New: Add this key
key: _globalKey,
child: const LineChart(),
),
const SizedBox(height: 20.0),
Text(widget.article.articleText!),
],
),
);
}
}
Cet exemple apporte trois modifications à la classe _ArticleScreenState.
Crée un GlobalKey.
GlobalKey obtient le contexte du widget spécifique, qui est nécessaire pour obtenir la taille de ce widget .
lib/article_screen.dart
class _ArticleScreenState extends State<ArticleScreen> {
// New: add this GlobalKey
final _globalKey = GlobalKey();
...
}
Ajoute imagePath
La propriété imagePath stocke l'emplacement de l'image où le widget Flutter est affiché.
lib/article_screen.dart
class _ArticleScreenState extends State<ArticleScreen> {
...
// New: add this imagePath
String? imagePath;
...
}
Ajoute la clé au widget à afficher
_globalKey contient le widget Flutter qui est rendu dans l'image. Dans ce cas, le widget Flutter est le widget Center qui contient le LineChart.
lib/article_screen.dart
class _ArticleScreenState extends State<ArticleScreen> {
...
Center(
// New: Add this key
key: _globalKey,
child: const LineChart(),
),
...
}
- Enregistre le widget en tant qu'image
La méthode renderFlutterWidget est appelée lorsque l'utilisateur clique sur floatingActionButton. La méthode enregistre le fichier PNG obtenu sous le nom "screenshot" dans le répertoire du conteneur partagé. La méthode enregistre également le chemin d'accès complet à l'image en tant que clé de nom de fichier dans le stockage de l'appareil.
lib/article_screen.dart
class _ArticleScreenState extends State<ArticleScreen> {
...
floatingActionButton: FloatingActionButton.extended(
onPressed: () async {
if (_globalKey.currentContext != null) {
var path = await HomeWidget.renderFlutterWidget(
LineChart(),
fileName: 'screenshot',
key: 'filename',
logicalSize: _globalKey.currentContext!.size,
pixelRatio:
MediaQuery.of(_globalKey.currentContext!).devicePixelRatio,
);
setState(() {
imagePath = path as String?;
});
}
updateHeadline(widget.article);
},
...
}
Mettre à jour le code iOS
Pour iOS, mettez à jour le code afin d'obtenir le chemin d'accès au fichier depuis le stockage et d'afficher le fichier en tant qu'image à l'aide de SwiftUI.
Ouvrez le fichier NewsWidgets.swift pour apporter les modifications suivantes :
Ajoutez filename et displaySize à la struct NewsArticleEntry.
La propriété filename contient la chaîne représentant le chemin d'accès au fichier image. La propriété displaySize contient la taille du widget de l'écran d'accueil sur l'appareil de l'utilisateur. La taille du widget de l'écran d'accueil provient de context.
ios/NewsWidgets/NewsWidgets.swift
struct NewsArticleEntry: TimelineEntry {
...
// New: add the filename and displaySize.
let filename: String
let displaySize: CGSize
}
Mettre à jour la fonction placeholder
Incluez un espace réservé filename et displaySize.
ios/NewsWidgets/NewsWidgets.swift
func placeholder(in context: Context) -> NewsArticleEntry {
NewsArticleEntry(date: Date(), title: "Placeholder Title", description: "Placeholder description", filename: "No screenshot available", displaySize: context.displaySize)
}
Récupérez le nom de fichier à partir de userDefaults dans getSnapshot.
Cela définit la variable filename sur la valeur filename dans le stockage userDefaults lorsque le widget de l'écran d'accueil est mis à jour.
ios/NewsWidgets/NewsWidgets.swift
func getSnapshot(
...
let title = userDefaults?.string(forKey: "headline_title") ?? "No Title Set"
let description = userDefaults?.string(forKey: "headline_description") ?? "No Description Set"
// New: get fileName from key/value store
let filename = userDefaults?.string(forKey: "filename") ?? "No screenshot available"
...
)
Créez un ChartImage qui affiche l'image à partir d'un chemin d'accès
La vue ChartImage crée une image à partir du contenu du fichier généré côté Dart. Ici, vous définissez la taille sur 50 % du cadre.
ios/NewsWidgets/NewsWidgets.swift
struct NewsWidgetsEntryView : View {
...
// New: create the ChartImage view
var ChartImage: some View {
if let uiImage = UIImage(contentsOfFile: entry.filename) {
let image = Image(uiImage: uiImage)
.resizable()
.frame(width: entry.displaySize.height*0.5, height: entry.displaySize.height*0.5, alignment: .center)
return AnyView(image)
}
print("The image file could not be loaded")
return AnyView(EmptyView())
}
...
}
Utiliser ChartImage dans le corps de NewsWidgetsEntryView
Ajoutez la vue ChartImage au corps de NewsWidgetsEntryView pour afficher ChartImage dans le widget de l'écran d'accueil.
ios/NewsWidgets/NewsWidgets.swift
VStack {
Text(entry.title).font(Font.custom("Chewy", size: 13))
Text(entry.description).font(.system(size: 12)).padding(10)
// New: add the ChartImage to the NewsWidgetEntryView
ChartImage
}
Tester les modifications
Pour tester les modifications, réexécutez la cible de votre application Flutter (Runner) et la cible de votre extension depuis Xcode. Pour afficher l'image, accédez à l'une des pages d'article de l'application et appuyez sur le bouton permettant de mettre à jour le widget de l'écran d'accueil.

Mettre à jour le code Android
Le code Android fonctionne de la même manière que le code iOS.
- Ouvrez le fichier
android/app/res/layout/news_widget.xml. Il contient les éléments d'UI de votre widget d'écran d'accueil. Remplacez son contenu par le code suivant :
android/app/res/layout/news_widget.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/widget_container"
style="@style/Widget.Android.AppWidget.Container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@android:color/white"
android:theme="@style/Theme.Android.AppWidgetContainer">
<TextView
android:id="@+id/headline_title"
style="@style/Widget.Android.AppWidget.InnerView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:background="@android:color/white"
android:text="Title"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/headline_description"
style="@style/Widget.Android.AppWidget.InnerView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/headline_title"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="4dp"
android:background="@android:color/white"
android:text="Title"
android:textSize="16sp" />
<!--New: add this image view -->
<ImageView
android:id="@+id/widget_image"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_below="@+id/headline_description"
android:layout_alignBottom="@+id/headline_title"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="-134dp"
android:layout_weight="1"
android:adjustViewBounds="true"
android:background="@android:color/white"
android:scaleType="fitCenter"
android:src="@android:drawable/star_big_on"
android:visibility="visible"
tools:visibility="visible" />
</RelativeLayout>
Ce nouveau code ajoute une image au widget de l'écran d'accueil, qui affiche (pour l'instant) une icône étoile générique. Remplacez cette icône en forme d'étoile par l'image que vous avez enregistrée dans le code Dart.
- Ouvrez le fichier
NewsWidget.kt. Remplacez son contenu par le code suivant :
android/app/java/com.mydomain.homescreen_widgets/NewsWidget.kt
// Import will depend on App ID.
package com.mydomain.homescreen_widgets
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.widget.RemoteViews
import java.io.File
import es.antonborri.home_widget.HomeWidgetPlugin
/**
* Implementation of App Widget functionality.
*/
class NewsWidget : AppWidgetProvider() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray,
) {
for (appWidgetId in appWidgetIds) {
val widgetData = HomeWidgetPlugin.getData(context)
val views = RemoteViews(context.packageName, R.layout.news_widget).apply {
val title = widgetData.getString("headline_title", null)
setTextViewText(R.id.headline_title, title ?: "No title set")
val description = widgetData.getString("headline_description", null)
setTextViewText(R.id.headline_description, description ?: "No description set")
// New: Add the section below
// Get chart image and put it in the widget, if it exists
val imageName = widgetData.getString("filename", null)
val imageFile = File(imageName)
val imageExists = imageFile.exists()
if (imageExists) {
val myBitmap: Bitmap = BitmapFactory.decodeFile(imageFile.absolutePath)
setImageViewBitmap(R.id.widget_image, myBitmap)
} else {
println("image not found!, looked @: ${imageName}")
}
// End new code
}
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
}
Ce code Dart enregistre une capture d'écran dans le stockage local avec la clé filename. Il obtient également le chemin d'accès complet de l'image et crée un objet File à partir de celui-ci. Si l'image existe, le code Dart remplace l'image du widget de l'écran d'accueil par la nouvelle image.
- Rechargez votre application et accédez à l'écran d'un article. Appuyez sur Mettre à jour l'écran d'accueil. Le widget de l'écran d'accueil affiche le graphique.
7. Étapes suivantes
Félicitations !
Félicitations, vous avez réussi à créer des widgets d'écran d'accueil pour vos applications Flutter iOS et Android !
Créer des liens vers du contenu dans votre application Flutter
Vous pouvez rediriger l'utilisateur vers une page spécifique de votre application, en fonction de l'endroit où il clique. Par exemple, dans l'application d'actualités de cet atelier de programmation, vous pouvez souhaiter que l'utilisateur puisse consulter l'article correspondant au titre affiché.
Cette fonctionnalité ne fait pas partie de cet atelier de programmation. Vous trouverez des exemples d'utilisation d'un flux fourni par le package home_widget pour identifier les lancements d'applications à partir des widgets de l'écran d'accueil et envoyer des messages à partir du widget de l'écran d'accueil via l'URL. Pour en savoir plus, consultez la documentation sur le lien profond sur docs.flutter.dev.
Mettre à jour votre widget en arrière-plan
Dans cet atelier de programmation, vous avez déclenché la mise à jour du widget de l'écran d'accueil à l'aide d'un bouton. Bien que cela soit raisonnable pour les tests, dans le code de production, vous pouvez souhaiter que votre application mette à jour le widget de l'écran d'accueil en arrière-plan. Vous pouvez utiliser le plug-in WorkManager pour créer des tâches en arrière-plan afin de mettre à jour les ressources dont le widget de l'écran d'accueil a besoin. Pour en savoir plus, consultez la section Mise à jour en arrière-plan dans le package home_widget.
Pour iOS, vous pouvez également demander au widget de l'écran d'accueil d'envoyer une requête réseau pour mettre à jour son interface utilisateur. Pour contrôler les conditions ou la fréquence de cette demande, utilisez la chronologie. Pour en savoir plus sur l'utilisation de Timeline, consultez la documentation d'Apple sur la mise à jour d'un widget .