Flutter Uygulamanıza Ana Ekran widget'ı ekleme

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.

f0027e8a7d0237e0.png b991e79ea72c8b65.png

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.

819b9fffd700e571.png 92d62ccfd17d770d.png

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.

a36b7ba379151101.png

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 CustomPaint kullanılarak oluşturulmuş bir grafiğin yer aldığı makalenin tamamı gösteriliyor.

.

9c02f8b62c1faa3a.png d97d44051304cae4.png

Öğ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:

  1. Flutter proje dizininizdeki bir terminal penceresinde open ios/Runner.xcworkspace komutunu ç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.
  2. Menüden Dosya → Yeni → Hedef'i seçin. Bu işlem, projeye yeni bir hedef ekler.
  3. Şablonların listesi gösterilir. Widget Uzantısı'nı seçin.
  4. 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

  1. Ö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
  1. 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.

bbb519df1782881d.png

  1. 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.

18eff1cae152014d.png

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

a0c00df87615493e.png

  1. Ana ekran widget'ını ekledikten sonra, saat bilgisini veren basit bir metin gösterilir.

Temel Android Widget'ı Oluşturma

  1. 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.
  2. 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.

f19d8b7f95ab884e.png

  1. 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

AndroidManifest.xml

NewsWidget'ı kaydeden yeni bir alıcı ekler.

Oluştur

res/layout/news_widget.xml

Ana ekran widget'ı kullanıcı arayüzünü tanımlar.

Oluştur

res/xml/news_widget_info.xml

Ana ekran widget'ı yapılandırmanızı tanımlar. Bu dosyada widget'ınızın boyutlarını veya adını ayarlayabilirsiniz.

Oluştur

java/com/example/homescreen_widgets/NewsWidget.kt

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.

dff7c9f9f85ef1c7.png

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.

acb90343a3e51b6d.png

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:

  1. Flutter uygulamanızda hem Android hem de iOS'in kullanabileceği Dart kodu yazma
  2. Yerel iOS işlevselliği ekleme
  3. 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.

135e1a8c4652dac.png

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.

707ae86f6650ac55.png

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:

  1. placeholder yöntemi, kullanıcı ilk kez ana ekran widget'ının önizlemesini yaptığında yer tutucu bir giriş oluşturur.

45a0f64240c12efe.png

  1. getSnapshot yöntemi, verileri kullanıcı varsayılanlarından okur ve geçerli zaman için girişi oluşturur.
  2. getTimeline yö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.

  1. Uygulama hedefini çalıştırmak için Xcode'da uygulama şemasını seçin.
  2. Uzantı hedefini çalıştırmak için Xcode'da uzantı şemasını seçin.
  3. Uygulamada bir makale sayfasına gidin.
  4. 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.

5ce1c9914b43ad79.png

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:

93f8b9d767aacfb2.png

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(),
   ),
   ...
}
  1. 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.

33bdfe2cce908c48.png

Android kodunu güncelleme

Android kodu, iOS koduyla aynı şekilde çalışır.

  1. android/app/res/layout/news_widget.xml dosyası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.

  1. NewsWidget.kt dosyası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.

  1. 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.

Daha fazla bilgi