플러터에서 iOS와 안드로이드 홈위젯 구현하기

2024. 6. 30. 02:55Flutter/Flutter Programming

반응형

개발자 여러분! 오늘은 플러터 앱에서 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와 안드로이드 모두를 위한 홈 화면 위젯을 만들고 업데이트할 수 있습니다.

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

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

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

 

 

 

수발가족을 위한 일기장 “나비일기장

 

https://play.google.com/store/apps/details?id=com.maccrey.navi_diary_release

 

구글플레이 앱 배포의 시작! 비공개테스트 20명의 테스터모집을 위한 앱 "테스터 쉐어"

 

https://play.google.com/store/apps/details?id=com.maccrey.tester_share_release

 

카카오톡 오픈 채팅방

https://open.kakao.com/o/gsS8Jbzg

 

 
반응형