fluent_ui icon indicating copy to clipboard operation
fluent_ui copied to clipboard

Allow overwriting the NavigationAppBar Layout

Open fischerscode opened this issue 2 years ago • 5 comments

Is your feature request related to a problem? Please describe. I want to implement some custom layout for the NavigationAppBar.

Describe the solution you'd like It would be nice if NavigationAppBar would take an optional builder, in order to allow overwriting this behavior: https://github.com/bdlukaa/fluent_ui/blob/0522bf08d28676619324c3ec5be5405fc93a22d2/lib/src/controls/navigation/navigation_view/view.dart#L889-L925

Describe alternatives you've considered The NavigationAppBar could also have a build-like method instead of the builder. For building a custom design, one could extend NavigationAppBar and override the build method.

Additional context I want the design shown in the screenshot. The MyApp should never overlay the Hamburger Icon and the Close button should fill the corner. Additionally there should be a DragToMoveArea stacked behind the Row. grafik

fischerscode avatar Jan 29 '23 15:01 fischerscode

The title widget should take the whole app bar. actions, on the other hand, will be drawn on top of the title.

You can perform this layout by providing the desired layout to the title property

bdlukaa avatar Jan 31 '23 00:01 bdlukaa

I think I tried this and it resulted in a white bar at the right side of the red button.

I'll retry it tomorrow.

fischerscode avatar Jan 31 '23 00:01 fischerscode

TLDR; By using title and action and manually accounting for the actions width, my goal seems to be achievable, but I don't think it's an elegant solution and it still has a limitation.

Using actions

grafik grafik The CommandBarCard is in front of the leading Button. ❌

Code
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter_acrylic/flutter_acrylic.dart' as flutter_acrylic;
import 'package:window_manager/window_manager.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await flutter_acrylic.Window.initialize();
  await WindowManager.instance.ensureInitialized();
  windowManager.waitUntilReadyToShow(
    const WindowOptions(
      minimumSize: Size(350, 600),
      center: true,
      titleBarStyle: TitleBarStyle.hidden,
      fullScreen: false,
    ),
    () async {
      await windowManager.show();
      await windowManager.setPreventClose(false);
      await windowManager.focus();
    },
  );

  runApp(FluentApp(
    debugShowCheckedModeBanner: false,
    home: NavigationView(
      appBar: NavigationAppBar(
          actions: Row(
        children: [
          Expanded(
              child: CommandBarCard(
            child: CommandBar(
                mainAxisAlignment: MainAxisAlignment.end,
                crossAxisAlignment: CrossAxisAlignment.center,
                primaryItems: [
                  CommandBarButton(onPressed: () {}, label: Text("Button1")),
                  CommandBarButton(onPressed: () {}, label: Text("Button2")),
                  CommandBarButton(onPressed: () {}, label: Text("Button3")),
                  CommandBarButton(onPressed: () {}, label: Text("Button4")),
                ]),
          )),
          SizedBox(
            width: 138,
            height: 50,
            child: WindowCaption(
              backgroundColor: Colors.transparent,
            ),
          ),
        ],
      )),
      pane: NavigationPane(items: []),
    ),
  ));
}

Using title

grafik The red close button is not in the corner. ❌

Code
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter_acrylic/flutter_acrylic.dart' as flutter_acrylic;
import 'package:window_manager/window_manager.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await flutter_acrylic.Window.initialize();
  await WindowManager.instance.ensureInitialized();
  windowManager.waitUntilReadyToShow(
    const WindowOptions(
      minimumSize: Size(350, 600),
      center: true,
      titleBarStyle: TitleBarStyle.hidden,
      fullScreen: false,
    ),
    () async {
      await windowManager.show();
      await windowManager.setPreventClose(false);
      await windowManager.focus();
    },
  );

  runApp(FluentApp(
    debugShowCheckedModeBanner: false,
    home: NavigationView(
      appBar: NavigationAppBar(
          title: Row(
        children: [
          Expanded(
              child: CommandBarCard(
            child: CommandBar(
                mainAxisAlignment: MainAxisAlignment.end,
                crossAxisAlignment: CrossAxisAlignment.center,
                primaryItems: [
                  CommandBarButton(onPressed: () {}, label: Text("Button1")),
                  CommandBarButton(onPressed: () {}, label: Text("Button2")),
                  CommandBarButton(onPressed: () {}, label: Text("Button3")),
                  CommandBarButton(onPressed: () {}, label: Text("Button4")),
                ]),
          )),
          SizedBox(
            width: 138,
            height: 50,
            child: WindowCaption(
              backgroundColor: Colors.transparent,
            ),
          ),
        ],
      )),
      pane: NavigationPane(items: []),
    ),
  ));
}

