1. Giriş
Widget'lar nedir?
Flutter geliştiricileri için widget'ın yaygın tanımı, Flutter çerçevesi kullanılarak oluşturulan kullanıcı arayüzü bileşenlerini ifade eder. Bu codelab bağlamında widget, uygulamayı açmadan uygulamanın bilgilerine göz atma imkanı sunan, uygulamanın mini bir sürümünü ifade eder. Android'de widget'lar ana ekranda bulunur. iOS'te ana ekrana, kilit ekranına veya Bugün görünümüne eklenebilirler.

Widget'lar ne kadar karmaşık olabilir?
Çoğu ana ekran widget'ı basittir. Temel metin, basit grafikler veya Android'de temel kontroller içerebilirler. Hem Android hem de iOS, kullanabileceğiniz kullanıcı arayüzü bileşenlerini ve özellikleri sınırlar.

Widget'lar için kullanıcı arayüzü oluşturma
Bu kullanıcı arayüzü sınırlamaları nedeniyle, Flutter çerçevesini kullanarak doğrudan bir ana ekran widget'ının kullanıcı arayüzünü çizemezsiniz. Bunun yerine, Jetpack Compose veya SwiftUI gibi platform çerçeveleriyle oluşturulan widget'ları Flutter uygulamanıza ekleyebilirsiniz. Bu codelab'de, karmaşık kullanıcı arayüzlerini yeniden yazmamak için uygulamanız ve widget'lar arasında kaynak paylaşma örnekleri ele alınmaktadır.
Ne oluşturacaksınız?
Bu codelab'de, kullanıcıların makale okumasına olanak tanıyan basit bir Flutter uygulaması için home_widget paketini kullanarak hem Android hem de iOS'te ana ekran widget'ları oluşturacaksınız. Widget'larınız:
- Flutter uygulamanızdaki verileri gösterin.
- Flutter uygulamasından paylaşılan yazı tipi öğelerini kullanarak metin görüntüleme.
- Oluşturulmuş bir Flutter widget'ının resmini gösterir.

Bu Flutter uygulamasında iki ekran (veya rota) bulunur:
- İlkinde, başlık ve açıklamaları içeren bir haber makalesi listesi gösterilir.
- İkincisinde ise
CustomPaintkullanılarak oluşturulmuş bir grafiğin yer aldığı makalenin tamamı gösteriliyor.
.

