Coder Social home page Coder Social logo

survey-flow's Introduction

Survey flow for Flutter

License: MIT

Survey Flow is a Flutter package that provides a framework for creating surveys or quizzes within your app. It allows you to easily define a survey's structure and customize its appearance, and it includes a range of pre-built widgets to help you display questions and collect user responses.

Inspired by survey_kit.

Example

Custom design + bottom sheet

Installation

To use Survey Flow in your Flutter app, add it to your pubspec.yaml file:

dependencies:
  survey_flow: ^latest_version

Then, run flutter pub get to download the package.

Usage

To use Survey Flow, import the package in your Dart code:

import 'package:survey_flow/survey_flow.dart';

Creating a Survey

To create a survey, use the one of the SurveyStep classes or create your own:

final List<SurveyStep> steps = [
   InformationStep(
      title: 'Information title',
      description: 'Bla bla bla description for this step',
      image: StepImage.svg(
         'https://www.svgrepo.com/show/24762/round-done-button.svg',
         source: StepImageSource.network,
         width: 0.3,
      ),
      primaryButton: StepButton(
         action: 'action:notificationsPermission',
         text: 'Next',
      ),
   ),
];

Predefined step types and components documentation you can find below. More examples you can find in this Example repository.

Displaying a Survey

To display a survey, use the SurveyFlow widget:

class MySurveyScreen extends StatefulWidget {
   @override
   _MySurveyScreenState createState() => _MySurveyScreenState();
}

class _MySurveyScreenState extends State<MySurveyScreen> {
   final List<SurveyStep> survey = // create your survey here

   @override
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text('My Survey')),
         body: SurveyFlow(
            initialSteps: survey,
            onSubmit: (results) {
               // handle the survey results here
            },
            onFinish: () {
               // handle the survey finished here
            },
         ),
      );
   }
}

In this example, we've created a screen that displays a SurveyFlow widget with our SurveySteps, an onSubmit callback that will be called when user submits the last step or some step has a submit action (you can find how actions works in the documentation below) and onFinish callback that will be called when the user completes the survey.

Displaying a modal Survey

To display a modal survey, use the showModalSurveyFlow function:

final List<SurveyStep> survey = // create your survey here

showModalSurveyFlow(
   context: context,
   initialSteps: survey, 
   onSubmit: (results) {
      // handle the survey results here
   },
   onFinish: () {
      // handle the survey finished here
   },
);

Predefined step types:

InformationStep

InformationStep(
   title: 'Information title',
   description: 'Bla bla bla description for this step',
   image: StepImage.svg(
      'https://www.svgrepo.com/show/24762/round-done-button.svg',
      source: StepImageSource.network,
      width: 0.3,
   ),
   primaryButton: StepButton.next(),
);
Parameter Default Description
id String? Null Step id. Can be used for custom steps navigation.
title String required Title that will be displayed on the step.
description String? Null Description that will be displayed on the step.
image StepImage? Null Image that will be displayed on the step. Read more about StepImage below.
backgroundImage StepImage? Null Image that will be displayed on the background. Read more about StepImage below.
primaryButton StepButton StepButton.next() Primary button with step action. Read more about StepButton below.
secondaryButton StepButton? Null Secondary button with step action. Read more about StepButton below.
buttonsAlignment ButtonsAlignment ButtonsAlignment.vertical Defines how buttons should be aligned - vertically or horizontally.

SingleSelectStep

SingleSelectStep(
  title: 'Select one option to move forward',
  description: 'Bla bla bla description for this step',
  options: [
     SelectOption(
        text: 'Custom navigation option',
        description:
        'If you select this option number_request_step would be opened next',
        value: 'option_1',
        navigationConditions: [
           ButtonNavigationCondition(
            nextStepId: 'number_request_step',
           ),
        ],
    ),
     SelectOption(
        text: 'Notification permission',
        value: 'option_2',
        action: 'action:notificationsPermission',
     ),
     SelectOption(
        text: 'Option 3',
        description:
        'Option long description that should take at least two rows',
        value: 'option_3',
     ),
     SelectOption(
        text: 'Option 4',
        value: 'option_4',
     ),
  ],
);
Parameter Default Description
id String? Null Step id. Can be used for custom steps navigation.
title String required Title that will be displayed on the step.
options List required Options list. Read more about SelectOption below.
description String? Null Description that will be displayed on the step.
backgroundImage StepImage? Null Image that will be displayed on the background. Read more about StepImage below.

MultiSelectStep

MultiSelectStep(
   title: 'Select from 1 to 2 options to move forward',
   description: 'Bla bla bla description for this step',
   minimumAmountOfOptionsSelected: 1,
   maximumAmountOfOptionsSelected: 2,
   options: [
      SelectOption(
         text: 'Option 1',
         description: 'Option description',
         value: 'option_1',
      ),
      SelectOption(
         text: 'Option 2',
         value: 'option_2',
      ),
      SelectOption(
         text: 'Option 3',
         description:
         'Option long description that should take at least two rows',
         value: 'option_3',
      ),
      SelectOption(
         text: 'Option 4',
         value: 'option_4',
      ),
   ],
);
Parameter Default Description
id String? Null Step id. Can be used for custom steps navigation.
title String required Title that will be displayed on the step.
options List required Options list. Read more about SelectOption below.
description String? Null Description that will be displayed on the step.
backgroundImage StepImage? Null Image that will be displayed on the background. Read more about StepImage below.
primaryButton StepButton StepButton.next() Primary button with step action. Read more about StepButton below.
minimumAmountOfOptionsSelected int? Null Number of minimum options selected to enable the button.
maximumAmountOfOptionsSelected int? Null Number of maximum options selected.

NumberRequestStep

NumberRequestStep(
   id: 'number_request_step',
   title: 'Number request title',
   description: 'Bla bla bla description for this step',
   hint: 'Your age',
   primaryButton: StepButton.next(
      predicate: ButtonPredicate.moreThan(18),
   ),
   secondaryButton: StepButton.skip(),
);
Parameter Default Description
id String? Null Step id. Can be used for custom steps navigation.
title String required Title that will be displayed on the step.
description String? Null Description that will be displayed on the step.
primaryButton StepButton StepButton.next() Primary button with step action. Read more about StepButton below.
secondaryButton StepButton? Null Secondary button with step action. Read more about StepButton below.
buttonsAlignment ButtonsAlignment ButtonsAlignment.vertical Defines how buttons should be aligned - vertically or horizontally.
backgroundImage StepImage? Null Image that will be displayed on the background. Read more about StepImage below.
hint String? Null Text field hint.
type RequestType RequestType.numberInt Requested data type. Read more RequestType values below.

TextRequestStep

TextRequestStep(
   title: 'Text request title',
   description: 'Bla bla bla description for this step',
   hint: 'Your name',
   primaryButton: StepButton.next(),
   secondaryButton: StepButton.skip(),
);
Parameter Default Description
id String? Null Step id. Can be used for custom steps navigation.
title String required Title that will be displayed on the step.
description String? Null Description that will be displayed on the step.
primaryButton StepButton StepButton.next() Primary button with step action. Read more about StepButton below.
secondaryButton StepButton? Null Secondary button with step action. Read more about StepButton below.
buttonsAlignment ButtonsAlignment ButtonsAlignment.vertical Defines how buttons should be aligned - vertically or horizontally.
backgroundImage StepImage? Null Image that will be displayed on the background. Read more about StepImage below.
hint String? Null Text field hint.
type RequestType RequestType.text Requested data type. Read more RequestType values below.

DateRequestStep

DateRequestStep(
   title: 'Date request title',
   description: 'Bla bla bla description for this step',
   hint: 'Your date of birth',
   primaryButton: StepButton.next(),
);
Parameter Default Description
id String? Null Step id. Can be used for custom steps navigation.
title String required Title that will be displayed on the step.
description String? Null Description that will be displayed on the step.
primaryButton StepButton StepButton.next() Primary button with step action. Read more about StepButton below.
secondaryButton StepButton? Null Secondary button with step action. Read more about StepButton below.
buttonsAlignment ButtonsAlignment ButtonsAlignment.vertical Defines how buttons should be aligned - vertically or horizontally.
backgroundImage StepImage? Null Image that will be displayed on the background. Read more about StepImage below.
hint String? Null Text field hint.
dateFormat String? Null Default 'd MMMM yyyy' for date, 'hh:mm' for time and 'd MMM yyyy hh:mm' for datetime.
type RequestType RequestType.date Requested data type. Read more RequestType values below.

Custom Step

class CustomSurveyStep implements SurveyStep {
   const CustomSurveyStep({
      required this.id,
      required this.title,
      required this.description,
      required this.primaryButton,
      required this.image,
      this.backgroundImage,
   });

   @override
   final String id;

   @override
   final String title;

   @override
   final String description;

   @override
   final StepImage? backgroundImage;

   final StepImage image;
   final StepButton primaryButton;
}

You can create your own step by inheriting from SurveyStep and add you custom widget handler for it. Only imagination is your limit ☺️

RequestType

enum RequestType {
  text,
  textMultiline,
  name,
  email,
  numberInt,
  numberDouble,
  date,
  time,
  dateAndTime,
}

StepButton

Parameter Default Description
id String? Null Button id.
text String required Button title.
action String required Button action. What should be done when user clicks on the button. You can find few default actions in StepActions.
style StepButtonStyle StepButtonStyle.elevated Button style elevated, outlined or text. Widget styles could be defined using SurveyFlowThemeData in SurveyFlow.
type StepButtonType StepButtonType.primary Button type primary or secondary. Widget styles could be defined using SurveyFlowThemeData in SurveyFlow.
navigationConditions List? Null Navigation conditions that would define where should we navigate on button click. Read more about ButtonNavigationCondition below.
predicate ButtonPredicate? Null Predicate that will define when button should be enabled or disabled. Read more about ButtonPredicate below.
enum StepButtonType { primary, secondary }
enum StepButtonStyle { elevated, outlined, text }

ButtonPredicate

Is used to define when button should be enabled or disabled. Works by default on Request steps.

For example, if you want button to be enabled on the NumberRequestScreen only if provided number is more than 18, you should use predicate like:

ButtonPredicate.moreThan(18);
Parameter Default Description
type ButtonPredicateType required Predicate type. You can find enum values below.
value dynamic Null Value which be used to compare depending on the type.
enum ButtonPredicateType {
   notEmpty,
   lengthMoreThan,
   matches,
   equals,
   lessThan,
   moreThan,
}

ButtonNavigationCondition

Is used to define where used should be navigated on button click. Can have many conditions with different step ids.

For example, if you want to navigate to step with id 'my_step_id' only if provided number is more than 18, you should use navigation condition like:

ButtonNavigationCondition.moreThan('my_step_id', 18);
Parameter Default Description
type ButtonNavigationConditionType ButtonNavigationConditionType.nextStep Navigation condition type. You can find enum values below.
nextStepId String required Id of the step that should be opened.
value dynamic Null Value which be used to compare depending on the type.
enum ButtonNavigationConditionType {
   nextStep,
   notEmpty,
   lengthMoreThan,
   matches,
   equals,
   lessThan,
   moreThan,
}

SelectOption

Select option is child of StepButton, so action and navigationConditions works just the same way.

Parameter Default Description
id String? Null Button id.
text String required Button title.
value dynamic required Value of the option that will be stored as step result.
description String? Null Description that will be displayed on the step.
action String StepActions.next Button action. What should be done when user clicks on the button. You can find few default actions in StepActions.
navigationConditions List? Null Navigation conditions that would define where should we navigate on button click. Read more about ButtonNavigationCondition below.

StepImage

Image that can be displayed on the step or step background. Supports different sources (local or network) and image types (default images as png, jpeg etc., svg and lottie animations).

StepImage.svg(
   'assets/badge.svg',
   source: StepImageSource.local,
   position: ImagePosition(right: 0.0, top: 0.0),
)
Parameter Default Description
path String required Image path like url or local path.
source StepImageSource required Image source local or network.
type StepImageType required Image type image, svg or lottie.
width double? Null Image width. If less than 1 will use that value as fraction of screen width.
height double? Null Image height.
position ImagePosition? Null Position of image on the screen. Usually used to position background image.
opacity double 1.0 Image opacity.
enum StepImageSource {
  local,
  network,
}
enum StepImageType {
   image,
   svg,
   lottie,
}

SurveyFlowThemeData

You set your own style for the survey using SurveyFlowThemeData for SurveyFlow widget.

Json Serialization

You can use json to provide steps for your survey. Every model supports json serialization.

That means that you can use Firebase Remote Config, local jsons, your own backend or any other service to dynamically change your survey without updating the application.

// if you have your own step, add converter to stepsConverters
stepsConverters.addAll({
  'customStep': (v) => CustomSurveyStep.fromJson(v),
});

final String initialStepsData = await rootBundle.loadString("assets/initial_steps.json");
final List<dynamic> initialStepsDecoded = jsonDecode(initialStepsData);

// use SurveyStepConverter to convert models from json
// You can use this converter directly inside model with annotation.
final List<SurveyStep> initialSteps = initialStepsDecoded
        .map((e) =>
            const SurveyStepConverter().fromJson(e as Map<String, dynamic>?))
        .toList();
[
   {
      "id": "information_svg_step",
      "stepType": "information",
      "title": "Information title",
      "description": "Bla bla bla description for this step",
      "image": {
         "path": "https://www.svgrepo.com/show/24762/round-done-button.svg",
         "source": "network",
         "type": "svg",
         "width": 0.3
      },
      "primaryButton": {
         "action": "action:showBottomSheetSurvey",
         "text": "Next"
      }
   }
]

Features:

  1. Submit steps result
  2. Update steps list
  3. Loading state
  4. Widgets
    1. Information
    2. Single select
    3. Multi select
    4. Number request
    5. Date request
    6. Text request
  5. Custom actions
  6. Custom steps
  7. Custom styling (use inherited widget theme)
  8. JsonSerializable
  9. Extend styling for components, check default style
  10. Primary button disabled predicate for RequestScreens
  11. Custom next step navigation
  12. Button navigation conditions
  13. Buttons alignment (horizontal / vertical)
  14. BottomSheet view

Contributing

Feel free to contact me ([email protected]) or create Pull Requests/Issues for this repository :)

License

Survey Flow is licensed under the MIT License.

survey-flow's People

Contributors

therealgetman avatar

Stargazers

Geek__Lee avatar  avatar  avatar Kellvem Barbosa avatar

Watchers

 avatar

survey-flow's Issues

timed step

Great library !!

I would like to create a custom SurveyStep which plays a video (or other operation) for a fixed time.

Is there a way to create a StepButton or ButtonPredicate that would activate the next step after the time expires?

theme

I have the following theme defined.

I would like to replicate something similar to your demo when the background color of a selected option (MultiSelectStep) changes color. I have not been able to achieve this. Where is the background color of a selected option controlled. I have tried using colors: optionSelected: but this is not working for this element.

SurveyFlowThemeData get customTheme {
  return SurveyFlowThemeData(
      dimens: const SFDimens(
        buttonBorderRadius: 8.0,
      ),
      colors: const SFColors(
        background: Colors.white, //Color(0xff1e1f1f),
        button: Colors.black,
        option: Colors.blue,
        optionSelected: Colors.green, // Color(0xff009473),
        progressIndicatorColor: Colors.white,
      ),
      textStyles: SFTextStyles(
        titleTextAlign: TextAlign.center,
        title: const TextStyle(
          fontWeight: FontWeight.w700,
          fontSize: 24.0,
          height: 30.0 / 24.0,
          color: Colors.black,
        ),
        descriptionTextAlign: TextAlign.start,
        description: TextStyle(
          fontWeight: FontWeight.w400,
          fontSize: 16.0,
          height: 20.0 / 16.0,
          color: Colors.black.withOpacity(0.7),
        ),
        secondaryButton: const TextStyle(
          fontWeight: FontWeight.w400,
          fontSize: 16.0,
          height: 1.0,
          color: Colors.black,
        ),
        option: const TextStyle(
          fontWeight: FontWeight.w400,
          fontSize: 16.0,
          height: 1.0,
          color: Colors.grey,
        ),
        optionSelected: const TextStyle(
          fontWeight: FontWeight.w700,
          fontSize: 16.0,
          height: 1.0,
          color: Colors.black,
        ),
        optionDescription: TextStyle(
          fontWeight: FontWeight.w400,
          fontSize: 12.0,
          height: 1.0,
          color: Colors.black.withOpacity(0.7),
        ),
        textField: const TextStyle(
          fontWeight: FontWeight.w400,
          fontSize: 16.0,
          height: 20.0 / 16.0,
          color: Colors.black,
        ),
      ),
      inputStyles: SFInputStyles(
        textAlign: TextAlign.center,
        decorationBuilder: (BuildContext context, SurveyRequestStep step) {
          return InputDecoration(
            labelText: step.hint,
            floatingLabelAlignment: FloatingLabelAlignment.center,
            labelStyle: SurveyFlowTheme.of(context)
                .theme
                .textStyles
                .description
                .copyWith(
                  color: SurveyFlowTheme.of(context)
                      .theme
                      .textStyles
                      .description
                      .color
                      ?.withOpacity(0.5),
                ),
            contentPadding: EdgeInsets.symmetric(
              vertical: SurveyFlowTheme.of(context)
                  .theme
                  .dimens
                  .textFieldVerticalPadding,
              horizontal: SurveyFlowTheme.of(context)
                  .theme
                  .dimens
                  .textFieldHorizontalPadding,
            ),
            border: const UnderlineInputBorder(),
            enabledBorder: const UnderlineInputBorder(
              borderSide: BorderSide(color: Colors.white70),
            ),
            focusedBorder: const UnderlineInputBorder(
              borderSide: BorderSide(color: Colors.white),
            ),
          );
        },
      ),
      buttonStyles: SFButtonStyles(
          selectOption: ElevatedButton.styleFrom(
        side: const BorderSide(color: Colors.grey, width: 2.0),
      )));
}

Step results

Is there a way for a step to get the results of a previous step?

MultiSelect overflow

have run into this issue a couple of times. If the list of options for a MultiSelect is too long, a screen overflow results.

Screenshot_20230927-133040

I forked the survey-flow repository and have been looking at the MultiSelectStepWidget. No solution yet. I thought the ListView.separated in _options() would handle this.

themes

I have not been able to duplicate the thin grey border around the options in this picture.

Which theme parameters control this?
single-select

Custom Step Results

I'm really liking the SurveyFlow package.

I have written a custom SurveyStep and with the associated Widget.

The CustomStepWidget is a custom Material Slider. When the step completes how does the custom Widget add to the Survey results?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.