multi_step_flow 0.3.0+2 copy "multi_step_flow: ^0.3.0+2" to clipboard
multi_step_flow: ^0.3.0+2 copied to clipboard

A platform-agnostic solution for managing multi-step flows with support for different flow types, validation, and customization.

Multi-Step Flow #

A Flutter package for creating type-safe multi-step flows and wizards with modern architecture.

Benefits & Use Cases #

This package provides a powerful solution for implementing various types of multi-step flows in your Flutter applications:

  • Multi-Step Registration Forms: Create complex registration processes that break lengthy forms into manageable steps with built-in validation
  • User Onboarding Experiences: Design engaging onboarding tutorials with auto-advancing slides and progress tracking
  • Interactive Stories and Narratives: Build interactive storytelling apps with branching paths and user choices
  • E-commerce Checkout Flows: Implement streamlined checkout processes with predictable state management
  • Questionnaires and Surveys: Create dynamic questionnaires with skip logic and conditional steps
  • Tutorial Wizards: Guide users through complex features with step-by-step instructions
  • Multi-Page Document Flows: Handle document submission workflows with file uploads and validation

Features #

  • 🎯 Type-safe - Generic implementation with strong typing for compile-time safety
  • 🔄 BLoC-based state management - Clean architecture with predictable state transitions
  • 💾 Persistent state - Optional state persistence with hydrated_bloc to survive app restarts
  • 🔌 Extensible - Create custom step types and extensions for your specific needs
  • 🧩 Modular - Specialized components for different use cases that work together seamlessly
  • 🎨 Customizable - Full control over UI and behavior with flexible theming options

Installation #

dependencies:
  multi_step_flow: ^0.3.0

Basic Usage #

1. Define your steps #

// Define your step data type
class RegistrationData {
  final String? name;
  final String? email;
  final bool acceptTerms;
  
  RegistrationData({this.name, this.email, this.acceptTerms = false});
}

// Create step definitions
final steps = [
  FlowStep<RegistrationData>(
    id: 'nameStep',
    title: 'Your Name',
    description: 'Please enter your name',
    data: RegistrationData(),
  ),
  FlowStep<RegistrationData>(
    id: 'emailStep',
    title: 'Email Address',
    description: 'Enter your email address',
    data: RegistrationData(),
  ),
  FlowStep<RegistrationData>(
    id: 'termsStep',
    title: 'Terms & Conditions',
    description: 'Please accept our terms and conditions',
    data: RegistrationData(),
  )
];

2. Create a FlowBloc #

// Create a bloc
final bloc = FlowBloc<RegistrationData>(steps);

// Or use a hydrated bloc for persistence
final bloc = HydratedFlowBloc<RegistrationData>(
  steps,
  persistState: true,
);

3. Use the FlowBloc in your UI #

class RegistrationFlow extends StatelessWidget {
  final FlowBloc<RegistrationData> bloc;

  const RegistrationFlow({Key? key, required this.bloc}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FlowBlocProvider<RegistrationData>(
      bloc: bloc,
      child: BlocBuilder<FlowBloc<RegistrationData>, FlowState<RegistrationData>>(
        bloc: bloc,
        builder: (context, state) {
          // Build your UI based on the current step
          return Scaffold(
            appBar: AppBar(title: Text(state.currentStep.title ?? 'Registration')),
            body: _buildStepContent(context, state.currentStep),
            bottomNavigationBar: FlowNavigationBar<RegistrationData>(bloc: bloc),
          );
        },
      ),
    );
  }
  
  Widget _buildStepContent(BuildContext context, FlowStep<RegistrationData> step) {
    // Build the content for each step based on the step ID
    switch (step.id) {
      case 'nameStep':
        return NameStepContent(step: step, bloc: bloc);
      case 'emailStep':
        return EmailStepContent(step: step, bloc: bloc);
      case 'termsStep':
        return TermsStepContent(step: step, bloc: bloc);
      default:
        return const SizedBox();
    }
  }
}

Specialized Components #

Form Flow #

The package provides specialized components for form-based flows:

// Create a form step
final formStep = FlowStep<FormStepData>(
  id: 'contactForm',
  title: 'Contact Information',
  data: FormStepData(),
);

// Use the FormStepBuilder widget
FormStepBuilder<UserData>(
  bloc: bloc,
  step: step,
  formDataExtractor: (data) => data?.contactForm ?? FormStepData(),
  formDataUpdater: (data, formData) {
    return UserData(contactForm: formData);
  },
  builder: (context, formData, onChanged, formKey) {
    return Column(
      children: [
        FlowFormField(
          fieldName: 'name',
          formData: formData,
          onChanged: onChanged,
          decoration: InputDecoration(labelText: 'Full Name'),
          validator: (value) => value.isEmpty ? 'Name is required' : null,
        ),
        FlowFormField(
          fieldName: 'email',
          formData: formData,
          onChanged: onChanged,
          decoration: InputDecoration(labelText: 'Email'),
          validator: (value) => !value.contains('@') ? 'Invalid email' : null,
        ),
      ],
    );
  },
);

Information Flow #

For content-focused flows with auto-advance and read tracking:

// Create an information step
final infoStep = FlowStep<InformationStepData>(
  id: 'introductionStep',
  title: 'Welcome',
  data: InformationStepData(autoAdvance: true, autoAdvanceAfterSeconds: 10),
);

// Use the InformationStepBuilder widget
InformationStepBuilder<TutorialData>(
  bloc: bloc,
  step: step,
  infoDataExtractor: (data) => data?.introduction ?? InformationStepData(),
  infoDataUpdater: (data, infoData) {
    return TutorialData(introduction: infoData);
  },
  contentBuilder: (context, infoData, onUpdate) {
    return InformationStepLayout(
      title: 'Welcome to the App',
      description: 'Learn how to use our amazing features',
      child: Column(
        children: [
          Image.asset('assets/welcome.png'),
          Text('This tutorial will guide you through the main features.'),
          // Auto-advances after the configured time
        ],
      ),
    );
  },
);

Custom Step Types #

You can create custom step types using the extension system:

// Define a custom step extension
class VideoStepExtension extends StepExtension<LessonData> {
  @override
  String get id => 'video_extension';
  
  @override
  void onStepEnter(FlowStep<LessonData> step) {
    // Start video playback
    print('Starting video playback for step: ${step.id}');
  }
  
  @override
  void onStepExit(FlowStep<LessonData> step) {
    // Pause video playback
    print('Pausing video playback for step: ${step.id}');
  }
}

// Register the extension
final registry = StepExtensionRegistry<LessonData>();
registry.registerExtension(VideoStepExtension());

// Create a step with the extension
final videoStep = FlowStep<LessonData>(
  id: 'lesson1',
  title: 'Introduction to Flutter',
  data: LessonData(videoUrl: 'https://example.com/video.mp4'),
).withExtension('video_extension');
1
likes
140
points
5
downloads

Publisher

unverified uploader

Weekly Downloads

A platform-agnostic solution for managing multi-step flows with support for different flow types, validation, and customization.

Homepage

Documentation

API reference

License

MIT (license)

Dependencies

bloc, formz, freezed_annotation, hydrated_bloc, json_annotation, meta

More

Packages that depend on multi_step_flow