Feature discavery based on material design

Overview

feature_discovery

This Flutter package implements Feature Discovery following the Material Design guidelines.

With Feature Discovery, you can add context to any UI element, i.e. any Widget in your Flutter app.
Here is a small demo of the example app:

Installing

To use this package, follow the installing guide.

Usage

FeatureDiscovery

To be able to work with any of the global functions provided by the feature_discovery package, you will have to wrap your widget tree in a FeatureDiscovery widget.
There are many places where you can add FeatureDiscovery in your build tree, but the easiest to assure that it sits on top is to wrap your MaterialApp with it:

const FeatureDiscovery(
  child: MaterialApp(
   ...
  )
)

DescribedFeatureOverlay

For every UI element ( Widget ) that you want to describe using Feature Discovery, you will need to add a DescribedFeatureOverlay .
This widget takes all the parameters for the overlay that will be displayed during Feature Discovery and takes the Widget you want to display the overlay about as its child .

Feature ids

Every feature you describe should have a unique identifier, which is a String passed to the featureId parameter. You will also need to provide these ids when starting the discovery. And library will use this ids to save flag internally to indicate which feature is showed to user and completed by user. And if this feature is completed by user library will not show it again.

DescribedFeatureOverlay(
  featureId: 'add_item_feature_id', // Unique id that identifies this overlay.
  tapTarget: const Icon(Icons.add), // The widget that will be displayed as the tap target.
  title: Text('Add item'),
  description: Text('Tap the plus icon to add an item to your list.'),
  backgroundColor: Theme.of(context).primaryColor,
  targetColor: Colors.white,
  textColor: Colors.white,
  child: IconButton( // Your widget that is actually part of the UI.
    icon: cons Icon(Icons.add),
    onPressed: addItem,
  ),
);
Additional parameters

contentLocation

This is ContentLocation.trivial by default, however, the package cannot always determine the correct placement for the overlay. In those cases, you can provide either of these two:

  • ContentLocation.below : Text is displayed below the target.

  • ContentLocation.above : Text is displayed above the target.

onComplete

   onComplete: () async {
    // Executed when the tap target is tapped. The overlay will not close before
    // this function returns and after that, the next step will be opened.
    print('Target tapped.');
    // You can prevent completion by returning false.
    return true;
  },

onDismiss

  onDismiss: () async {
    // Called when the user taps outside of the overlay, trying to dismiss it.
    print('Overlay dismissed.');
    // You can prevent dismissal by returning false.
    return true;
  },

onOpen

  onOpen: () async {
    // This callback is called before the overlay is displayed.
    print('The overlay is about to be displayed.');
    // If you return false, the overlay will not be opened and the next step
    // will be attempted to open.
    return true;
  },

enablePulsingAnimation

This is set to true by default, but you can disable the pulsing animation about the tap target by setting this to false .

allowShowingDuplicate

If multiple DescribedFeatureOverlay s have the same featureId , they will interfere with each other during discovery and if you want to display multiple overlays at the same time, you will have to set allowShowingDuplicate to true for all of them.

barrierDismissible

This is set to true by default, but you can disable "dissmiss overlay on touch outside" by setting this to false .

overflowMode

This is OverflowMode.ignore by default, which will simply render the content you pass to title and description , even if it overflows the background area, i.e. the circle of the overlay. Alternatively, you can specify any of the following if you desire different behavior:

  • OverflowMode.clipContent will clip any content that is outside of the inner area (the background's circle).

  • OverflowMode.extendBackground will expand the background circle if necessary.

  • OverflowMode.wrapBackground will expand the background circle if necessary, but also shrink it if the content is smaller than the default background size.

FeatureDiscovery.discoverFeatures

When you want to showcase your features, you can call FeatureDiscovery.discoverFeatures with the applicable feature ids. The features will be displayed as steps in order if the user does not dismiss them.
By tapping the tap target, the user will be sent on to the next step and by tapping outside of the overlay, the user will dismiss all queued steps.

FeatureDiscovery.discoverFeatures(
  context,
  const <String>{ // Feature ids for every feature that you want to showcase in order.
    'add_item_feature_id',
  },
);

If you want to display Feature Discovery for a page right after it has been opened, you can use SchedulerBinding.addPostFrameCallback in the initState method of your StatefulWidget :

@override
void initState() {
  // ...
  SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
    FeatureDiscovery.discoverFeatures(
      context,
      const <String>{ // Feature ids for every feature that you want to showcase in order.
        'add_item_feature_id',
      },
    ); 
  });
  super.initState();
}

