wolt_modal_sheet 0.0.4 copy "wolt_modal_sheet: ^0.0.4" to clipboard
wolt_modal_sheet: ^0.0.4 copied to clipboard

This package provides a responsive modal with multiple pages, motion animation for page transitions, and scrollable content within each page.

pub package package publisher

WoltModalSheet #

WoltModalSheet is designed to revolutionize the use of Flutter modal sheets. Built with Wolt-grade design quality and used extensively in Wolt products, this UI component offers a visually appealing and user-friendly modal sheet with multiple pages, motion animation for page transitions, and scrollable content within each page.

Features #

Multi-Page Layout #

Traverse through numerous pages within a single sheet.

Experience multi-page navigation in WoltModalSheet

Scrollable Content #

Greater flexibility with scrollable content per page, accommodating large content effortlessly.

Scroll with ease in WoltModalSheet

Responsive Design #

The modal sheet adjusts to fit all screen sizes, appearing as a dialog on larger screens and as a bottom sheet on smaller screens, guided by user-specified conditions.

Adaptability to different screen sizes in WoltModalSheet

Motion Animation #

Engage users with dynamic motion animation for page transitions and scrolling.

Pagination Scrolling
Pagination Scrolling

Imperative and Declarative Navigation #

The library showcases examples of both imperative and declarative navigation patterns to display modal sheet on screen.

Illustration of imperative and declarative navigation in WoltModalSheet

Dynamic Pagination #

User input can dynamically shape the modal sheet's page list.

Dynamic pagination in action in WoltModalSheet

State Management Integration #

Pages in the Wolt Modal Sheet offer a customizable look and the page components are supplied with the WoltModalSheetPage class instance. The API provides a way to manage the state among the page components to be used with popular libraries such as Bloc and Provider

Understanding the page elements #

Each element within the WoltModalSheet has a role to play, offering context, navigational assistance, and explicit action prompts to the user. By understanding these elements and their roles, you can fully harness the power of WoltModalSheet and create an intuitive and engaging user experience.

Modal sheet elements breakdown

Top bar, navigation icons, and top bar title #

Top Bar acts as the anchor of the sheet. It houses critical navigation actions, allowing the user to close the sheet or navigate back to previous pages. The Top Bar also features an optional title that provides context to the main goal of the flow. For sheets with longer content necessitating scrolling, the Top Bar remains 'sticky' at the top, ensuring users always have access to these actions.

Sticky action bar (SAB) #

The Sticky Action Bar (SAB) guides the user towards the next step. Anchored to the bottom of the view, the SAB elevates above the content with a gentle gradient. This position guarantees that the action remains visible, subtly hinting to the user that there is more content to be explored below the fold.

Hero image #

An optional Hero Image can be positioned at the top of the main content. This element immediately grabs the user's attention, effectively conveying the primary theme or message of the content.

Page Title #

An optional page title above the main content provides users with a quick understanding of what to expect from the page. As the user scrolls, this title becomes hidden, at which point the top bar title continues to serve this context-providing purpose.

Main content #

The main content delivers information according to user need. It can be scrollable to handle larger content. The content is built lazily to improve the performance.

Here is an example that shows all the modal sheet elements in use:

Modal sheet elements in use

Getting started #

To use this plugin, add wolt_modal_sheet as a dependency in your pubspec.yaml file.

Usage #

This package has 4 example projects.

Example app #

The example app demonstrates how to display a two-page modal sheet.


