🌅
【Flutter】Image.networkで表示する画像の背景を透過させる
はじめに
Flutterアプリケーションで画像を表示する際、背景を透過させたいケースは多くあります。例えば、
- 商品画像のカタログ表示
- プロフィール画像の表示
- アイコンやロゴの表示
この記事では、Image.network
で読み込む画像の背景を透過させる実装方法について解説します。
before | after |
---|---|
![]() |
![]() |
実装の概要
実装のポイントは以下の3つです:
- 画像データをピクセルレベルで処理
- 指定した背景色に近い色を持つピクセルを透過
- 処理した画像を新しく生成して表示
実装
pubspec.yaml
に以下のパッケージを追加します:
dependencies:
flutter:
sdk: flutter
http: ^1.1.0 # ネットワーク画像の取得用
透過Widget
import 'dart:developer';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class BackgroundRemover extends StatefulWidget {
final String imageUrl;
final double tolerance; // 白色の許容範囲(0.0 〜 1.0)
const BackgroundRemover({
Key? key,
required this.imageUrl,
this.tolerance = 0.1,
}) : super(key: key);
State<BackgroundRemover> createState() => _BackgroundRemoverState();
}
class _BackgroundRemoverState extends State<BackgroundRemover> {
ui.Image? processedImage;
void initState() {
super.initState();
_processImage();
}
Future<void> _processImage() async {
try {
// 画像をネットワークから読み込む
final response = await http.get(Uri.parse(widget.imageUrl));
final Uint8List bytes = response.bodyBytes;
// デコードして画像オブジェクトを取得
final codec = await ui.instantiateImageCodec(bytes);
final frame = await codec.getNextFrame();
final image = frame.image;
// 画像データを取得
final byteData = await image.toByteData();
if (byteData == null) return;
// RGBA形式のデータに変換
final pixels = byteData.buffer.asUint32List();
// 各ピクセルを処理
for (var i = 0; i < pixels.length; i++) {
final color = Color(pixels[i]);
// RGBの値が閾値以上なら透明に
if (isNearWhite(color, widget.tolerance)) {
pixels[i] = 0x00000000; // 完全な透明
}
}
// 新しい画像を作成
final ui.ImmutableBuffer buffer = await ui.ImmutableBuffer.fromUint8List(
pixels.buffer.asUint8List(),
);
final descriptor = ui.ImageDescriptor.raw(
buffer,
width: image.width,
height: image.height,
pixelFormat: ui.PixelFormat.rgba8888,
);
final processedCodec = await descriptor.instantiateCodec();
final processedFrame = await processedCodec.getNextFrame();
setState(() {
processedImage = processedFrame.image;
});
} catch (e) {
log('Error processing image: $e');
}
}
bool isNearWhite(Color color, double tolerance) {
final threshold = 255 * (1 - tolerance);
return color.red > threshold &&
color.green > threshold &&
color.blue > threshold;
}
Widget build(BuildContext context) {
if (processedImage == null) {
return const Center(child: CircularProgressIndicator());
}
return RawImage(
image: processedImage,
fit: BoxFit.contain,
);
}
}
使用する
BackgroundRemover(
imageUrl: '画像URL',
tolerance: 0.1 // 白の許容量(0.0 〜 1.0で透過を調節できます)
);
まとめ
これでダークモードでも不自然じゃなくなるかも🥳
Discussion