۱. مقدمه
ویجتها (ابزارکها) چیستند؟
برای توسعهدهندگان فلاتر، تعریف رایج ویجت به اجزای رابط کاربری ایجاد شده با استفاده از چارچوب فلاتر اشاره دارد. در متن این آزمایشگاه کد، یک ویجت به نسخه کوچکی از یک برنامه اشاره دارد که بدون باز کردن برنامه، امکان مشاهده اطلاعات برنامه را فراهم میکند. در اندروید، ویجتها روی صفحه اصلی قرار دارند. در iOS، میتوان آنها را به صفحه اصلی، صفحه قفل یا نمای امروز اضافه کرد.


یک ویجت چقدر میتواند پیچیده باشد؟
بیشتر ویجتهای صفحه اصلی ساده هستند. آنها ممکن است شامل متن ساده، گرافیک ساده یا در اندروید، کنترلهای اولیه باشند. هم اندروید و هم iOS اجزا و ویژگیهای رابط کاربری قابل استفاده را محدود میکنند.


ایجاد رابط کاربری برای ابزارکها
به دلیل این محدودیتهای رابط کاربری، شما نمیتوانید مستقیماً رابط کاربری یک ویجت صفحه اصلی را با استفاده از چارچوب Flutter ترسیم کنید. در عوض، میتوانید ویجتهای ایجاد شده با چارچوبهای پلتفرم مانند Jetpack Compose یا SwiftUI را به برنامه Flutter خود اضافه کنید. این codelab مثالهایی را برای اشتراکگذاری منابع بین برنامه شما و ویجتها برای جلوگیری از بازنویسی رابط کاربری پیچیده مورد بحث قرار میدهد.
آنچه خواهید ساخت
در این آزمایشگاه کد، شما با استفاده از پکیج home_widget، ویجتهای صفحه اصلی را هم در اندروید و هم در iOS برای یک برنامه ساده Flutter خواهید ساخت که به کاربران امکان خواندن مقالات را میدهد. ویجتهای شما:
- نمایش دادهها از برنامه Flutter شما.
- متن را با استفاده از فونتهای به اشتراک گذاشته شده از برنامه Flutter نمایش دهید.
- تصویری از یک ویجت رندر شدهی Flutter را نمایش میدهد.

این برنامه Flutter شامل دو صفحه (یا مسیر ) است:
- اولی فهرستی از مقالات خبری را به همراه تیترها و توضیحات نمایش میدهد.
- دومی کل مقاله را به همراه نموداری که با استفاده از
CustomPaintایجاد شده است، نمایش میدهد.
.