Öğrenecekleriniz
- iOS ve Android'de ana ekran widget'ları oluşturma
- Ana ekran widget'ınız ile Flutter uygulamanız arasında veri paylaşmak için home_widget paketini kullanma
- Yeniden yazmanız gereken kod miktarını azaltma
- Ana ekran widget'ınızı Flutter uygulamanızdan nasıl güncelleyeceğinizi öğrenin.
2. Geliştirme ortamınızı kurma
Her iki platform için de Flutter SDK ve bir IDE'ye ihtiyacınız vardır. Flutter ile çalışmak için tercih ettiğiniz IDE'yi kullanabilirsiniz. Bu, Dart Code ve Flutter uzantıları ile Visual Studio Code, ya da Flutter ve Dart eklentileri yüklenmiş Android Studio veya IntelliJ olabilir.
iOS ana ekran widget'ı oluşturmak için:
- Bu codelab'i fiziksel bir iOS cihazda veya iOS simülatöründe çalıştırabilirsiniz.
- Xcode IDE ile bir macOS sistemi yapılandırmanız gerekir. Bu işlem, uygulamanızın iOS sürümünü oluşturmak için gereken derleyiciyi yükler.
Android ana ekran widget'ı oluşturmak için:
- Bu codelab'i fiziksel bir Android cihazda veya Android emülatöründe çalıştırabilirsiniz.
- Geliştirme sisteminizi Android Studio ile yapılandırmanız gerekir. Bu işlem, uygulamanızın Android sürümünü oluşturmak için gereken derleyiciyi yükler.
Başlangıç kodunu alma
Projenizin ilk sürümünü GitHub'dan indirme
Komut satırından GitHub deposunu flutter-codelabs dizinine kopyalayın:
$ git clone https://github.com/flutter/codelabs.git flutter-codelabs
Depoyu klonladıktan sonra bu codelab'in kodunu flutter-codelabs/homescreen_codelab dizininde bulabilirsiniz. Bu dizin, codelab'deki her adım için tamamlanmış proje kodunu içerir.
Başlangıç uygulamasını açın
flutter-codelabs/homescreen_codelab/step_03 dizinini tercih ettiğiniz IDE'de açın.
Paketleri yükleme
Gerekli tüm paketler projenin pubspec.yaml dosyasına eklendi. Proje bağımlılıklarını almak için aşağıdaki komutu çalıştırın:
$ flutter pub get
3. Temel bir ana ekran widget'ı ekleme
Öncelikle, yerel platform araçlarını kullanarak ana ekran widget'ını ekleyin.
Temel bir iOS ana ekran widget'ı oluşturma
Flutter iOS uygulamanıza uygulama uzantısı eklemek, SwiftUI veya UIKit uygulamasına uygulama uzantısı eklemeye benzer:
- Flutter proje dizininizdeki bir terminal penceresinde
open ios/Runner.xcworkspacekomutunu çalıştırın. Alternatif olarak, VSCode'da ios klasörünü sağ tıklayıp Open in Xcode'u (Xcode'da aç) seçin. Bu işlem, Flutter projenizde varsayılan Xcode çalışma alanını açar. - Menüden Dosya → Yeni → Hedef'i seçin. Bu işlem, projeye yeni bir hedef ekler.
- Şablonların listesi gösterilir. Widget Uzantısı'nı seçin.
- Bu widget için Ürün Adı kutusuna "NewsWidgets" yazın. Hem Canlı Etkinliği Dahil Et hem de Yapılandırma Amacını Dahil Et onay kutularını temizleyin.
Örnek kodu inceleme
Yeni bir hedef eklediğinizde Xcode, seçtiğiniz şablona göre örnek kod oluşturur. Oluşturulan kod ve WidgetKit hakkında daha fazla bilgi için Apple'ın uygulama uzantısı dokümanlarına bakın.
Örnek widget'ınızda hata ayıklama ve test yapma
- Öncelikle Flutter uygulamanızın yapılandırmasını güncelleyin. Flutter uygulamanıza yeni paketler eklediğinizde ve projede Xcode'dan hedef çalıştırmayı planladığınızda bunu yapmanız gerekir. Uygulamanızın yapılandırmasını güncellemek için Flutter uygulamanızın dizininde aşağıdaki komutu çalıştırın:
$ flutter build ios --config-only
- Hedeflerin listesini görmek için Runner'ı tıklayın. Yeni oluşturduğunuz widget hedefi NewsWidgets'ı seçin ve Çalıştır'ı tıklayın. iOS widget kodunu değiştirdiğinizde widget hedefini Xcode'dan çalıştırın.

- Simülatör veya cihaz ekranında temel bir ana ekran widget'ı gösterilmelidir. Bu seçeneği görmüyorsanız ekrana ekleyebilirsiniz. Ana ekranda tıklayıp basılı tutun, ardından sol üst köşedeki + işaretini tıklayın.

- Uygulamanın adını arayın. Bu codelab için "Homescreen Widgets" (Ana Ekran Widget'ları) ifadesini arayın.

- Ana ekran widget'ını ekledikten sonra, saat bilgisini veren basit bir metin gösterilir.
Temel Android Widget'ı Oluşturma
- Android'de ana ekran widget'ı eklemek için projenin derleme dosyasını Android Studio'da açın. Bu dosyayı android/build.gradle adresinde bulabilirsiniz. Alternatif olarak, VSCode'da android klasörünü sağ tıklayıp Open in Android Studio'yu (Android Studio'da Aç) seçin.
- Proje oluşturulduktan sonra sol üst köşedeki uygulama dizinini bulun. Yeni ana ekran widget'ınızı bu dizine ekleyin. Dizini sağ tıklayın, Yeni -> Widget -> Uygulama Widget'ı'nı seçin.

- Android Studio'da yeni bir form gösterilir. Ana ekran widget'ınızla ilgili sınıf adı, yerleştirme, boyut ve kaynak dil gibi temel bilgileri ekleyin.
Bu codelab için aşağıdaki değerleri ayarlayın:
- Class Name (Sınıf Adı) kutusunu NewsWidget olarak değiştirin.
- Minimum Genişlik (hücreler) açılır listesini 3 olarak ayarlayın.
- Minimum Yükseklik (hücreler) açılır listesini 3 olarak ayarlayın.
Örnek kodu inceleme
Formu gönderdiğinizde Android Studio birkaç dosya oluşturur ve günceller. Bu codelab ile ilgili değişiklikler aşağıdaki tabloda listelenmiştir.
İşlem | Hedef Dosya | Değiştir |
Güncelle |
| NewsWidget'ı kaydeden yeni bir alıcı ekler. |
Oluştur |
| Ana ekran widget'ı kullanıcı arayüzünü tanımlar. |
Oluştur |
| Ana ekran widget'ı yapılandırmanızı tanımlar. Bu dosyada widget'ınızın boyutlarını veya adını ayarlayabilirsiniz. |
Oluştur |
| Ana ekran widget'ınıza işlevsellik eklemek için Kotlin kodunuzu içerir. |
Bu dosyalar hakkında daha fazla bilgiyi bu Codelab'de bulabilirsiniz.
Örnek widget'ınızda hata ayıklama ve test yapma
Şimdi uygulamanızı çalıştırın ve ana ekran widget'ını görün. Uygulamayı oluşturduktan sonra Android cihazınızın uygulama seçimi ekranına gidin ve bu Flutter projesinin simgesine uzun basın. Pop-up menüden Widget'lar'ı seçin.

Android cihaz veya emülatör, Android için varsayılan ana ekran widget'ınızı gösterir.
4. Flutter uygulamanızdaki verileri ana ekran widget'ınıza gönderme
Oluşturduğunuz temel ana ekran widget'ını özelleştirebilirsiniz. Ana ekran widget'ını, bir haber makalesinin başlığını ve özetini gösterecek şekilde güncelleyin. Aşağıdaki ekran görüntüsünde, ana ekran widget'ının başlık ve özet gösterdiği bir örnek yer almaktadır.

Uygulamanız ile ana ekran widget'ı arasında veri aktarmak için Dart ve yerel kod yazmanız gerekir. Bu bölümde, bu süreç üç bölüme ayrılmıştır:
- Flutter uygulamanızda hem Android hem de iOS'in kullanabileceği Dart kodu yazma
- Yerel iOS işlevselliği ekleme
- Yerel Android işlevselliği ekleme
iOS uygulama gruplarını kullanma
Bir iOS üst uygulaması ile bir widget uzantısı arasında veri paylaşmak için her iki hedefin de aynı uygulama grubuna ait olması gerekir. Uygulama grupları hakkında daha fazla bilgi edinmek için Apple'ın uygulama grubu belgelerine bakın.
Paket kimliğinizi güncelleyin:
Xcode'da hedefinizin ayarlarına gidin. Signing & Capabilities (İmzalama ve Özellikler) sekmesinde, ekibinizin ve paket tanımlayıcınızın ayarlandığından emin olun.
Uygulama grubunu Xcode'da hem Runner hedefi hem de NewsWidgetExtension hedefine ekleyin:
+ Yetenek -> Uygulama Grupları'nı seçin ve yeni bir uygulama grubu ekleyin. Hem Runner (üst uygulama) hedefi hem de widget hedefi için tekrarlayın.

Dart kodunu ekleme
Hem iOS hem de Android uygulamaları, Flutter uygulamasıyla verileri birkaç farklı şekilde paylaşabilir.Bu uygulamalarla iletişim kurmak için cihazın yerel key/value deposundan yararlanın. iOS bu depoya UserDefaults, Android ise SharedPreferences adını verir. home_widget paketi, verilerin her iki platforma da kaydedilmesini kolaylaştırmak için bu API'leri kapsar ve ana ekran widget'larının güncellenmiş verileri çekmesini sağlar.

Başlık ve açıklama verileri, news_data.dart dosyasından alınır. Bu dosya, örnek veriler ve bir NewsArticle veri sınıfı içerir.
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,
});
}
Başlık ve açıklama değerlerini güncelleme
Ana ekran widget'ınızı Flutter uygulamanızdan güncelleme işlevini eklemek için lib/home_screen.dart dosyasına gidin. Dosyanın içeriğini aşağıdaki kodla değiştirin. Ardından, <YOUR APP GROUP> yerine uygulama grubunuzun tanımlayıcısını yazın.
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);
},
),
);
},
);
},
));
}
}
updateHeadline işlevi, anahtar/değer çiftlerini cihazınızın yerel depolama alanına kaydeder. headline_title anahtarı, newHeadline.title değerini tutar. headline_description anahtarı, newHeadline.description değerini tutar. Bu işlev, ana ekran widget'ları için yeni verilerin alınabileceğini ve oluşturulabileceğini yerel platforma da bildirir.
floatingActionButton öğesini değiştirme
floatingActionButton düğmesine basıldığında updateHeadline işlevini aşağıdaki gibi çağırın:
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'),
),
...
Bu değişiklikle birlikte, kullanıcı bir makale sayfasından Başlığı Güncelle düğmesine bastığında ana ekran widget'ı ayrıntıları güncellenir.
Makale verilerini görüntülemek için iOS kodunu güncelleme
iOS için ana ekran widget'ını güncellemek üzere Xcode'u kullanın.
Xcode'da NewsWidgets.swift dosyasını açın:
TimelineEntryyapılandırın.
SimpleEntry yapısını aşağıdaki kodla değiştirin:
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
}
Bu NewsArticleEntry yapısı, güncellendiğinde ana ekran widget'ına iletilecek gelen verileri tanımlar. TimelineEntry türü için tarih parametresi gerekir.TimelineEntry protokolü hakkında daha fazla bilgi edinmek için Apple'ın TimelineEntry belgelerine göz atın.
View içeriğini düzenleme
Ana ekran widget'ınızı, tarih yerine haber makalesi başlığını ve açıklamasını gösterecek şekilde değiştirin. SwiftUI'da metin görüntülemek için Text görünümünü kullanın. SwiftUI'da görünümleri üst üste yerleştirmek için VStack görünümünü kullanın.
Oluşturulan NewsWidgetEntryView görünümünü aşağıdaki kodla değiştirin:
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)
}
}
}
Ana ekran widget'ına ne zaman ve nasıl güncelleneceğini bildirmek için sağlayıcıyı düzenleme
Mevcut Provider öğesini aşağıdaki kodla değiştirin. Ardından, <YOUR APP GROUP> yerine uygulama grubu tanımlayıcınızı girin:
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)
}
}
}
Önceki koddaki Provider, TimelineProvider ile uyumludur. Provider için üç farklı yöntem vardır:
placeholderyöntemi, kullanıcı ilk kez ana ekran widget'ının önizlemesini yaptığında yer tutucu bir giriş oluşturur.

