การเพิ่มวิดเจ็ตหน้าจอหลักลงในแอป Flutter

1. บทนำ

วิดเจ็ตคืออะไร

สำหรับนักพัฒนาแอป Flutter คำจำกัดความทั่วไปของวิดเจ็ตหมายถึงคอมโพเนนต์ UI ที่สร้างขึ้นโดยใช้เฟรมเวิร์ก Flutter ในบริบทของโค้ดแล็บนี้ วิดเจ็ตหมายถึงแอปเวอร์ชันมินิที่แสดงข้อมูลของแอปโดยไม่ต้องเปิดแอป ใน Android วิดเจ็ตจะอยู่ในหน้าจอหลัก ใน iOS คุณจะเพิ่มวิดเจ็ตลงในหน้าจอหลัก หน้าจอล็อก หรือมุมมองวันนี้ได้

f0027e8a7d0237e0.png b991e79ea72c8b65.png

วิดเจ็ตมีความซับซ้อนได้มากเพียงใด

วิดเจ็ตหน้าจอหลักส่วนใหญ่ใช้งานง่าย โดยอาจมีข้อความพื้นฐาน กราฟิกเรียบง่าย หรือการควบคุมพื้นฐานใน Android ทั้ง Android และ iOS จำกัดคอมโพเนนต์ UI และฟีเจอร์ที่คุณใช้ได้

819b9fffd700e571.png 92d62ccfd17d770d.png

สร้าง UI สำหรับวิดเจ็ต

เนื่องจากข้อจำกัดของ UI เหล่านี้ คุณจึงวาด UI ของวิดเจ็ตหน้าจอหลักโดยใช้เฟรมเวิร์ก Flutter โดยตรงไม่ได้ แต่คุณสามารถเพิ่มวิดเจ็ตที่สร้างด้วยเฟรมเวิร์กของแพลตฟอร์ม เช่น Jetpack Compose หรือ SwiftUI ลงในแอป Flutter ได้ Codelab นี้จะอธิบายตัวอย่างการแชร์ทรัพยากรระหว่างแอปกับวิดเจ็ตเพื่อหลีกเลี่ยงการเขียน UI ที่ซับซ้อนซ้ำ

สิ่งที่คุณจะสร้าง

ใน Codelab นี้ คุณจะได้สร้างวิดเจ็ตหน้าจอหลักทั้งใน Android และ iOS สำหรับแอป Flutter อย่างง่าย โดยใช้แพ็กเกจ home_widget ซึ่งช่วยให้ผู้ใช้สามารถอ่านบทความได้ วิดเจ็ตจะทำสิ่งต่อไปนี้

  • แสดงข้อมูลจากแอป Flutter
  • แสดงข้อความโดยใช้ชิ้นงานแบบอักษรที่แชร์จากแอป Flutter
  • แสดงรูปภาพของวิดเจ็ต Flutter ที่แสดงผล

a36b7ba379151101.png

แอป Flutter นี้มี 2 หน้าจอ (หรือเส้นทาง) ดังนี้

  • ส่วนแรกจะแสดงรายการบทความข่าวพร้อมบรรทัดแรกและคำอธิบาย
  • ส่วนที่ 2 แสดงบทความฉบับเต็มพร้อมแผนภูมิที่สร้างขึ้นโดยใช้ CustomPaint

.

9c02f8b62c1faa3a.png d97d44051304cae4.png

สิ่งที่คุณจะได้เรียนรู้

  • วิธีสร้างวิดเจ็ตหน้าจอหลักใน iOS และ Android
  • วิธีใช้แพ็กเกจ home_widget เพื่อแชร์ข้อมูลระหว่างวิดเจ็ตหน้าจอหลักกับแอป Flutter
  • วิธีลดปริมาณโค้ดที่คุณต้องเขียนใหม่
  • วิธีอัปเดตวิดเจ็ตหน้าจอหลักจากแอป Flutter

2. ตั้งค่าสภาพแวดล้อมในการพัฒนาซอฟต์แวร์

สำหรับทั้ง 2 แพลตฟอร์ม คุณต้องมี Flutter SDK และIDE คุณสามารถใช้ IDE ที่ต้องการเพื่อทำงานกับ Flutter ได้ ซึ่งอาจเป็น Visual Studio Code ที่มีส่วนขยาย Dart Code และ Flutter หรือ Android Studio หรือ IntelliJ ที่ติดตั้งปลั๊กอิน Flutter และ Dart

วิธีสร้างวิดเจ็ตหน้าจอหลักของ iOS

  • คุณเรียกใช้ Codelab นี้ได้ในอุปกรณ์ iOS จริงหรือโปรแกรมจำลอง iOS
  • คุณต้องกำหนดค่าระบบ macOS ด้วย Xcode IDE ซึ่งจะติดตั้งคอมไพเลอร์ที่จำเป็นต่อการสร้างแอปเวอร์ชัน iOS

วิธีสร้างวิดเจ็ตหน้าจอหลักของ Android

  • คุณเรียกใช้ Codelab นี้ได้ในอุปกรณ์ Android จริงหรือโปรแกรมจำลอง Android
  • คุณต้องกำหนดค่าระบบพัฒนาด้วย Android Studio ซึ่งจะติดตั้งคอมไพเลอร์ที่จำเป็นต่อการสร้างแอปเวอร์ชัน Android

รับโค้ดเริ่มต้น

ดาวน์โหลดโปรเจ็กต์เวอร์ชันเริ่มต้นจาก GitHub

จากบรรทัดคำสั่ง ให้โคลนที่เก็บ GitHub ลงในไดเรกทอรี flutter-codelabs โดยใช้คำสั่งต่อไปนี้

$ git clone https://github.com/flutter/codelabs.git flutter-codelabs

หลังจากโคลนที่เก็บแล้ว คุณจะเห็นโค้ดสำหรับ Codelab นี้ในไดเรกทอรี flutter-codelabs/homescreen_codelab ไดเรกทอรีนี้มีโค้ดโปรเจ็กต์ที่เสร็จสมบูรณ์แล้วสำหรับแต่ละขั้นตอนในโค้ดแล็บ

เปิดแอปเริ่มต้น

เปิดไดเรกทอรี flutter-codelabs/homescreen_codelab/step_03 ใน IDE ที่ต้องการ

ติดตั้งแพ็กเกจ

เพิ่มแพ็กเกจที่จำเป็นทั้งหมดลงในไฟล์ pubspec.yaml ของโปรเจ็กต์แล้ว หากต้องการดึงข้อมูลทรัพยากร Dependency ของโปรเจ็กต์ ให้เรียกใช้คำสั่งต่อไปนี้

$ flutter pub get

3. เพิ่มวิดเจ็ตหน้าจอหลักพื้นฐาน

ก่อนอื่น ให้เพิ่มวิดเจ็ตหน้าจอหลักโดยใช้เครื่องมือแพลตฟอร์มดั้งเดิม

สร้างวิดเจ็ตหน้าจอหลักของ iOS ขั้นพื้นฐาน

การเพิ่มส่วนขยายแอปไปยังแอป Flutter iOS จะคล้ายกับการเพิ่มส่วนขยายแอปไปยังแอป SwiftUI หรือ UIKit ดังนี้

  1. เรียกใช้ open ios/Runner.xcworkspace ในหน้าต่างเทอร์มินัลจากไดเรกทอรีโปรเจ็กต์ Flutter หรือคลิกขวาที่โฟลเดอร์ ios จาก VSCode แล้วเลือกเปิดใน Xcode ซึ่งจะเปิดพื้นที่ทำงาน Xcode เริ่มต้นในโปรเจ็กต์ Flutter
  2. เลือกไฟล์ → ใหม่ → เป้าหมายจากเมนู ซึ่งจะเป็นการเพิ่มเป้าหมายใหม่ลงในโปรเจ็กต์
  3. รายการเทมเพลตจะปรากฏขึ้น เลือกส่วนขยายวิดเจ็ต
  4. พิมพ์ "NewsWidgets" ลงในช่องชื่อผลิตภัณฑ์สำหรับวิดเจ็ตนี้ ล้างทั้งช่องทำเครื่องหมายรวมกิจกรรมถ่ายทอดสดและรวมเจตนาในการกำหนดค่า

ตรวจสอบโค้ดตัวอย่าง

เมื่อเพิ่มเป้าหมายใหม่ Xcode จะสร้างโค้ดตัวอย่างตามเทมเพลตที่คุณเลือก ดูข้อมูลเพิ่มเติมเกี่ยวกับโค้ดที่สร้างขึ้นและ WidgetKit ได้ที่เอกสารประกอบส่วนขยายแอปของ Apple

แก้ไขข้อบกพร่องและทดสอบวิดเจ็ตตัวอย่าง

  1. ก่อนอื่น ให้อัปเดตการกำหนดค่าของแอป Flutter คุณต้องดำเนินการนี้เมื่อเพิ่มแพ็กเกจใหม่ในแอป Flutter และวางแผนที่จะเรียกใช้เป้าหมายในโปรเจ็กต์จาก Xcode หากต้องการอัปเดตการกำหนดค่าของแอป ให้เรียกใช้คำสั่งต่อไปนี้ในไดเรกทอรีแอป Flutter
$ flutter build ios --config-only
  1. คลิก Runner เพื่อแสดงรายการเป้าหมาย เลือกเป้าหมายวิดเจ็ตที่คุณเพิ่งสร้าง NewsWidgets แล้วคลิกเรียกใช้ เรียกใช้เป้าหมายวิดเจ็ตจาก Xcode เมื่อเปลี่ยนโค้ดวิดเจ็ต iOS

bbb519df1782881d.png

  1. หน้าจอจำลองหรือหน้าจออุปกรณ์ควรแสดงวิดเจ็ตหน้าจอหลักพื้นฐาน หากไม่เห็น ให้เพิ่มลงในหน้าจอ คลิกหน้าจอหลักค้างไว้ แล้วคลิก + ที่มุมซ้ายบน

18eff1cae152014d.png

  1. ค้นหาชื่อแอป สำหรับ Codelab นี้ ให้ค้นหา "วิดเจ็ตหน้าจอหลัก"

a0c00df87615493e.png

  1. เมื่อเพิ่มวิดเจ็ตหน้าจอหลักแล้ว วิดเจ็ตควรแสดงข้อความง่ายๆ ที่ระบุเวลา

การสร้างวิดเจ็ต Android พื้นฐาน

  1. หากต้องการเพิ่มวิดเจ็ตหน้าจอหลักใน Android ให้เปิดไฟล์บิลด์ของโปรเจ็กต์ใน Android Studio คุณดูไฟล์นี้ได้ที่ android/build.gradle หรือคลิกขวาที่โฟลเดอร์ android จาก VSCode แล้วเลือกเปิดใน Android Studio
  2. หลังจากสร้างโปรเจ็กต์แล้ว ให้ค้นหาไดเรกทอรีแอปที่มุมซ้ายบน เพิ่มวิดเจ็ตหน้าจอหลักใหม่ลงในไดเรกทอรีนี้ คลิกขวาที่ไดเรกทอรี เลือกใหม่ -> วิดเจ็ต -> วิดเจ็ตแอป

f19d8b7f95ab884e.png

  1. Android Studio จะแสดงแบบฟอร์มใหม่ เพิ่มข้อมูลพื้นฐานเกี่ยวกับวิดเจ็ตหน้าจอหลัก รวมถึงชื่อคลาส ตำแหน่ง ขนาด และภาษาต้นฉบับ

สำหรับ Codelab นี้ ให้ตั้งค่าต่อไปนี้

  • กล่องชื่อชั้นเรียนไปยัง NewsWidget
  • ความกว้างขั้นต่ำ (เซลล์) เป็น 3
  • ความสูงขั้นต่ำ (เซลล์) เป็น 3

ตรวจสอบโค้ดตัวอย่าง