آنچه یاد خواهید گرفت
- نحوه ایجاد ویجتهای صفحه اصلی در iOS و اندروید
- نحوه استفاده از پکیج home_widget برای اشتراکگذاری دادهها بین ویجت صفحه اصلی و برنامه Flutter شما.
- چگونه میزان کدی را که باید دوباره بنویسید کاهش دهید.
- نحوه بهروزرسانی ویجت صفحه اصلی از برنامه Flutter.
۲. محیط توسعه خود را تنظیم کنید
برای هر دو پلتفرم، به SDK فلاتر و یک IDE نیاز دارید. میتوانید از IDE مورد نظر خود برای کار با فلاتر استفاده کنید. این میتواند Visual Studio Code با Dart Code و افزونههای Flutter یا Android Studio یا IntelliJ با افزونههای Flutter و Dart نصب شده باشد.
برای ایجاد ویجت صفحه اصلی iOS:
- شما میتوانید این codelab را روی یک دستگاه iOS فیزیکی یا شبیهساز iOS اجرا کنید.
- شما باید یک سیستم macOS را با Xcode IDE پیکربندی کنید. این کار کامپایلر مورد نیاز برای ساخت نسخه iOS برنامه شما را نصب میکند.
برای ایجاد ویجت صفحه اصلی اندروید:
- شما میتوانید این codelab را روی یک دستگاه اندروید فیزیکی یا شبیهساز اندروید اجرا کنید.
- شما باید سیستم توسعه خود را با اندروید استودیو پیکربندی کنید. این کار کامپایلر مورد نیاز برای ساخت نسخه اندروید برنامه شما را نصب میکند.
کد شروع را دریافت کنید
نسخه اولیه پروژه خود را از GitHub دانلود کنید
از خط فرمان، مخزن گیتهاب را در دایرکتوری flutter-codelabs کپی کنید:
$ git clone https://github.com/flutter/codelabs.git flutter-codelabs
پس از کپی کردن مخزن، میتوانید کد مربوط به این codelab را در دایرکتوری flutter-codelabs/homescreen_codelab پیدا کنید. این دایرکتوری شامل کد تکمیلشدهی پروژه برای هر مرحله در codelab است.
برنامه شروع کننده را باز کنید
پوشه flutter-codelabs/homescreen_codelab/step_03 را در IDE مورد نظر خود باز کنید.
نصب بستهها
تمام بستههای مورد نیاز به فایل pubspec.yaml پروژه اضافه شدند. برای بازیابی وابستگیهای پروژه، دستور زیر را اجرا کنید:
$ flutter pub get
۳. یک ویجت پایه به صفحه اصلی اضافه کنید
ابتدا، ویجت صفحه اصلی را با استفاده از ابزار بومی پلتفرم اضافه کنید.
ایجاد یک ویجت ساده برای صفحه اصلی iOS
افزودن یک افزونه برنامه به برنامه Flutter iOS شما مشابه افزودن یک افزونه برنامه به یک برنامه SwiftUI یا UIKit است:
-
open ios/Runner.xcworkspaceدر یک پنجره ترمینال از دایرکتوری پروژه Flutter خود اجرا کنید. روش دیگر، کلیک راست روی پوشه ios از VSCode و انتخاب گزینه Open in Xcode است. این کار فضای کاری پیشفرض Xcode را در پروژه Flutter شما باز میکند. - از منو، مسیر File → New → Target را انتخاب کنید. این کار یک target جدید به پروژه اضافه میکند.
- فهرستی از قالبها نمایش داده میشود. افزونه ویجت را انتخاب کنید.
- عبارت "NewsWidgets" را در کادر نام محصول برای این ویجت تایپ کنید. هر دو کادر انتخاب "Include Live Activity" و "Include Configuration Intent" را پاک کنید.
کد نمونه را بررسی کنید
وقتی یک هدف جدید اضافه میکنید، Xcode بر اساس الگویی که انتخاب کردهاید، کد نمونه تولید میکند. برای اطلاعات بیشتر در مورد کد تولید شده و WidgetKit، به مستندات افزونه برنامه اپل مراجعه کنید.
ویجت نمونه خود را اشکالزدایی و آزمایش کنید
- ابتدا، پیکربندی برنامه Flutter خود را بهروزرسانی کنید. این کار را باید زمانی انجام دهید که بستههای جدیدی را به برنامه Flutter خود اضافه میکنید و قصد دارید یک هدف را در پروژه از Xcode اجرا کنید. برای بهروزرسانی پیکربندی برنامه خود، دستور زیر را در دایرکتوری برنامه Flutter خود اجرا کنید:
$ flutter build ios --config-only
- برای نمایش لیستی از اهداف، روی Runner کلیک کنید. هدف ویجتی که اخیراً ایجاد کردهاید، NewsWidgets، را انتخاب کنید و روی Run کلیک کنید. هنگام تغییر کد ویجت iOS، هدف ویجت را از Xcode اجرا کنید.

- شبیهساز یا صفحه دستگاه باید یک ویجت صفحه اصلی (Home Screen) را نمایش دهد. اگر آن را نمیبینید، میتوانید آن را به صفحه اضافه کنید. روی صفحه اصلی کلیک کنید و نگه دارید، سپس روی + در گوشه بالا سمت چپ کلیک کنید.