getSnapshotyöntemi, verileri kullanıcı varsayılanlarından okur ve geçerli zaman için girişi oluşturur.getTimelineyöntemi, zaman çizelgesi girişlerini döndürür. Bu özellik, içeriğinizi güncellemek için öngörülebilir zaman noktalarınız olduğunda faydalıdır. Bu codelab'de mevcut durumu almak için getSnapshot işlevi kullanılır. Yöntemi, ana ekran widget'ına geçerli saat geçtikten sonra verileri yenilemesini söyler..atEnd
NewsWidgets_Previews yorumunu devre dışı bırakın.
Önizlemeleri kullanmak bu codelab'in kapsamı dışındadır. SwiftUI ana ekran widget'larını önizleme hakkında daha fazla bilgi için Apple'ın Widget'larda Hata Ayıklama ile İlgili Belgeleri'ne bakın.
Tüm dosyaları kaydedin ve uygulama ile widget hedefini yeniden çalıştırın.
Uygulamanın ve ana ekran widget'ının çalıştığını doğrulamak için hedefleri tekrar çalıştırın.
- Uygulama hedefini çalıştırmak için Xcode'da uygulama şemasını seçin.
- Uzantı hedefini çalıştırmak için Xcode'da uzantı şemasını seçin.
- Uygulamada bir makale sayfasına gidin.
- Başlığı güncellemek için düğmeyi tıklayın. Ana ekran widget'ı da başlığı güncellemelidir.
Android kodunu güncelleme
Ana ekran widget'ı XML'sini ekleyin.
Android Studio'da, önceki adımda oluşturulan dosyaları güncelleyin.res/layout/news_widget.xml dosyasını açın. Ana ekran widget'ınızın yapısını ve düzenini tanımlar. Sağ üst köşedeki Kod'u seçin ve bu dosyanın içeriğini aşağıdaki kodla değiştirin:
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>
Bu XML, biri makale başlığı, diğeri makale açıklaması için olmak üzere iki metin görünümü tanımlar. Bu metin görünümleri, stil oluşturmayı da tanımlar. Bu codelab boyunca bu dosyaya geri döneceksiniz.
NewsWidget işlevselliğini güncelleme
NewsWidget.kt Kotlin kaynak kodu dosyasını açın. Bu dosya, AppWidgetProvider sınıfını genişleten NewsWidget adlı oluşturulmuş bir sınıf içeriyor.
NewsWidget sınıfı, üst sınıfından üç yöntem içerir. onUpdate yöntemini değiştirirsiniz. Android, bu yöntemi sabit aralıklarla widget'lar için çağırır.
NewsWidget.kt dosyasının içeriğini aşağıdaki kodla değiştirin:
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)
}
}
}
Artık onUpdate çağrıldığında Android, the widgetData.getString() yöntemini kullanarak yerel depolamadan en yeni değerleri alır ve ardından Ana Ekran widget'ında gösterilen metni değiştirmek için setTextViewText yöntemini çağırır.
Güncellemeleri test etme
Ana ekran widget'larınızın yeni verilerle güncellendiğinden emin olmak için uygulamayı test edin. Verileri güncellemek için makale sayfalarındaki Ana ekranı güncelle 'yi FloatingActionButton kullanın. Ana ekran widget'ınız makale başlığıyla güncellenir.

