본문 바로가기
Flutter/Package

플러터에서 iOS와 안드로이드 홈위젯 구현하기[ flutter_widgetkit패키지 ]

by Maccrey Coding 2024. 7. 28.
반응형

 

개발자 여러분! 오늘은 플러터 앱에서 iOS와 안드로이드 모두를 위한 홈 화면 위젯을 구현하는 방법에 대해 알아보겠습니다.

 

1. iOS 위젯 구현 (flutter_widgetkit 사용)


플러터 앱에서 flutter_widgetkit 패키지를 사용하여 iOS 위젯을 만드는 방법에 대해 알아보겠습니다.

iOS 14부터 지원되는 홈 화면 위젯을 플러터 앱과 연동하여 만들 수 있습니다.

 

1.1. 준비 단계


먼저, pubspec.yaml 파일에 flutter_widgetkit 패키지를 추가합니다

dependencies:
  flutter:
    sdk: flutter
  flutter_widgetkit: ^latest_version

 

1.2. iOS 프로젝트 설정

 

a. Xcode에서 iOS 프로젝트를 엽니다.

b. File > New > Target을 선택하고 'Widget Extension'을 선택합니다.

c. 위젯의 이름을 지정하고 (예: MyFlutterWidget) 'Finish'를 클릭합니다.

d. 생성된 위젯 익스텐션 폴더에 있는 Swift 파일을 열고 다음과 같이 수정합니다

import WidgetKit
import SwiftUI

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), widgetData: "Placeholder")
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date(), widgetData: "Snapshot")
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        let userDefaults = UserDefaults(suiteName: "group.your.app.group.identifier")
        let widgetData = userDefaults?.string(forKey: "widgetKey") ?? "No data"
        
        let entry = SimpleEntry(date: Date(), widgetData: widgetData)
        let timeline = Timeline(entries: [entry], policy: .atEnd)
        completion(timeline)
    }
}

struct SimpleEntry: TimelineEntry {
    let date: Date
    let widgetData: String
}

struct MyFlutterWidgetEntryView : View {
    var entry: Provider.Entry

    var body: some View {
        Text(entry.widgetData)
    }
}

@main
struct MyFlutterWidget: Widget {
    let kind: String = "MyFlutterWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            MyFlutterWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("My Flutter Widget")
        .description("This is an example widget.")
    }
}

1.3. 앱 그룹 설정

 

a. Xcode에서 메인 앱 타겟과 위젯 익스텐션 타겟 모두에 대해 'Signing & Capabilities' 탭을 엽니다.

b. '+ Capability'를 클릭하고 'App Groups'를 추가합니다.

c. 두 타겟 모두에 동일한 앱 그룹 식별자를 추가합니다 (예: group.your.app.group.identifier).

 

1.4. 플러터 코드 구현

 

main.dart 파일에서 flutter_widgetkit을 사용하여 위젯 데이터를 업데이트하는 코드를 작성합니다

import 'package:flutter/material.dart';
import 'package:flutter_widgetkit/flutter_widgetkit.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Flutter WidgetKit Demo')),
      body: Center(
        child: ElevatedButton(
          child: Text('위젯 업데이트'),
          onPressed: () async {
            await updateWidget();
          },
        ),
      ),
    );
  }

  Future<void> updateWidget() async {
    await WidgetKit.setItem('widgetKey', 'Hello from Flutter!', 'group.your.app.group.identifier');
    WidgetKit.reloadAllTimelines();
  }
}

 

1.5. 위젯 테스트

 

이제 앱을 실행하고 "위젯 업데이트" 버튼을 누르면, iOS 홈 화면의 위젯에 "Hello from Flutter!"라는 메시지가 표시될 것입니다.

 