- نام برنامه را جستجو کنید. برای این codelab، عبارت "Homescreen Widgets" را جستجو کنید.

- وقتی ویجت صفحه اصلی را اضافه کردید، باید متن سادهای که زمان را نشان میدهد، نمایش داده شود.
ایجاد یک ویجت پایه اندروید
- برای افزودن ویجت صفحه اصلی در اندروید، فایل ساخت پروژه را در اندروید استودیو باز کنید. میتوانید این فایل را در android/build.gradle پیدا کنید. روش دیگر، کلیک راست روی پوشه اندروید از VSCode و انتخاب گزینه Open in Android Studio است.
- پس از ساخت پروژه، پوشه app را در گوشه بالا سمت چپ پیدا کنید. ویجت صفحه اصلی جدید خود را به این پوشه اضافه کنید. روی پوشه کلیک راست کنید، New -> Widget -> App Widget را انتخاب کنید.

- اندروید استودیو یک فرم جدید نمایش میدهد. اطلاعات اولیه در مورد ویجت صفحه اصلی خود از جمله نام کلاس، محل قرارگیری، اندازه و زبان منبع آن را اضافه کنید.
برای این codelab، مقادیر زیر را تنظیم کنید:
- کادر نام کلاس به NewsWidget
- منوی کشویی حداقل عرض (سلولها) به ۳
- منوی کشویی حداقل ارتفاع (سلولها) به ۳
کد نمونه را بررسی کنید
وقتی فرم را ارسال میکنید، اندروید استودیو چندین فایل ایجاد و بهروزرسانی میکند. تغییرات مربوط به این codelab در جدول زیر فهرست شدهاند.
اکشن | فایل هدف | تغییر |
بهروزرسانی | | یک گیرنده جدید اضافه میکند که NewsWidget را ثبت میکند. |
ایجاد کردن | | رابط کاربری ویجت صفحه اصلی را تعریف میکند. |
ایجاد کردن | | پیکربندی ویجت صفحه اصلی شما را تعریف میکند. میتوانید ابعاد یا نام ویجت خود را در این فایل تنظیم کنید. |
ایجاد کردن | | شامل کد کاتلین شما برای افزودن قابلیت به ویجت صفحه اصلی شما است. |
میتوانید جزئیات بیشتری در مورد این فایلها را در این codelab پیدا کنید.
ویجت نمونه خود را اشکالزدایی و آزمایش کنید
حالا، برنامه خود را اجرا کنید و ویجت صفحه اصلی را ببینید. پس از ساخت برنامه، به صفحه انتخاب برنامه در دستگاه اندروید خود بروید و روی آیکون این پروژه فلاتر لمس طولانی کنید. از منوی باز شده، ویجتها را انتخاب کنید.

دستگاه یا شبیهساز اندروید، ویجت پیشفرض صفحه اصلی شما را برای اندروید نمایش میدهد.
۴. ارسال دادهها از برنامه Flutter به ویجت صفحه اصلی
شما میتوانید ویجت صفحه اصلی که ایجاد کردهاید را سفارشی کنید. ویجت صفحه اصلی را بهروزرسانی کنید تا عنوان و خلاصهای از یک مقاله خبری را نمایش دهد. تصویر زیر نمونهای از ویجت صفحه اصلی را نشان میدهد که عنوان و خلاصهای را نمایش میدهد.

