flutter desktop macos でウィンドウ透明にしたり色々するぞー

はじめに

flutterのデスクトップを作るにあたって透明なウィンドウが欲しかったのでウィンドウのスタイルをいじる方法を調べたり実装したりした。 あと上記にあたって、blurのぼかし具合が微妙だったので調整できたら楽しそうだなーと思って色々調べてたけど無理っぽそうなので諦めた、というお話。人生。

サンプル

こちら、以下を設定できる

  • ウィンドウの影
  • 透過ウィンドウ
  • blur
  • 背景色rgbaで設定

github.com

  • こんなかんじ
  • 実際はこんなにインタラクティブな動きをする必要ないと思うので AppDelegateapplicationDidFinishLaunching を追加して任意の項目をコピペすればいいかなあという所感 f:id:Qsk:20211022122502g:plain

やったことなど

flutterのwidgetの背景色を透明にするだけではだめで、プラットフォーム側でウィンドウを透明にするなりなんなりしてやる必要がある。 多分不透明なウィンドウの上にflutterのwidgetが乗ってるイメージ。widgetが透明でも透過した後ろの要素が不透明なのでだめですよってお話。 なので透過したかったらプラットフォーム側で定義してく。

swiftを書け

具体的には extension NSWindow で任意のメソッドを追加して、FlutterMethodChannel 経由で呼び出す。それだーけ。 該当箇所は github.com

github.com らへん

flutter側はいい塩梅にinvokeMethod から対象のメソッド呼ぶ感じ

  Future<void> _invokeMethod(String method, Map<String, dynamic> arg) async {
    await platform.invokeMethod(method, arg);
  }

  Future<void> _setBlur() async {
    await _invokeMethod("blur", <String, dynamic>{
      "blur": _blur,
    });
  }

かんたんですね

補遺

なおカラーピッカーは flex_color_picker という神パッケージがあったのでこちらを利用。ありがたい

pub.dev

対象OS

flutter desktop macos で色々頑張ったというお話なのでそれ以外のことは何もわからない。

windows, linuxは↓のパッケージで良いと思う。 pub.dev

課題

macosblurの透過度を変更できないっぽいので前述のパッケージで言う aero, acrylic のような変更ができないっぽそうなので実現方法を模索した 以下なんとなくモヤモヤしてるけど諦めたことと試したこと

blurが地味

ダークテーマのblurが地味。 背景が同系色だったらblurかかってるのかどうかよくわからない具合のぼかし度合い。 なのでぼかし度をなんとかして調整できないかな〜〜と思って試行錯誤などをした(結果ダメだった)。

以下とても有益な情報

↓は無理やり使うとリジェクトされるって話っぽそう?(なのでこの段階で諦めた)

Translucent NSWindow with blurred background · GitHub

個人的なアプリを作る過程で今回の一連のアレコレをしているだけで、ストア通したりする予定はないけど、個人的なアプリなので若干気に食わないけど無理やり頑張る必要もないんだよなーと思って考えるのをやめた。

stackoverflowでアニメーションを途中で止めたらそれっぽいみたいな話もあったけどiosの話だったのでそのままは適用できなかった。 UIBlurEffectがあるんやからNSBlurEffect もあってくれ。

swift - How to edit the UIBlurEffect intensity? - Stack Overflow

以下な感じで一応アニメーションを試したけれどだめだた。 普通に透過風味になる。

self.isOpaque = false
self.backgroundColor = .clear

let contentView = contentViewController!.view
let superView = contentView.superview!

let blurView = NSVisualEffectView()
blurView.frame = superView.bounds
blurView.autoresizingMask = [.width, .height]
blurView.blendingMode = NSVisualEffectView.BlendingMode.behindWindow
blurView.material = NSVisualEffectView.Material.underWindowBackground

let views = contentView.subviews
let view = views.first!
contentView.subviews = [blurView]
blurView.addSubview(view)

// animation
let basicAnimation = CABasicAnimation(keyPath: "opacity")
basicAnimation.fromValue = 1
basicAnimation.toValue = 0.0
basicAnimation.duration = 10.0

// NSThemeFrame
superView.layer?.add(basicAnimation, forKey: "opacity")
superView.layer?.opacity = 0.5

// FlutterViewWrapper
contentView.layer?.add(basicAnimation, forKey: "opacity")
contentView.layer?.opacity = 0.5

// NSVisualEffectView
blurView.layer?.add(basicAnimation, forKey: "opacity")
blurView.layer?.opacity = 0.5

その他試したこと

NSImageViewで半透明画像を利用してCIFilterのblur使ったりもしたけどalpha値をいじるのとほとんど見た目からわなかったからやめた。若干濃淡あるくらいでopacityのグラデーションくらいにしかならなそう。ぼかしたいのよこっちは。

f:id:Qsk:20211020182429p:plain
失敗作

おわりに

flutterで遊んでるつもりがずっとswift書いてるな? blur関連で知見ある方いたら教えて下さい。 また、当方がflutterでお仕事をしたいそうで、こちらが本稿の最も重要な内容となります。 flutterのお仕事ください。