A library that makes it easy for you to create your own custom wizard.

Overview

Flutter Wizard

Author: Jop Middelkamp

A library that makes it easy for you to create your custom wizard. You'll have 100% control over the appearance of your wizard.

Responsive wizard example

How to use

In this chapter I'll explain to you how you could use the flutter_wizard package to create you own custom wizard.

Wrapping your widget with a wizard controller

To start you've to wrap all of your wizard components with the DefaultWizardController which could receive a couple of arguments.

stepController (required)
A list of WizardStepControllers, more details will be described in one of the following chapters.

IMPORTANT: The order of the stepControllers reflects to the order of the steps as displayed in the Wizard widget.

initialIndex
The initial of the initial step to display.

onStepChanged
A callback that gets triggered when the step changes.

onControllerCreated
A callback that gets triggered when the [WizardController] is created.

child (required)
The child widget of the DefaultWizardController. This widget (or one of its children) should contain all the widgets that need to interact with the Wizard widget. The reason for this is that all widgets under the DefaultWizardController are able to get the WizardController by calling the WizardController.of(context) or the provided extension context.wizardController.

Example:

DefaultWizardController(
  stepControllers: [
    WizardStepController(
      step: provider.stepOneProvider,
    ),
    WizardStepController(
      step: provider.stepTwoProvider,
      isNextEnabled: false,
    ),
    WizardStepController(
      step: provider.stepThreeProvider,
    ),
    ...
  ],
  child: Column(
    children: [
      ProgressIndicator(),
      Expanded(
        child: Wizard(
          ...
        ),
      ),
      ActionBar(),
    ],
  ),
);

In this example above the ProgressIndicator, Wizard, and ActionBar all have access to the WizardController because they are wrapped inside of the DefaultWizardController.

Step state management

As described in the last chapter you can provide the DefaultWizardController with stepControllers. This argument expects a list of WizardStepControllers. These step controllers need to be provided a step state. This step state could be a bloc, cubit, provider, notifier, etc. The only thing you are required to do is mixin the WizardStep into your statemanaged object.

When mixin the WizardStep your statemanaged object will be extended with the following properties and methods.

wizardController
A property that contains the WizardController.

onControllerReceived
A callback that gets fired when the wizardController property has been set. This callback will receive the WizardController as an argument.

onShowing
A callback that triggeres when the step has started showing.

onShowingCompleted
A callback that triggeres when the step has completed showing.

onHiding
A callback that triggers when the step has started hiding.

onHidingCompleted
A callback that triggers when the step has completed hiding.

Provider example:

class StepOneProvider with WizardStep {
  StepOneProvider();

  final _description = BehaviorSubject<String>.seeded('');

  final descriptionFocusNode = FocusNode();

  final descriptionTextController = TextEditingController();

  String get description {
    return _description.value;
  }

  @override
  Future<void> onShowing() async {
    if (_description.value.isEmpty) {
      descriptionFocusNode.requestFocus();
    }
  }

  @override
  Future<void> onHiding() async {
    if (descriptionFocusNode.hasFocus) {
      descriptionFocusNode.unfocus();
    }
  }

  void updateDescription(String description) {
    _description.add(description);
  }

  void dispose() {
    descriptionTextController.dispose();
  }
}

Determine the widgets to show for each step

Now with the DefaultWizardController setup it's time to setup the widgets to show for eacht step.

We are going to need to Wizard widget for this. This widget contains only one arguments.

stepBuilder
The builder method to build the steps corresponding widget. The builder method provides you with a BuildContext and a wizard step state. The wizard step state is the object in which you've mixed the WizardStep. This could be your bloc, cubit, provider, notifier, etc. Then based on the class type of the state you can determine the widget to show and pass the state into this widget to display the state to the user.

Example:

Wizard(
  stepBuilder: (context, state) {
    if (state is StepOneProvider) {
      return StepOne(
        provider: state,
      );
    }
    if (state is StepTwoProvider) {
      return StepTwo(
        provider: state,
      );
    }
    if (state is StepThreeProvider) {
      return StepThree(
        provider: state,
      );
    }
    return Container();
  },
);