برای انتقال دادهها بین برنامه و ویجت صفحه اصلی، باید کد Dart و native بنویسید. این بخش این فرآیند را به سه بخش تقسیم میکند:
- نوشتن کد دارت در برنامه Flutter که هم اندروید و هم iOS بتوانند از آن استفاده کنند
- افزودن قابلیتهای بومی iOS
- اضافه کردن قابلیتهای بومی اندروید
استفاده از گروههای اپلیکیشن iOS
برای اشتراکگذاری دادهها بین یک برنامهی مادر iOS و یک افزونهی ویجت، هر دو هدف باید به یک گروه برنامه تعلق داشته باشند. برای کسب اطلاعات بیشتر در مورد گروههای برنامه، به مستندات گروه برنامهی اپل مراجعه کنید.
شناسه بسته خود را بهروزرسانی کنید:
در Xcode، به تنظیمات هدف خود بروید. در تب Signing & Capabilities ، بررسی کنید که شناسه تیم و بسته شما تنظیم شده باشد.
گروه App را هم به Runner target و هم به NewsWidgetExtension target در Xcode اضافه کنید:
+ قابلیت -> گروههای برنامه را انتخاب کنید و یک گروه برنامه جدید اضافه کنید. این کار را برای هر دو هدف Runner (برنامه والد) و هدف ویجت تکرار کنید.

کد دارت را اضافه کنید
هر دو برنامه iOS و اندروید میتوانند دادهها را با یک برنامه Flutter به چند روش مختلف به اشتراک بگذارند. برای برقراری ارتباط با این برنامهها، از مخزن key/value محلی دستگاه استفاده کنید. iOS این مخزن را UserDefaults و اندروید آن را SharedPreferences مینامد. بسته home_widget این APIها را در بر میگیرد تا ذخیره دادهها در هر دو پلتفرم را ساده کند و ویجتهای صفحه اصلی را قادر میسازد تا دادههای بهروز شده را دریافت کنند.

دادههای تیتر و توضیحات از فایل news_data.dart میآیند. این فایل شامل دادههای آزمایشی و یک کلاس داده NewsArticle است.
lib/news_data.dart
class NewsArticle {
final String title;
final String description;
final String? articleText;
NewsArticle({
required this.title,
required this.description,
this.articleText = loremIpsum,
});
}
مقادیر عنوان و توضیحات را بهروزرسانی کنید
برای افزودن قابلیت بهروزرسانی ویجت صفحه اصلی از برنامه Flutter خود، به فایل lib/home_screen.dart بروید. محتویات فایل را با کد زیر جایگزین کنید. سپس، <YOUR APP GROUP> را با شناسه گروه برنامه خود جایگزین کنید.
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 جفتهای کلید/مقدار را در حافظه محلی دستگاه شما ذخیره میکند. کلید headline_title مقدار newHeadline.title را در خود نگه میدارد. کلید headline_description مقدار newHeadline.description را در خود نگه میدارد. این تابع همچنین به پلتفرم بومی اطلاع میدهد که دادههای جدید برای ویجتهای صفحه اصلی قابل بازیابی و رندر هستند.
دکمه شناور ActionButton را تغییر دهید
تابع updateHeadline را زمانی که دکمهی floatingActionButton فشرده میشود، فراخوانی کنید، همانطور که نشان داده شده است:
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'),
),
...
با این تغییر، وقتی کاربر دکمه بهروزرسانی تیتر را از صفحه مقاله فشار میدهد، جزئیات ویجت صفحه اصلی بهروزرسانی میشود.
کد iOS را برای نمایش دادههای مقاله بهروزرسانی کنید
برای بهروزرسانی ویجت صفحه اصلی برای iOS، از Xcode استفاده کنید.
فایل NewsWidgets.swift را در Xcode باز کنید:
TimelineEntry را پیکربندی کنید .
کد زیر را جایگزین ساختار SimpleEntry کنید:
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
}
این ساختار NewsArticleEntry دادههای ورودی را برای ارسال به ویجت صفحه اصلی هنگام بهروزرسانی تعریف میکند. نوع TimelineEntry به یک پارامتر تاریخ نیاز دارد. برای کسب اطلاعات بیشتر در مورد پروتکل TimelineEntry ، مستندات TimelineEntry اپل را بررسی کنید.
ویرایش View که محتوا را نمایش میدهد
ویجت صفحه اصلی خود را طوری تغییر دهید که به جای تاریخ، تیتر و توضیحات مقاله خبری را نمایش دهد. برای نمایش متن در SwiftUI، از نمای Text استفاده کنید. برای قرار دادن نماها روی هم در SwiftUI، از نمای VStack استفاده کنید.
نمای NewsWidgetEntryView تولید شده را با کد زیر جایگزین کنید:
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)
}
}
}
ارائهدهنده را ویرایش کنید تا به ویجت صفحه اصلی بگویید چه زمانی و چگونه بهروزرسانی شود
کد زیر را جایگزین Provider موجود کنید. سپس، شناسه گروه برنامه خود را به جای <YOUR APP GROUP> قرار دهید:
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)
}
}
}
Provider در کد قبلی با TimelineProvider مطابقت دارد. Provider سه متد مختلف دارد:
- متد
placeholderزمانی که کاربر برای اولین بار پیشنمایش ویجت صفحه اصلی را مشاهده میکند، یک ورودی placeholder ایجاد میکند.

