alexmercerind / flutter_acrylic

Flutter library for window acrylic, mica & transparency effects.
MIT License
607 stars 54 forks source link

[Question] got flutter_acrylic/FlutterAcrylicPlugin.swift:471: Fatal error: Unexpectedly found nil while unwrapping an Optional value when intergate in a not-nullsafety project #32

Closed lucasjinreal closed 2 years ago

lucasjinreal commented 2 years ago

Hi, my project not fully using nullsafety. when intergate to my project, the window appear a second and crash, error:

flutter_acrylic/FlutterAcrylicPlugin.swift:471: Fatal error: Unexpectedly found nil while unwrapping an Optional value

do u know why?

I have init it:

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Window.initialize();
  if (Platform.isWindows) {
    await Window.hideWindowControls();
  }

  initializeDateFormatting().then((_) => runApp(MyApp()));

  if (Platform.isWindows) {
    doWhenWindowReady(() {
      appWindow
        ..minSize = Size(640, 360)
        ..size = Size(720, 540)
        ..alignment = Alignment.center
        ..show();
    });
  }
}

the rest of the thing are exactly same on example app, the example app I can run it sucess.

Adrian-Samoticha commented 2 years ago

Did you follow the installation instructions and modify the MainFlutterWindow.swift file as described? Also, did you get any warnings in your console output?

lucasjinreal commented 2 years ago

@Adrian-Samoticha thank u sir! Soved it.

btw, may I ask one more question? Do u know is there a way to add a left column which is fully blurred the the right is the content like this:

image
Adrian-Samoticha commented 2 years ago

What you can do is define the sidebar as a transparent SizedBox of fixed width (assuming its width should not be modifiable by the user) that is placed to the left of the opaque content view using the Row widget. Now call the following code inside your initState() method:

Window.makeTitlebarTransparent();
Window.enableFullSizeContentView();
Window.hideTitle();

Window.setEffect(effect: WindowEffect.sidebar);

This way, the sidebar, and the content view occupy the entirety of the window. You should now place the TitlebarSafeArea widget inside the sidebar so that only its content is affected by it.

You will get something that looks like this:

image

This is the entire main.dart file for the example I have just described:

OUTDATED CODE ```dart import 'package:flutter/material.dart'; import 'package:flutter_acrylic/flutter_acrylic.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Window.initialize(); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const MaterialApp( home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @override void initState() { Window.makeTitlebarTransparent(); Window.enableFullSizeContentView(); Window.hideTitle(); Window.setEffect(effect: WindowEffect.sidebar); super.initState(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.transparent, body: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _generateSidebar(), Expanded( child: _generateContentView(), ), ], ), ); } SizedBox _generateSidebar() { return const SizedBox( width: 192.0, child: TitlebarSafeArea( child: Padding( padding: EdgeInsets.symmetric(horizontal: 8.0), child: Text( 'sidebar', style: TextStyle( color: Colors.white, ), ), ), ), ); } DecoratedBox _generateContentView() { return const DecoratedBox( decoration: BoxDecoration( color: Color.fromRGBO(64, 64, 64, 1.0), border: Border( left: BorderSide( color: Color.fromRGBO(0, 0, 0, 0.35), ), ), ), child: Padding( padding: EdgeInsets.all(8.0), child: Text( 'content view', style: TextStyle( color: Colors.white, ), ), ), ); } } ```

EDIT: The code above is outdated. See https://github.com/alexmercerind/flutter_acrylic/issues/32#issuecomment-1479311482

lucasjinreal commented 2 years ago

thanks a ton! will try it out!

lucasjinreal commented 1 year ago

@Adrian-Samoticha Hello, I tried your code on windows, this is only what I got,:

image

how can I have a move windows bar in the left, which is tint, while a opaque window bar on the right?

lucasjinreal commented 1 year ago

@Adrian-Samoticha I got only like this when hiden titlebar:


class MyHomePageDesktop extends StatefulWidget {
  const MyHomePageDesktop({Key? key}) : super(key: key);

