flutter_easy_dialogs 2.0.1-alpha copy "flutter_easy_dialogs: ^2.0.1-alpha" to clipboard
flutter_easy_dialogs: ^2.0.1-alpha copied to clipboard

Easy, lightweight and flexible service for showing dialogs inside your Flutter application.

FlutterEasyDialogs #

Introduction #

What is FlutterEasyDialogs ?

It is an open-source package for the Flutter framework that provides easy-to-use and customizable dialogs based on Overlay for use in your mobile app. With this library, you can quickly create and display dialogs such as alerts, confirmation dialogs, info dialogs, and more. The library is designed to be lightweight, efficient, and easy to use, making it ideal for developers who want to add dialog functionality to their apps without having to write complex code.

With FlutterEasyDialogs you can quickly create and customize dialogs. The package provides a flexible API that allows to customize various aspects of the dialogs, including animations, appearance, positions and providing completely handmade realizations of dialog manipulation.

Overall, if you're looking for an easy-to-use and customizable dialog package for your Flutter app, you should take a try FlutterEasyDialogs.

Installation #

In the pubspec.yaml of your flutter project, add the following dependency:

dependencies:
  flutter_easy_dialogs: <latest_version>

In your library add the following import:

import 'package:flutter_easy_dialogs/flutter_easy_dialogs.dart'

Setup and usage #

Wrap your MaterialApp with FlutterEasyDialogs.builder().

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      builder: FlutterEasyDialogs.builder(),
    );
  }
}

You're done. Now you are able to call show methods from IEasyDialogController like so:

 FlutterEasyDialogs.controller.showPositioned(
      params: const PositionedShowParams(
        content: Text('dialog'),
      ),
    );

Or

    FlutterEasyDialogs.controller.showFullScreen(
        params: const FullScreenShowParams(
      content: Text('dialog'),
    ));

Basics #

Manager

What is Manager, exactly? They are objects that are responsible for showing, hiding, and taking care of all related dialogs (including positioning, playing animations, inserting in overlay etc.)

We have a class that serves as the base for all Managers that have exactly two responsibilities: to show and to hide

abstract class EasyDialogManager<S extends EasyDialogManagerShowParams?,
    H extends EasyDialogManagerHideParams?> {
  final IEasyOverlayController overlayController;
 
  const EasyDialogManager({
    required this.overlayController,
  });

  Future<void> show({
    required S params,
  });

  Future<void> hide({
    required H params,
  });
}

You may wonder what IEasyOverlayController is and why it's there. We'll talk about it a bit later.

Parameters

The generic data which the Manager operates on are the Show/Hide parameters. Each Manager is able to extend the base parameters or not define them at all:

abstract class EasyDialogManagerShowParams { 
  final Widget content;
 
  final EasyAnimationConfiguration animationConfiguration;
 
  const EasyDialogManagerShowParams({
    required this.content,
    this.animationConfiguration = const EasyAnimationConfiguration(),
  });
}
 
abstract class EasyDialogManagerHideParams { 
  const EasyDialogManagerHideParams();
}

The mandatory things are Widget-content and EasyAnimationConfiguration-animationConfiguration. The purpose of the first one is pretty obvious, while the last one is responsible for providing options that the Manager may use to configure the AnimationController.

Strategies

The core thing that allows you to inject your beautiful dialogs into the Overlay is encapsulated within the Insert/Remove Strategy, which will be covered a bit later in the Overlay section. Now you are just need to know that they exists and they are similar to the Strategy/Command pattern.

Overlay

Controller

Here comes IEasyOverlayController which suddenly has pretty similar responsibilities to that of the Manager, such as inserting and removing dialogs into the Overlay using two methods.

abstract class IEasyOverlayController implements TickerProvider {
  void insertDialog(EasyOverlayBoxInsert strategy);

  void removeDialog(EasyOverlayBoxRemove strategy);
}