- متد
getSnapshotدادهها را از مقادیر پیشفرض کاربر میخواند و ورودی مربوط به زمان فعلی را تولید میکند. - متد
getTimelineورودیهای جدول زمانی را برمیگرداند. این به شما کمک میکند زمانی که نقاط زمانی قابل پیشبینی برای بهروزرسانی محتوای خود دارید. این آزمایشگاه کد از تابع getSnapshot برای دریافت وضعیت فعلی استفاده میکند. متد.atEndبه ویجت صفحه اصلی میگوید که دادهها را پس از گذشت زمان فعلی بهروزرسانی کند.
پیشنمایشهای NewsWidgets_Previews را کامنت کنید
استفاده از پیشنمایشها خارج از محدودهی این آزمایشگاه کد است. برای جزئیات بیشتر در مورد پیشنمایش ویجتهای صفحه اصلی SwiftUI، به مستندات اپل در مورد اشکالزدایی ویجتها مراجعه کنید.
تمام فایلها را ذخیره کنید و برنامه و ویجت را دوباره اجرا کنید.
اهداف را دوباره اجرا کنید تا تأیید کنید که برنامه و ویجت صفحه اصلی کار میکنند.
- طرحواره برنامه را در Xcode انتخاب کنید تا هدف برنامه اجرا شود.
- طرحواره افزونه را در Xcode انتخاب کنید تا هدف افزونه اجرا شود.
- به صفحه مقاله در برنامه بروید.
- برای بهروزرسانی عنوان، روی دکمه کلیک کنید. ویجت صفحه اصلی نیز باید عنوان را بهروزرسانی کند.
کد اندروید را بهروزرسانی کنید
ویجت XML صفحه اصلی را اضافه کنید.
در اندروید استودیو، فایلهای تولید شده در مرحله قبل را بهروزرسانی کنید. فایل res/layout/news_widget.xml . این فایل ساختار و طرحبندی ویجت صفحه اصلی شما را تعریف میکند. در گوشه بالا سمت راست، گزینه Code را انتخاب کنید و محتویات آن فایل را با کد زیر جایگزین کنید:
اندروید/اپ/رز/لایوت/نیوز_ویجت.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>
این XML دو نمای متن تعریف میکند، یکی برای عنوان مقاله و دیگری برای توضیحات مقاله. این نماهای متن همچنین سبکبندی را تعریف میکنند. در طول این آزمایشگاه کد به این فایل باز خواهید گشت.
بهروزرسانی قابلیت ویجت اخبار
فایل کد منبع کاتلین NewsWidget.kt را باز کنید. این فایل شامل یک کلاس تولید شده به نام NewsWidget است که از کلاس AppWidgetProvider ارثبری میکند.
کلاس NewsWidget شامل سه متد از کلاس بالادست خود است. شما متد onUpdate تغییر خواهید داد. اندروید این متد را برای ویجتها در فواصل زمانی ثابت فراخوانی میکند.
محتویات فایل NewsWidget.kt را با کد زیر جایگزین کنید:
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)
}
}
}
حالا، وقتی onUpdate فراخوانی میشود، اندروید جدیدترین مقادیر را با استفاده از متد the widgetData.getString() از حافظه محلی دریافت میکند و سپس setTextViewText برای تغییر متن نمایش داده شده در ویجت صفحه اصلی فراخوانی میکند.
بهروزرسانیها را آزمایش کنید
برنامه را آزمایش کنید تا مطمئن شوید ویجتهای صفحه اصلی شما با دادههای جدید بهروزرسانی میشوند. برای بهروزرسانی دادهها، FloatingActionButton بهروزرسانی صفحه اصلی» در صفحات مقاله استفاده کنید. ویجت صفحه اصلی شما باید با عنوان مقاله بهروزرسانی شود.

