[Flutter] 위젯을 이미지로 찍어내기
2023. 3. 19. 14:37ㆍDevelopers 공간 [Shorts]/Frontend
728x90
반응형
<분류>
A. 수단
- OS/Platform/Tool : Linux, Kubernetes(k8s), Docker, AWS
- Package Manager : node.js, yarn, brew,
- Compiler/Transpillar : React, Nvcc, gcc/g++, Babel, Flutter
- Module Bundler : React, Webpack, Parcel
B. 언어
- C/C++, python, Javacsript, Typescript, Go-Lang, CUDA, Dart, HTML/CSS
C. 라이브러리 및 프레임워크 및 SDK
- OpenCV, OpenCL, FastAPI, PyTorch, Tensorflow, Nsight
1. What? (현상)
위젯 자체를 이미지로 찍어내는 방법에 대해 정리하고자 합니다.
2. Why? (원인)
- X
3. How? (해결책)
- 먼저 어떤 위젯을 선언할 때 원하는 위젯을 RepaintBoundary()로 감싼다
- RepaintBoundary() : 분리된 Layer Tree, 즉 Display 리스트를 만들어 내기 위한 위젯입니다.
(자세하게는 아래 더보기를 참조) - GlobalKey() : 나중에 어떤 object를 활용해 Rendering할지를 알기 위해 Global key를 지정해주고, RepainBoundary()의 key로 넣어줍니다.
- RepaintBoundary() : 분리된 Layer Tree, 즉 Display 리스트를 만들어 내기 위한 위젯입니다.
더보기
-----------------------------------------------------------------------------------------------------
<Flutter에서 Rendering하는 단계에 대해서>
- User Input & Animation : user에게 Input을 새로받거나, 새로운 이벤트로 인해 build()가 불립니다.
- Build :Build가 불리면 Widget Tree를 구성하고, 이는 Element Tree와 Render Tree를 구성합니다.
===============Rendering!================== - Layout : Render Tree의 Object(RenderObject)가 레이아웃을 구성합니다. 이 때 Constraints 혹은 BoxConstraints를 활용해 계산된 사이즈를 child에게 차례대로 건네 줍니다.
- Paint : Render Tree의 Object(RenderObject)는 말 그대로 pixel단위로 캔버스라는 곳에 페인팅을 진행합니다.
- Compositing : 합성이라는 뜻으로, 스크롤바 처럼 이어지는 동작의 경우 여러가지 Paint된 캔버스들을 합성하는 단계입니다. 이때 Layer tree라는 것을 활용하며, Layer Tree의 하나의 Object가 캔버스라고 할 수 있습니다.
** 이때 Flutter는 RenderObject 여러개를 하나의 레이어로 그룹화하므로, 레이어가 구분되어 불필요한 추가 페인팅 작업을 줄일 수 있습니다. 근데, 사용자가 RepaintBoundary() 위젯을 통해 자체적인 레이어를 구분해준다면, 해당 레이어를 기준으로 페인팅 작업이 이루어지기 때문에, 성능이 향상될 수도 있습니다. - Rasterize : 이제 Layer Tree를 전달받아 프린트 가능한 픽셀 형태로 전환하는 작업을 진행합니다.
-----------------------------------------------------------------------------------------------------
List<Widget> tabNames = <Widget>[
Page1(),
Page2(),
Page3(),
Page4()
];
final gkeytemp = GlobalKey();
...
child: PageView(
scrollDirection: Axis.horizontal,
controller: viewModel.pageController,
children: List.generate(tabNames.length, (index) => RepaintBoundary(
key:gkeytemp,
child: tabNames[index],
)),
onPageChanged: (value) {
},
),
...
- 이제 RenderRepaintBoundary를 만들어 진행합니다.
- join :임시 디렉토리의 임시파일에 저장할 것입니다.
- RenderRepaintBoundary : Global key를 활용해 Render Object를 찾아 냅니다. 이 때 선언되는 RenderRepaintBoundary 클래스는 Render Object를 나타내는 클래스입니다.
- 이를 활용해 toImage()함수로 Image를 얻어 냅니다.
- MediaQuery.of(context).devicePixelRatio : toImage의 파라미터로 pixelRatio를 넣어주는데, 이는 Logical Pixel과 Output Image간의 배율을 의미합니다. 사실 MediaQuery에서 얻어지는 값을 굳이 줄 필요는 없지만, physical Pixel 개수를 넣어주기 위해 MediaQuery를 활용했습니다.
- 이후에 Uint8 List로 바꾸어준다음에 File에 기록을 해주면 파일형태의 이미지가 완성됩니다.
import 'package:flutter/rendering.dart';
import 'package:flutter_native_image/flutter_native_image.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:ui' as ui;
import 'dart:io'
...
final path = join(
(await getTemporaryDirectory()).path,
'${DateTime.now()}.png',
);
if(gkeytemp!=null){
final RenderRepaintBoundary rojecet = gkeytemp.currentContext!.findRenderObject() as RenderRepaintBoundary;
final ui.Image tempscreen = await rojecet.toImage(
pixelRatio:MediaQuery.of(gkeytemp.currentContext!).devicePixelRatio
);
final ByteData? byteData = await tempscreen.toByteData(format:ui.ImageByteFormat.png);
final Uint8List png8Byttes =byteData!.buffer.asUint8List();
final File file = File(path);
await file.writeAsBytes(png8Byttes);
}else{
print("ERROR!!");
}
...
https://www.educative.io/answers/how-to-convert-a-widget-to-an-image-in-flutter
https://api.flutter.dev/flutter/rendering/RenderRepaintBoundary/toImage.html
https://terry1213.github.io/flutter/flutter-decoding-flutter-rendering/
728x90
반응형
'Developers 공간 [Shorts] > Frontend' 카테고리의 다른 글
[Flutter] Flutter에서 KakaoMap WebView API 활용하기 (0) | 2023.03.24 |
---|---|
[Flutter] Stacked 패키지 Reactive Provider 설계하기 (0) | 2023.03.19 |
[Flutter] Stacked Services Navigator 정리 (0) | 2023.03.19 |
[Flutter] Future Builder를 활용해 Display하기 (0) | 2023.03.17 |
[Flutter] 미리 정해놓으면 좋은 것들 (Theme, Icon, Block, Sound...) (0) | 2023.03.12 |