เมื่อคุณส่งแบบฟอร์ม Android Studio จะสร้างและอัปเดตไฟล์หลายไฟล์ การเปลี่ยนแปลงที่เกี่ยวข้องกับโค้ดแล็บนี้แสดงอยู่ในตารางด้านล่าง

การดำเนินการ

ไฟล์เป้าหมาย

เปลี่ยน

อัปเดต

AndroidManifest.xml

เพิ่มตัวรับใหม่ที่ลงทะเบียน NewsWidget

สร้าง

res/layout/news_widget.xml

กำหนด UI ของวิดเจ็ตหน้าจอหลัก

สร้าง

res/xml/news_widget_info.xml

กำหนดการกำหนดค่าวิดเจ็ตหน้าจอหลัก คุณปรับขนาดหรือชื่อของวิดเจ็ตได้ในไฟล์นี้

สร้าง

java/com/example/homescreen_widgets/NewsWidget.kt

มีโค้ด Kotlin เพื่อเพิ่มฟังก์ชันการทำงานให้กับวิดเจ็ตหน้าจอหลัก

ดูรายละเอียดเพิ่มเติมเกี่ยวกับไฟล์เหล่านี้ได้ตลอดทั้ง Codelab นี้

แก้ไขข้อบกพร่องและทดสอบวิดเจ็ตตัวอย่าง

ตอนนี้เรียกใช้แอปพลิเคชันและดูวิดเจ็ตหน้าจอหลัก เมื่อสร้างแอปแล้ว ให้ไปที่หน้าจอการเลือกแอปพลิเคชันของอุปกรณ์ Android แล้วกดไอคอนโปรเจ็กต์ Flutter นี้ค้างไว้ เลือกวิดเจ็ตจากเมนูป๊อปอัป

dff7c9f9f85ef1c7.png

อุปกรณ์ Android หรือโปรแกรมจำลองจะแสดงวิดเจ็ตหน้าจอหลักเริ่มต้นสำหรับ Android

4. ส่งข้อมูลจากแอป Flutter ไปยังวิดเจ็ตหน้าจอหลัก

คุณปรับแต่งวิดเจ็ตหน้าจอหลักพื้นฐานที่สร้างขึ้นได้ อัปเดตวิดเจ็ตหน้าจอหลักเพื่อแสดงพาดหัวและข้อมูลสรุปของบทความข่าว ภาพหน้าจอด้านล่างแสดงตัวอย่างวิดเจ็ตหน้าจอหลักที่แสดงพาดหัวและข้อมูลสรุป

acb90343a3e51b6d.png

หากต้องการส่งข้อมูลระหว่างแอปกับวิดเจ็ตหน้าจอหลัก คุณต้องเขียนโค้ด Dart และโค้ดเนทีฟ ส่วนนี้จะแบ่งกระบวนการนี้ออกเป็น 3 ส่วน

  1. การเขียนโค้ด Dart ในแอป Flutter ที่ทั้ง Android และ iOS ใช้ได้
  2. การเพิ่มฟังก์ชันการทำงานของ iOS ดั้งเดิม
  3. การเพิ่มฟังก์ชันการทำงานของ Android แบบเนทีฟ

การใช้กลุ่มแอป iOS

หากต้องการแชร์ข้อมูลระหว่างแอปหลักใน iOS กับส่วนขยายวิดเจ็ต เป้าหมายทั้ง 2 รายการต้องอยู่ในกลุ่มแอปเดียวกัน ดูข้อมูลเพิ่มเติมเกี่ยวกับกลุ่มแอปได้ที่เอกสารประกอบเกี่ยวกับกลุ่มแอปของ Apple

อัปเดตรหัสชุดซอฟต์แวร์

ใน Xcode ให้ไปที่การตั้งค่าของเป้าหมาย ในแท็บการลงนามและความสามารถ ให้ตรวจสอบว่าได้ตั้งค่าทีมและตัวระบุแพ็กเกจแล้ว