۵. استفاده از فونتهای سفارشی برنامه Flutter در ویجت صفحه اصلی iOS شما
تا اینجا، شما ویجت صفحه اصلی را برای خواندن دادههایی که برنامه Flutter ارائه میدهد، پیکربندی کردهاید. برنامه Flutter شامل یک فونت سفارشی است که ممکن است بخواهید در ویجت صفحه اصلی از آن استفاده کنید. میتوانید از فونت سفارشی در ویجت صفحه اصلی iOS خود استفاده کنید. استفاده از فونتهای سفارشی در ویجتهای صفحه اصلی در اندروید در دسترس نیست.
کد iOS را بهروزرسانی کنید
فلاتر داراییهای خود را در mainBundle برنامههای iOS ذخیره میکند. شما میتوانید از طریق کد ویجت صفحه اصلی خود به داراییهای موجود در این bundle دسترسی داشته باشید.
در ساختار NewsWidgetsEntryView در فایل NewsWidgets.swift خود، تغییرات زیر را اعمال کنید
یک تابع کمکی برای دریافت مسیر دایرکتوری asset در فلاتر ایجاد کنید:
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
}
...
}
فونت را با استفاده از URL به فایل فونت سفارشی خود ثبت کنید.
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)
}
...
}
نمای متن تیتر را بهروزرسانی کنید تا از فونت سفارشی شما استفاده کند.
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)
}
}
...
}
وقتی ویجت صفحه اصلی خود را اجرا میکنید، اکنون از فونت سفارشی برای عنوان استفاده میکند، همانطور که در تصویر زیر نشان داده شده است:

