fujidaiti / smooth_sheets

Sheet widgets with smooth motion and great flexibility.
https://pub.dev/packages/smooth_sheets
MIT License
231 stars 22 forks source link

Feature Request: Responsive Sheet API's #200

Open cabaucom376 opened 4 months ago

cabaucom376 commented 4 months ago

Description

Currently, the smooth_sheets package supports easy bottom sheets, which slide in from the bottom of the screen. However, it would be highly beneficial to have the ability to customize the direction from which the sheet comes in, or even what type of sheet it is. This enhancement will allow developers to create more versatile and user-friendly interfaces by enabling sheets to slide in from the top, left, or right of the screen. wolt_modal_sheet has recently added functionality like this, the smooth_sheets API aligns better with my intended use case so this would be a great addition.

Benefits

Potential Use Cases

Additional Info

If needed, I am willing to try and contribute to the implementation of this feature or help with testing and documentation. This sort of responsive sheet nature will be core to the UX of what I am working on and I would like rather work on something existing rather than start my own bespoke package.

Issue #152 may be beneficial to have done before this, but as it seems to be a long standing issue out of this packages control so I believe it still beneficial to discuss what responsiveness would look like for smooth_sheets.

SheetContentScaffold's API might also require some additional careful considerations. (e.g. Search bars pinning to the top of the screen on sheets coming from the top).

Scrollable sheets would benefit from reverse scrolling behavior.

fujidaiti commented 4 months ago

Hi @cabaucom376,

Thanks for sharing your ideas. I basically agree with all of them. I've had similar ideas since the beginning of the package, which is why the package name isn't smooth_bottom_sheets :) However, this is a broad topic, so we need careful discussion before working on it.

It would be highly beneficial to have the ability to customize the direction from which the sheet comes in.

I think implementing this could be relatively easy because, under the hood, the sheets are just a custom Transform widget, plus a controller (called SheetExtent) for the translation delta along the y-axis. We would need to add some if-else statements to direction-sensitive logic, and it would be a lot of work, but it's far from impossible.

Imperative Navigation Dialogs: Constructing dialogs while utilizing the same navigation behavior of the existing sheets.

I'm not sure what this actually means, but implementing the Wolt-like resizable nested navigation dialogs is a difficult task. While smooth_sheets has a similar component called NavigationSheet, which uses a Navigator widget for nested navigation, it never resizes the Navigator, just sliding it up or down along with user gestures. This simple design works fine for the bottom sheet UI since the unnecessary bottom part of the Navigator can be hidden outside the bottom of the screen.

But such a design is not well-suited for the dialog UI. Maybe we can actually resize the Navigator along with page transitions, but it's unreasonable in terms of rendering performance (wolt_modal_sheet doesn't have a Navigator, it uses its own navigation system). We need to figure out some tricks to solve this problem (maybe restricting the painting area or something?).

Scrollable sheets would benefit from reverse scrolling behavior.

This topic has been discussed in #81.

If needed, I am willing to try and contribute to the implementation of this feature or help with testing and documentation.

Feel free to send me a PR. It doesn't need to be completed; preliminary ideas or some experiments are also welcome!

cabaucom376 commented 4 months ago

@fujidaiti everything you said totally makes sense. I wonder what your vision is on the desired user-facing API? It should be easier to work backwards from there. It would be useful to break down an end goal into clear steps and create dedicated issues for each to help coordinate/focus efforts and help with chronology. I want to use this issue to workshop the overall idea if thats okay.

Rough Ideas:

Nice to haves/consider:

What would you add/remove from this list for an end goal? Maybe a few mock examples to demonstrate use-cases would be useful?

fujidaiti commented 4 months ago

@cabaucom376 sorry for the late reply. Here are my current thoughts on this topic.

Multi-direction support

I think it would be better to employ the composition style as TextDirection and Padding from the Flutter SDK, rather than creating a lot of similar sheet widgets for each direction and type, like TopDraggableSheet, TopScrollableSheet, SideDraggableSheet, etc.

// Top sheet (↓)
SheetDirectionality(
  axisDirection: AxisDirection.down,
  child: DraggableSheet(...),
);

// Left side sheet (→)
SheetDirectionality(
  axisDirection: AxisDirection.right,
  child: DraggableSheet(...),
);

This approach is also beneficial for modal routes, as they need to be aware of the sheet's axis direction to perform some direction-aware behaviors in, for example, the route transitions and the swipe-to-dismiss gestures:

// Modal bottom sheet
ModalSheetRoute(
  axisDirection: AxisDirection.up,
  builder: (context) => DraggableSheet(...),
);

Then, a rough implementation of the modal route class could be:

class ModalSheetRoute<T> extends ModalRoute<T> {
  final Widget Function(BuildContext) builder;
  final AxisDirection axisDirection;

  @override
  Widget buildPage(...) {
    return SheetDirectionality(
      axisDirection: axisDirection,
      child: builder(context),
    );
  }

  @override
  Widget buildTransitions(...) {
    // Creates direction-aware transitions.
    return switch (axisDirection) {
      SheetAxisDirection.down => // Slides in from the top
      SheetAxisDirection.up => // Slides in from the bottom
      SheetAxisDirection.right => // Slides in from the left
      SheetAxisDirection.left => // Slides in from the right
    };
  }
}

Responsiveness

How to handle user logic? (e.g., bottom sheet on small screens dialog past certain breakpoint, bottom sheet when mobile device is portrait side sheet when device is landscape, modals/dialog fill the entire screen when too small, etc...)

As mentioned here, this package aims to be flexible enough to be easily integrated with other packages. For this reason, I think it's better to leverage existing packages that support building responsive UIs (as listed below), rather than creating our own layout system from scratch.

If someone wants an all-in-one solution for such use cases, wolt_modal_sheet might be a better choice than this package.

Dialog UIs

Imo, dialog UIs shouldn't be integrated with the sheets as they are completely different UIs. The characteristic of the sheets is that they can be dragged, whereas dialogs can't. Creating different outermost widgets for different types of UIs and sharing their content as a child widget is more flexible and aligns with the Flutter way:

Widget build(BuildContext context) {
  final Widget content = ...;
  if (largeScreen) {
    return MyDialog(child: content);
  } else {
    return DraggableSheet(child: content);
  }
}

Further discussions

cabaucom376 commented 4 months ago

Hi @fujidaiti,

Thanks for your thoughts on this topic. I appreciate the detailed explanation of the multi-direction support and responsiveness.

Multi-Direction Support

I agree that employing a composition style similar to TextDirection and Padding is more efficient and flexible than having a ton of separate sheets. I've been digging around the codebase and realized there are good many changes to consider for full multi-directionality. Transition support was fairly trivial, but I see now that extent, dragging, and overall just where the sheet decides to anchor will require some more deliberation and familiarity with the codebase. (I.e., sheets still assume a bottom anchor with width expansion. Side sheets behavior should calculate the extent based on the devices width and fill the height)

Responsiveness & Dialogs

I can see your point here this makes sense to me, my main intent was just to further develop the use cases for desktop platforms. Multi-directionality should be a good enough solution.

Further discussions

As far as a sticky app bar, my vision was to have a similar widget to the Github search bar on desktop platforms: image mobile example:

drawing

And to be honest I didn't consider the interaction on mobile too much. I imagine allowing the keyboard to overlap would be okay but with an optional flag to enable keyboard avoidance where it displaces the sheet. Maybe a StickyBottomBar could make that flag implicit as you would want it to pin to the top of the keyboard?

fujidaiti commented 4 months ago

my main intent was just to further develop the use cases for desktop platforms

I am currently reluctant to support desktop platforms. The reason is that the UX of mouse wheel scrolling in draggable (or swipeable) sheet UIs is very poor. I have previously worked on a similar issue in another project, and the results were quite terrible. As mentioned here, Flutter treats mouse wheel scrolling as a series of tiny scrolls (onPointerDownonPointerMoveonPointerUponPointerDownonPointerMove → ...), which prevents momentum scrolling from working properly. This is particularly incompatible with the snapping effects.

On desktop platforms, we could specify that "sheets cannot be dragged or swiped," but we can also easily achieve almost the same functionality using only built-in widgets without adding extra logic to the codebase. Therefore, I don't think there's much benefit in supporting this functionality in smooth_sheets.

// Top floating pane
Align(
  alignment: Alignment.topCenter,
  child: ConstrainedBox(
    constraints: BoxConstraints(maxWidth: 600, maxHeight: 400),
    child: Card(
       child: sharedContent,
    ),
  ),
);

On the other hand, dialog UIs that support resizable nested navigation like WoltModalSheet can't be achieved through the solution mentioned in the earlier comment. This has made me reconsider that there might be room for discussion regarding supporting responsive design in this package.

Either way, we need careful discussion before starting to work on this, as all-in-one solutions tend to be inflexible or unnecessarily complicated.

Maybe a StickyBottomBar could make that flag implicit as you would want it to pin to the top of the keyboard?

I think this is the natural behavior, too.

cabaucom376 commented 3 months ago

Flutter treats mouse wheel scrolling as a series of tiny scrolls (onPointerDown → onPointerMove → onPointerUp → onPointerDown → onPointerMove → ...), which prevents momentum scrolling from working properly. This is particularly incompatible with the snapping effects.

Hmm, that definitely presents an obstacle.

On desktop platforms, we could specify that "sheets cannot be dragged or swiped," but we can also easily achieve almost the same functionality using only built-in widgets without adding extra logic to the codebase. Therefore, I don't think there's much benefit in supporting this functionality in smooth_sheets.

I understand the concern about codebase complexity versus usage ergonomics. However, there are significant real-world benefits to adding multi-directional support and desktop compatibility:

  1. Adapting to Modern UI Trends:

    • Side sheets have gained popularity, influenced by modern browsers like Arc, which utilize side panels for navigation and additional features. These sheets don't necessarily need to be draggable on desktop platforms since there is typically some sort of button and/or hover trigger for interacting with the state.
  2. Enhanced Developer Experience:

    • While built-in widgets can be used for desktop solutions, maintaining two separate implementations adds unnecessary complexity. A unified package that supports multi-directional sheets across both mobile and desktop platforms simplifies development. Developers can leverage a consistent API, reducing the need to manage multiple solutions while not interfering with those who only need bottom sheets.
    • Maintaining consistent animations across the different sheet directions and platforms can ensure a smooth and cohesive user experience. For example, the same sliding animations can be reused for both mobile and desktop implementations.
  3. Improved User Interaction:

    • Multi-directional sheets can enhance user interaction by providing intuitive access to different functionalities. For example, a top sheet for search bars or notifications and side sheets for navigation can make applications more interactive and user-friendly. Sheet-style UI elements work well for both mobile and desktop interfaces, making them appealing for a responsive implementation.
  4. Platform-Specific Behavior:

    • Ensuring that interactions have their expected behaviors on each platform is crucial. For example, on mobile, sheets might be draggable, while on desktop, they could be static but interactive through buttons or hover triggers. This approach maintains usability across different devices, and is also not too unusual compared to how a lot of desktop software utilizes a sheet/pane.
  5. Reusing Existing Navigation:

    • For desktop responsiveness, reusing the same navigation stack built around smooth_sheets current implementation would benefit from having more space-efficient sheet alternatives. This ensures a consistent user experience and efficient use of screen real estate across different devices. Hence my initial liking to Dialog's.

While I understand the challenges, I believe these enhancements might be worth the codebase trade-offs. What are your thoughts?

fujidaiti commented 3 months ago

I agree with supporting multi-direction, and while I can't start on it immediately due to time constraints and other priority tasks, I'd like to work on it in the future when resources allow. I also have the same opinion about responsiveness (Dialog style on large screens), and I think if #76 is solved, it will pave the way. Since these features are orthogonal, we'll likely address them one by one in sequence.

What concerns me is supporting desktop platforms. The current package is designed only for touch devices, so I'm not clear on how the sheets should respond to user interactions on desktop (i.g. mouse wheel scrolling). It's likely that almost all of this package's key features, such as the snapping effect, swipe-to-dismiss action, and draggability, would become unnecessary on desktop. Therefore, I currently can't find a compelling reason to support desktop platforms with this package.

cabaucom376 commented 1 month ago

Apologies, the idea I had for desktop was to achieve similar behavior to this:

https://github.com/user-attachments/assets/f5d23fe4-236b-43af-a30e-20520eee94bb

As to your point on not knowing how to handle user interaction via a mouse wheel/trackpad I think a good execution example of this would be how the new iPhone Mirroring behavior works on the latest macOS update.

Flutter treats mouse wheel scrolling as a series of tiny scrolls (onPointerDown → onPointerMove → onPointerUp → onPointerDown → onPointerMove → ...), which prevents momentum scrolling from working properly.

I am aware that Flutter had some changes on the master branch recently to support iPhone Mirroring but I am not sure if any of those changes were relevant to this or not.