1. Giriş
Widget nedir?
Flutter geliştiricileri için widget teriminin 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, bir uygulamanın açılmadan uygulama bilgilerini görüntüleme olanağı sunan mini sürümünü ifade eder. Android'de, widget'lar ana ekranda yer alır. iOS'te ana ekrana, kilit ekranına veya bugün görünümüne eklenebilirler.
Bir Widget ne kadar karmaşık olabilir?
Ana Ekran widget'larının çoğu basittir. Bunlar temel metinleri, basit grafikleri veya Android'de temel kontrolleri içerebilir. Hem Android hem iOS, kullanabileceğiniz kullanıcı arayüzü bileşenlerini ve özelliklerini 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 Ana Ekran widget'ının kullanıcı arayüzünü doğrudan ç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üzünü yeniden yazmamak için uygulamanız ve widget'lar arasında kaynak paylaşmayla ilgili örnekler verilmektedir.
Oluşturacaklarınız
Bu codelab'de, kullanıcıların makaleleri okumasına olanak tanıyan home_widget paketini kullanarak basit bir Flutter uygulaması için hem Android hem de iOS'te Ana Ekran widget'ları oluşturacaksınız. Widget'larınız şunları yapar:
- Flutter uygulamanızdaki verileri görün.
- Flutter uygulamasından paylaşılan yazı tipi öğelerini kullanarak metin görüntüleyin.
- Oluşturulan Flutter widget'ının resmini gösterir.
Bu Flutter uygulaması iki ekran (veya yönlendirme) içerir:
- İlkinde, başlık ve açıklamaların bulunduğu bir haber makalesi listesi gösteriliyor.
- İkincisinde,
CustomPaint
kullanılarak oluşturulmuş bir grafikle birlikte makalenin tamamı gösterilir.
.
Neler öğreneceksiniz?
- iOS ve Android'de Ana Ekran widget'ları nasıl oluşturulur?
- 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 güncelleme.
2. Geliştirme ortamınızı ayarlama
Her iki platform için de Flutter SDK ve IDE gerekir. Flutter ile çalışmak için tercih ettiğiniz entegre geliştirme ortamını kullanabilirsiniz. Bu, Dart Code ve Flutter uzantılarına sahip Visual Studio Kodu veya Flutter ve Dart eklentilerinin yüklü olduğu Android Studio veya IntelliJ olabilir.
iOS ana ekran widget'ını 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'ını 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 alın
Projenizin ilk sürümünü GitHub'dan indirin
GitHub deposunu, komut satırından flutter-codelabs dizinine klonlayı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
İlk olarak 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:
open ios/Runner.xcworkspace
komutunu Flutter proje dizininizdeki bir terminal penceresinde çalıştırın. Alternatif olarak, VSCode'daki ios klasörünü sağ tıklayın ve 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.
- Şablon listesi görünür. Widget Uzantısı'nı seçin.
- "NewsWidgets" yazın Ürün Adı kutusuna yazabilirsiniz. Canlı Etkinliği Dahil Et ve Yapılandırma Amacını Dahil Et onay kutularının ikisini de 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 edinmek için Apple'ın uygulama uzantısı dokümanlarına bakın. 'nı inceleyin.
Örnek widget'ınızda hataları ayıklayın ve test edin
- Önce Flutter uygulamanızın yapılandırmasını güncelleyin. Flutter uygulamanıza yeni paketler eklerken ve projede Xcode'dan bir hedef çalıştırmayı planladığınızda bunu yapmanız gerekir. Uygulamanızın yapılandırmasını güncellemek için Flutter uygulama dizininizde aşağıdaki komutu çalıştırın:
$ flutter build ios --config-only
- Hedef listesini görüntülemek için Çalıştırıcı'yı tıklayın. Yeni oluşturduğunuz widget hedefini, 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. Uygulamayı göremiyorsanız ekrana ekleyebilirsiniz. Ana ekranı tıklayıp basılı tutun, ardından sol üst köşedeki + simgesini tıklayın.
- Uygulamanın adını arayın. Bu codelab için "Ana Ekran Widget'ları" araması yapın
- Ana Ekran widget'ını eklediğinizde saati veren basit bir metin gösterilir.
Temel Android Widget'ı Oluşturma
- Android'de Ana Ekran widget'ı eklemek için Android Studio'da projenin derleme dosyasını açın. Bu dosyayı android/build.gradle adresinde bulabilirsiniz. Alternatif olarak, VSCode'daki android klasörünü sağ tıklayıp 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 ve Yeni -> Widget -> Uygulama Widget'ı.
- Android Studio'da yeni bir form gösteriliyor. Ana Ekran widget'ınızın sınıf adı, yerleşimi, boyutu ve kaynak dili dahil olmak üzere temel bilgileri ekleme
Bu codelab için aşağıdaki değerleri ayarlayın:
- NewsWidget'a Sınıf Adı kutusu
- Minimum Genişlik (hücre) açılır listesini 3'e çıkarın
- Minimum Yükseklik (hücre) açılır listesini 3'e çıkarı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şlev eklemek için Kotlin kodunuzu içerir. |
Bu codelab'de bu dosyalarla ilgili daha ayrıntılı bilgileri bulabilirsiniz.
Örnek widget'ınızda hataları ayıklayın ve test edin
Ş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çim 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ızdan Ana Ekran widget'ınıza veri gönderin
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, başlık ve özetin gösterildiği Ana Ekran widget'ının bir örneği gösterilmektedir.
Uygulamanız ile Ana Ekran widget'ı arasında veri aktarmak için Dart ve yerel kodu yazmanız gerekir. Bu bölümde söz konusu süreç üç bölüme ayrılır:
- Flutter uygulamanızda hem Android'in hem de iOS'in kullanabileceği Dart kodu yazma
- Yerel iOS işlevleri ekleme
- Yerel Android işlevleri ekleme
iOS uygulama gruplarını kullanma
iOS üst uygulaması ile 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 dokümanlarına bakın.
Paket tanımlayıcınızı güncelleme:
Xcode'da hedefinizin ayarlarına gidin. İmzalama ve Özellikler sekmesinde, ekip ve paket tanımlayıcınızın ayarlandığından emin olun.
Uygulama Grubu'nu Xcode'da Runner hedefine ve NewsWidgetExtension hedefine her ikisine ekleyin:
+ Kapasite -> Uygulama Grupları'nı tıklayın ve yeni bir Uygulama Grubu ekleyin. Hem Çalıştırıcı (üst uygulama) hem de widget hedefi için bu işlemi tekrarlayın.
Dart kodunu ekleme
Hem iOS hem de Android uygulamaları Flutter uygulamasıyla birkaç farklı şekilde veri paylaşabilir.Bu uygulamalarla iletişim kurmak için cihazın yerel key/value
mağazasından yararlanın. Bu mağaza iOS'te UserDefaults
olarak, Android'de ise SharedPreferences
olarak adlandırılır. home_widget paketi bu API'leri sarmalayarak verilerin herhangi bir platforma kaydedilmesini kolaylaştırır ve Ana Ekran widget'larının güncellenmiş verileri almasını sağlar.
Başlık ve açıklama verileri news_data.dart
dosyasından gelir. Bu dosya örnek veriler ve bir NewsArticle
veri sınıfı içeriyor.
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>
kısmını uygulama grubunuzun tanımlayıcısıyla değiştirin.
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 içeriyor. headline_description
anahtarı, newHeadline.description
değerini barındırır. İşlev, yerel platforma da Ana Ekran widget'ları için yeni verilerin alınıp oluşturulabileceğini bildirir.
floatingActionButton öğesini değiştirme
floatingActionButton
düğmesine basıldığında aşağıda gösterildiği gibi updateHeadline
işlevini ç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.
iOS kodunu makale verilerini gösterecek şekilde güncelleme
iOS için Ana Ekran widget'ını güncellemek üzere Xcode'u kullanın.
NewsWidgets.swift
dosyasını Xcode'da açın:
TimelineEntry
öğesini yapılandırın.
SimpleEntry
struct'ı 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 bir tarih parametresi gerekir.TimelineEntry
protokolü hakkında daha fazla bilgi edinmek için Apple'ın zaman çizelgesi girişi belgelerine göz atın.
İçeriği gösteren View
öğesini düzenleyin
Ana ekran widget'ınızı tarih yerine haber makalesi başlığını ve açıklamasını gösterecek şekilde değiştirin. SwiftUI'de metin görüntülemek için Text
görünümünü kullanın. SwiftUI'de görünümleri üst üste yığmak 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'ının ne zaman ve nasıl güncelleneceğini bildirmek için sağlayıcıyı düzenleyin
Mevcut Provider
değerini aşağıdaki kodla değiştirin. Ardından, uygulama grubu tanımlayıcınızı <UYGULAMA GRUBUNUZ> ile değiştirin:
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
, bir TimelineProvider
ile uyumlu. Provider
uygulamasının üç farklı yöntemi vardır:
placeholder
yöntemi, kullanıcı Ana Ekran widget'ını ilk kez önizlediğinde bir yer tutucu girişi oluşturur.
getSnapshot
yöntemi, kullanıcı varsayılanlarındaki verileri okur ve geçerli zamanın girişini oluşturur.getTimeline
yöntemi, zaman çizelgesi girişlerini döndürür. Bu, içeriğinizi güncellemek için öngörülebilir zamanlarınız olduğunda işinize yarar. Bu codelab'de, geçerli durumu öğrenmek için getSnapshot işlevi kullanılır..atEnd
yöntemi, Ana Ekran widget'ına geçerli süre geçtikten sonra verileri yenilemesini söyler.
NewsWidgets_Previews
hakkında yorum yapın
Önizlemeleri kullanmak bu codelab'de kapsam dışındadır. SwiftUI Ana Ekran widget'larının önizlemesi hakkında daha fazla bilgi edinmek için Apple'ın Hata Ayıklama Widget'larıyla İlgili Dokümanları'nı inceleyebilirsiniz.
Tüm dosyaları kaydedin ve uygulama ve 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öşede 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, stili de tanımlar. Bu codelab boyunca bu dosyaya döneceksiniz.
NewsWidget işlevlerini güncelleme
NewsWidget.kt
Kotlin kaynak kodu dosyasını açın. Bu dosya, AppWidgetProvider
sınıfını genişleten NewsWidget
adında 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ştireceksiniz. Android, widget'lar için sabit aralıklardaki yöntemi ç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 depolama alanından en yeni değerleri alır ve ardından, Ana Ekran widget'ında görüntülenen 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ında 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ı ö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ı, Ana Ekran widget'ında kullanmak isteyebileceğiniz özel bir yazı tipi içerir. Özel yazı tipini iOS Ana Ekran widget'ınızda kullanabilirsiniz. Ana Ekran widget'larında özel yazı tipleri Android'de kullanılamaz.
iOS kodunu güncelleme
Flutter, öğelerini iOS uygulamalarının mainBundle öğesinde depolar. Bu paketteki öğelere Ana Ekran widget kodunuzdan erişebilirsiniz.
NewsWidgets.swift dosyanızdaki NewsWidgetsEntryView yapılandırması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 metin 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 başlık için aşağıdaki resimde gösterildiği gibi özel yazı tipi kullanılıyor:
6. Flutter widget'larını resim olarak oluşturma
Bu bölümde, Flutter uygulamanızdan ana ekran widget'ı olarak bir grafik görürsünüz.
Bu widget, ana ekranda görüntülediğiniz metinden daha büyük bir sıkıntı sunuyor. Flutter grafiğini resim olarak görüntülemek, yerel kullanıcı arayüzü bileşenlerini kullanarak yeniden oluşturmaya çalışmaktan çok daha kolaydır.
Ana Ekran widget'ınızı Flutter grafiğinizi PNG dosyası olarak oluşturacak şekilde 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öntemde bir widget, bir dosya adı ve bir anahtar kullanılır. Flutter widget'ının bir görüntüsünü döndürür ve bunu paylaşılan bir kapsayıcıya kaydeder. Kodunuzda resim adını sağlayın ve Ana Ekran widget'ının kapsayıcıya erişebildiğinden emin olun. key
, tam dosya yolunu cihazın yerel depolama alanına bir dize olarak kaydeder. Bu, Dart kodundaki ad değişirse Ana Ekran widget'ının dosyayı bulmasına olanak tanır.
Bu codelab için lib/article_screen.dart
dosyasındaki LineChart
sınıfı, grafiği temsil eder. Derleme yöntemi, bu grafiği ekrana boyayan 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. Sonra, _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ılmaktadır.
GlobalKey oluşturur
GlobalKey
, ilgili widget'ın bağlamını alır. Bu bağlam, söz konusu widget'ın boyutunu almak için gereklidir.
lib/article_screen.dart
class _ArticleScreenState extends State<ArticleScreen> {
// New: add this GlobalKey
final _globalKey = GlobalKey();
...
}
imagePath ekler
imagePath
özelliği, resmin Flutter widget'ının oluşturulduğu konumu depolar.
lib/article_screen.dart
class _ArticleScreenState extends State<ArticleScreen> {
...
// New: add this imagePath
String? imagePath;
...
}
Oluşturulacak anahtarı widget'a ekler
_globalKey
, resimde oluşturulan Flutter widget'ını içerir. Bu durumda Flutter widget'ı, LineChart
öğesini içeren Merkez'dir.
lib/article_screen.dart
class _ArticleScreenState extends State<ArticleScreen> {
...
Center(
// New: Add this key
key: _globalKey,
child: const LineChart(),
),
...
}
- Widget'ı resim olarak kaydeder
renderFlutterWidget
yöntemi, kullanıcı floatingActionButton
öğesini tıkladığında çağrılır. Bu yöntem, elde edilen PNG dosyasını "ekran görüntüsü" olarak kaydeder. dizine eklenir. Yöntem ayrıca resmin tam yolunu da cihazın depolama alanına dosya adı anahtarı olarak 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'ta, dosya yolunu depolama alanından almak ve SwiftUI kullanarak dosyayı bir resim olarak görüntülemek için kodu güncelleyin.
Aşağıdaki değişiklikleri yapmak için NewsWidgets.swift
dosyasını açın:
NewsArticleEntry
struct'a filename
ve displaySize
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 içerir. Ana Ekran widget'ının boyutu context
ürününden gelir.
ios/NewsWidgets/NewsWidgets.swift
struct NewsArticleEntry: TimelineEntry {
...
// New: add the filename and displaySize.
let filename: String
let displaySize: CGSize
}
placeholder
işlevini güncelleme
Bir yer tutucu filename
ve displaySize
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 kullanarak userDefaults
alanından dosya adını alın
Bu işlem, Ana Ekran widget'ı güncellendiğinde filename
değişkenini userDefaults
depolama alanındaki filename
değerine 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 görsel oluşturur. Burada boyutu, karenin% 50'sine 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 öğesinin gövdesinde ChartImage öğesini kullanma
ChartImage görünümünü Ana Ekran widget'ında görüntülemek 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 uygulaması (Runner) hedefinizi hem de uzantı hedefinizi Xcode'dan tekrar çalıştırın. Resmi görmek için uygulamadaki makale sayfalarından birine gidin ve Ana Ekran widget'ını güncellemek için düğmeye basın.
Android kodunu güncelleme
Android kodu, iOS koduyla aynı şekilde çalışır.
android/app/res/layout/news_widget.xml
dosyasını açın. Ana Ekran widget'ınızın kullanıcı arayüzü öğelerini içerir. 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" />
<!--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. Widget'ta, (şimdilik) genel bir yıldız simgesi görüntülenir. Bu yıldız simgesini Dart koduna kaydettiğiniz resimle değiştirin.
NewsWidget.kt
dosyasını açın. Dosyanı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.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
tuşunu kullanarak yerel depolama alanına bir ekran görüntüsü kaydeder. Ayrıca, resmin tam yolunu alır ve bundan bir File
nesnesi oluşturur. Resim mevcutsa 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'ı grafiği görüntüler.
7. Sonraki Adımlar
Tebrikler!
Tebrikler, Flutter iOS ve Android uygulamalarınız için başarıyla Ana Ekran widget'ları oluşturdunuz.
Flutter uygulamanızda içeriklere bağlantı oluşturma
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örüntülenen başlıkla ilgili haber makalesini görmesini isteyebilirsiniz.
Bu özellik, bu codelab'in kapsamı dışındadır. Ana Ekran widget'larından uygulama başlatmaları tanımlamak ve URL aracılığıyla Ana Ekran widget'ından mesaj göndermek için home_widget paketinin sağladığı akışı kullanma örneklerini bulabilirsiniz. Daha fazla bilgi edinmek için docs.flutter.dev adresindeki derin bağlantı dokümanlarına göz atın.
Widget'ınızı arka planda güncelleme
Bu codelab'de, bir düğme kullanarak Ana Ekran widget'ının güncellemesini tetiklediniz. Bu, test için makul olsa da üretim kodunda, uygulamanızın arka planda Ana Ekran widget'ını güncellemesini isteyebilirsiniz. Ana ekran widget'ının ihtiyaç duyduğu kaynakları güncellemek üzere arka plan görevleri oluşturmak için Work Manager eklentisini kullanabilirsiniz. Daha fazla bilgi edinmek için home_widget paketindeki Arka plan güncelleme bölümüne göz atın.
iOS'te, Ana Ekran widget'ının kullanıcı arayüzünü güncellemek için bir ağ isteğinde bulunmasını da isteyebilirsiniz. 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" bölümüne bakın belgelerinden faydalanabilirsiniz. 'nı inceleyin.