# 下載組件截圖
不是全螢幕截圖,<br/>
而是要把特定範圍的組件存成圖片。<br/>
類似 web 端的 html2canvas<br/>
要完成這件事需要三個步驟:
1. 用 `RepaintBoundary` 包圍想擷取成圖片的組件,並綁定一個 `GlobalKey`
2. `Widget` 轉 `Unit8List`
3. 下載 `Uint8List` 至裝置的圖庫
### Widget 轉 Uint8List
傳入之 key 為 flutter 的 `GlobalKey`,方便我們像是操作 DOM 一樣可以指到想截圖的 `RepaintBoundary` 組件。
##### 程式碼
```dart
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'dart:ui' as ui;
Future<Uint8List> captureImage(
GlobalKey key, [
double pixelRatio = 3.0,
ui.ImageByteFormat format = ui.ImageByteFormat.png,
]) async {
try {
RenderRepaintBoundary boundary =
key.currentContext!.findRenderObject()! as RenderRepaintBoundary;
// Issue: https://github.com/flutter/flutter/issues/22308
await Future.delayed(const Duration(milliseconds: 200));
ui.Image image = await boundary.toImage(pixelRatio: pixelRatio);
ByteData? byteData = await image.toByteData(format: format);
final bytes = byteData!.buffer.asUint8List();
return bytes;
} catch (e) {
throw e;
}
}
```
### 下載 Uint8List 至裝置的圖庫
要寫入剛剛轉換成 unit8List 到圖庫,<br>
我們僅需要使用 `image_gallery_saver` 這個插件並補上兩個平台對應所需要的權限字串即可。
##### 權限
###### Android
AndroidManifest.xml
```xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application android:requestLegacyExternalStorage="true" ...>...</application>
```
###### iOS
Info.plist
```plist
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app requires permission to save images to gallery.</string>
```
##### 程式碼
```dart
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:permission_handler/permission_handler.dart';
Future<bool> saveImage(
bytes,
String name, {
quality = 60,
}) async {
try {
final isGranted = await Permission.storage.request().isGranted;
if (!isGranted) return false;
final res = await ImageGallerySaver.saveImage(
bytes,
quality: quality,
name: name,
);
return res['isSuccess'];
} catch (e) {
throw e;
}
}
```
### 在組件中使用
在想擷取的組件外包一個 `RepaintBoundary`,並設定一個 `GlobalKey`。
##### 程式碼
```dart
import 'package:flutter/material.dart';
import 'package:cadddy/utils/image.dart';
class Example extends StatefulWidget {
const Example({ Key? key }): super(key: key);
@override
_ExampleState createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
const _ExampleState({
Key? key,
}): super(key: key);
final _globalKey = new GlobalKey();
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
child: RepaintBoundary(
key: _globalKey,
// 這邊裡面的 Widget 就是你想擷取的部分
child: Text('邀请卡'),
),
),
),
floatActionButton: FloatActionButton(
onPressed: () async {
final bytes = await captureImage(_globalKey);
final res = await saveImage(bytes, '邀请卡.png');
print('下载成功');
}
),
);
}
}
```
##### 成果
 
### 參考
- [Export your widget to image with flutter](https://medium.com/flutter-community/export-your-widget-to-image-with-flutter-dc7ecfa6bafb)
- [image_gallery_saver](https://pub.dev/packages/image_gallery_saver)
###### tags: `Flutter`