  @override
  State<MyHomePageDesktop> createState() => _MyHomePageDesktopState();
}

class _MyHomePageDesktopState extends State<MyHomePageDesktop> {
  @override
  void initState() {
    Window.makeTitlebarTransparent();
    Window.enableFullSizeContentView();
    Window.hideTitle();
    Window.setEffect(effect: WindowEffect.sidebar, dark: false);

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.transparent,
      body: Row(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          _generateSidebar(),
          Column(
            children: [
              Expanded(
                child: Row(
                  children: [
                    WindowTitleBar(
                      brightness: InterfaceBrightness.dark,
                    ),
                  ],
                ),
              ),
              Expanded(
                child: _generateContentView(),
              )
            ],
          ),
        ],
      ),
    );
  }

  Widget _generateSidebar() {
    return TitlebarSafeArea(
      child: Padding(
        padding: EdgeInsets.symmetric(horizontal: 8.0),
        child: Text(
          'sidebar',
          style: TextStyle(
            color: Colors.white,
          ),
        ),
      ),
    );
  }

  DecoratedBox _generateContentView() {
    return const DecoratedBox(
      decoration: BoxDecoration(
        color: Color.fromRGBO(64, 64, 64, 1.0),
        border: Border(
          left: BorderSide(
            color: Color.fromRGBO(0, 0, 0, 0.35),
          ),
        ),
      ),
      child: Padding(
        padding: EdgeInsets.all(8.0),
        child: Text(
          'content view',
          style: TextStyle(
            color: Colors.white,
          ),
        ),
      ),
    );
  }
}

enum InterfaceBrightness {
  light,
  dark,
  auto,
}

extension InterfaceBrightnessExtension on InterfaceBrightness {
  bool getIsDark(BuildContext context) {
    if (this == InterfaceBrightness.light) return false;
    if (this == InterfaceBrightness.auto) {
      if (context == null) return true;
      return MediaQuery.of(context).platformBrightness == Brightness.dark;
    }

    return true;
  }

  Color getForegroundColor(BuildContext context) {
    return getIsDark(context) ? Colors.white : Colors.black;
  }
}

class WindowTitleBar extends StatelessWidget {
  final InterfaceBrightness brightness;
  const WindowTitleBar({Key? key, required this.brightness}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Platform.isWindows
        ? Container(
            width: MediaQuery.of(context).size.width,
            height: 32.0,
            color: Colors.transparent,
            child: MoveWindow(
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  Expanded(
                    child: Container(),
                  ),
                  MinimizeWindowButton(
                    colors: WindowButtonColors(
                      iconNormal: brightness == InterfaceBrightness.light
                          ? Colors.black
                          : Colors.white,
                      iconMouseDown: brightness == InterfaceBrightness.light
                          ? Colors.black
                          : Colors.white,
                      iconMouseOver: brightness == InterfaceBrightness.light
                          ? Colors.black
                          : Colors.white,
                      normal: Colors.transparent,
                      mouseOver: brightness == InterfaceBrightness.light
                          ? Colors.black.withOpacity(0.04)
                          : Colors.white.withOpacity(0.04),
                      mouseDown: brightness == InterfaceBrightness.light
                          ? Colors.black.withOpacity(0.08)
                          : Colors.white.withOpacity(0.08),
                    ),
                  ),
                  MaximizeWindowButton(
                    colors: WindowButtonColors(
                      iconNormal: brightness == InterfaceBrightness.light
                          ? Colors.black
                          : Colors.white,
                      iconMouseDown: brightness == InterfaceBrightness.light
                          ? Colors.black
                          : Colors.white,
                      iconMouseOver: brightness == InterfaceBrightness.light
                          ? Colors.black
                          : Colors.white,
                      normal: Colors.transparent,
                      mouseOver: brightness == InterfaceBrightness.light
                          ? Colors.black.withOpacity(0.04)
                          : Colors.white.withOpacity(0.04),
                      mouseDown: brightness == InterfaceBrightness.light
                          ? Colors.black.withOpacity(0.08)
                          : Colors.white.withOpacity(0.08),
                    ),
                  ),
                  CloseWindowButton(
                    onPressed: () {
                      appWindow.close();
                    },
                    colors: WindowButtonColors(
                      iconNormal: brightness == InterfaceBrightness.light
                          ? Colors.black
                          : Colors.white,
                      iconMouseDown: brightness == InterfaceBrightness.light
                          ? Colors.black
                          : Colors.white,
                      iconMouseOver: brightness == InterfaceBrightness.light
                          ? Colors.black
                          : Colors.white,
                      normal: Colors.transparent,
                      mouseOver: brightness == InterfaceBrightness.light
                          ? Colors.black.withOpacity(0.04)
                          : Colors.white.withOpacity(0.04),
                      mouseDown: brightness == InterfaceBrightness.light
                          ? Colors.black.withOpacity(0.08)
                          : Colors.white.withOpacity(0.08),
                    ),
                  ),
                ],
              ),
            ),
          )
        : Container();
  }
}
image
Adrian-Samoticha commented 1 year ago