You may notice the strategy named argument, and you would be correct in thinking that it refers to the strategies mentioned earlier.

Box

It is the strict variation of Map with generic types which is using only for storing Overlay entries and associated Managers:

abstract class IEasyOverlayBox {
  void put(Object key, Object value);

  T? remove<T>(Object key);

  T? get<T>(Object key);

  T putIfAbsent<T>(Object key, T Function() ifAbsent);
}

Note:
Each Manager can provide its own complex structure for state as complex as it needs to be. For example, the PositionedManager stores its data in the format of another Map, or the CustomManager uses a List of integers

Box mutation

So that's it - the strategy for inserting/removing Manager data into IEasyOverlayBox. The basis of this is called BoxMutation, which has the single responsibility of mutating the storage state of this Box:

abstract class EasyOverlayBoxMutation<M extends EasyDialogManager,
    R extends EasyOverlayEntry?> {
  const EasyOverlayBoxMutation();

  Type get key => M;

  R apply(IEasyOverlayBox box);
}

The result of applying of the mutation strategy must be an any derived class of EasyOverlayEntry (specific class derived from OverlayEntry) which can later could be used within IEasyOverlayController to insert that entry into EasyOverlay. For the sake of simplicity, there are two classes.

One is for inserting:

abstract class EasyOverlayBoxInsert<M extends EasyDialogManager>
    extends EasyOverlayBoxMutation<M, EasyOverlayEntry> {
  final Widget dialog;

  const EasyOverlayBoxInsert({
    required this.dialog,
  });
}

Which provides the dialog to be inserted into EasyOverlay.

And the another is for removing, which result could be nullable as it could be no such dialog entry existing withing the IEasyOverlayBox

abstract class EasyOverlayBoxRemove<M extends EasyDialogManager>
    extends EasyOverlayBoxMutation<M, EasyOverlayEntry?> {
  const EasyOverlayBoxRemove();
}
Easy overlay

This is the core widget that provides all possibilities of dialogs to appear at any time and in any place within the wrapped application. Its state is responsible for storing IEasyOverlayBox and implementing IEasyOverlayController. There isn't much to know, to be honest.

Animators

This is the way to animate the dialogs:

abstract class IEasyAnimator { 
  Widget animate({
    required Animation<double> parent,
    required Widget child,
  });
}

It is used within the Manager logic of showing the dialogs.

The stumbling block is parent argument. Simply put, it is AnimationController that was created by a specific Manager and provided to Animator, so it could apply some transitions or any other change-based value to the child.

The implementing class is EasyAnimator, which is simply another type of abstraction that can be extended and has the optional property of a Curve :

abstract class EasyAnimator implements IEasyAnimator {
  final Curve? curve;

  const EasyAnimator({
    this.curve,
  });
}

Note: There are several Manager-specific Animators such as EasyPositionedAnimator or EasyFullScreenBackgroundAnimator. The point is that the usage of animators could be simplified and scoped to a single Manager.

Dismissible

Generally, it is the way to dismiss dialogs with provided VoidCallback. It's responsibility is to provide specific dismissible behavior to the dialog:

abstract class IEasyDismissible {
  Widget makeDismissible(Widget dialog);
}
typedef OnEasyDismiss = void Function();
 
abstract class EasyDismissible implements IEasyDismissible { 
  final OnEasyDismiss? onDismiss;

  const EasyDismissible({
    this.onDismiss,
  });
}

Like the Animator, the Dismissible is provided to the Manager, and the Manager is responsible for hooking up all of these components into the dialog.

Shells

This is the wrapper Widget that provides some sort of shape to the content of the dialog. It is not mandatory, but it could be very handy to apply some additional components around the provided content.

Scoping

Some components such as Shells or Dismissible depend on Scoping- specific data that is providing by some Manager. This is just a derived class from InheritedWidget:

class _EasyScope<T extends EasyDialogScopeData> extends InheritedWidget {
  final T data;