เพิ่มกลุ่มแอปไปยังเป้าหมาย Runner และเป้าหมาย NewsWidgetExtension ทั้ง 2 รายการใน Xcode โดยทำดังนี้

เลือก + ความสามารถ -> กลุ่มแอป แล้วเพิ่มกลุ่มแอปใหม่ ทำซ้ำสำหรับทั้งเป้าหมาย Runner (แอปหลัก) และเป้าหมายวิดเจ็ต

135e1a8c4652dac.png

เพิ่มโค้ด Dart

ทั้งแอป iOS และ Android สามารถแชร์ข้อมูลกับแอป Flutter ได้หลายวิธี ในการสื่อสารกับแอปเหล่านี้ ให้ใช้ประโยชน์จากkey/value Store ในเครื่องของอุปกรณ์ iOS เรียก Store นี้ว่า UserDefaults และ Android เรียก Store นี้ว่า SharedPreferences แพ็กเกจ home_widget จะรวม API เหล่านี้ไว้ด้วยกันเพื่อลดความซับซ้อนในการบันทึกข้อมูลไปยังแพลตฟอร์มใดแพลตฟอร์มหนึ่ง และช่วยให้วิดเจ็ตหน้าจอหลักดึงข้อมูลที่อัปเดตได้

707ae86f6650ac55.png

ข้อมูลบรรทัดแรกและคำอธิบายมาจากไฟล์ 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 ฟังก์ชันนี้ยังแจ้งแพลตฟอร์มเนทีฟว่าสามารถดึงและแสดงข้อมูลใหม่สำหรับวิดเจ็ตหน้าจอหลักได้

แก้ไข FloatingActionButton

เรียกใช้ฟังก์ชัน 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 ของ Apple

แก้ไข 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 มี 3 วิธีดังนี้

  1. placeholder วิธีนี้จะสร้างรายการตัวยึดตำแหน่งเมื่อผู้ใช้ดูตัวอย่างวิดเจ็ตหน้าจอหลักเป็นครั้งแรก

45a0f64240c12efe.png

  1. วิธี getSnapshot จะอ่านข้อมูลจากค่าเริ่มต้นของผู้ใช้และสร้างรายการสำหรับเวลาปัจจุบัน
  2. เมธอด getTimeline จะแสดงรายการไทม์ไลน์ ซึ่งจะมีประโยชน์เมื่อคุณมีช่วงเวลาที่คาดการณ์ได้ในการอัปเดตเนื้อหา Codelab นี้ใช้ฟังก์ชัน getSnapshot เพื่อรับสถานะปัจจุบัน .atEnd เมธอดจะบอกให้วิดเจ็ตหน้าจอหลักรีเฟรชข้อมูลหลังจากเวลาปัจจุบันผ่านไป

แสดงความคิดเห็นใน NewsWidgets_Previews

การใช้ตัวอย่างอยู่นอกขอบเขตของโค้ดแล็บนี้ ดูรายละเอียดเพิ่มเติมเกี่ยวกับการแสดงตัวอย่างวิดเจ็ตหน้าจอหลักของ SwiftUI ได้ที่เอกสารประกอบของ Apple เกี่ยวกับการแก้ไขข้อบกพร่องของวิดเจ็ต

บันทึกไฟล์ทั้งหมดและเรียกใช้เป้าหมายแอปและวิดเจ็ตอีกครั้ง

เรียกใช้เป้าหมายอีกครั้งเพื่อตรวจสอบว่าแอปและวิดเจ็ตหน้าจอหลักทำงานได้

  1. เลือกสคีมาแอปใน Xcode เพื่อเรียกใช้เป้าหมายแอป
  2. เลือกสคีมาของส่วนขยายใน Xcode เพื่อเรียกใช้เป้าหมายของส่วนขยาย
  3. ไปยังหน้าบทความในแอป
  4. คลิกปุ่มเพื่ออัปเดตพาดหัว วิดเจ็ตหน้าจอหลักควรจะอัปเดตบรรทัดแรกด้วย

อัปเดตโค้ด Android

เพิ่ม XML ของวิดเจ็ตหน้าจอหลัก

