Flutter Uygulamanıza Ana Ekran widget'ı ekleme

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.

f0027e8a7d0237e0.png b991e79ea72c8b65.png

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.

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

a36b7ba379151101.png

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.

.

9c02f8b62c1faa3a.png d97d44051304cae4.png

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:

  1. 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.
  2. Menüden Dosya → Yeni → Hedef'i seçin. Bu işlem, projeye yeni bir hedef ekler.
  3. Şablon listesi görünür. Widget Uzantısı'nı seçin.
  4. "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

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

bbb519df1782881d.png

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

18eff1cae152014d.png

  1. Uygulamanın adını arayın. Bu codelab için "Ana Ekran Widget'ları" araması yapın

a0c00df87615493e.png

  1. Ana Ekran widget'ını eklediğinizde saati veren basit bir metin gösterilir.

Temel Android Widget'ı Oluşturma

  1. 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.
  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 ve Yeni -> Widget -> Uygulama Widget'ı.

f19d8b7f95ab884e.png

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

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

dff7c9f9f85ef1c7.png

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.

acb90343a3e51b6d.png

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:

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

135e1a8c4652dac.png

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.

707ae86f6650ac55.png

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:

  1. placeholder yöntemi, kullanıcı Ana Ekran widget'ını ilk kez önizlediğinde bir yer tutucu girişi oluşturur.

45a0f64240c12efe.png

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

  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öş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.

5ce1c9914b43ad79.png

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:

93f8b9d767aacfb2.png

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

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

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

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

Daha fazla bilgi