  const _EasyScope({
    required super.child,
    required this.data,
    super.key,
  });

  @override
  bool updateShouldNotify(_EasyScope oldWidget) => oldWidget.data != data;
}
abstract class EasyDialogScopeData {
  const EasyDialogScopeData();
}

Here is some handy extension-function on BuildContext:

  D readDialog<D extends EasyDialogScopeData>() =>
      EasyDialogScope.of<D>(this, listen: false);

Easy dialog controller

The highest level API of this package is IEasyDialogController. This object provides access to methods that allow the showing and hiding of dialogs of any specific Manager.

abstract class IEasyDialogsController { 
  Future<void> showPositioned({
    required PositionedShowParams params,
  });
 
  Future<void> hidePositioned({
    required EasyDialogPosition position,
  });
 
  Future<void> hideAllPositioned();

  Future<void> showFullScreen({
    required FullScreenShowParams params,
  });
 
  Future<void> hideFullScreen();

  T useCustom<T extends EasyDialogManager>();
}

The last method will be covered in more detail later. Generally, it provides access to your CustomManagers as a strongly-typed object.

Positioned #

Positioned Examples

Positioned customization

You can observe the example in: example/lib/screens/positioned_dialog_customization_screen.dart

Slide:

Slide

Fade:

Fade

Expansion:

Expansion

Full screen #

Full screen examples

Custom #

Custom manager setup

If you want to use your own handmade Manager, you can define a class that extends CustomManager and optionally specify your hide/show parameters. Pay attention to the usage of CustomInsert/Remove Strategy. The insertion strategy callback passes an ID of the inserted dialog entry within EasyOverlay, which later can be used to remove that dialog. Lastly, it is necessary to provide a List of CustomManagers that you want to be able to use within the IEasyDialogsController.useCustom() method:


int? _customDialogId;

class MyDialogManager extends CustomManager<CustomManagerShowParams,
    EasyDialogManagerHideParams?> {
  MyDialogManager({required super.overlayController});

  @override
  Future<void> hide({EasyDialogManagerHideParams? params}) async {
    super.overlayController.removeDialog(
          CustomDialogRemoveStrategy(
            dialogId: _customDialogId!,
          ),
        );
  }

  @override
  Future<void> show({required CustomManagerShowParams params}) async {
    if (_customDialogId != null) {
      super.overlayController.removeDialog(
            CustomDialogRemoveStrategy(
              dialogId: _customDialogId!,
            ),
          );
    }
    super.overlayController.insertDialog(
          CustomDialogInsertStrategy(
            dialog: Align(
              alignment: Alignment.bottomCenter,
              child: Container(
                height: 200.0,
                width: 200.0,
                color: params.color,
                child: Center(child: params.content),
              ),
            ),
            onInserted: (dialogId) => _customDialogId = dialogId,
          ),
        );
  }
}

class CustomManagerShowParams extends EasyDialogManagerShowParams {
  final Color color;

  const CustomManagerShowParams({
    required super.content,
    required this.color,
  });
}
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      builder: FlutterEasyDialogs.builder(
        customManagerBuilder: (overlayController) =>
            [MyDialogManager(overlayController: overlayController)],
      ),
    );
  }
}

Custom manager usage

Simply call with your Manager type provided as a generic:

    FlutterEasyDialogs.controller.useCustom<MyDialogManager>().show(
          params: CustomManagerShowParams(
            content: const Text('Custom'),
            color: Color((math.Random().nextDouble() * 0xFFFFFF).toInt())
                .withOpacity(1.0),
          ),
        );

Custom manager example

Special thanks #

Special thanks to back_button_interceptor

59
likes
0
points
1.67k
downloads

Publisher

verified publisherfeduke-nukem.dev

Weekly Downloads

Easy, lightweight and flexible service for showing dialogs inside your Flutter application.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

back_button_interceptor, flutter

More

Packages that depend on flutter_easy_dialogs