ใน Android Studio ให้อัปเดตไฟล์ที่สร้างในขั้นตอนก่อนหน้า เปิดไฟล์ res/layout/news_widget.xml ซึ่งจะกำหนดโครงสร้างและเลย์เอาต์ของวิดเจ็ตหน้าจอหลัก เลือกโค้ดที่มุมขวาบน แล้วแทนที่เนื้อหาของไฟล์นั้นด้วยโค้ดต่อไปนี้

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>

XML นี้กำหนดมุมมองข้อความ 2 รายการ รายการหนึ่งสำหรับบรรทัดแรกของบทความ และอีกรายการหนึ่งสำหรับคำอธิบายบทความ มุมมองข้อความเหล่านี้ยังกำหนดการจัดรูปแบบด้วย คุณจะกลับมาที่ไฟล์นี้ตลอดทั้ง Codelab นี้

อัปเดตฟังก์ชันการทำงานของ NewsWidget

เปิดNewsWidget.ktไฟล์ซอร์สโค้ด Kotlin ไฟล์นี้มีคลาสที่สร้างขึ้นชื่อ NewsWidget ซึ่งขยายคลาส AppWidgetProvider

คลาส NewsWidget มี 3 เมธอดจากคลาสแม่ คุณจะแก้ไขเมธอด onUpdate Android จะเรียกใช้เมธอดนี้สำหรับวิดเจ็ตในช่วงเวลาที่กำหนด

แทนที่เนื้อหาของไฟล์ 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 Android จะรับค่าล่าสุดจากที่เก็บข้อมูลในเครื่องโดยใช้วิธี the widgetData.getString() จากนั้นจะเรียกใช้ setTextViewText เพื่อเปลี่ยนข้อความที่แสดงในวิดเจ็ตหน้าจอหลัก

ทดสอบการอัปเดต

ทดสอบแอปเพื่อให้แน่ใจว่าวิดเจ็ตหน้าจอหลักจะอัปเดตข้อมูลใหม่ หากต้องการอัปเดตข้อมูล ให้ใช้อัปเดตหน้าจอหลัก FloatingActionButton ในหน้าบทความ วิดเจ็ตหน้าจอหลักควรจะอัปเดตชื่อบทความ

5ce1c9914b43ad79.png

5. การใช้แบบอักษรที่กำหนดเองของแอป Flutter ในวิดเจ็ตหน้าจอหลักของ iOS

ตอนนี้คุณได้กำหนดค่าวิดเจ็ตหน้าจอหลักให้อ่านข้อมูลที่แอป Flutter ให้ไว้แล้ว แอป Flutter มีแบบอักษรที่กำหนดเองซึ่งคุณอาจต้องการใช้ในวิดเจ็ตหน้าจอหลัก คุณใช้แบบอักษรที่กำหนดเองในวิดเจ็ตหน้าจอหลักของ iOS ได้ การใช้แบบอักษรที่กำหนดเองในวิดเจ็ตหน้าจอหลักไม่พร้อมใช้งานใน Android

อัปเดตโค้ด iOS

Flutter จัดเก็บชิ้นงานใน mainBundle ของแอปพลิเคชัน iOS คุณเข้าถึงชิ้นงานในชุดนี้ได้จากโค้ดวิดเจ็ตหน้าจอหลัก

ในโครงสร้าง NewsWidgetsEntryView ในไฟล์ NewsWidgets.swift ให้ทำการเปลี่ยนแปลงต่อไปนี้

สร้างฟังก์ชันตัวช่วยเพื่อรับเส้นทางไปยังไดเรกทอรีชิ้นงาน Flutter:

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

เมื่อคุณเรียกใช้วิดเจ็ตหน้าจอหลัก วิดเจ็ตจะใช้แบบอักษรที่กำหนดเองสำหรับพาดหัวตามที่แสดงในรูปภาพต่อไปนี้

93f8b9d767aacfb2.png

6. การแสดงผลวิดเจ็ต Flutter เป็นรูปภาพ

