728x90
반응형

 

Flutter로 macOS 앱을 빌드해서 이미지를 보여주려고 했습니다.

 

그런데 아래와 같은 오류가 발생하면서 Crash가 발생하고 이미지는 불러와지지 못했는데요.

 

macOS에서 Network통신을 하려면 특정 권한이 필요합니다.

The following SocketException was thrown resolving an image codec:
Connection failed (OS Error: Operation not permitted, errno = 1), address = xxx.xxx.xxx, port = 443

 

 

 

https://docs.flutter.dev/platform-integration/macos/building#setting-up-entitlements

 

Building macOS apps with Flutter

Platform-specific considerations for building for macOS with Flutter.

docs.flutter.dev

문서를 읽어보면 네트워크 요청을 하는 경우 권한을 추가해야 된다고 명시되어 있습니다.

 

 

 

# 네트워크 권한 추가

macos/Runner/*.entitlements를 열고 Key-Value를 쌍으로 추가해줘야 합니다.

<key>com.apple.security.network.client</key>
<true/>

 

 

 

Debug, Release에 맞게 권한을 추가 하고 다시 앱을 빌드하게 되면 정상적으로 네트워크 통신이 되는것을 확인할 수 있습니다.

728x90
반응형
728x90
반응형

 

Flutter로 iOS, AOS 테스트를 하다보면 화면 전환 효과가 다르죠.

 

iOS는 CupertinoPageRoute와 같이 옆으로 화면이 전환되고, 뒤로가기 swipe를 하면 pop이 되는 구조 입니다.

 

하지만 AOS는 MaterialPageRoute와 같이 가운데화 화면이 채워지는 효과가 있죠.

 

 

 

 

AOS도 iOS처럼 화면 전환효과를 동일하게 하고 싶을때에는 CupertinoPageRoute로 바꿔서 Navigate를 하곤 했는데... 언제 다 수정하지..

 

하지만 역시 Flutter는 이러한 부분을 알기라도 하듯이 사용자에 의해 설정을 변경할 수 있도록 제공하고 있습니다.

 

 

 

# ThemeData

ThemeData로 테마 설정을 하는데 pageTransitionsTheme이라는 속성이 존재 합니다.

 

https://api.flutter.dev/flutter/material/PageTransitionsTheme-class.html

 

PageTransitionsTheme class - material library - Dart API

Defines the page transition animations used by MaterialPageRoute for different TargetPlatforms. The MaterialPageRoute.buildTransitions method looks up the current PageTransitionsTheme with Theme.of(context).pageTransitionsTheme and delegates to buildTransi

api.flutter.dev

 

MaterialPageRouter에서 사용하는 전환 효과를 설정하는 정의하는데요.

 

Default로 iOS이면 CupertionPageTransitionBuilder, AOS이면 ZoomPageTransitionsBuilder를 사용하고 있습니다.

class PageTransitionsTheme with Diagnosticable {
  ...
  
  const PageTransitionsTheme({ Map<TargetPlatform, PageTransitionsBuilder> builders = _defaultBuilders }) : _builders = builders;

  static const Map<TargetPlatform, PageTransitionsBuilder> _defaultBuilders = <TargetPlatform, PageTransitionsBuilder>{
    TargetPlatform.android: ZoomPageTransitionsBuilder(),
    TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
    TargetPlatform.macOS: CupertinoPageTransitionsBuilder(),
  };
  
  ...
}

 

 

 

# TransitionsBuilder 변경

위 처럼 기본값이 ZoomPageTransitionsBuilder로 지정되어 있으니 ThemeData에서 아래처럼 변경해주겠습니다.

 

TargetPlatform의 android일때 iOS의 CupertinoPageTransitionsBuilder를 제공함으로써 iOS와 동일한 Transition효과를 기대할 수 있겠습니다.

pageTransitionsTheme: const PageTransitionsTheme(
  builders: {
    TargetPlatform.android: CupertinoPageTransitionsBuilder(),
  }
),

 

 

 

 

이제 Android 일때에도 MaterialRoutePage만 사용해도 CupertinoRoutePage의 Transition효과를 얻을 수 있습니다.

 

크로스 플랫폼이기에 화면 전환까지 동일하게 변경했더니 AOS 특유의 버벅임이 줄어든 느낌이 들어서 좋은것 같습니다.

 

* 위 설정은 go_router에서도 동일하게 적용됩니다.

 

728x90
반응형
728x90
반응형

 

현재 진행중인 프로젝트에서 웹페이지 url을 입력받아 Open Graph 정보를 표시하는 작업을 하려고 합니다.

 

카카오톡이나 소셜 sns를 사용할 때 url만 입력해도 이미지와 제목이 뜨는걸 보셨을꺼에요.

 

일반적으로 웹 페이지를 만들때 약속된 meta tag라고 할 수 있는데요, 이런걸 Open Graph 프로토콜이라고 합니다. 

 

https://ogp.me/

 

Open Graph protocol

The Open Graph protocol enables any web page to become a rich object in a social graph.

ogp.me

 

 

 

# og meta tag 확인

쿠팡을 기준으로 작업을 해보겠습니다.

 

쿠팡 사이트에 들어가서 맥북을 검색해 본 뒤 크롬 기준으로 [도구 더보기 -> 개발자 도구] 항목을 클릭합니다.

 

 

 

펼쳐진 개발자 도구에서 'Elements' 탭을 선택하면 HTML요소들어 보여집니다.

 

head tag부분에서 아래와같이 link, meta, script 태그들이 보이네요.

 

 

 

저는 여기서 og: 라고 시작되는 태그들만을 원합니다.

 

보통 다른 쇼핑몰 같은 경우에도 image, title, url, description 항목은 필수적으로 사용하고 있는데요.

 

이 정보들이 소셜 sns나 채팅에 공유될때 나오는 정보들입니다.

 

image - 상품의 이미지

title - 상품 제목

url - 해당 상품 url

description - 간략한 설명

 

 

 

 

# 개발 준비

 

Flutter 프레임워크에서는 특정 패키지가 필요합니다.

 

http 패키지로 사이트의 html문자열을 가져온 다음, html 패키지로 문자열을 html로 파싱파여 원하는 정보를 가져올겁니다.

https://pub.dev/packages/http

 

http | Dart Package

A composable, multi-platform, Future-based API for HTTP requests.

pub.dev

https://pub.dev/packages/html/install

 

html | Dart Package

APIs for parsing and manipulating HTML content outside the browser.

pub.dev

 

 

 

 

# 페이지 정보 읽기

아래처럼 패키지를 import 해줍니다.

import 'package:http/http.dart' as http;
import 'package:html/dom.dart' as dom;
import 'package:html/parser.dart' as parse;

 

 

http.get 함수로 웹페이지 주소를 넣어 호출합니다.

 

아래처럼만 작성해도 일반적인 사이트에서는 문제 없이 html 문자열을 응답받습니다.

final String uri = "웹 페이지 주소";
final response = await http.get(Uri.parse(uri));

 

 

 

하지만 쿠팡같은 경우에는 pending이 걸려버리고 response가 오지 않는것을 알 수 있습니다.

 

각종 블로그에서도 해당 문제에 대해서 다루지만 user-agent를 넣는 방법으로는 동작하지 않습니다.

 

 

 

 

해결을 위해 쿠팡 사이트에 들어와 다시 [도구 더보기 -> 개발자 도구] 로 진입해서 웹 페이지 요청 항목을 선택한 뒤 [Network -> Headers] 탭으로 들어가보겠습니다.

 

Request Headers 부분에서 아래와 같이 accept 정보를 넣어주어 압축된 정보를 주고받는걸 알 수 있는데요. 

 

 

 

 

저도 이와 같이 header정보를 추가 해주겠습니다.

 

그리고 만약 동작하지 않을 수 있다는 부분을 위해 timeout 시간을 2초로 주었습니다.

 

아래처럼 작성하시면 쿠팡 사이트의 정보까지 문제없이 html문자열을 응답받을 수 있습니다

final response = await http.get(Uri.parse(uri), headers: {
  'accept-encoding': 'gzip, deflate, br',
  'accept-language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
}).timeout(const Duration(seconds: 2));

 

 

 

# og tag 추출

이제 html문자열에서 og tag만 추출하면 되겠네요.

 

위에서 import한 alias를 확인해주시고 코드를 봐주시면 됩니다.

 

response.body에서 html 문자열만을 추출해 html로 변환하면 Document라는 객체로 변환됩니다.

 

이 Document 객체에서 어떻게 정보를 꺼내냐면 meta tag 속성을 살펴보면 됩니다.

<meta property="og:title" content="Apple 2020 맥북 에어 13">

 

 

 

 

[head -> meta 에서 property가 'og:title' 인것의 content라는 속성을 꺼내줘] 라고 한다면 html 패키지에서는 아래처럼 코드를 작성하면 됩니다.

 

정말 간단하게 원하는 tag 정보를 가져올 수 있습니다.

dom.Document document = parse.parse(response.body);

String? title = document.head
    ?.querySelector("meta[property='og:title']")
    ?.attributes['content'];
String? description = document.head
    ?.querySelector("meta[property='og:description']")
    ?.attributes['content'];
String? image = document.head
    ?.querySelector("meta[property='og:image']")
    ?.attributes['content'];
String? url = document.head
    ?.querySelector("meta[property='og:url']")
    ?.attributes['content'];

 

 

 

 

전체 코드 입니다.

 

return type은 원하는 model을 생성해서 응답해주면 딱이겠군요!

import 'package:http/http.dart' as http;
import 'package:html/dom.dart' as dom;
import 'package:html/parser.dart' as parse;

static void urlToMeta(String uri) async {
    final response = await http.get(Uri.parse(uri), headers: {
      'accept-encoding': 'gzip, deflate, br',
      'accept-language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
    }).timeout(const Duration(seconds: 2));
    dom.Document document = parse.parse(response.body);

    String? title = document.head
        ?.querySelector("meta[property='og:title']")
        ?.attributes['content'];
    String? description = document.head
        ?.querySelector("meta[property='og:description']")
        ?.attributes['content'];
    String? image = document.head
        ?.querySelector("meta[property='og:image']")
        ?.attributes['content'];
    String? url = document.head
        ?.querySelector("meta[property='og:url']")
        ?.attributes['content'];
 }

 

 

 

# 마무리

Flutter 뿐만 아니라 다른 언어에서도 방법만 안다면 문제없어 원하는 정보를 Crawling 할 수 있습니다.

 

728x90
반응형
728x90
반응형

 

플러터로 개발중에 sdk 를 3.3.0 으로 버전업을 한 뒤로 firebase_messging을 사용하는 Android에서 BackgroundHandler함수 호출을 못하는 문제점이 생겼어요.

 

 

문제발생

당황스러운건 Debug 모드에서는 정상적으로 호출이 되다가 Release 모드에서만 갑자기 동작을 안해서 오류가 있는지도 몰랐다는 것이었죠.

 

release 모드로 빌드를 한다음 해당 오류가 발생하는것을 알게되었습니다.

flutter run --release
E/flutter (12708): [ERROR:flutter/lib/ui/dart_runtime_hooks.cc(38)] Dart Error: Unhandled exception:
E/flutter (12708): NoSuchMethodError: No top-level getter '_firebaseMessagingBackgroundHandler' declared.
E/flutter (12708): Receiver: top-level
E/flutter (12708): Tried calling: _firebaseMessagingBackgroundHandler
E/flutter (12708): #0      NoSuchMethodError._throwNew (dart:core-patch/errors_patch.dart:225)
E/flutter (12708): #1      FfiTrampoline___getCallbackFromHandle$Method$FfiNative$Ptr (dart:ffi)
E/flutter (12708): #2      _getCallbackFromHandle (dart:ui/natives.dart:141)
E/flutter (12708): #3      PluginUtilities.getCallbackFromHandle.<anonymous closure> (dart:ui/plugins.dart:81)
E/flutter (12708): #4      _LinkedHashMapMixin.putIfAbsent (dart:collection-patch/compact_hash.dart:524)
E/flutter (12708): #5      PluginUtilities.getCallbackFromHandle (dart:ui/plugins.dart:80)
E/flutter (12708): #6      _firebaseMessagingCallbackDispatcher.<anonymous closure> (package:firebase_messaging_platform_interface/src/method_channel/method_channel_messaging.dart:40)
E/flutter (12708): #7      MethodChannel._handleAsMethodCall (package:flutter/src/services/platform_channel.dart:555)
E/flutter (12708): #8      MethodChannel.setMethodCallHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:548)
E/flutter (12708): #9      _DefaultBinaryMessenger.setMessageHandler.<anonymous closure> (package:flutter/src/services/binding.dart:393)
E/flutter (12708): #10     _invoke2 (dart:ui/hooks.dart:183)
E/flutter (12708): #11     _ChannelCallbackRecord.invoke (dart:ui/channel_buffers.dart:40)
E/flutter (12708): #12     _Channel.push (dart:ui/channel_buffers.dart:130)
E/flutter (12708): #13     ChannelBuffers.push (dart:ui/channel_buffers.dart:326)
E/flutter (12708): #14     PlatformDispatcher._dispatchPlatformMessage (dart:ui/platform_dispatcher.dart:664)
E/flutter (12708): #15     _dispatchPlatformMessage (dart:ui/hooks.dart:86)

 

 

 

이슈확인

NoSuchMethodError: No top-level getter '_firebaseMessagingBackgroundHandler' declared. 오류로 고통받는 개발자가 많이 생길것이라고 봅니다.

 

문제점 해결은 github 링크를 참고했습니다.

 

https://github.com/firebase/flutterfire/issues/9446#issuecomment-1240554285

 

🐛 [firebase_messaging] `onBackgroundMessage` is never called when using Flutter 3.3.0 in release mode on Android · Issue #94

Bug report When an app using firebase_messaging is built using Flutter 3.3.0, onBackgroundMessage is not being called at all on Android. When using Flutter 3.0.5, the app receives notifications in ...

github.com

 

 

 

해결

backgroundHandler에 @pragma 애노테이션을 작성하고 다시 Release 모드로 시작하면 문제가 사라진것을 알 수 있습니다.

@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  ...
}

 

https://api.dart.dev/stable/2.1.0/dart-core/pragma-class.html

 

pragma class - dart:core library - Dart API

pragma class A hint to tools. Tools that work with Dart programs may accept hints to guide their behavior as pragma annotations on declarations. Each tool decides which hints it accepts, what they mean, and whether and how they apply to sub-parts of the an

api.dart.dev

 

 

 

 

firease_messaging Release모드에서 백그라운드 알림 수신을 하지 않는다는것은 정말 치명적이네요.

 

firebase_messaging을 사용하시는 분들에게 문제점 해결에 도움이 되었으면 합니다.

 

728x90
반응형
728x90
반응형

 

iOS 에서 NavigationViewController 를 사용하면 다양한 옵션들이 있습니다.

 

기본 옵션이지만 상당히 부드럽고 깔끔하죠.

 

Flutter로 개발을 하는 이유는 Android에도 iOS처럼 부드러운 애니메이션 작업이 가능하다는 겁니다.

 

iOS스러운 앱을 만들기 위해 작은 예제를 정리하도록 하겠습니다.

 

 

 

 

 

첫번째로는 iOS 에서 지원하는 LargeTitle NavigationBar 기능을 Flutter로 구현해 보겠습니다.

Navigation Bar Title

 

Scroll시 Large Title에 NavigationBar 에 올려진다

 

 

하나의 StatelessWidget 클래스를 상속하는 Page 클래스를 만들어 줍니다.

 

Scaffold를 리턴해줘야 흰색 배경이 나옵니다.

class ExmaplePage extends StatelessWidget {
	@override
	Widget build(BuildContext context) {
  		return Scaffold();
  	}
}

 

 

 

 

Scaffold의 속성중 body에 NestedScrollView를 넣어줍니다.

 

NestedScrollView 속성에는 headerSliverBuilder가 있는데 이부분에서 CupertinoSliverNavigationBar 위젯을 사용하면 됩니다.

 

NestedScrollView를 감싼다음 body에는 기존에 사용하던 코드를 넣어주면 iOS처럼 멋진 디자인이 보여집니다.

class ExamplePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NestedScrollView(
        headerSliverBuilder: (context, innerBoxIsScrolled) {
          return [
            CupertinoSliverNavigationBar(
              largeTitle: Text('Example'),
            ),
          ];
        },
        body: Center(
          child: Text("ExamplePage"),
        ),
      ),
    );
  }
}

 

 

 

 

NestedScrollView 또한 physics 속성이 있으니 BouncingScrollPhysics()코드를 넣어 효과를 넣을수도 있습니다.

 

이렇게 기존 iOS 네이티브 디자인을 Flutter코드로 작성해보았습니다.

 

728x90
반응형
728x90
반응형

 

Android Studio 를 실행해 줍니다.

 

이전 글에서 Flutter Plugin 을 설치 했으니 아래와 같이 Start a new Flutter project 메뉴가 보이게 됩니다.

 

Start a new Flutter project 메뉴를 클합니다.

 

 

 

 

 

현재 4가지의 Project 를 생성할 수 있습니다. Application, Plugin, Package, Module을 만들 수 있습니다.

 

기본 선택인 Fultter Application 항목을 그대로 두고 Next 버튼을 클릭합니다.

 

 

 

name에 Project 이름을 대문자 없이 입력해줍니다.

 

SDK path 는 이전에 다운받았다면 연결을 해주고, 오른쪽 Install SDK... 을 클릭해서 다운로드 할 수 있습니다.

 

location은 프로젝트가 저장될 경로를 지정해줍니다.

 

Description은 프로젝트의 설명을 입력해줍니다.

 

확인 후 Next 버튼을 클릭합니다.

 

 

Package name 은 겹치지 않는 회사 domain을 입력하면 됩니다.

 

Android X와 Kotlin, iOS 지원하도록 체크하고 Finish 버튼을 클릭합니다.

 

 

 

기본적인 dart 언어로 되어진 Project 가 생성이 되었습니다.

 

728x90
반응형
728x90
반응형

Android 환경에서 Flutter Plugin을 설치해보겠습니다.

 

Android Studio를 실행합니다.

 

Configure > Plugins 클릭합니다.

 

 

 

 

Plugins 팝업이 보여지게 됩니다.

 

flutter를 검색창에 입력하면 첫번째에 Flutter Plugin이 보여집니다.

 

 

현재 44.0 버전인 Flutter의 상세 정보입니다.

 

Install 버튼을 클릭해서 설치를 진행해주겠습니다.

 

설치 시간은 그리 길지 않습니다.

 

정상 설치가 되었다면 Install 버튼이 Restart IDE 변경되어 집니다.

 

Restart IDE 버튼을 클릭해서 Android Studio 를 재시작 합니다.

 

 

Android Studio 가 재시작 되었다면 아래와 같이 Start a new Flutter project 항목이 보여지게 됩니다.

 

728x90
반응형
728x90
반응형

Android와 iOS를 한번에 개발할 수 있는 플랫폼은 기존에도 있었습니다.

 

하지만 웹뷰 방식으로 제작되었다 보니 반응이 느리거나 디자인도 다르게 개발되기도 했습니다.

 

장점도 많고 단점도 있지만 네이티브와 비슷한 성능을 내는 Flutter 프로젝트를 진행해 보려고 합니다.

 

https://flutter-ko.dev/

 

Flutter - 가장 빠른 아름다운 네이티브 앱

Flutter(플러터)는 하나의 코드베이스로 모바일, 웹, 데스크톱에서 네이티브로 컴파일 되는 구글의 아름다운 UI 툴킷입니다. Flutter는 기존 코드와 함께 동작하고, 전세계 개발자와 조직에 쓰이고 있으며, 무료 오픈 소스입니다.

flutter-ko.dev

 

 

 

 

 

Flutter 설치 페이지로 이동합니다.

 

https://flutter-ko.dev/docs/get-started/install

 

설치

Flutter를 설치할 운영 체제를 선택해주세요:{{site.alert.note}} **Are you on Chrome OS?** If so, see the official [Chrome OS Flutter installation docs!](/docs/get-started/install/chromeos){{site.alert.end}}

flutter-ko.dev

 

 

 

 

링크를 클릭해서 들어가면 아래와 같은 OS별 설치 버튼이 보여집니다.

 

저는 OS X 를 사용하고 있으니 "맥OS" 를 클릭해서 설치 페이지로 이동하겠습니다.

 

 

 

 

설치 페이지로 진입했다면 Flutter SDK 다운 순서대로 진행합니다.

 

 

 

 

 

압축까지 풀어주었다면 환경변수에 등록을 해주어야 합니다.

 

저는 /Users/gigas/Documents/flutter 위치에 설치하였습니다.

 

flutter 경로로 진입해 아래의 명령어로 PATH를 등록하였습니다.

 

export PATH="$PATH:`pwd`/bin"

 

터미널에서 flutter doctor 명령어를 입력하면 아래와 같은 문구들이 보여집니다.

 

Flutter 라는 항목이 체크되어있다면 정상적으로 설치가 된겁니다.

 

 

 

 

간단하게 설치까지 완료해봤습니다.

 

설치 진행에 어려움이 있다면 댓글로 문의 해주시면 도움을 드리겠습니다.

 

728x90
반응형

+ Recent posts