Custom widget interaction with the wizard

Now let me explain to you how you could make your own custom widgets and how you could make them interact with the Wizard widget.

So you could create your own widget the same way like you create every other widget. Then in your widget you can get the WizardController by calling the WizardController.of(context) or the provided extension context.wizardController.

With the WizardController you can interact with the Wizard widget. The WizardController contains the following properties and methods to control the widget or receive information about its state.

eventStream
Streams the events that happen on the WizardController. The events have a base type of WizardEvent and can be casted to the specific event type. The events are:

  • WizardEnableGoBackEvent: Triggered when enableGoBack is called.
  • WizardEnableGoNextEvent: Triggered when enableGoNext is called.
  • WizardDisableGoBackEvent: Triggered when disableGoBack is called.
  • WizardDisableGoNextEvent: Triggered when disableGoNext is called.
  • WizardGoNextEvent: Triggered when goNext is called.
  • WizardGoBackEvent: Triggered when goBack is called.
  • WizardGoToEvent: Triggered when goTo is called.
  • WizardForcedGoBackToEvent: Triggered when disableGoNext is called with an index lower as the current index.

stepControllers
A list of the WizardStepControllers.

stepCount
The step count.

isFirstStep
Indicates whether a specific int index is the first.

isLastStep
Indicates whether a specific int index is the last.

indexStream
The step index as stream.

index
The step index.

getStepIndex
A method that returns the index for the provided WizardStep step.

getIsGoBackEnabledStream
Indicates weather the go back action is enabled as a stream.

getIsGoBackEnabled
Indicates weather the go back action is enabled as a stream.

enableGoBack
A method to enable the go back button for a specific int index.

disableGoBack
A method to disable the go back button for a specific int index.

getIsGoEnabledStream
Indicates weather the go back action is enabled as a stream.

getIsGoBackEnabled
Indicates weather the go back action is enabled as a stream.

enableGoNext
A method to enable the go next button for a specific int index.

disableGoNext
A method to disable the go next button for a specific int index. When disabling an index that is lower then the current index the Wizard will automatically animate back to the provided index.

getIsAnimateToEnabledStream
Indicates weather the animate to action is enabled for a specific int index as a stream.

getIsAnimateToEnabled
Indicates weather the animate to action is enabled for a specific int index.

previous
Animate to the previous step.

next
Animate to the next step.

animateTo
Animate to a specified index.

dispose
Dispose the controller

Example of move next button:

return StreamBuilder<bool>(
  stream: context.wizardController.getIsGoNextEnabledStream(),
  initialData: context.wizardController.getIsGoNextEnabled(),
  builder: (context, snapshot) {
    if (!snapshot.hasData || snapshot.hasError) {
      return const SizedBox.shrink();
    }
    final enabled = snapshot.data!;
    return ElevatedButton(
      child: const Text("Next"),
      onPressed: enabled ? context.wizardController.next : null,
    );
  },
);

Example of steps overview:

return ListView.builder(
  itemCount: context.wizardController.stepControllers.length,
  itemBuilder: (context, index) {
    final step = context.wizardController.stepControllers[index].step;
    return StreamBuilder<bool>(
      stream: context.wizardController.getIsAnimateToEnabledStream(index),
      initialData: context.wizardController.getIsAnimateToEnabled(index),
      builder: (context, snapshot) {
        final enabled = snapshot.data!;
        String title;
        switch (step.runtimeType) {
          case StepOneProvider:
            title = "Step one";
            break;
          case StepTwoProvider:
            title = "Step two";
            break;
          case StepThreeProvider:
            title = "Step Three";
            break;
          default:
            title = "Unknown step description";
            break;
        }
        return StreamBuilder<int>(
          stream: context.wizardController.indexStream,
          initialData: context.wizardController.index,
          builder: (context, snapshot) {
            final selectedIndex = snapshot.data!;
            return ListTile(
              title: Text(title),
              onTap: enabled
                  ? () => context.wizardController.animateTo(index: index)
                  : null,
              enabled: enabled,
              selected: index == selectedIndex,
            );
          },
        );
      },
    );
  },
);