5. iOS ana ekran widget'ınızda Flutter uygulamasının özel yazı tiplerini kullanma
Şimdiye kadar ana ekran widget'ını, Flutter uygulamasının sağladığı verileri okuyacak şekilde yapılandırdınız. Flutter uygulamasında, ana ekran widget'ında kullanmak isteyebileceğiniz özel bir yazı tipi bulunur. Özel yazı tipini iOS ana ekran widget'ınızda kullanabilirsiniz. Ana ekran widget'larında özel yazı tipleri kullanma özelliği Android'de kullanılamaz.
iOS kodunu güncelleme
Flutter, öğelerini iOS uygulamalarının mainBundle'ında saklar. Bu paketteki öğelere ana ekran widget'ınızın kodundan erişebilirsiniz.
NewsWidgets.swift dosyanızdaki NewsWidgetsEntryView yapısında aşağıdaki değişiklikleri yapın.
Flutter öğe dizininin yolunu almak için bir yardımcı işlev oluşturun:
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
}
...
}
Özel yazı tipi dosyanızın URL'sini kullanarak yazı tipini kaydedin.
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)
}
...
}
Başlık metni görünümünü, özel yazı tipinizi kullanacak şekilde güncelleyin.
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)
}
}
...
}
Ana ekran widget'ınızı çalıştırdığınızda artık aşağıdaki resimde gösterildiği gibi başlık için özel yazı tipi kullanılır:

6. Flutter widget'larını resim olarak oluşturma
Bu bölümde, Flutter uygulamanızdaki bir grafiği ana ekran widget'ı olarak göstereceksiniz.
Bu widget, ana ekranda gösterdiğiniz metinden daha zorlu bir meydan okuma sunar. Flutter grafiğini yerel kullanıcı arayüzü bileşenlerini kullanarak yeniden oluşturmaya çalışmak yerine resim olarak göstermek çok daha kolaydır.
Flutter grafiğinizi PNG dosyası olarak oluşturmak için ana ekran widget'ınızı kodlayın. Ana ekran widget'ınız bu resmi gösterebilir.
Dart kodunu yazma
Dart tarafında, home_widget paketinden renderFlutterWidget yöntemini ekleyin. Bu yöntem bir widget, bir dosya adı ve bir anahtar alır. Flutter widget'ının resmini döndürür ve paylaşılan bir kapsayıcıya kaydeder. Kodunuzda resim adını belirtin ve ana ekran widget'ının kapsayıcıya erişebildiğinden emin olun. key, tam dosya yolunu cihazın yerel depolama alanına dize olarak kaydeder. Bu, Dart kodunda ad değişirse ana ekran widget'ının dosyayı bulmasına olanak tanır.
Bu codelab'de, lib/article_screen.dart dosyasındaki LineChart sınıfı grafiği temsil eder. Derleme yöntemi, bu grafiği ekrana çizen bir CustomPainter döndürür.
Bu özelliği uygulamak için lib/article_screen.dart dosyasını açın. home_widget paketini içe aktarın. Ardından, _ArticleScreenState sınıfındaki kodu aşağıdaki kodla değiştirin:
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!),
],
),
);
}
}
Bu örnekte, _ArticleScreenState sınıfında üç değişiklik yapılıyor.
GlobalKey oluşturur
GlobalKey, ilgili widget'ın boyutunu almak için gereken bağlamı alır .
lib/article_screen.dart
class _ArticleScreenState extends State<ArticleScreen> {
// New: add this GlobalKey
final _globalKey = GlobalKey();
...
}
Adds imagePath
imagePath özelliği, Flutter widget'ının oluşturulduğu resmin konumunu depolar.
lib/article_screen.dart
class _ArticleScreenState extends State<ArticleScreen> {
...
// New: add this imagePath
String? imagePath;
...
}
Anahtarı, oluşturulacak widget'a ekler.
_globalKey, resimde oluşturulan Flutter widget'ını içerir. Bu örnekte, Flutter widget'ı LineChart içeren Center'dır.
lib/article_screen.dart
class _ArticleScreenState extends State<ArticleScreen> {
...
Center(
// New: Add this key
key: _globalKey,
child: const LineChart(),
),
...
}
- Widget'ı resim olarak kaydeder
Kullanıcı floatingActionButton simgesini tıkladığında renderFlutterWidget yöntemi çağrılır. Bu yöntem, ortaya çıkan PNG dosyasını paylaşılan kapsayıcı dizine "screenshot" olarak kaydeder. Bu yöntem, resmin tam yolunu cihaz depolama alanında dosya adı anahtarı olarak da kaydeder.
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);
},
...
}
iOS kodunu güncelleme
iOS için kodu, dosya yolunu depolama alanından alacak ve dosyayı SwiftUI kullanarak resim olarak gösterecek şekilde güncelleyin.
Aşağıdaki değişiklikleri yapmak için NewsWidgets.swift dosyasını açın:
filename ve displaySize değerlerini NewsArticleEntry struct öğesine ekleyin.
filename özelliği, resim dosyasının yolunu temsil eden dizeyi içerir. displaySize özelliği, kullanıcının cihazındaki ana ekran widget'ının boyutunu tutar. Ana ekran widget'ının boyutu context'dan alınır.
ios/NewsWidgets/NewsWidgets.swift
struct NewsArticleEntry: TimelineEntry {
...
// New: add the filename and displaySize.
let filename: String
let displaySize: CGSize
}
placeholder işlevini güncelleme
filename ve displaySize yer tutucularını ekleyin.
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)
}
getSnapshot içinde userDefaults dosyasından dosya adını alma
Bu işlem, Ana Ekran widget'ı güncellendiğinde filename değişkenini userDefaults depolama alanındaki filename değeri olarak ayarlar.
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"
...
)
Bir yoldaki resmi görüntüleyen ChartImage oluşturma
ChartImage Görünümü, Dart tarafında oluşturulan dosyanın içeriğinden bir resim oluşturur. Burada, boyutu çerçevenin% 50'si olarak ayarlarsınız.
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())
}
...
}
NewsWidgetsEntryView gövdesinde ChartImage'ı kullanın.
Grafik resmini ana ekran widget'ında göstermek için ChartImage görünümünü NewsWidgetsEntryView'un gövdesine ekleyin.
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
}
Değişiklikleri test etme
Değişiklikleri test etmek için hem Flutter uygulamanızın (Runner) hedefini hem de uzantı hedefinizin Xcode'dan yeniden çalıştırın. Resmi görmek için uygulamadaki makale sayfalarından birine gidin ve ana ekran widget'ını güncelleme düğmesine basın.