ในส่วนนี้ คุณจะแสดงกราฟจากแอป Flutter เป็นวิดเจ็ตหน้าจอหลัก

วิดเจ็ตนี้มีความท้าทายมากกว่าข้อความที่คุณแสดงบนหน้าจอหลัก การแสดงแผนภูมิ Flutter เป็นรูปภาพนั้นง่ายกว่าการพยายามสร้างใหม่โดยใช้คอมโพเนนต์ UI ดั้งเดิมมาก

เขียนโค้ดวิดเจ็ตหน้าจอหลักเพื่อแสดงแผนภูมิ Flutter เป็นไฟล์ PNG วิดเจ็ตหน้าจอหลักจะแสดงรูปภาพนั้นได้

เขียนโค้ด Dart

ในฝั่ง Dart ให้เพิ่มเมธอด renderFlutterWidget จากแพ็กเกจ home_widget เมธอดนี้รับวิดเจ็ต ชื่อไฟล์ และคีย์ โดยจะแสดงผลรูปภาพของวิดเจ็ต Flutter และบันทึกลงในคอนเทนเนอร์ที่แชร์ ระบุชื่อรูปภาพในโค้ดและตรวจสอบว่าวิดเจ็ตหน้าจอหลักเข้าถึงคอนเทนเนอร์ได้ key จะบันทึกเส้นทางแบบเต็มของไฟล์เป็นสตริงในพื้นที่เก็บข้อมูลในเครื่องของอุปกรณ์ ซึ่งจะช่วยให้วิดเจ็ตหน้าจอหลักค้นหาไฟล์ได้หากมีการเปลี่ยนชื่อในโค้ด Dart

สำหรับโค้ดแล็บนี้ คลาส LineChart ในไฟล์ lib/article_screen.dart จะแสดงแผนภูมิ เมธอดการสร้างจะแสดงผล 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!),
        ],
      ),
    );
  }
}

ตัวอย่างนี้ทำการเปลี่ยนแปลง 3 อย่างในคลาส _ArticleScreenState

สร้าง GlobalKey

GlobalKey จะรับบริบทของวิดเจ็ตที่เฉพาะเจาะจง ซึ่งจำเป็นต่อการรับขนาดของวิดเจ็ตนั้น

lib/article_screen.dart

class _ArticleScreenState extends State<ArticleScreen> {
   // New: add this GlobalKey
   final _globalKey = GlobalKey();
   ...
}

เพิ่ม imagePath

พร็อพเพอร์ตี้ 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(),
   ),
   ...
}
  1. บันทึกวิดเจ็ตเป็นรูปภาพ

ระบบจะเรียกใช้เมธอด 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 struct

พร็อพเพอร์ตี้ filename จะมีสตริงที่แสดงเส้นทางไปยังไฟล์รูปภาพ พร็อพเพอร์ตี้ displaySize จะเก็บขนาดของวิดเจ็ตหน้าจอหลักในอุปกรณ์ของผู้ใช้ ขนาดของวิดเจ็ตหน้าจอหลักมาจากcontext

ios/NewsWidgets/NewsWidgets.swift

struct NewsArticleEntry: TimelineEntry {
   ...

   // New: add the filename and displaySize.
   let filename: String
   let displaySize: CGSize
}

อัปเดตplaceholder ฟังก์ชัน

รวมตัวยึดตำแหน่ง filename และ displaySize

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 View จะสร้างรูปภาพจากเนื้อหาของไฟล์ที่สร้างขึ้นในฝั่ง Dart ในที่นี้ คุณตั้งค่าขนาดเป็น 50% ของเฟรม

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 อีกครั้ง หากต้องการดูรูปภาพ ให้ไปที่หน้าบทความหน้าใดหน้าหนึ่งในแอป แล้วกดปุ่มเพื่ออัปเดตวิดเจ็ตหน้าจอหลัก

33bdfe2cce908c48.png

อัปเดตโค้ด Android