How to act on wizard events

In some cases it can be helpful to see the events that are being triggered on the wizard. To get insights in these events you could add the WizardEventListener to your widget tree.

IMPORTANT NOTE: The WizardEventListener depends on the WizardController so it needs to added underneath the DefaultWizardController widget.

The arguments:

child
The child widget

listener
A callback that listens to the WizardEvent events from the WizardController.
The events are:

  • WizardEnableGoBackEvent: Triggered when enableGoBack is called.
  • WizardEnableGoNextEvent: Triggered when enableGoNext is called.
  • WizardDisableGoBackEvent: Triggered when disableGoBack is called.
  • WizardDisableGoNextEvent: Triggered when disableGoNext is called.
  • WizardGoNextEvent: Triggered when goNext is called.
  • WizardGoBackEvent: Triggered when goBack is called.
  • WizardGoToEvent: Triggered when goTo is called.
  • WizardForcedGoBackToEvent: Triggered when disableGoNext is called with an index lower as the current index.

Example:

WizardEventListener(
  listener: (context, event) {
    if (event is WizardForcedGoBackToEvent) {
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
        content: Text(
          'Step ${event.toIndex + 2} got disabled so the wizard is moving back to step ${event.toIndex + 1}.',
        ),
        dismissDirection: DismissDirection.horizontal,
      ));
    }
  },
),

For a full detailed example see the the example project.

You might also like...

A Flutter add-to-app demo you can try with your own apps

A Flutter add-to-app demo you can try with your own apps

Put Flutter to Work 🏠 Hello! This project is a demo intended to help people test drive Flutter by integrating it into their existing applications. In

Jan 8, 2023

A Flutter library aims to help you create animated, simple, and stylish Material Dialogs in your app.

A Flutter library aims to help you create animated,  simple, and stylish Material Dialogs in your app.

Flutter Material Dialogs Flutter Material Dialogs 📱 A Flutter library aims to help you create 💪🏻animated, 😃 simple, 😎 stylish Material Dialogs in

Dec 25, 2022

Flutter makes it easy and fast to build beautiful apps for mobile and beyond

Flutter makes it easy and fast to build beautiful apps for mobile and beyond

Flutter is Google's SDK for crafting beautiful, fast user experiences for mobile, web, and desktop from a single codebase. Flutter works with existing

Jan 8, 2023

🙌🏾 This package makes it easy to use the Mono connect widget in a flutter project

🙌🏾 This package makes it easy to use the Mono connect widget in a flutter project

Flutter Mono ** This is an unofficial SDK for flutter This package makes it easy to use the Mono connect widget in a flutter project. 📸 Screen Shots

Dec 20, 2022

flutter_thrio makes it easy and fast to add flutter to existing mobile applications, and provide a simple and consistent navigator APIs.

flutter_thrio makes it easy and fast to add flutter to existing mobile applications, and provide a simple and consistent navigator APIs.

中文文档 英文文档 问题集 原仓库不再维护,代码已经很老了 最近版本更新会很快,主要是增加新特性,涉及到混合栈的稳定性的问题应该不多,可放心升级,发现问题加 QQ 群号码:1014085473,我会尽快解决。 不打算好好看看源码的使用者可以放弃这个库了,因为很多设定是比较死的,而我本人不打算花时间写

Dec 29, 2022

The flutter_ibm_watson makes it easy to integrate IBM Watson

The flutter_ibm_watson makes it easy to integrate IBM Watson

Flutter Ibm Watson Installation Add this to your package's pubspec.yaml file: dependencies: flutter_ibm_watson: ^0.0.1 You can install packages fro

Nov 4, 2022

flutter_thrio makes it easy and fast to add flutter to existing mobile applications, and provide a simple and consistent navigator APIs.

flutter_thrio makes it easy and fast to add flutter to existing mobile applications, and provide a simple and consistent navigator APIs.

本仓库不再维护,可移步新仓库 https://github.com/flutter-thrio/flutter_thrio 中文文档 问题集 QQ 群号码:1014085473 The Navigator for iOS, Android, Flutter. Version 0.2.2 requir

Dec 5, 2022

A Flutter tool that makes golden testing easy.

A Flutter tool that makes golden testing easy.

🧙🏼 Alchemist Developed with 💙 by Very Good Ventures 🦄 and Betterment ☀️ . A Flutter tool that makes golden testing easy. Alchemist is a Flutter pa

Dec 12, 2022

Toor makes service locators compile-time safe and easy to manage

🌱 What is Toor Toor makes service locators compile-time safe and easy to manage. 🚀 Getting Started Define your dependencies somewhere in the project

Jul 25, 2022
Comments
  • Dispose page controller

    Dispose page controller

    Thanks for this great package

    I need to implement copy in WizardControllerImpl In didUpdateWidget method.

    Why?? Disposing page controller lead to an error when using expandable_pageview, calling after being disposed.

     ​  ​@override 
     ​  ​void​ ​didUpdateWidget​( 
     ​    ​covariant​ ​DefaultWizardController​ oldWidget, 
     ​  ) { 
     ​    ​// TODO: improve to copy with 
     ​    controller.​dispose​(); 
     ​    ​_createController​(); 
     ​    ​super​.​didUpdateWidget​(oldWidget); 
     ​  }
    

    I noticed there is a Todo in didUpdateWidget So i opened this issue for discussing before pull request 😅

    opened by mr7ssam 2
Owner
Baseflow
We provide software, skills and knowledge and with this we want to make a contribution to the world. We love to make innovation happen.
Baseflow
New trick on how to create your own custom icons in flutter with bottom bar navigation

Customized Bottom Navigation Bar in Flutter | Tech With Sam Customized Bottom Navigation Bar in Flutter - Watch on youtube ✌   App Preview App Screens

Samuel Adekunle 10 Oct 26, 2022
Flashcard App where you can learn different topics and create your own flashcards in Google Drive.

flashcard_project Flashcard app connected with Google Spreadsheet Getting Started This is a Flutter Project that works on iOS, Android, Web and MacOS.

Max Weber 11 Oct 24, 2022
In this project, we will design a travel app UI with a parallax effect for a unique scroll experience. You will learn how to create your own parallax effect without using external libraries.

Travel App UI In this part, we will design a travel app UI with a parallax effect for a unique scroll experience. You will learn how to create your ow

DebugErrorX 5 Dec 5, 2022
An extended version of Flutter Colors with more swatches and more flexibility to generate your own custom swatch.

Colours An extended version of Flutter Colors with more swatches and more flexibility to generate your own custom swatch. Getting Started In your flut

Salman S 4 Nov 23, 2021
Use the template to create your own repository, complete the project and verify

Proyecto Nivelación MisionTic Usar el template para crear un repositorio propio,

nockturb 0 Dec 20, 2021
This library provides a customizable Flutter widget that makes it easy to display text in the middle of a Divider.

1. About 1.1. Introduction 1.1.1. Install Library 1.1.2. Import It 1.1.3. Use TextDivider 1.2. Details 1.2.1. Customization Options 1.2.2. Horizontal

Kato Shinya 2 Feb 9, 2022
Flutter package that provides you custom clippers to help you achieve various custom shapes.

flutter_custom_clippers Flutter package that provides you custom clippers to help you achieve various custom shapes. Usage To use this plugin, add flu

Damodar Lohani 291 Dec 23, 2022
Library to create custom Toggle / Tab on your apps

Flutter Tab Toggle A Beautiful and Simple Tab/Toggle switch widget. It can be fully customized with desired icons, width, colors, text, corner radius

Mudassir 11 Sep 6, 2022
Dart package to which makes data communication easy among different modules of your application.

LiveStream - Dart LiveStream is a data holder class which can observe change of data in real-time and emit values too. Here's emitter subscriber patte

Shreyas Patil 75 Sep 28, 2022