۶. رندر کردن ویجتهای فلاتر به عنوان تصویر
در این بخش، نموداری از برنامه Flutter خود را به عنوان یک ویجت صفحه اصلی نمایش خواهید داد.
این ویجت چالش بزرگتری نسبت به متنی که روی صفحه اصلی نمایش دادهاید، ایجاد میکند. نمایش نمودار فلاتر به صورت تصویر بسیار آسانتر از تلاش برای بازسازی آن با استفاده از اجزای رابط کاربری بومی است.
ویجت صفحه اصلی خود را طوری کدنویسی کنید که نمودار فلاتر شما را به صورت یک فایل PNG رندر کند. ویجت صفحه اصلی شما میتواند آن تصویر را نمایش دهد.
کد دارت را بنویسید
در سمت Dart، متد renderFlutterWidget از پکیج home_widget اضافه کنید. این متد یک ویجت، یک نام فایل و یک کلید میگیرد. تصویری از ویجت Flutter را برمیگرداند و آن را در یک کانتینر مشترک ذخیره میکند. نام تصویر را در کد خود وارد کنید و مطمئن شوید که ویجت Home Screen میتواند به کانتینر دسترسی داشته باشد. key مسیر کامل فایل را به صورت یک رشته در حافظه محلی دستگاه ذخیره میکند. این به ویجت Home Screen اجازه میدهد در صورت تغییر نام فایل در کد Dart، آن را پیدا کند.
برای این کد، کلاس LineChart در فایل lib/article_screen.dart نمودار را نمایش میدهد. متد build آن یک CustomPainter برمیگرداند که این نمودار را روی صفحه نمایش رسم میکند.
برای پیادهسازی این ویژگی، فایل lib/article_screen.dart را باز کنید. پکیج home_widget را وارد کنید. سپس، کد موجود در کلاس _ArticleScreenState را با کد زیر جایگزین کنید:
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!),
],
),
);
}
}
این مثال سه تغییر در کلاس _ArticleScreenState ایجاد میکند.
یک کلید عمومی (GlobalKey) ایجاد میکند
GlobalKey زمینهی ویجت خاص را دریافت میکند که برای دریافت اندازهی آن ویجت مورد نیاز است.
lib/article_screen.dart
class _ArticleScreenState extends State<ArticleScreen> {
// New: add this GlobalKey
final _globalKey = GlobalKey();
...
}
مسیر تصویر را اضافه میکند
ویژگی imagePath موقعیت مکانی تصویر را که ویجت Flutter در آن رندر میشود، ذخیره میکند.
lib/article_screen.dart
class _ArticleScreenState extends State<ArticleScreen> {
...
// New: add this imagePath
String? imagePath;
...
}
کلید را به ویجت برای رندر اضافه میکند
_globalKey شامل ویجت Flutter است که به تصویر رندر میشود. در این مورد، ویجت Flutter همان Center است که LineChart در خود جای داده است.
lib/article_screen.dart
class _ArticleScreenState extends State<ArticleScreen> {
...
Center(
// New: Add this key
key: _globalKey,
child: const LineChart(),
),
...
}
- ویجت را به عنوان تصویر ذخیره میکند
متد renderFlutterWidget زمانی فراخوانی میشود که کاربر روی floatingActionButton کلیک کند. این متد فایل PNG حاصل را به عنوان "screenshot" در دایرکتوری کانتینر مشترک ذخیره میکند. این متد همچنین مسیر کامل تصویر را به عنوان کلید نام فایل در حافظه دستگاه ذخیره میکند.
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 را بهروزرسانی کنید
برای iOS، کد را بهروزرسانی کنید تا مسیر فایل را از حافظه دریافت کرده و با استفاده از SwiftUI، فایل را به صورت تصویر نمایش دهد.
فایل NewsWidgets.swift را باز کنید تا تغییرات زیر را اعمال کنید:
filename و displaySize به ساختار NewsArticleEntry اضافه کنید.
ویژگی filename رشتهای را در خود نگه میدارد که مسیر فایل تصویر را نشان میدهد. ویژگی displaySize اندازه ویجت صفحه اصلی (Home Screen) را در دستگاه کاربر نگه میدارد. اندازه ویجت صفحه اصلی از context میآید.
ios/NewsWidgets/NewsWidgets.swift
struct NewsArticleEntry: TimelineEntry {
...
// New: add the filename and displaySize.
let filename: String
let displaySize: CGSize
}
تابع placeholder را بهروزرسانی کنید
یک filename و displaySize به عنوان placeholder قرار دهید.
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)
}
نام فایل را از userDefaults در getSnapshot دریافت کنید
این کد، متغیر filename را هنگام بهروزرسانی ویجت صفحه اصلی، روی مقدار filename در حافظه userDefaults تنظیم میکند.
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"
...
)
ایجاد ChartImage که تصویر را از یک مسیر نمایش میدهد
نمای ChartImage از محتویات فایل تولید شده در سمت Dart، تصویری ایجاد میکند. در اینجا، اندازه را روی ۵۰٪ از قاب تنظیم میکنید.
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())
}
...
}
از ChartImage در بدنهی NewsWidgetsEntryView استفاده کنید.
نمای ChartImage را به بدنهی NewsWidgetsEntryView اضافه کنید تا ChartImage در ویجت صفحه اصلی نمایش داده شود.
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
}
تغییرات را آزمایش کنید
برای آزمایش تغییرات، هم برنامه Flutter (Runner) و هم افزونه خود را از Xcode دوباره اجرا کنید. برای دیدن تصویر، به یکی از صفحات مقاله در برنامه بروید و دکمه را برای بهروزرسانی ویجت صفحه اصلی فشار دهید.