@jinfagang Ah, I see. window_plus might offer what you're after.

lucasjinreal commented 1 year ago

@Adrian-Samoticha hi, thanks, how to combine window-plugs and acrylic to get the desired effect people usually need?

Adrian-Samoticha commented 1 year ago

@jinfagang Frankly, I don't really know. I only maintain the macOS port and have never compiled a Flutter application for Windows before. Perhaps @alexmercerind can help you, instead.

lucasjinreal commented 1 year ago

@Adrian-Samoticha @alexmercerind Hello guys.

I now got very close:

image

the question is, the oriingla title bar seems not hiden at all even I force it:

 @override
  void initState() {
    Window.makeTitlebarTransparent();
    Window.hideWindowControls();
    Window.enableFullSizeContentView();
    Window.enableFullSizeContentView();
    Window.hideTitle();
    Window.setEffect(effect: WindowEffect.sidebar, dark: false);

    super.initState();
  }

And the to make the windowTitlebar not translucent but same color as Content view?

bdairy commented 1 year ago

using the suggested code,, using TitleBarSafeArea always crashes the application with this error

macos_window_utils/MainFlutterWindowManipulator.swift:364: Fatal error: Unexpectedly found nil while unwrapping an Optional value

TitlebarSafeArea(
         child: Padding(
            padding: EdgeInsets.symmetric(horizontal: 8.0),
            child: Text(
              'sidebar',
              style: TextStyle(
                color: Colors.white,
              ),
            ),
          ),
       ),

I solved the issue by changing MainFlutterWindowManipulator.swift: as follows

let windowFrameHeight = (self.mainFlutterWindow?.contentView?.frame.height) ?? 0
        let contentLayoutRectHeight = self.mainFlutterWindow?.contentLayoutRect.height ?? 0
        let fullSizeContentViewNoContentAreaHeight = windowFrameHeight - contentLayoutRectHeight
        return fullSizeContentViewNoContentAreaHeight

But the app I guess not handling the effects correctly

image
Adrian-Samoticha commented 1 year ago

using the suggested code,, using TitleBarSafeArea always crashes the application with this error

macos_window_utils/MainFlutterWindowManipulator.swift:364: Fatal error: Unexpectedly found nil while unwrapping an Optional value

TitlebarSafeArea(
         child: Padding(
            padding: EdgeInsets.symmetric(horizontal: 8.0),
            child: Text(
              'sidebar',
              style: TextStyle(
                color: Colors.white,
              ),
            ),
          ),
       ),

Did you follow the installation instructions? What does your MainFlutterWindow.swift file look like?

bdairy commented 1 year ago

Window.makeTitlebarTransparent(); Window.hideWindowControls(); Window.enableFullSizeContentView(); Window.enableFullSizeContentView(); Window.hideTitle(); Window.setEffect(effect: WindowEffect.sidebar, dark: false);

super.initState();

@Adrian-Samoticha appreciate the quick reply,,,