โค้ด Android ทำงานเหมือนกับโค้ด iOS

  1. เปิดไฟล์ android/app/res/layout/news_widget.xml ซึ่งมีองค์ประกอบ UI ของวิดเจ็ตหน้าจอหลัก แทนที่เนื้อหาด้วยโค้ดต่อไปนี้

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>

โค้ดใหม่นี้จะเพิ่มรูปภาพลงในวิดเจ็ตหน้าจอหลัก ซึ่งจะแสดงไอคอนดาวทั่วไป (ในตอนนี้) แทนที่ไอคอนดาวนี้ด้วยรูปภาพที่คุณบันทึกไว้ในโค้ด Dart

  1. เปิดไฟล์ 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)
        }
    }
}

โค้ด Dart นี้จะบันทึกภาพหน้าจอไปยังพื้นที่เก็บข้อมูลในเครื่องด้วยคีย์ filename นอกจากนี้ ยังรับเส้นทางแบบเต็มของรูปภาพและสร้างออบเจ็กต์ File จากรูปภาพนั้นด้วย หากมีรูปภาพอยู่ โค้ด Dart จะแทนที่รูปภาพในวิดเจ็ตหน้าจอหลักด้วยรูปภาพใหม่

  1. โหลดแอปซ้ำแล้วไปที่หน้าจอของบทความ กดอัปเดตหน้าจอหลัก วิดเจ็ตหน้าจอหลักจะแสดงแผนภูมิ

7. ขั้นตอนถัดไป

ยินดีด้วย

ขอแสดงความยินดี คุณสร้างวิดเจ็ตหน้าจอหลักสำหรับแอป Flutter iOS และ Android ได้สำเร็จแล้ว

การลิงก์ไปยังเนื้อหาในแอป Flutter

คุณอาจต้องการนำผู้ใช้ไปยังหน้าใดหน้าหนึ่งในแอป ทั้งนี้ขึ้นอยู่กับว่าผู้ใช้คลิกที่ใด ตัวอย่างเช่น ในแอปข่าวจากโค้ดแล็บนี้ คุณอาจต้องการให้ผู้ใช้เห็นบทความข่าวสำหรับพาดหัวที่แสดง

ฟีเจอร์นี้อยู่นอกขอบเขตของโค้ดแล็บนี้ คุณดูตัวอย่างการใช้สตรีมที่แพ็กเกจ home_widget มีให้เพื่อระบุการเปิดแอปจากวิดเจ็ตหน้าจอหลักและส่งข้อความจากวิดเจ็ตหน้าจอหลักผ่าน URL ได้ ดูข้อมูลเพิ่มเติมได้ที่เอกสารประกอบเกี่ยวกับการทำ Deep Link ใน docs.flutter.dev

การอัปเดตวิดเจ็ตในเบื้องหลัง

ใน Codelab นี้ คุณได้ทริกเกอร์การอัปเดตวิดเจ็ตหน้าจอหลักโดยใช้ปุ่ม แม้ว่าการดำเนินการนี้จะสมเหตุสมผลสำหรับการทดสอบ แต่ในโค้ดเวอร์ชันที่ใช้งานจริง คุณอาจต้องการให้แอปอัปเดตวิดเจ็ตหน้าจอหลักในเบื้องหลัง คุณสามารถใช้ปลั๊กอิน Workmanager เพื่อสร้างงานในเบื้องหลังเพื่ออัปเดตทรัพยากรที่วิดเจ็ตหน้าจอหลักต้องการ ดูข้อมูลเพิ่มเติมได้ที่ส่วนการอัปเดตเบื้องหลังในแพ็กเกจ home_widget

สำหรับ iOS คุณยังสามารถให้วิดเจ็ตหน้าจอหลักส่งคำขอเครือข่ายเพื่ออัปเดต UI ได้ด้วย หากต้องการควบคุมเงื่อนไขหรือความถี่ของคำขอนั้น ให้ใช้ไทม์ไลน์ ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้ไทม์ไลน์ได้ที่เอกสารประกอบของ Apple เรื่อง "การอัปเดตวิดเจ็ตให้เป็นปัจจุบัน"

อ่านเพิ่มเติม