کد اندروید را بهروزرسانی کنید
کد اندروید همانند کد iOS عمل میکند.
- فایل
android/app/res/layout/news_widget.xmlرا باز کنید. این فایل شامل عناصر رابط کاربری ویجت صفحه اصلی شما است. محتویات آن را با کد زیر جایگزین کنید:
اندروید/اپ/رز/لایوت/نیوز_ویجت.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>
این کد جدید یک تصویر به ویجت صفحه اصلی اضافه میکند که (فعلاً) یک آیکون ستاره عمومی را نمایش میدهد. این آیکون ستاره را با تصویری که در کد Dart ذخیره کردهاید، جایگزین کنید.
- فایل
NewsWidget.ktرا باز کنید. محتوای آن را با کد زیر جایگزین کنید:
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)
}
}
}
این کد دارت، یک اسکرینشات را با کلید filename در حافظه محلی ذخیره میکند. همچنین مسیر کامل تصویر را دریافت کرده و یک شیء File از آن ایجاد میکند. اگر تصویر وجود داشته باشد، کد دارت تصویر موجود در ویجت صفحه اصلی را با تصویر جدید جایگزین میکند.
- برنامه خود را مجدداً بارگذاری کنید و به صفحه مقاله بروید. بهروزرسانی صفحه اصلی را فشار دهید. ویجت صفحه اصلی نمودار را نمایش میدهد.
۷. مراحل بعدی
تبریک میگویم!
تبریک میگویم، شما در ایجاد ویجتهای صفحه اصلی برای برنامههای Flutter iOS و اندروید خود موفق شدید!
لینک دادن به محتوا در برنامه Flutter شما
ممکن است بخواهید کاربر را بسته به جایی که کلیک میکند، به صفحه خاصی در برنامه خود هدایت کنید. برای مثال، در برنامه خبری از این codelab، ممکن است بخواهید کاربر مقاله خبری مربوط به تیتر نمایش داده شده را ببیند.
این ویژگی خارج از محدوده این آزمایشگاه کد است. میتوانید نمونههایی از استفاده از جریانی که بسته home_widget برای شناسایی راهاندازی برنامه از ویجتهای صفحه اصلی و ارسال پیام از ویجت صفحه اصلی از طریق URL ارائه میدهد را بیابید. برای کسب اطلاعات بیشتر، به مستندات پیوند عمیق در docs.flutter.dev مراجعه کنید.
بهروزرسانی ویجت در پسزمینه
در این آزمایشگاه کد، شما با استفاده از یک دکمه، بهروزرسانی ویجت صفحه اصلی را فعال کردید. اگرچه این برای آزمایش منطقی است، اما در کد عملیاتی ممکن است بخواهید برنامه شما ویجت صفحه اصلی را در پسزمینه بهروزرسانی کند. میتوانید از افزونه workmanager برای ایجاد وظایف پسزمینه برای بهروزرسانی منابع مورد نیاز ویجت صفحه اصلی استفاده کنید. برای کسب اطلاعات بیشتر، بخش بهروزرسانی پسزمینه را در بسته home_widget بررسی کنید.
برای iOS، میتوانید از ویجت صفحه اصلی بخواهید که برای بهروزرسانی رابط کاربری خود، درخواست شبکه ارسال کند. برای کنترل شرایط یا دفعات آن درخواست، از Timeline استفاده کنید. برای کسب اطلاعات بیشتر در مورد استفاده از Timeline، به مستندات «بهروز نگهداشتن ویجت» اپل مراجعه کنید.