here is my MainFlutterWindow.swift


import macos_window_utils
import Cocoa
import FlutterMacOS

class MainFlutterWindow: NSWindow {
  override func awakeFromNib() {
      let flutterViewController = FlutterViewController.init()
      let windowFrame = self.frame
      self.contentViewController = flutterViewController
      self.setFrame(windowFrame, display: true)

      RegisterGeneratedPlugins(registry: flutterViewController)

    super.awakeFromNib()
  }
}
Adrian-Samoticha commented 1 year ago

Window.makeTitlebarTransparent(); Window.hideWindowControls(); Window.enableFullSizeContentView(); Window.enableFullSizeContentView(); Window.hideTitle(); Window.setEffect(effect: WindowEffect.sidebar, dark: false);

super.initState();

@Adrian-Samoticha appreciate the quick reply,,,

here is my MainFlutterWindow.swift


import macos_window_utils
import Cocoa
import FlutterMacOS

class MainFlutterWindow: NSWindow {
  override func awakeFromNib() {
      let flutterViewController = FlutterViewController.init()
      let windowFrame = self.frame
      self.contentViewController = flutterViewController
      self.setFrame(windowFrame, display: true)

      RegisterGeneratedPlugins(registry: flutterViewController)

    super.awakeFromNib()
  }
}

You are still using FlutterViewController, but you need to use MacOSWindowUtilsViewController instead. Also, it is important that you call MainFlutterWindowManipulator.start(mainFlutterWindow: self).

Make sure your code looks like this:

import Cocoa
import FlutterMacOS
import macos_window_utils

class MainFlutterWindow: NSWindow {
  override func awakeFromNib() {
    let windowFrame = self.frame
    let macOSWindowUtilsViewController = MacOSWindowUtilsViewController()
    self.contentViewController = macOSWindowUtilsViewController
    self.setFrame(windowFrame, display: true)

    /* Initialize the macos_window_utils plugin */
    MainFlutterWindowManipulator.start(mainFlutterWindow: self)

    RegisterGeneratedPlugins(registry: macOSWindowUtilsViewController.flutterViewController)

    super.awakeFromNib()
  }
}
bdairy commented 1 year ago

Window.makeTitlebarTransparent(); Window.hideWindowControls(); Window.enableFullSizeContentView(); Window.enableFullSizeContentView(); Window.hideTitle(); Window.setEffect(effect: WindowEffect.sidebar, dark: false);

super.initState();

@Adrian-Samoticha appreciate the quick reply,,, here is my MainFlutterWindow.swift


import macos_window_utils
import Cocoa
import FlutterMacOS

class MainFlutterWindow: NSWindow {
  override func awakeFromNib() {
      let flutterViewController = FlutterViewController.init()
      let windowFrame = self.frame
      self.contentViewController = flutterViewController
      self.setFrame(windowFrame, display: true)

      RegisterGeneratedPlugins(registry: flutterViewController)

    super.awakeFromNib()
  }
}

You are still using FlutterViewController, but you need to use MacOSWindowUtilsViewController instead. Also, it is important that you call MainFlutterWindowManipulator.start(mainFlutterWindow: self).

Make sure your code looks like this:

import Cocoa
import FlutterMacOS
import macos_window_utils

class MainFlutterWindow: NSWindow {
  override func awakeFromNib() {
    let windowFrame = self.frame
    let macOSWindowUtilsViewController = MacOSWindowUtilsViewController()
    self.contentViewController = macOSWindowUtilsViewController
    self.setFrame(windowFrame, display: true)

    /* Initialize the macos_window_utils plugin */
    MainFlutterWindowManipulator.start(mainFlutterWindow: self)

    RegisterGeneratedPlugins(registry: macOSWindowUtilsViewController.flutterViewController)

    super.awakeFromNib()
  }
}

amazing,, I thought I copied the same file,,, now it works fine

image
lucasjinreal commented 1 year ago

@Adrian-Samoticha Hello, Adrian, does macOS black background problem solved or not?