주의사항

  • flutter_widgetkit 패키지는 iOS 위젯만 지원합니다. Android의 경우 다른 방법을 사용해야 합니다.
  • 위젯 데이터 업데이트는 비동기적으로 이루어지며, 시스템에 의해 제어됩니다. 즉시 업데이트되지 않을 수 있습니다.
  • 앱 그룹 식별자는 고유해야 하며, 앱 번들 ID와 연관되어야 합니다.

추가 기능

  • 위젯의 크기에 따라 다른 레이아웃을 제공할 수 있습니다.
  • 복잡한 데이터 구조를 JSON으로 인코딩하여 전달할 수 있습니다.
  • 위젯에서 딥 링크를 사용하여 앱의 특정 화면으로 이동할 수 있습니다.

이렇게 구현하면 플러터 앱에서 iOS 홈 화면 위젯을 만들고 업데이트할 수 있습니다.

위젯은 사용자에게 빠르고 편리한 정보 접근을 제공하므로, 앱의 사용성을 크게 향상시킬 수 있습니다.

 

2. 안드로이드 위젯 구현 (home_widget 패키지 사용)

안드로이드 위젯을 구현하기 위해 home_widget 패키지를 사용하겠습니다.

2.1. 패키지 추가 pubspec.yaml 파일에 다음을 추가합니다

dependencies:
  flutter:
    sdk: flutter
  home_widget: ^latest_version

 

2.2. 안드로이드 프로젝트 설정

android/app/src/main/AndroidManifest.xml 파일에 다음을 추가합니다

<receiver android:name="HomeWidgetProvider" android:exported="true">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider"
        android:resource="@xml/home_widget_provider" />
</receiver>

 

2.3. 위젯 레이아웃 생성

android/app/src/main/res/layout/home_widget_layout.xml 파일을 생성하고 다음 내용을 추가합니다

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="8dp">

    <TextView
        android:id="@+id/widget_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp" />

</LinearLayout>

 

2.4. 위젯 프로바이더 설정

android/app/src/main/res/xml/home_widget_provider.xml 파일을 생성하고 다음 내용을 추가합니다

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="40dp"
    android:minHeight="40dp"
    android:updatePeriodMillis="1800000"
    android:initialLayout="@layout/home_widget_layout"
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen">
</appwidget-provider>

 

2.5. 코드 추가

 

Kotlin를 사용할 경우

android/app/src/main/kotlin/com/example/your_app/HomeWidgetProvider.kt 파일을 생성하고 다음 내용을 추가합니다

import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.widget.RemoteViews
import es.antonborri.home_widget.HomeWidgetPlugin

class HomeWidgetProvider : 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.home_widget_layout).apply {
                setTextViewText(R.id.widget_text, widgetData.getString("widgetData", "No data"))
            }
            appWidgetManager.updateAppWidget(appWidgetId, views)
        }
    }
}

 

Java를 사용할 경우

android/app/src/main/java/com/example/your_app/HomeWidgetProvider.java 파일을 생성하고 다음 내용을 추가합니다

package com.example.your_app;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.widget.RemoteViews;

import es.antonborri.home_widget.HomeWidgetPlugin;

public class HomeWidgetProvider extends AppWidgetProvider {
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        for (int appWidgetId : appWidgetIds) {
            String widgetData = HomeWidgetPlugin.getData(context).getString("widgetData", "No data");
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.home_widget_layout);
            views.setTextViewText(R.id.widget_text, widgetData);
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}

 

이 Java 코드는 Kotlin 코드와 동일한 기능을 수행합니다. 주요 차이점은 다음과 같습니다

  1. 클래스 선언: Java에서는 public class HomeWidgetProvider extends AppWidgetProvider로 선언합니다.
  2. 메서드 오버라이드: Java에서는 @Override 어노테이션을 사용하지만, 메서드 시그니처는 동일합니다.
  3. 변수 선언: Java에서는 변수 타입을 명시적으로 선언해야 합니다. 예를 들어, String widgetData와 같이 사용합니다.
  4. for 루프: Java에서는 for (int appWidgetId : appWidgetIds)와 같이 향상된 for 루프를 사용합니다.
  5. 메서드 체이닝: Java에서는 Kotlin의 apply 함수 대신 일반적인 메서드 호출 방식을 사용합니다.