Using title and action

grafik Title and actions are stacked on top of each other. ❌

Code
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter_acrylic/flutter_acrylic.dart' as flutter_acrylic;
import 'package:window_manager/window_manager.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await flutter_acrylic.Window.initialize();
  await WindowManager.instance.ensureInitialized();
  windowManager.waitUntilReadyToShow(
    const WindowOptions(
      minimumSize: Size(350, 600),
      center: true,
      titleBarStyle: TitleBarStyle.hidden,
      fullScreen: false,
    ),
    () async {
      await windowManager.show();
      await windowManager.setPreventClose(false);
      await windowManager.focus();
    },
  );

  runApp(FluentApp(
    debugShowCheckedModeBanner: false,
    home: NavigationView(
      appBar: NavigationAppBar(
        title: Expanded(
            child: CommandBarCard(
          child: CommandBar(
              mainAxisAlignment: MainAxisAlignment.end,
              crossAxisAlignment: CrossAxisAlignment.center,
              primaryItems: [
                CommandBarButton(onPressed: () {}, label: Text("Button1")),
                CommandBarButton(onPressed: () {}, label: Text("Button2")),
                CommandBarButton(onPressed: () {}, label: Text("Button3")),
                CommandBarButton(onPressed: () {}, label: Text("Button4")),
              ]),
        )),
        actions: SizedBox(
          width: 138,
          height: 50,
          child: WindowCaption(
            backgroundColor: Colors.transparent,
          ),
        ),
      ),
      pane: NavigationPane(items: []),
    ),
  ));
}

Using title and action and manually accounting for the actions width

grafik Actually works, I did not think about this solution earlier, but I don't think this solution is elegant. ✅

Code
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter_acrylic/flutter_acrylic.dart' as flutter_acrylic;
import 'package:window_manager/window_manager.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await flutter_acrylic.Window.initialize();
  await WindowManager.instance.ensureInitialized();
  windowManager.waitUntilReadyToShow(
    const WindowOptions(
      minimumSize: Size(350, 600),
      center: true,
      titleBarStyle: TitleBarStyle.hidden,
      fullScreen: false,
    ),
    () async {
      await windowManager.show();
      await windowManager.setPreventClose(false);
      await windowManager.focus();
    },
  );

  runApp(FluentApp(
    debugShowCheckedModeBanner: false,
    home: NavigationView(
      appBar: NavigationAppBar(
        title: Row(
          children: [
            Expanded(
                child: CommandBarCard(
              child: CommandBar(
                  mainAxisAlignment: MainAxisAlignment.end,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  primaryItems: [
                    CommandBarButton(onPressed: () {}, label: Text("Button1")),
                    CommandBarButton(onPressed: () {}, label: Text("Button2")),
                    CommandBarButton(onPressed: () {}, label: Text("Button3")),
                    CommandBarButton(onPressed: () {}, label: Text("Button4")),
                  ]),
            )),
            SizedBox(width: 138),
          ],
        ),
        actions: SizedBox(
          width: 138,
          height: 50,
          child: WindowCaption(
            backgroundColor: Colors.transparent,
          ),
        ),
      ),
      pane: NavigationPane(items: []),
    ),
  ));
}
Disadvantage about this solution: It's not possible to add a `DragToMoveArea` that triggers when the user drags at the marked area. ❌ This would be possible with #709.

grafik

fischerscode avatar Feb 02 '23 12:02 fischerscode

On the native side, the title always go behind the window features (title and actions). But I also agree that there is a "search bar" (or something related) in some native UWP apps

This is a hard problem to solve, because desktop aims adaptability of different window sizes, otherwise it can cause to some weird effects. Here is a screenshot of "Slack" with a small size:

image

On the other hand, on Task Manager, the "search bar" window feature works differently. If there isn't enough space, the bar is changed to a button:

Before / Small

After / Big

Differently, Microsoft Store just resizes to the minimum size and keep the search bar functionality:

image

But, after it all, none of the above is described in the Win UI documentation. In fact, the only option Microsoft provides to add some custom content is either:

  • AutoSuggestBox
  • PaneCustomContent, a property that displays some content after the pane items

image


I like the idea of having a possibility to add a custom functionality to the top bar, but this is not something that can be built-in the package. I will stand the point that the title property can be used to display such things, since a LayoutBuilder can be used to achieve adaptability.

I don't think this can be implemented into the package because there isn't a strict documentation about it, so the developers will need to invent the desired behavior themselves. In fact, not even the window buttons (minimize, maximize/resume, close) are provided by the package, since it's not in the documentation.

bdlukaa avatar May 29 '23 21:05 bdlukaa