Adrian-Samoticha commented 1 year ago

@Adrian-Samoticha Hello, Adrian, does macOS black background problem solved or not?

It is solved. Make sure you’re running the latest version of flutter_acrylic.

lucasjinreal commented 1 year ago

@Adrian-Samoticha But I try previous flutter_acrylic breaks windows, if I remember correctly

Adrian-Samoticha commented 1 year ago

@Adrian-Samoticha But I try previous flutter_acrylic breaks windows, if I remember correctly

The issue was an accidental breaking change in macos_window_utils, not in flutter_acrylic. More specifically, I have moved a source file in macos_window_utils and it broke an import statement in flutter_acrylic, however, that was solvable by patching macos_window_utils without touching flutter_acrylic.

Honestly, the way Dart handles imports from third-party packages is quite odd. A package typically has a source file called <package_name>.dart which exports all the libraries the package provides. However, dependents can still import files directly and there is no way to prevent that as a package author. This means that technically, any moved file is a breaking change by default, and refactoring your directory structure becomes impossible. Perhaps I should enable linter warnings in flutter_acrylic and patch the imports to prevent that from happening in the future, but in my opinion, this shouldn’t have occurred in the first place.

lucasjinreal commented 1 year ago

@Adrian-Samoticha How to add minmax button on the left?

lucasjinreal commented 1 year ago

@Adrian-Samoticha Looks like no window button if so::

image
Adrian-Samoticha commented 1 year ago

By default, these buttons should be visible. You can call the showCloseButton, showMiniaturize, and showZoomButton methods to show them if they are hidden.

lucasjinreal commented 1 year ago

@Adrian-Samoticha thanks, where should I call show these buttons in flutter_acrylic api?

Adrian-Samoticha commented 1 year ago

I just noticed you are calling Window.hideWindowControls(); in the code you posted above. That’s the method call that hides your buttons. Don’t call it if you want the buttons to appear.

Also, the code I’ve posted above is outdated anyway. You should use the TransparentMacOSSidebar widget if you are running the latest version of flutter_acrylic so that you can use wallpaper tinting in your windows:

image
import 'package:flutter/material.dart';
import 'package:flutter_acrylic/flutter_acrylic.dart';
import 'package:flutter_acrylic/widgets/transparent_macos_sidebar.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Window.initialize();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    Window.makeTitlebarTransparent();
    Window.enableFullSizeContentView();
    Window.hideTitle();

    Window.setEffect(effect: WindowEffect.solid);

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.transparent,
      body: Row(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: const [
          _Sidebar(),
          Expanded(
            child: _ContentView(),
          ),
        ],
      ),
    );
  }
}

class _ContentView extends StatelessWidget {
  const _ContentView();

  @override
  Widget build(BuildContext context) {
    return const DecoratedBox(
      decoration: BoxDecoration(
        border: Border(
          left: BorderSide(
            color: Color.fromRGBO(0, 0, 0, 0.35),
          ),
        ),
      ),
      child: Padding(
        padding: EdgeInsets.all(8.0),
        child: Text(
          'content view',
          style: TextStyle(
            color: Colors.white,
          ),
        ),
      ),
    );
  }
}

class _Sidebar extends StatelessWidget {
  const _Sidebar();

  @override
  Widget build(BuildContext context) {
    return const TransparentMacOSSidebar(
      child: SizedBox(
        width: 192.0,
        child: TitlebarSafeArea(
          child: Padding(
            padding: EdgeInsets.symmetric(horizontal: 8.0),
            child: Text(
              'sidebar',
              style: TextStyle(
                color: Colors.white,
              ),
            ),
          ),
        ),
      ),
    );
  }
}
lucasjinreal commented 1 year ago

thanks so much! BTW, does the same code can be used on windows? Or it must need sperated code for 2 platforms?

Adrian-Samoticha commented 1 year ago

TransparentMacOSSidebar doesn’t work on Windows. You will need to call the Window.setEffect method with some transparent effect and make sure the content view has a non-transparent background color when targeting non-macOS platforms.