그 외의 설정 (AndroidManifest.xml, 레이아웃 파일 등)은 Kotlin을 사용할 때와 동일합니다.

 

주의사항

  • Java 파일의 위치가 src/main/java 디렉토리 아래에 있어야 합니다.
  • 패키지 이름 (com.example.your_app)을 여러분의 실제 앱 패키지 이름으로 변경해야 합니다.
  • R.layout.home_widget_layout과 R.id.widget_text가 실제로 존재하는 리소스를 가리키고 있는지 확인해야 합니다.

이렇게 Java로 구현하면, Kotlin 버전과 동일하게 안드로이드 위젯을 만들고 업데이트할 수 있습니다.

3. 플러터 코드 구현

이제 플러터 코드에서 iOS와 안드로이드 위젯을 모두 지원하도록 구현합니다

import 'package:flutter/material.dart';
import 'package:flutter_widgetkit/flutter_widgetkit.dart';
import 'package:home_widget/home_widget.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Flutter Widget Demo')),
      body: Center(
        child: ElevatedButton(
          child: Text('위젯 업데이트'),
          onPressed: () async {
            await updateWidget();
          },
        ),
      ),
    );
  }

  Future<void> updateWidget() async {
    final widgetData = 'Hello from Flutter!';

    // iOS 위젯 업데이트
    await WidgetKit.setItem('widgetKey', widgetData, 'group.your.app.group.identifier');
    WidgetKit.reloadAllTimelines();

    // 안드로이드 위젯 업데이트
    await HomeWidget.saveWidgetData<String>('widgetData', widgetData);
    await HomeWidget.updateWidget(
      name: 'HomeWidgetProvider',
      iOSName: 'MyFlutterWidget',
    );
  }
}

4. 주의사항 및 팁

  • iOS와 안드로이드의 위젯 업데이트 메커니즘이 다르므로, 각 플랫폼의 특성을 고려해야 합니다.
  • 안드로이드 위젯은 주기적으로 업데이트되지만, iOS 위젯은 앱이나 시스템에 의해 트리거될 때 업데이트됩니다.
  • 위젯 데이터는 간단하고 가벼워야 합니다. 복잡한 데이터는 JSON으로 인코딩하여 전달할 수 있습니다.
  • 위젯 디자인은 각 플랫폼의 디자인 가이드라인을 따라야 합니다.
  • 배터리 소모를 고려하여 업데이트 빈도를 적절히 조절해야 합니다.
  1. 확장 가능성
  • 위젯 클릭 시 앱의 특정 화면으로 이동하는 기능을 추가할 수 있습니다.
  • 다양한 크기의 위젯을 지원하여 사용자에게 더 많은 옵션을 제공할 수 있습니다.
  • 위젯에 이미지나 아이콘을 추가하여 시각적으로 더 풍부하게 만들 수 있습니다.

이렇게 구현하면 플러터 앱에서 iOS와 안드로이드 모두를 위한 홈 화면 위젯을 만들고 업데이트할 수 있습니다.

위젯은 사용자에게 빠르고 편리한 정보 접근을 제공하므로, 앱의 사용성을 크게 향상시킬 수 있습니다.

각 패키지와 플랫폼별 구현 방식은 계속 발전하고 있으므로, 최신 기능과 사용법은 공식 문서를 참조하는 것이 좋습니다.

추가 질문이나 설명이 필요한 부분이 있다면 언제든 물어보세요!

 

Starting Google Play App Distribution! "Tester Share" for Recruiting 20 Testers for a Closed Test.

 

Tester Share [테스터쉐어] - Google Play 앱

Tester Share로 Google Play 앱 등록을 단순화하세요.

play.google.com

 

반응형