class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    final pageIndexNotifier = ValueNotifier(0);

    WoltModalSheetPage page1(BuildContext modalSheetContext) {
      return WoltModalSheetPage.withSingleChild(
        stickyActionBar: StickyActionBarWrapper(
          child: Column(
            children: [
              WoltElevatedButton(
                onPressed: () => Navigator.of(modalSheetContext).pop(),
                theme: WoltElevatedButtonTheme.secondary,
                child: const Text('Cancel'),
              ),
              const SizedBox(height: 8),
              WoltElevatedButton(
                onPressed: () {
                  pageIndexNotifier.value = pageIndexNotifier.value + 1;
                },
                child: const Text('Next page'),
              ),
            ],
          ),
        ),
        pageTitle: const ModalSheetTitle('Pagination'),
        topBarTitle: const ModalSheetTopBarTitle('Pagination'),
        closeButton: WoltModalSheetCloseButton(onClosed: Navigator.of(modalSheetContext).pop),
        mainContentPadding: const EdgeInsetsDirectional.all(16),
        child: const Padding(
            padding: EdgeInsets.only(bottom: 120, top: 16),
            child: Padding(
              padding: EdgeInsets.only(bottom: 16),
              child: Text(
                '''
Pagination involves a sequence of screens the user navigates sequentially. We chose a lateral motion for these transitions. When proceeding forward, the next screen emerges from the right; moving backward, the screen reverts to its original position. We felt that sliding the next screen entirely from the right could be overly distracting. As a result, we decided to move and fade in the next page using 30% of the modal side.
''',
              ),
            )),
      );
    }

    WoltModalSheetPage page2(BuildContext modalSheetContext) {
      return WoltModalSheetPage.withCustomSliverList(
        mainContentPadding: EdgeInsetsDirectional.zero,
        stickyActionBar: StickyActionBarWrapper(
          padding: EdgeInsets.zero,
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16.0),
            child: WoltElevatedButton(
              onPressed: () {
                Navigator.of(modalSheetContext).pop();
                pageIndexNotifier.value = 0;
              },
              child: const Text('Close'),
            ),
          ),
        ),
        pageTitle: const Padding(
          padding: EdgeInsets.symmetric(horizontal: 16),
          child: ModalSheetTitle('Material Colors'),
        ),
        heroImageHeight: 200,
        heroImage: const Image(
          image: AssetImage('lib/assets/images/material_colors_hero.png'),
          fit: BoxFit.cover,
        ),
        topBarTitle: const ModalSheetTopBarTitle('Material Colors'),
        backButton: WoltModalSheetBackButton(onBackPressed: () {
          pageIndexNotifier.value = pageIndexNotifier.value - 1;
        }),
        closeButton: WoltModalSheetCloseButton(onClosed: () {
          Navigator.of(modalSheetContext).pop();
          pageIndexNotifier.value = 0;
        }),
        sliverList: SliverList(
          delegate: SliverChildBuilderDelegate(
                (_, index) => ColorTile(color: allMaterialColors[index]),
            childCount: allMaterialColors.length,
          ),
        ),
      );
    }

    return MaterialApp(
      home: Scaffold(
        body: Builder(
          builder: (context) {
            return Center(
              child: SizedBox(
                width: 200,
                child: WoltElevatedButton(
                  child: const Text('Show Wolt Modal Sheet'),
                  onPressed: () {
                    WoltModalSheet.show<void>(
                      pageIndexNotifier: pageIndexNotifier,
                      context: context,
                      pageListBuilder: (modalSheetContext) {
                        return [
                          page1(modalSheetContext),
                          page2(modalSheetContext),
                        ];
                      },
                      modalTypeBuilder: (context) {
                        final size = MediaQuery.of(context).size.width;
                        if (size < 768) {
                          return WoltModalType.bottomSheet;
                        } else {
                          return WoltModalType.dialog;
                        }
                      },
                      onModalDismissedWithBarrierTap: () {
                        debugPrint('Closed modal sheet with barrier tap');
                        pageIndexNotifier.value = 0;
                      },
                      maxDialogWidth: 560,
                      minDialogWidth: 400,
                      minPageHeight: 0.4,
                      maxPageHeight: 0.9,
                    );
                  },
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

The code snippet above produces the following:

Dynamic pagination in action in WoltModalSheet

Playground app with imperative navigation #

The playground app demonstrates how to imperatively show the modal sheet. The purpose of this module is to play and experiment with various use cases. These use cases include: It demonstrates

  • A page with forced max height independent of its content.
  • A page with a hero image
  • A page with a list whose items are lazily built.
  • A page with a text field.

Playground app with declarative navigation #

The playground_navigator2 has the same content with the playground app but the modal sheet is shown using Navigator 2.0 (Router API) in a declarative way.

Coffee maker app for state management example #

Finally, the coffee_maker app demonstrates how to manage the state among the page components with an opinionated use of the Provider state management library.

The code snippet demonstrates how to decorate the modal sheet with a change notifier provider so that the page components can be rebuilt according to the current state:

  void _onCoffeeOrderSelectedInAddWaterState(BuildContext context, String coffeeOrderId) {
  final model = context.read<StoreOnlineViewModel>();
  final pageIndexNotifier = ValueNotifier(0);

  WoltModalSheet.show(
    pageIndexNotifier: pageIndexNotifier,
    context: context,
    decorator: (child) {
      return ChangeNotifierProvider<StoreOnlineViewModel>.value(
        value: model,
        builder: (_, __) => child,
      );
    },
    pageListBuilderNotifier: AddWaterModalPageBuilder.build(
      coffeeOrderId: coffeeOrderId,
      goToPreviousPage: () => pageIndexNotifier.value = pageIndexNotifier.value - 1,
      goToNextPage: () => pageIndexNotifier.value = pageIndexNotifier.value + 1,
    ),
    modalTypeBuilder: _modalTypeBuilder,
  );
}

Dynamic pagination in action in WoltModalSheet

Additional information #

In this blog post, we explore the foundational design decisions that guide the WoltModalSheet's functionality.

1067
likes
0
pub points
97%
popularity

Publisher

verified publisherwolt.com

This package provides a responsive modal with multiple pages, motion animation for page transitions, and scrollable content within each page.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter

More

Packages that depend on wolt_modal_sheet