FeatureDiscovery.clearPreferences

If you want to clear feature discovery flag use this command like that

FeatureDiscovery.clearPreferences(context, <String>{ 'add_item_feature_id', });

FeatureDiscovery.hasPreviouslyCompleted

If you want to findout feature discovery is Displayed or not ? use this command like that

FeatureDiscovery.hasPreviouslyCompleted (context,'desired_feature_id');

Other methods

You can view the API reference for FeatureDiscovery to find other useful methods for controlling the Feature Discovery process programmatically.

EnsureVisible

You can use the EnsureVisible widget to automatically scroll to widgets that are inside of scrollable viewports when they are described during Feature Discovery:

// You need to save an instance of a GlobalKey in order to call ensureVisible in onOpen.
GlobalKey<EnsureVisibleState> ensureVisibleGlobalKey = GlobalKey<EnsureVisibleState>();

// The widget in your build tree:
DescribedFeatureOverlay(
  featureId: 'list_item_feature_id',
  tapTarget: const Icon(Icons.cake),
  onOpen: () async {
    WidgetsBinding.instance.addPostFrameCallback((Duration duration) {
      ensureVisibleGlobalKey.currentState.ensureVisible();
      return true;
    });
  },
  title: Text('Cake'),
  description: Text('This is your reward for making it this far.'),
  child: EnsureVisible(
    key: ensureVisibleGlobalKey,
    child: const Icon(Icons.cake),
  ),
)

Notes

In DescribedFeatureOverlay , tapTarget , title , and description can take any widget, but it is recommended to use an Icon for the tap target and simple Text widgets for the title and description. The package takes care of styling these widgets and having these as Widget s allows you to pass Key s, Semantics , etc.

Contributing

If you want to start contributing to this package, it is recommended to refer to the contributing guide.

Gratitude

Thanks to mattcarroll for their Flutter challenge about Feature Discovery on Fluttery.

Comments
  • Migrate to null safety

    Migrate to null safety

    Description

    I propose this pull request to migrate this plugin to null safety as it is the way to go now that Flutter 2 has been released.

    Here are the changes I introduced:

    • Update SDK dependency version to 2.12.0+
    • Run dart migrate on existing code to remove unsafe nulls (for the package & example)
    • Changed FlatButton to TextButton and RaisedButton to ElevationButton
    • Add a new version (0.14.0) to the changelog

    Please check everything twice!

    Testing

    • [x] All existing Tests are passing
    • [x] Tested it manually on a Android emulator

    Demo:

    https://user-images.githubusercontent.com/24459435/110187638-50370f80-7e19-11eb-9b44-e916ab27236c.mov

    Related tickets

    Closes #67

    opened by nilsreichardt 16
  • Wonderful wonderful package but causing a lot of issues

    Wonderful wonderful package but causing a lot of issues

    Hello, i LOVE your package but i need your help because it's not ready for production.

    On dismiss the package is ready to display the overlay but never does, when i stack another page on top of the navigation stack it bugs, it's quite problematic.

    Please tell me how we can get rid of some of these bugs.

    opened by nicolasvahidzein 9
  • Can I disable completion just for a period of time ?

    Can I disable completion just for a period of time ?

    My feature discovery fires when the user spams a certain button When he spams that button the feature discovery appears but since he is spamming the button he presses on the feature tap target and the feature disappears. I want to disable the feature tap target for a small period of time to ensure the user have read the feature discovery

    Approach ##1: I set onComplete to return a boolean variable and after 1.5 seconds of the feature firing change the variable's value Problem: onComplete is called only once so if it was fired first time with return value of false, it won't be fired again no matter what.

    Approach ##2: Use AbsorbPointer to absorb all touch events of the Feature overlay Problem: I don't know why but AbsorbPointer doesn't work with the Feature overlay (my theory is because that the widget is overlayed on top of the MaterialApp and not my current context widget)(placing an AbsorbPointer on top my material app is not an option!)

    I am open to any suggestions

    Thanks in advance!

    opened by andrewwahid 7
  • clear settings without context

    clear settings without context

    Hello, why do we need the context to clear the settings?

    i really need to be able to run this without the context and it's quite impossible.

    any workaround?

                      FeatureDiscovery.clearPreferences(context, <String>{
    			'feature_page_accountview_001',
    			'feature_page_accountview_002',
    			'feature_page_accountview_003',
    			'feature_page_menuview_001',
    			'feature_page_menuview_002',
    			'feature_page_menuview_003',
    			'feature_page_menuview_004',
    			'feature_page_appbar_001',
    			'feature_page_appbar_002',
    			'feature_page_appbar_003',
    			'feature_page_searchwidget_001',
    			'feature_page_mainsearchbar_001',
    		});
    
    opened by nicolasvahidzein 7
  • _alreayCompletedSteps where was called on null

    _alreayCompletedSteps where was called on null

    Just tried upgrading from 0.10.0 to 0.12.0 and receiving this exception:

    [VERBOSE-2:ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The method 'where' was called on null.
    Receiver: null                                                          
    Tried calling: where(Closure: (String) => bool)                         
    #0      Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)    
    #1      Bloc._alreayCompletedSteps (package:feature_discovery/src/foundation/bloc.dart:159:10)
    <asynchronous suspension>                                               
    #2      Bloc.discoverFeatures (package:feature_discovery/src/foundation/bloc.dart:111:28)
    #3      FeatureDiscovery.discoverFeatures (package:feature_discovery/src/foundation/feature_discovery.dart:32:24)
    #4      _HomeScreenState.initState.<anonymous closure> (package:kozzeted/screens/home_screen.dart:65:24)
    #5      SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1113:15)
    #6      SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1060:9)
    #7      SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:968:5)
    #8      _rootRun (dart:async/zone.dart:1184:13)                         
    #9     <…>  
    

    It appears that _alreayCompletedSteps is trying to reference _steps before it has been initialized.

    Oh, and I presume _alreayCompletedSteps should be _alreadyCompletedSteps .

    opened by awhitford 6
  • phantom feature discovery

    phantom feature discovery

    First off let me tell you how much i love your package, it is so beautiful and fun to use.

    Ok, so: I discovered a weird bug when i have multiple features i want to show case. In this case 3. When the last one is done, it takes me back to the second one and its lingers on it for about 5 seconds before disappearing.

    Any ideas? I will post a video soon.

    opened by nicolasvahidzein 6
  • Allow dismissing to complete the current step

    Allow dismissing to complete the current step

    It seems that dismissing the step by tapping the barrier rather than the tap target does not complete the step.

    In my case we want the step to be completed regardless of where the user taps. Could this be added?

    opened by josh-burton 5
  • Allow colored area to dismiss on tap

    Allow colored area to dismiss on tap

    Hi! Love this plugin. Wanted to see if you'd be willing to add an option for dismissing the overlay by tapping on the color. On smaller screens especially, the colored region often takes up a majority of the screen and make it hard to tap out. Thanks!

    opened by michinchin 5
  • Version 0.7.0

    Version 0.7.0

    • Breaking changes:
      • Deprecated static methods of FeatureDiscovery class have been removed.
      • Deprecated paramaters in the constructor of EnsureVisible have been removed.
    • Non breaking:
      • activeFeatureId has been deprecated and replaced by getCurrentFeatureId, to emphasize that this is a getter.
      • dismiss has been deprecated and replaced by dismissAll to be clear on the fact that no next step will be shown.
      • Incorrect documentation of some static methods in FeatureDiscovery has been edited.
      • Error messages have been improved : the error thrown when the widget tree isn't wrapped in a FeatureDiscovery widget is clearer.
      • Fixes a bug when onDismiss returns a Future<false> (see https://github.com/axel-op/feature_discovery/pull/44).

    This PR will fix #14.

    opened by axel-op 4
  • Update support for provider ^4.0.0

    Update support for provider ^4.0.0

    Hi @ayalma

    flutter_bloc v3.0.0 is out and uses provider ^4.0.0 and apparently feature_discovery ^0.6.0 is not compatible with the new version.

    I've currently rolled back to flutter_bloc v2 as a workaround for now.

    Running "flutter pub get" in my_app...
    Because flutter_bloc >=3.0.0 depends on provider ^4.0.0 and feature_discovery 0.6.0 depends on provider ^3.1.0, flutter_bloc >=3.0.0 is incompatible with feature_discovery 0.6.0.
    And because no versions of feature_discovery match >0.6.0 <0.7.0, flutter_bloc >=3.0.0 is incompatible with feature_discovery ^0.6.0.
    So, because my_app depends on both feature_discovery ^0.6.0 and flutter_bloc ^3.0.0, version solving failed.
    pub get failed (1; So, because my_app depends on both feature_discovery ^0.6.0 and flutter_bloc ^3.0.0, version solving failed.)
    Exited (1)
    

    Thank you.

    opened by charkala 4
  • Overlay appears panned to a side

    Overlay appears panned to a side

    I have a DescribedFeatureOverlay on a center-docked FAB, and only on iOS the overlay appears panned to the left side:

    sorry, I don't know how to create a thumbnail in github

    (In the screenshot, the FAB's icon is pulsating, that's why it looks so big).

    As mentioned, it is only happening on iOS (on Android it looks fine).

    This is my code in case it helps:

    DescribedFeatureOverlay(
      featureId: featureId,
      targetColor: localTheme.accentColor,
      tapTarget: Icon(CupertinoIcons.person_add_solid),
      contentLocation: ContentLocation.above,
      title: const Text('More fun with Friends'),
      child: FloatingActionButton(
        child: const Icon(CupertinoIcons.person_add_solid),
        onPressed: ...
      )
      description: Column(
        children: [
          const Text('Add your Friends [...]'),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 16),
            child: RaisedButton(child: const Text('GOT IT!'), onPressed: () { FeatureDiscovery.completeCurrentStep(context); }),
          )
        ],
      )
    )
    

    Also as mentioned, FAB's floatingActionButtonLocation is FloatingActionButtonLocation.centerDocked.

    Why is that overlay panned? What can I do? Is that a bug?

    Using Flutter 1.22, macOS 10.15.7, Xcode 12.0.1, feature_discovery: ^0.12.0+2

    opened by jagomf 3
  • Modernize Android build scripts (#2)

    Modernize Android build scripts (#2)

    Needed to build the example app successfully with Flutter 3.

    Note: gradlew and gradlew.bat should be checked in- these are wrapper scripts that download the actual gradle binary (the "w" in "gradlew" stands for wrapper). This allows the build to work out of the box.

    opened by ndahlquist 0
  • Outstanding PR's?

    Outstanding PR's?

    @ayalma apologies for the ping, but there are some outstanding PR's. Could you please merge them? Many people will be happy to continue to contribute, but we do need merging to be done when possible. Thanks!

    opened by flukejones 0
  • Flutter 3 compatibility

    Flutter 3 compatibility

    Thanks again for the awesome package. please consider updating the package and fixing flutter 3 warnings.

    Warning: Operand of null-aware operation '!' has type 'WidgetsBinding' which excludes null

    opened by P-B1101 3
  • [feature-request]: Dimmed or Blurred Background outside of Overlay

    [feature-request]: Dimmed or Blurred Background outside of Overlay

    Would it be possible to set the Apps background to be dimmed or blurred?

    It would make sense to focus on the Overlay if you have a significant looking background in your app.

    opened by Ahmadre 0
Owner
Ali Mohammadi
Mobile Developer (Flutter,Android,IOS)
Ali Mohammadi
First Open Source Flutter based material design music player with audio plugin to play online music

Flutter Music App First Open Source Flutter based dribbblel Design Music Player. logo free design http://www.freeuid.com/category/free material icons

佩奇的弟弟乔治 380 Jan 4, 2023
First Open Source Flutter based Beautiful Material Design Text fields.

Pretty text field First Open Source Flutter based Beautiful Material Design Text fields.(More designed text fields coming soon.) Features [*] Compatib

Darshh 1 Aug 29, 2022
A Very Flexible Widget that can Implement Material Sheets on all Directions, both modal and persistent, and consequently a Material Navigation Drawer

Flutter_MaterialSheetAndNavigationDrawer If this project helped you reduce developement time or you just want to help me continue making useful tools

Bryan Cancel 30 Dec 4, 2021
Material io ext - A collection of extensions for creating widgets following material.io guidelines

material_io_ext It is a collection of extensions for creating widgets following

BetterX.io 3 Jan 28, 2022
News App developed with Flutter featuring beautiful UI, category-based news, story for faster news reading, inbuilt article viewer, share feature, and more.

Ariel News App developed with Flutter featuring beautiful UI, category-based news, story for faster news reading, inbuilt article viewer, share featur

Hash Studios 30 Nov 9, 2022
A Flutter based to do list app (yes, another to do list... but... this time based on a beautiful design)

✔️ Flutter to do App "To Do List" A Flutter app based on the design of the To Do App, created by Rudi Hartono, see more on: Uplabs. Getting Started ??

Johan 561 Dec 31, 2022
Target the specific design of Material for Android and Cupertino for iOS widgets through a common set of Platform aware widgets

Flutter Platform Widgets This project is an attempt to see if it is possible to create widgets that are platform aware. Currently in order to render t

null 1.3k Jan 4, 2023
Target the specific design of Material for Android and Cupertino for iOS widgets through a common set of Platform aware widgets

Flutter Platform Widgets This project is an attempt to see if it is possible to create widgets that are platform aware. Currently in order to render t

null 1.3k Jan 4, 2023
The Material Design Icons Icon pack available as set of Flutter Icons.

material_design_icons_flutter The Material Design Icons Icon pack available as set of Flutter Icons. Based on Material Design Icons 6.5.95. See a web

ziofat 147 Oct 26, 2022
A simplistic mobile application with Material design that helps you communicate using different phonetic alphabets

Speak NATO A simplistic mobile application with Material design that helps you communicate using different phonetic alphabets. Installation Screenshot

Oleksandr Kravchuk 5 Nov 9, 2022
Flutter plugin to implement a Material Design Speed Dial

Flutter Speed Dial Flutter package to render a Material Design Speed Dial. Usage The SpeedDial widget is built to be placed in the Scaffold.floatingAc

Dario Ielardi 349 Jan 2, 2023
A Simple but elegant Calculator app made with Flutter using Google's Material Design with Currency (Exchange Rate) and Unit Converter.

Calculator A Simple but elegant Calculator app made with Flutter using Google's Material Design with Currency (Exchange Rate) and Unit Converter. Down

Hemant Rajput 15 Dec 7, 2022
Flutter package to render a Material Design Speed Dial.

Flutter Speed Dial Flutter package to render a Material Design Speed Dial. Usage The SpeedDial widget is built to be placed in the Scaffold.floatingAc

null 0 May 20, 2022
A degital diary with key feature of saving your thoughts with image

localstore A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if th

null 1 Nov 13, 2021
Full Feature Todos Flutter Mobile app with fireStore integration.

IONICFIREBASEAPP DOWNLOAD TODO APP Marketplace for Mobile app and Backend that developed on leading Enterprise Technologies with as well as with your

Ionicfirebaseapp 138 Nov 4, 2022
A Dart package which simulates pass by reference feature

Reference Wrapper is a Dart package which simulates pass by reference feature us

null 0 Dec 19, 2021
Rules - Powerful and feature-rich validation library for both Dart and Flutter.

Introduction Rules - Powerful and feature-rich validation library for both Dart and Flutter. Rules is a simple yet powerful and feature-rich validatio

Ganesh Rathinavel 24 Dec 12, 2022
A simple way to share Instagram stories as an embedded feature within the Flutter application

Loopi Share A simple way to share Instagram stories as an embedded feature within the Flutter application. Introduction Words on both platforms Androi

Loopi 5 Sep 24, 2022
A feature-rich cross-platform webview using webview_flutter for mobile and iframe for web. JS interop-ready.

A feature-rich cross-platform webview using webview_flutter for mobile and iframe for web. JS interop-ready. Getting started Gallery Basic usage Featu

null 2 Mar 17, 2022