Android kodunu güncelleme
Android kodu, iOS koduyla aynı şekilde çalışır.
android/app/res/layout/news_widget.xmldosyasını açın. Ana ekran widget'ınızın kullanıcı arayüzü öğelerini içerir. İçeriğini aşağıdaki kodla değiştirin:
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>
Bu yeni kod, ana ekran widget'ına bir resim ekler. Bu resim (şimdilik) genel bir yıldız simgesi gösterir. Bu yıldız simgesini, Dart koduna kaydettiğiniz resimle değiştirin.
NewsWidget.ktdosyasını açın. İçeriğini aşağıdaki kodla değiştirin:
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)
}
}
}
Bu Dart kodu, filename anahtarıyla yerel depolama alanına ekran görüntüsü kaydeder. Ayrıca, resmin tam yolunu alır ve bundan bir File nesnesi oluşturur. Resim varsa Dart kodu, ana ekran widget'ındaki resmi yeni resimle değiştirir.
- Uygulamanızı yeniden yükleyin ve bir makale ekranına gidin. Ana ekranı güncelle'ye basın. Ana ekran widget'ında grafik gösterilir.
7. Sonraki Adımlar
Tebrikler!
Tebrikler, Flutter iOS ve Android uygulamalarınız için ana ekran widget'ları oluşturmayı başardınız.
Flutter uygulamanızdaki içeriklere bağlantı oluşturma
Kullanıcının tıkladığı yere bağlı olarak, kullanıcıyı uygulamanızdaki belirli bir sayfaya yönlendirmek isteyebilirsiniz. Örneğin, bu codelab'deki haber uygulamasında kullanıcının, gösterilen başlığa ait haber makalesini görmesini isteyebilirsiniz.
Bu özellik, bu codelab'in kapsamı dışındadır. home_widget paketinin sağladığı bir akışı kullanarak ana ekran widget'larından uygulama başlatmalarını tanımlama ve ana ekran widget'ından URL üzerinden mesaj gönderme örneklerini inceleyebilirsiniz. Daha fazla bilgi edinmek için docs.flutter.dev adresindeki derin bağlantı belgelerine bakın.
Widget'ınızı arka planda güncelleme
Bu codelab'de, bir düğme kullanarak ana ekran widget'ının güncellenmesini tetiklediniz. Bu durum test için makul olsa da üretim kodunda uygulamanızın ana ekran widget'ını arka planda güncellemesini isteyebilirsiniz. Ana ekran widget'ının ihtiyaç duyduğu kaynakları güncellemek için arka plan görevleri oluşturmak üzere workmanager eklentisini kullanabilirsiniz. Daha fazla bilgi edinmek için home_widget paketindeki Arka plan güncellemesi bölümünü inceleyin.
iOS'te, ana ekran widget'ının kullanıcı arayüzünü güncellemek için ağ isteğinde bulunmasını da sağlayabilirsiniz. Bu isteğin koşullarını veya sıklığını kontrol etmek için Zaman Çizelgesi'ni kullanın. Zaman Çizelgesi'ni kullanma hakkında daha fazla bilgi edinmek için Apple'ın "Widget'ı güncel tutma" dokümanını inceleyin.