Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support transparent space around sheet #76

Open
naamapps opened this issue Mar 26, 2024 · 4 comments · May be fixed by #268
Open

Support transparent space around sheet #76

naamapps opened this issue Mar 26, 2024 · 4 comments · May be fixed by #268
Assignees
Labels
feature request New feature or request P2
Milestone

Comments

@naamapps
Copy link

Hi,
While pushing a new navigation modal sheet, it's not taking SafeArea and bottom margin into consideration.
Code:

class SheetShape extends StatelessWidget {
  final Widget child;
  const SheetShape({super.key, required this.child});

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Card(
        color: Theme.of(context).bottomSheetTheme.backgroundColor,
        elevation: 0,
        clipBehavior: Clip.antiAlias,
        margin: const EdgeInsets.only(left: 15, right: 15, bottom: 3),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(36),
        ),
        child: child,
      ),
    );
  }
}

class StaticSheetContent extends StatelessWidget {
  final bool includeShape;
  final Widget? header;
  final Widget body;
  final Widget? footer;
  const StaticSheetContent({
    super.key,
    required this.body,
    this.header,
    this.footer,
    this.includeShape = true,
  });

  @override
  Widget build(BuildContext context) {
    final content = Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        if (header != null) header!,
        Padding(
          padding: const EdgeInsets.symmetric(vertical: 10),
          child: body,
        ),
        if (footer != null) footer!,
      ],
    );

    if (includeShape) {
      return SheetShape(
        child: content,
      );
    }

    return content;
  }
}

final _transitionObserver = NavigationSheetTransitionObserver();

Future<T?> showOptionsSheet<T>({
  required List<SheetOptionProps> options,
}) async {
  final modalRoute = ModalSheetRoute<T>(
    transitionDuration: const Duration(milliseconds: 250),
    builder: (context) => SheetDismissible(
      child: NavigationSheet(
        transitionObserver: _transitionObserver,
        child: SheetShape(
          child: Navigator(
            observers: [_transitionObserver],
            onGenerateInitialRoutes: (navigator, initialRoute) {
              return [
                DraggableNavigationSheetRoute(
                  builder: (context) {
                    return StaticSheetContent(
                      includeShape: false,
                      header: SheetHeader(
                        title: 'options'.tr(),
                      ),
                      body: SheetOptionsList(
                        options: options,
                      ),
                    );
                  },
                ),
              ];
            },
          ),
        ),
      ),
    ),
  );
  return await Navigator.push<T>(rootContext, modalRoute);
}

Expected - There should be a gap from the bottom of the screen:
flutter_09

Actual - The sheet is attached to the bottom of the screen:
flutter_08

@fujidaiti fujidaiti added the question Further information is requested label Mar 26, 2024
@fujidaiti
Copy link
Owner

Unfortunately, with the current version, adding invisible space around the sheet is a bit tricky. I hope the following steps will solve your problem:

  1. Remove the SheetShape.
  2. Move the SafeArea to out of the NavigationSheet from the SheetShape.
    SafeArea(child: NavigationSheet(...));
  3. To create the rounded corners, use Card, ClipRRect or something, not outside of the Navigator, but inside each page.
    ClipRRect(child: StaticSheetContent(...));

Here's a fork from the tutorial code of the NavigationSheet, adding extra space around the sheet.

Code
import 'package:flutter/material.dart';
import 'package:smooth_sheets/smooth_sheets.dart';

void main() {
  runApp(const _ImperativeNavigationSheetExample());
}

class _ImperativeNavigationSheetExample extends StatelessWidget {
  const _ImperativeNavigationSheetExample();

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        body: Stack(
          children: [
            Placeholder(),
            _ExampleSheet(),
          ],
        ),
      ),
    );
  }
}

// NavigationSheet requires a special NavigatorObserver in order to
// smoothly change its extent during a route transition.
final _transitionObserver = NavigationSheetTransitionObserver();

class _ExampleSheet extends StatelessWidget {
  const _ExampleSheet();

  @override
  Widget build(BuildContext context) {
    // Create a navigator somehow that will be used for nested navigation in the sheet.
    final nestedNavigator = Navigator(
      // Do not forget to attach the observer!
      observers: [_transitionObserver],
      onGenerateInitialRoutes: (navigator, initialRoute) {
        return [
          // Use DraggableNavigationSheetRoute for a draggable page.
          DraggableNavigationSheetRoute(
            builder: (context) {
              return const _DraggablePage();
            },
            transitionsBuilder:
                (context, animation, secondaryAnimation, child) {
              return SlideTransition(
                position: Tween(begin: const Offset(1, 0), end: Offset.zero)
                    .animate(animation),
                child: child,
              );
            },
          ),
        ];
      },
    );

    // Wrap the nested navigator in a NavigationSheet.
    return SafeArea(
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16),
        child: NavigationSheet(
          transitionObserver: _transitionObserver,
          child: nestedNavigator,
        ),
      ),
    );
  }
}

class _DraggablePage extends StatelessWidget {
  const _DraggablePage();

  void navigateToScrollablePage(BuildContext context) {
    // Use ScrollableNavigationSheetRoute for a scrollable page.
    final route = ScrollableNavigationSheetRoute(
      builder: (context) {
        return const _ScrollablePage();
      },
      // You may want to use a custom page transition.
      transitionsBuilder: (context, animation, secondaryAnimation, child) {
        return SlideTransition(
          position: Tween(begin: const Offset(1, 0), end: Offset.zero)
              .animate(animation),
          child: child,
        );
      },
    );

    Navigator.push(context, route);
  }

  @override
  Widget build(BuildContext context) {
    final title = Text(
      'Draggable Page',
      style: Theme.of(context).textTheme.titleLarge?.copyWith(
            color: Theme.of(context).colorScheme.onSecondaryContainer,
          ),
    );

    return LayoutBuilder(
      builder: (context, constraints) {
        return Container(
          width: constraints.maxWidth,
          height: constraints.maxHeight * 0.5,
          // Add rounded corners
          decoration: ShapeDecoration(
            color: Theme.of(context).colorScheme.secondaryContainer,
            shape: const RoundedRectangleBorder(
              borderRadius: BorderRadius.all(
                Radius.circular(16),
              ),
            ),
          ),
          child: Column(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              title,
              const SizedBox(height: 32),
              TextButton(
                onPressed: () => navigateToScrollablePage(context),
                child: const Text('Next'),
              ),
            ],
          ),
        );
      },
    );
  }
}

class _ScrollablePage extends StatelessWidget {
  const _ScrollablePage();

  @override
  Widget build(BuildContext context) {
    final backgroundColor = Theme.of(context).colorScheme.secondaryContainer;
    return ClipRRect(
      // Add rounded corners
      borderRadius: BorderRadius.circular(16),
      child: Scaffold(
        backgroundColor: backgroundColor,
        appBar: AppBar(
          title: const Text('Scrollable Page'),
          backgroundColor: backgroundColor,
        ),
        body: ListView.builder(
          itemCount: 30,
          itemBuilder: (context, index) {
            return ListTile(
              title: Text('Item #$index'),
            );
          },
        ),
      ),
    );
  }
}
RocketSim_Recording_iPhone_15_6.1_2024-03-26_22.53.28.mp4

@naamapps
Copy link
Author

naamapps commented Mar 26, 2024

I got a normal modal sheet working with invisible space around, it's just that the navigation sheet isn't working like the normal one.

Are you sure there isn't a bug regarding the navigation sheet?
I also didn't succeed reproducing the example you wrote here..
Can you try pushing a new ModalSheetRoute with NavigationSheet instead of putting the sheet inside a Stack?

Also, your example transition from one sheet to the other is not really smooth because the transition not only for the content but rather for the whole sheet! - which is not the expected result I'm trying to achieve.

This is a working code with gaps all around the sheet (using the widgets I wrote above & without NavigationSheet):

    final modalRoute = ModalSheetRoute<bool>(
      transitionDuration: const Duration(milliseconds: 250),
      builder: (context) => const SheetDismissible(
        child: DraggableSheet(
          child: StaticSheetContent(
            header: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text('hello'),
              ],
            ),
            body: Padding(
              padding: EdgeInsets.all(8.0),
              child: Text('wow'),
            ),
            footer: Text('hi'),
          ),
        ),
      ),
    );
    final result = await Navigator.push<bool>(context, modalRoute);

@fujidaiti fujidaiti added feature request New feature or request and removed question Further information is requested labels Mar 27, 2024
@fujidaiti fujidaiti changed the title [Bug] Modal sheet not taking SafeArea and bottom margin into consideration with NavigationSheet Support invisible space around sheet Mar 27, 2024
@fujidaiti fujidaiti added the P3 label Mar 27, 2024
@fujidaiti
Copy link
Owner

fujidaiti commented Mar 27, 2024

Are you sure there isn't a bug regarding the navigation sheet?

No. It's a limitation of the current version.

I got a normal modal sheet working with invisible space around

Although it seems to work in some configurations, adding invisible space around a sheet is not yet officially supported. For example, wrapping a SheetContentScaffold with a SafeArea won't work as we expected:

DraggableSheet(
  child: SafeArea(
    child: SheetContentScaffold(...),
  ),
);

Can you try pushing a new ModalSheetRoute with NavigationSheet instead of putting the sheet inside a Stack?

Try this:

class _ImperativeNavigationSheetExample extends StatelessWidget {
  const _ImperativeNavigationSheetExample();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Builder(
          builder: (context) {
            return Center(
              child: ElevatedButton(
                onPressed: () {
                  Navigator.push(
                    context,
                    ModalSheetRoute(
                      builder: (_) => const _ExampleSheet(),
                    ),
                  );
                },
                child: const Text('Show Sheet'),
              ),
            );
          },
        ),
      ),
    );
  }
}

@fujidaiti fujidaiti changed the title Support invisible space around sheet Support transparent space around sheet Mar 27, 2024
@fujidaiti fujidaiti added P2 and removed P3 labels Apr 1, 2024
@fujidaiti fujidaiti added this to the First stable version milestone May 26, 2024
@fujidaiti fujidaiti self-assigned this Jun 2, 2024
@cabaucom376
Copy link

Just wanted to express my interest in this as well. Being able to have the padding around the sheet as well as the ability to navigate/animate the sheets themselves would align with my intended design language.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request New feature or request P2
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants